diff --git a/.vscode/settings.json b/.vscode/settings.json index 08147063..0e519683 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,4 +3,5 @@ "stddef.h": "c" }, "rust-analyzer.checkOnSave.allTargets": false, + "rust-analyzer.cargo.target": "kernel/targets/riscv64-virt-ableos.json", } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index cc2676df..48f20a26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -328,6 +328,7 @@ dependencies = [ "crossbeam-queue", "limine", "log", + "sbi", "slab", "spin", "uart_16550", @@ -510,6 +511,12 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" +[[package]] +name = "sbi" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cb0870400aca7e4487e8ec1e93f9d4288da763cb1da2cedc5102e62b6522ad" + [[package]] name = "scopeguard" version = "1.1.0" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 37fd3344..635ab8dc 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -8,6 +8,7 @@ slab = { version = "0.4", default-features = false } spin = "0.9" versioning = { git = "https://git.ablecorp.us/able/aos_userland" } log = "0.4" +uart_16550 = "0.2" [dependencies.crossbeam-queue] version = "0.3" @@ -16,6 +17,8 @@ features = ["alloc"] [target.'cfg(target_arch = "x86_64")'.dependencies] limine = { version = "0.1", git = "https://github.com/limine-bootloader/limine-rs" } -uart_16550 = "0.2" x86_64 = "0.14" x2apic = "0.4" + +[target.'cfg(target_arch = "riscv64")'.dependencies] +sbi = "0.2.0" diff --git a/kernel/lds/riscv64-virt.ld b/kernel/lds/riscv64-virt.ld new file mode 100644 index 00000000..afeefa87 --- /dev/null +++ b/kernel/lds/riscv64-virt.ld @@ -0,0 +1,67 @@ +OUTPUT_ARCH(riscv) +ENTRY(_start) +START_ADDRESS = 0x80200000; + +SECTIONS { + . = START_ADDRESS; + + .text : { + PROVIDE(_text_start = .); + *(.text.entry) + + . = ALIGN(4K); + + *(.text .text.*) + PROVIDE(_text_end = .); + } + + . = ALIGN(4K); + + .rodata : { + PROVIDE(_rodata_start = .); + *(.rodata .rodata.*) + PROVIDE(_rodata_end = .); + } + + . = ALIGN(4K); + + .data : { + PROVIDE(_data_start = .); + *(.data .data.*) + PROVIDE(_data_end = .); + } + + . = ALIGN(4K); + + .sdata : { + PROVIDE(_sdata_start = .); + *(.sdata) + *(.sdata.*) + *(.srodata.*) + *(.gnu.linkonce.s.*) + PROVIDE(_sdata_end = .); + } + + . = ALIGN(4K); + + .bss : { + PROVIDE(_bss_start = .); + *(.sbss*) + *(.bss.stack) + *(.bss .bss.*) + PROVIDE(_initial_kernel_heap_start = .); + PROVIDE(_initial_kernel_heap_size = 1024 * 1024); + . += _initial_kernel_heap_size; + PROVIDE(_bss_end = .); + } + + /* FIXME: Currently this has to be aligned to PAGE_SIZE << MAX_ORDER */ + . = ALIGN(4M); + PROVIDE(_usable_memory_start = .); + PROVIDE(_usable_memory_size = 0x88000000 - .); + + /DISCARD/ : { + *(.comment) + *(.eh_frame) + } +} diff --git a/kernel/src/allocator.rs b/kernel/src/allocator.rs index 7f06e746..fd45b1d3 100644 --- a/kernel/src/allocator.rs +++ b/kernel/src/allocator.rs @@ -35,14 +35,6 @@ use core::{ use spin::Mutex; -extern "C" { - fn _initial_kernel_heap_start(); - fn _initial_kernel_heap_size(); -} - -const INITIAL_KERNEL_HEAP_START: *mut u8 = _initial_kernel_heap_start as _; -const INITIAL_KERNEL_HEAP_SIZE: *const () = _initial_kernel_heap_size as _; - struct Allocator(Mutex>); unsafe impl GlobalAlloc for Allocator { @@ -66,10 +58,10 @@ unsafe impl GlobalAlloc for Allocator { #[global_allocator] static ALLOCATOR: Allocator = Allocator(Mutex::new(None)); -pub fn init() { +pub fn init(memory: *mut u8, memory_size: usize) { log::info!("Initialising kernel heap allocator"); *ALLOCATOR.0.lock() = - Some(unsafe { Heap::new(INITIAL_KERNEL_HEAP_START, INITIAL_KERNEL_HEAP_SIZE as _) }); + Some(unsafe { Heap::new(memory, memory_size) }); } // FIXME: these are arch-specific diff --git a/kernel/src/arch/riscv64/entry.s b/kernel/src/arch/riscv64/entry.s new file mode 100644 index 00000000..822036cd --- /dev/null +++ b/kernel/src/arch/riscv64/entry.s @@ -0,0 +1,31 @@ + .section .text.entry + .global _start +_start: + # load stack_top to sp register + la sp, stack_top + + # The BSS section is expected to be zero + la a0, _bss_start + la a1, _bss_end + bgeu a0, a1, 2f +1: + sd zero, (a0) + addi a0, a0, 8 + bltu a0, a1, 1b +2: + call _kernel_start + + .section .bss.stack + .global stack +stack: + # alloc stack memory + .space 4096 * 16 + .global stack_top +stack_top: + .section .bss.heap + .global _initial_kernel_heap_start +_initial_kernel_heap_start: + # alloc initial kmalloc memory + .space 4096 * 64 + .global _initial_kernel_heap_end +_initial_kernel_heap_end: diff --git a/kernel/src/arch/riscv64/memory_regions.s b/kernel/src/arch/riscv64/memory_regions.s new file mode 100644 index 00000000..c4cb76f7 --- /dev/null +++ b/kernel/src/arch/riscv64/memory_regions.s @@ -0,0 +1,35 @@ + .section .rodata + .global TEXT_START +TEXT_START: .quad _text_start + .global TEXT_END +TEXT_END: .quad _text_end + + .global RODATA_START +RODATA_START: .quad _rodata_start + .global RODATA_END +RODATA_END: .quad _rodata_end + + .global DATA_START +DATA_START: .quad _data_start + .global DATA_END +DATA_END: .quad _data_end + + .global SDATA_START +SDATA_START: .quad _sdata_start + .global SDATA_END +SDATA_END: .quad _sdata_end + + .global BSS_START +BSS_START: .quad _bss_start + .global BSS_END +BSS_END: .quad _bss_end + + .global INITIAL_KERNEL_HEAP_START +INITIAL_KERNEL_HEAP_START: .quad _initial_kernel_heap_start + .global INITIAL_KERNEL_HEAP_SIZE +INITIAL_KERNEL_HEAP_SIZE: .quad _initial_kernel_heap_size + + .global USABLE_MEMORY_START +USABLE_MEMORY_START: .quad _usable_memory_start + .global USABLE_MEMORY_SIZE +USABLE_MEMORY_SIZE: .quad _usable_memory_size diff --git a/kernel/src/arch/riscv64/mod.rs b/kernel/src/arch/riscv64/mod.rs index cfeed096..e1c5ec9c 100644 --- a/kernel/src/arch/riscv64/mod.rs +++ b/kernel/src/arch/riscv64/mod.rs @@ -1 +1,92 @@ -//! \ No newline at end of file +use core::{arch::{asm, global_asm}, fmt::Write}; +use alloc::vec; +use sbi::system_reset::{ResetType, ResetReason, system_reset}; +use spin::{Mutex, Once}; +use uart_16550::MmioSerialPort; + +use crate::{allocator, memory::MemoryManager}; + +global_asm!(include_str!("entry.s")); +global_asm!(include_str!("memory_regions.s")); + +pub const PAGE_SIZE: usize = 4096; + +extern { + static TEXT_START: usize; + static TEXT_END: usize; + + static RODATA_START: usize; + static RODATA_END: usize; + + static DATA_START: usize; + static DATA_END: usize; + + static SDATA_START: usize; + static SDATA_END: usize; + + static BSS_START: usize; + static BSS_END: usize; + + static INITIAL_KERNEL_HEAP_START: *mut u8; + static INITIAL_KERNEL_HEAP_SIZE: usize; + + static USABLE_MEMORY_START: usize; + static USABLE_MEMORY_SIZE: usize; +} + +static SERIAL_CONSOLE: Once> = Once::new(); + +mod memory { + use spin::{Mutex, Once}; + use crate::memory::MemoryManager; + + pub static MEMORY_MANAGER: Once> = Once::new(); +} + +#[no_mangle] +extern fn _kernel_start() -> ! { + SERIAL_CONSOLE.call_once(|| Mutex::new(unsafe { MmioSerialPort::new(0x1000_0000) })); + crate::logger::init().expect("failed to set logger"); + log::info!("Initialising AKern {}", crate::VERSION); + + unsafe { + allocator::init(INITIAL_KERNEL_HEAP_START, INITIAL_KERNEL_HEAP_SIZE); + } + + let mut memory_manager = MemoryManager::new(); + unsafe { + log::debug!("USABLE_MEMORY_START = 0x{USABLE_MEMORY_START:x}"); + memory_manager.add_range(USABLE_MEMORY_START, USABLE_MEMORY_SIZE / PAGE_SIZE); + } + memory::MEMORY_MANAGER.call_once(|| Mutex::new(memory_manager)); + + unsafe { + let mut mm = memory::MEMORY_MANAGER.get().unwrap().lock(); + let alloc_0 = mm.allocate_pages(0).unwrap(); + log::debug!("Addr: {:p}", alloc_0); + let alloc_1 = mm.allocate_pages(0).unwrap(); + log::debug!("Addr: {:p}", alloc_1); + mm.deallocate_pages(alloc_0, 0); + let alloc_2 = mm.allocate_pages(1).unwrap(); + log::debug!("Addr: {:p}", alloc_2); + mm.deallocate_pages(alloc_1, 0); + mm.deallocate_pages(alloc_2, 1); + let alloc_3 = mm.allocate_pages(1).unwrap(); + log::debug!("Addr: {:p}", alloc_3); + mm.deallocate_pages(alloc_3, 1); + } + + system_reset(ResetType::Shutdown, ResetReason::NoReason).unwrap(); + loop {} +} + +/// Spin loop +pub fn sloop() -> ! { + loop { + unsafe { asm!("wfi") } + } +} + +pub fn log(args: core::fmt::Arguments<'_>) -> core::fmt::Result { + SERIAL_CONSOLE.get().unwrap().lock().write_fmt(args) +} diff --git a/kernel/src/arch/x86_64/memory.rs b/kernel/src/arch/x86_64/memory.rs index e355be35..45536efa 100644 --- a/kernel/src/arch/x86_64/memory.rs +++ b/kernel/src/arch/x86_64/memory.rs @@ -2,7 +2,7 @@ use core::sync::atomic::AtomicU64; use limine::{LimineMemmapEntry, LimineMemoryMapEntryType, NonNullPtr}; use spin::{Mutex, Once}; use x86_64::{structures::paging::OffsetPageTable, VirtAddr}; -use crate::memory::MemoryManager; +use crate::memory::{MemoryManager, MAX_ORDER}; pub const PAGE_SIZE: usize = 4096; @@ -36,8 +36,17 @@ pub fn initialize(mmap: &'static [NonNullPtr]) { continue; } + let alignment = PAGE_SIZE << MAX_ORDER; + let start_addr_unaligned = entry.base as usize; + let diff = alignment - start_addr_unaligned % alignment; + if diff > entry.len as usize { + continue; + } + let start_addr = start_addr_unaligned + diff; + let page_count = (entry.len as usize - diff) / PAGE_SIZE; + unsafe { - memory_manager.add_range(entry.base as usize, entry.len as usize / PAGE_SIZE); + memory_manager.add_range(start_addr, page_count); } } diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index ac881f94..f6a02d2b 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -6,12 +6,21 @@ mod logging; pub use logging::log; pub use memory::PAGE_SIZE; -use crate::{allocator, arch::memory::MEMORY_MANAGER}; +use crate::allocator; +use memory::MEMORY_MANAGER; use limine::{ LimineHhdmRequest, LimineKernelFileRequest, LimineMemmapRequest, LimineModuleRequest, }; use x86_64::VirtAddr; +extern "C" { + fn _initial_kernel_heap_start(); + fn _initial_kernel_heap_size(); +} + +const INITIAL_KERNEL_HEAP_START: *mut u8 = _initial_kernel_heap_start as _; +const INITIAL_KERNEL_HEAP_SIZE: *const () = _initial_kernel_heap_size as _; + #[no_mangle] unsafe extern "C" fn _kernel_start() -> ! { logging::init(); @@ -27,7 +36,7 @@ unsafe extern "C" fn _kernel_start() -> ! { .offset, )); - allocator::init(); + allocator::init(INITIAL_KERNEL_HEAP_START, INITIAL_KERNEL_HEAP_SIZE as _); static MMAP_REQ: LimineMemmapRequest = LimineMemmapRequest::new(0); memory::initialize( diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index f02f744b..9fa076b7 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -32,9 +32,9 @@ pub const VERSION: Version = Version { #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { // TODO: Better panic handler - let _ = crate::arch::log(format_args!( - "\r\n\x1b[1m\x1b[4m\x1b[38;5;125mKernel Panic\x1b[0m\r\n", - )); + // let _ = crate::arch::log(format_args!( + // "\r\n\x1b[1m\x1b[4m\x1b[38;5;125mKernel Panic\x1b[0m\r\n", + // )); if let Some(loc) = info.location() { let _ = crate::arch::log(format_args!( diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 544a17b0..f5903f7f 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -1,18 +1,18 @@ //! The Memory Manager -use alloc::vec::Vec; +use alloc::collections::VecDeque; pub use crate::arch::PAGE_SIZE; pub const MAX_ORDER: usize = 10; pub struct MemoryManager { - free_lists: [Vec; MAX_ORDER + 1], + free_lists: [VecDeque; MAX_ORDER + 1], } impl MemoryManager { pub const fn new() -> Self { Self { - free_lists: [const { Vec::new() }; MAX_ORDER + 1], + free_lists: [const { VecDeque::new() }; MAX_ORDER + 1], } } @@ -23,18 +23,18 @@ impl MemoryManager { /// # Safety /// This method assumes that `address` is in range of this allocator pub unsafe fn deallocate_pages(&mut self, address: *mut u8, order: usize) { - self.free_lists[order].push(address as usize); + self.free_lists[order].push_front(address as usize); self.merge_buddies(order, address as usize) } /// # Safety /// This method assumes that the given address range, - /// a) starts at an address aligned to page boundaries, + /// a) starts and ends at an address aligned to page boundaries, /// b) are valid free pages not already added, - /// FIXME: c) has a multiple of `1 << MAX_ORDER` number of pages + /// FIXME: c) starts and ends at an address aligned to `PAGE_SIZE << MAX_ORDER` pub unsafe fn add_range(&mut self, start_addr: usize, page_count: usize) { for i in 0..page_count / 1024 { - self.free_lists[MAX_ORDER].push(start_addr + i * 1024 * PAGE_SIZE); + self.free_lists[MAX_ORDER].push_back(start_addr + i * 1024 * PAGE_SIZE); } } @@ -45,11 +45,11 @@ impl MemoryManager { } if self.free_lists[order].len() > 0 { - return self.free_lists[order].pop(); + return self.free_lists[order].pop_front(); } self.get_free_pages(order + 1).map(|addr| { - self.free_lists[order].push(addr ^ (PAGE_SIZE << order)); + self.free_lists[order].push_front(addr ^ (PAGE_SIZE << order)); addr }) } @@ -66,11 +66,11 @@ impl MemoryManager { .iter() .position(|blk| *blk == buddy_address) { - self.free_lists[order].pop(); + self.free_lists[order].pop_front(); self.free_lists[order].remove(buddy_index); let new_address = address.min(buddy_address); log::debug!("Merging 0x{address:x} @ {order} with 0x{buddy_address:x} at 0x{new_address:x}"); - self.free_lists[order + 1].push(new_address); + self.free_lists[order + 1].push_front(new_address); self.merge_buddies(order + 1, new_address) } } diff --git a/kernel/targets/riscv64-virt-ableos.json b/kernel/targets/riscv64-virt-ableos.json new file mode 100644 index 00000000..deeca60e --- /dev/null +++ b/kernel/targets/riscv64-virt-ableos.json @@ -0,0 +1,22 @@ +{ + "arch": "riscv64", + "code-model": "medium", + "cpu": "generic-rv64", + "data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128", + "eh-frame-header": false, + "emit-debug-gdb-scripts": false, + "features": "+m,+a,+f,+d,+c", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "llvm-abiname": "lp64d", + "llvm-target": "riscv64", + "max-atomic-width": 64, + "panic-strategy": "abort", + "relocation-model": "static", + "target-pointer-width": "64", + "pre-link-args": { + "ld.lld": [ + "--script=kernel/lds/riscv64-virt.ld" + ] + } +} diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index b2cbeb31..327f393b 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -8,19 +8,34 @@ fn main() -> Result<(), Error> { args.next(); match args.next().as_deref() { - Some("build" | "b") => build( - args.next() - .map(|x| x == "-r" || x == "--release") - .unwrap_or_default(), - ) - .change_context(Error::Build), + Some("build" | "b") => { + let mut release = false; + let mut target = Target::X86_64; + for arg in args { + if arg == "-r" || arg == "--release" { + release = true; + } + if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" { + target = Target::Riscv64Virt; + } + } + + build(release, target).change_context(Error::Build) + }, Some("run" | "r") => { - build( - args.next() - .map(|x| x == "-r" || x == "--release") - .unwrap_or_default(), - )?; - run() + let mut release = false; + let mut target = Target::X86_64; + for arg in args { + if arg == "-r" || arg == "--release" { + release = true; + } + if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" { + target = Target::Riscv64Virt; + } + } + + build(release, target)?; + run(release, target) } Some("help" | "h") => { println!(concat!( @@ -30,7 +45,8 @@ fn main() -> Result<(), Error> { " help (h): Print this message\n", " run (r): Build and run AbleOS in QEMU\n\n", "Options for build and run:\n", - " -r: build in release mode", + " -r: build in release mode", + " [target]: sets target" ),); Ok(()) } @@ -87,7 +103,7 @@ fn get_fs() -> Result, io::Error> { Ok(fs) } -fn build(release: bool) -> Result<(), Error> { +fn build(release: bool, target: Target) -> Result<(), Error> { let fs = get_fs().change_context(Error::Io)?; let mut com = Command::new("cargo"); com.current_dir("kernel"); @@ -96,12 +112,21 @@ fn build(release: bool) -> Result<(), Error> { com.arg("-r"); } + match target { + Target::Riscv64Virt => { com.args(["--target", "targets/riscv64-virt-ableos.json"]); }, + _ => {} + } + match com.status() { Ok(s) if s.code() != Some(0) => bail!(Error::Build), Err(e) => bail!(report!(e).change_context(Error::Build)), _ => (), } + if target != Target::X86_64 { + return Ok(()); + } + (|| -> std::io::Result<_> { io::copy( &mut File::open( @@ -117,24 +142,44 @@ fn build(release: bool) -> Result<(), Error> { .change_context(Error::Io) } -fn run() -> Result<(), Error> { - let mut com = Command::new("qemu-system-x86_64"); +fn run(release: bool, target: Target) -> Result<(), Error> { + let mut com = match target { + Target::X86_64 => Command::new("qemu-system-x86_64"), + Target::Riscv64Virt => Command::new("qemu-system-riscv64") + }; - #[rustfmt::skip] - com.args([ - "-bios", - std::env::var("REPBUILD_QEMU_FIRMWARE_PATH") - .as_deref() - .unwrap_or("/usr/share/OVMF/OVMF_CODE.fd"), - "-drive", "file=target/disk.img,format=raw", - "-m", "4G", - "-serial", "stdio", - "-smp", "cores=2", - ]); + if target == Target::X86_64 { + #[rustfmt::skip] + com.args([ + "-bios", + std::env::var("REPBUILD_QEMU_FIRMWARE_PATH") + .as_deref() + .unwrap_or("/usr/share/OVMF/OVMF_CODE.fd"), + "-drive", "file=target/disk.img,format=raw", + "-m", "4G", + "-serial", "stdio", + "-smp", "cores=2", + ]); - #[cfg(target_os = "linux")] - { - com.args(["-enable-kvm", "-cpu", "host"]); + #[cfg(target_os = "linux")] + { + com.args(["-enable-kvm", "-cpu", "host"]); + } + } + + if target == Target::Riscv64Virt { + #[rustfmt::skip] + com.args([ + "-M", "virt", + "-m", "128M", + "-serial", "stdio", + "-kernel", + if release { + "target/riscv64-virt-ableos/release/kernel" + } else { + "target/riscv64-virt-ableos/debug/kernel" + } + ]); } match com @@ -147,6 +192,12 @@ fn run() -> Result<(), Error> { } } +#[derive(Clone, Copy, PartialEq, Eq)] +enum Target { + X86_64, + Riscv64Virt, +} + #[derive(Debug)] enum Error { Build,