diff --git a/Cargo.lock b/Cargo.lock index a598a34c..811e3cf8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,6 +293,7 @@ dependencies = [ "aarch64-cpu", "align-address", "allocator-api2", + "cfg-if", "exclusive_cell", "fdt", "goblin", diff --git a/Cargo.toml b/Cargo.toml index 39a6199e..a586c8e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] align-address = "0.1" +cfg-if = "1" hermit-entry = { version = "0.9", features = ["loader"] } log = "0.4" one-shot-mutex = "0.1" @@ -38,7 +39,7 @@ spinning_top = "0.3" [target.'cfg(target_os = "uefi")'.dependencies] uefi = { version = "0.27", features = ["alloc"] } -uefi-services = { version = "0.24", features = ["qemu"] } +uefi-services = { version = "0.24", default-features = false, features = ["panic_handler", "qemu"] } qemu-exit = "3" [target.'cfg(target_arch = "riscv64")'.dependencies] diff --git a/src/arch/aarch64/console.rs b/src/arch/aarch64/console.rs new file mode 100644 index 00000000..911f8a6c --- /dev/null +++ b/src/arch/aarch64/console.rs @@ -0,0 +1,60 @@ +use core::ptr::NonNull; + +use hermit_dtb::Dtb; + +pub struct Console { + stdout: NonNull, +} + +fn stdout() -> u32 { + /// Physical address of UART0 at Qemu's virt emulation + const SERIAL_PORT_ADDRESS: u32 = 0x09000000; + + let dtb = unsafe { + Dtb::from_raw(sptr::from_exposed_addr(super::DEVICE_TREE as usize)) + .expect(".dtb file has invalid header") + }; + + let property = dtb.get_property("/chosen", "stdout-path"); + let uart_address = if let Some(stdout) = property { + let stdout = core::str::from_utf8(stdout) + .unwrap() + .trim_matches(char::from(0)); + if let Some(pos) = stdout.find('@') { + let len = stdout.len(); + u32::from_str_radix(&stdout[pos + 1..len], 16).unwrap_or(SERIAL_PORT_ADDRESS) + } else { + SERIAL_PORT_ADDRESS + } + } else { + SERIAL_PORT_ADDRESS + }; + uart_address +} + +impl Console { + pub fn write_bytes(&mut self, bytes: &[u8]) { + for byte in bytes.iter().copied() { + unsafe { + self.stdout.as_ptr().write_volatile(byte); + } + } + } + + pub(super) fn get_stdout(&self) -> NonNull { + self.stdout + } + + pub(super) fn set_stdout(&mut self, stdout: NonNull) { + self.stdout = stdout; + } +} + +impl Default for Console { + fn default() -> Self { + let stdout = NonNull::new(stdout() as *mut u8).unwrap(); + Self { stdout } + } +} + +unsafe impl Send for Console {} diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index fa7a147b..9e3f8028 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -1,9 +1,10 @@ +mod console; +pub use self::console::Console; pub mod entry; pub mod paging; -pub mod serial; use core::arch::asm; -use core::ptr; +use core::ptr::{self, NonNull}; use align_address::Align; use goblin::elf::header::header64::{Header, EI_DATA, ELFDATA2LSB, ELFMAG, SELFMAG}; @@ -15,7 +16,7 @@ use log::info; use sptr::Strict; use crate::arch::paging::*; -use crate::arch::serial::SerialPort; +use crate::os::CONSOLE; extern "C" { static loader_end: u8; @@ -29,8 +30,6 @@ extern "C" { /// start address of the RAM at Qemu's virt emulation const RAM_START: u64 = 0x40000000; -/// Physical address of UART0 at Qemu's virt emulation -const SERIAL_PORT_ADDRESS: u32 = 0x09000000; /// Default stack size of the kernel const KERNEL_STACK_SIZE: usize = 32_768; /// Qemu assumes for ELF kernel that the DTB is located at @@ -45,41 +44,6 @@ const PT_MEM: u64 = 0x713; const PT_MEM_CD: u64 = 0x70F; const PT_SELF: u64 = 1 << 55; -// VARIABLES -static mut COM1: SerialPort = SerialPort::new(SERIAL_PORT_ADDRESS); - -pub fn message_output_init() { - let dtb = unsafe { - Dtb::from_raw(sptr::from_exposed_addr(DEVICE_TREE as usize)) - .expect(".dtb file has invalid header") - }; - - let property = dtb.get_property("/chosen", "stdout-path"); - let uart_address = if let Some(stdout) = property { - let stdout = core::str::from_utf8(stdout) - .unwrap() - .trim_matches(char::from(0)); - if let Some(pos) = stdout.find('@') { - let len = stdout.len(); - u32::from_str_radix(&stdout[pos + 1..len], 16).unwrap_or(SERIAL_PORT_ADDRESS) - } else { - SERIAL_PORT_ADDRESS - } - } else { - SERIAL_PORT_ADDRESS - }; - - unsafe { - COM1.set_port(uart_address); - } -} - -pub fn output_message_byte(byte: u8) { - unsafe { - COM1.write_byte(byte); - } -} - pub unsafe fn get_memory(_memory_size: u64) -> u64 { (unsafe { ptr::addr_of!(loader_end) }.addr() as u64).align_up(LargePageSize::SIZE as u64) } @@ -152,7 +116,7 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { .count(); info!("Detect {} CPU(s)", cpus); - let uart_address: u32 = unsafe { COM1.get_port() }; + let uart_address: u32 = CONSOLE.lock().get().get_stdout().as_ptr() as u32; info!("Detect UART at {:#x}", uart_address); let pgt_slice = unsafe { core::slice::from_raw_parts_mut(ptr::addr_of_mut!(l0_pgtable), 512) }; @@ -198,9 +162,10 @@ pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { *entry = RAM_START + (i * BasePageSize::SIZE) as u64 + PT_MEM; } - unsafe { - COM1.set_port(0x1000); - } + CONSOLE + .lock() + .get() + .set_stdout(NonNull::new(0x1000 as *mut u8).unwrap()); // Load TTBRx unsafe { diff --git a/src/arch/aarch64/serial.rs b/src/arch/aarch64/serial.rs index bdc51c0c..e69de29b 100644 --- a/src/arch/aarch64/serial.rs +++ b/src/arch/aarch64/serial.rs @@ -1,33 +0,0 @@ -pub struct SerialPort { - port_address: u32, -} - -impl SerialPort { - pub const fn new(port_address: u32) -> Self { - Self { port_address } - } - - pub unsafe fn set_port(&mut self, addr: u32) { - unsafe { - core::ptr::write_volatile(&mut self.port_address, addr); - } - } - - pub unsafe fn get_port(&self) -> u32 { - unsafe { core::ptr::read_volatile(&self.port_address) } - } - - pub fn write_byte(&self, byte: u8) { - unsafe { - let port = - sptr::from_exposed_addr_mut(core::ptr::read_volatile(&self.port_address) as usize); - - // LF newline characters need to be extended to CRLF over a real serial port. - if byte == b'\n' { - core::ptr::write_volatile(port, b'\r'); - } - - core::ptr::write_volatile(port, byte); - } - } -} diff --git a/src/arch/mod.rs b/src/arch/mod.rs index a92a2e73..7bf01322 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -1,15 +1,12 @@ -#[cfg(target_arch = "aarch64")] -pub use crate::arch::aarch64::*; -#[cfg(target_arch = "riscv64")] -pub use crate::arch::riscv64::*; -#[cfg(all(target_arch = "x86_64", target_os = "none"))] -pub use crate::arch::x86_64::*; - -#[cfg(target_arch = "aarch64")] -pub mod aarch64; - -#[cfg(target_arch = "riscv64")] -pub mod riscv64; - -#[cfg(all(target_arch = "x86_64", target_os = "none"))] -pub mod x86_64; +cfg_if::cfg_if! { + if #[cfg(target_arch = "aarch64")] { + mod aarch64; + pub use self::aarch64::*; + } else if #[cfg(target_arch = "riscv64")] { + mod riscv64; + pub use self::riscv64::*; + } else if #[cfg(all(target_arch = "x86_64"))] { + mod x86_64; + pub use self::x86_64::*; + } +} diff --git a/src/arch/riscv64/console.rs b/src/arch/riscv64/console.rs new file mode 100644 index 00000000..0726cd72 --- /dev/null +++ b/src/arch/riscv64/console.rs @@ -0,0 +1,11 @@ +use sbi_rt::Physical; +use sptr::Strict; + +#[derive(Default)] +pub struct Console(()); + +impl Console { + pub fn write_bytes(&mut self, bytes: &[u8]) { + sbi_rt::console_write(Physical::new(bytes.len(), bytes.as_ptr().addr(), 0)); + } +} diff --git a/src/arch/riscv64/mod.rs b/src/arch/riscv64/mod.rs index 4cd5f7e5..b42ff143 100644 --- a/src/arch/riscv64/mod.rs +++ b/src/arch/riscv64/mod.rs @@ -1,3 +1,5 @@ +mod console; +pub use self::console::Console; mod address_range; mod start; @@ -14,10 +16,6 @@ use hermit_entry::Entry; use log::info; use sptr::Strict; -pub fn message_output_init() {} - -pub use sbi_rt::console_write_byte as output_message_byte; - fn find_kernel_linux(chosen: &FdtNode<'_, '_>) -> Option<&'static [u8]> { let initrd_start = chosen.property("linux,initrd-start")?.as_usize()?; let initrd_start = sptr::from_exposed_addr_mut::(initrd_start); diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs index d0060d34..c93b951e 100644 --- a/src/arch/riscv64/start.rs +++ b/src/arch/riscv64/start.rs @@ -52,7 +52,7 @@ extern "C" fn start(hart_id: usize, fdt: *const u8) -> ! { HART_ID.store(hart_id, Ordering::Relaxed); FDT.store(fdt.cast_mut(), Ordering::Relaxed); - unsafe { crate::none::loader_main() } + unsafe { crate::os::loader_main() } } // Align to page size diff --git a/src/arch/x86_64/console.rs b/src/arch/x86_64/console.rs new file mode 100644 index 00000000..9642c225 --- /dev/null +++ b/src/arch/x86_64/console.rs @@ -0,0 +1,21 @@ +use uart_16550::SerialPort; + +pub struct Console { + serial_port: SerialPort, +} + +impl Console { + pub fn write_bytes(&mut self, bytes: &[u8]) { + for byte in bytes.iter().copied() { + self.serial_port.send(byte); + } + } +} + +impl Default for Console { + fn default() -> Self { + let mut serial_port = unsafe { SerialPort::new(0x3F8) }; + serial_port.init(); + Self { serial_port } + } +} diff --git a/src/arch/x86_64/fdt.rs b/src/arch/x86_64/fdt.rs deleted file mode 100644 index 54f58fe2..00000000 --- a/src/arch/x86_64/fdt.rs +++ /dev/null @@ -1,50 +0,0 @@ -use alloc::format; - -use multiboot::information::{MemoryType, Multiboot}; -use vm_fdt::{Error as FdtError, FdtWriter}; - -use super::{mb_info, Mem}; - -pub struct DeviceTree; - -impl DeviceTree { - #[cfg(all(target_os = "none", not(feature = "fc")))] - pub fn create() -> Result<&'static [u8], FdtError> { - let mut mem = Mem; - let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() }; - - let all_regions = multiboot - .memory_regions() - .expect("Could not find a memory map in the Multiboot information"); - let ram_regions = all_regions.filter(|m| m.memory_type() == MemoryType::Available); - - let mut fdt = FdtWriter::new()?; - - let root_node = fdt.begin_node("")?; - fdt.property_string("compatible", "linux,dummy-virt")?; - fdt.property_u32("#address-cells", 0x2)?; - fdt.property_u32("#size-cells", 0x2)?; - - if let Some(cmdline) = multiboot.command_line() { - let chosen_node = fdt.begin_node("chosen")?; - fdt.property_string("bootargs", cmdline)?; - fdt.end_node(chosen_node)?; - } - - for m in ram_regions { - let start_address = m.base_address(); - let length = m.length(); - - let memory_node = fdt.begin_node(format!("memory@{:x}", start_address).as_str())?; - fdt.property_string("device_type", "memory")?; - fdt.property_array_u64("reg", &[start_address, length])?; - fdt.end_node(memory_node)?; - } - - fdt.end_node(root_node)?; - - let fdt = fdt.finish()?; - - Ok(fdt.leak()) - } -} diff --git a/src/arch/x86_64/firecracker.rs b/src/arch/x86_64/firecracker.rs new file mode 100644 index 00000000..6dc4de3e --- /dev/null +++ b/src/arch/x86_64/firecracker.rs @@ -0,0 +1,274 @@ +use core::arch::asm; +use core::ptr::write_bytes; +use core::{ptr, slice}; + +use align_address::Align; +use hermit_entry::boot_info::{BootInfo, HardwareInfo, PlatformInfo, RawBootInfo, SerialPortBase}; +use hermit_entry::elf::LoadedKernel; +use hermit_entry::fc::{ + BOOT_FLAG_OFFSET, CMD_LINE_PTR_OFFSET, CMD_LINE_SIZE_OFFSET, E820_ENTRIES_OFFSET, + E820_TABLE_OFFSET, HDR_MAGIC_OFFSET, LINUX_KERNEL_BOOT_FLAG_MAGIC, LINUX_KERNEL_HRD_MAGIC, + LINUX_SETUP_HEADER_OFFSET, RAMDISK_IMAGE_OFFSET, RAMDISK_SIZE_OFFSET, +}; +use hermit_entry::Entry; +use log::info; +use sptr::Strict; +use x86_64::structures::paging::{PageSize, PageTableFlags, Size2MiB, Size4KiB}; + +use super::physicalmem::PhysAlloc; +use super::{paging, KERNEL_STACK_SIZE, SERIAL_IO_PORT}; + +extern "C" { + static loader_end: u8; + static boot_params: usize; +} + +mod entry { + core::arch::global_asm!(include_str!("entry_fc.s")); +} + +pub fn find_kernel() -> &'static [u8] { + use core::cmp; + + paging::clean_up(); + + // Identity-map the Multiboot information. + unsafe { + assert!(boot_params > 0, "Could not find boot_params"); + info!("Found boot_params at 0x{:x}", boot_params); + } + let page_address = unsafe { boot_params }.align_down(Size4KiB::SIZE as usize); + paging::map::(page_address, page_address, 1, PageTableFlags::empty()); + + let linux_kernel_boot_flag_magic: u16 = unsafe { + *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + BOOT_FLAG_OFFSET)) + }; + let linux_kernel_header_magic = unsafe { + sptr::from_exposed_addr::(boot_params + LINUX_SETUP_HEADER_OFFSET + HDR_MAGIC_OFFSET) + .read_unaligned() + }; + if linux_kernel_boot_flag_magic == LINUX_KERNEL_BOOT_FLAG_MAGIC + && linux_kernel_header_magic == LINUX_KERNEL_HRD_MAGIC + { + info!("Found Linux kernel boot flag and header magic! Probably booting in firecracker."); + } else { + info!("Kernel boot flag and hdr magic have values 0x{:x} and 0x{:x} which does not align with the normal linux kernel values", + linux_kernel_boot_flag_magic, + linux_kernel_header_magic + ); + } + + // Load the boot_param memory-map information + let linux_e820_entries: u8 = + unsafe { *(sptr::from_exposed_addr(boot_params + E820_ENTRIES_OFFSET)) }; + info!("Number of e820-entries: {}", linux_e820_entries); + + let e820_entries_address = unsafe { boot_params } + E820_TABLE_OFFSET; + info!("e820-entry-table at 0x{:x}", e820_entries_address); + let page_address = e820_entries_address.align_down(Size4KiB::SIZE as usize); + + if !(unsafe { boot_params } >= page_address + && unsafe { boot_params } < page_address + Size4KiB::SIZE as usize) + { + paging::map::(page_address, page_address, 1, PageTableFlags::empty()); + } + + // Load the Hermit-ELF from the initrd supplied by Firecracker + let ramdisk_address: u32 = unsafe { + *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + RAMDISK_IMAGE_OFFSET)) + }; + let ramdisk_size: u32 = unsafe { + *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + RAMDISK_SIZE_OFFSET)) + }; + + info!( + "Initrd: Address 0x{:x}, Size 0x{:x}", + ramdisk_address, ramdisk_size + ); + + let elf_start = ramdisk_address as usize; + let elf_len = ramdisk_size as usize; + + let free_memory_address = unsafe { ptr::addr_of!(loader_end) } + .addr() + .align_up(Size2MiB::SIZE as usize); + // TODO: Workaround for https://github.com/hermitcore/loader/issues/96 + let free_memory_address = cmp::max(free_memory_address, 0x800000); + info!("Intialize PhysAlloc with {:#x}", free_memory_address); + // Memory after the highest end address is unused and available for the physical memory manager. + PhysAlloc::init(free_memory_address); + + assert!(ramdisk_address > 0); + info!("Found an ELF module at {:#x}", elf_start); + let page_address = elf_start.align_down(Size4KiB::SIZE as usize); + let counter = + (elf_start.align_up(Size2MiB::SIZE as usize) - page_address) / Size4KiB::SIZE as usize; + paging::map::(page_address, page_address, counter, PageTableFlags::empty()); + + // map also the rest of the module + let address = elf_start.align_up(Size2MiB::SIZE as usize); + let counter = ((elf_start + elf_len).align_up(Size2MiB::SIZE as usize) - address) + / Size2MiB::SIZE as usize; + if counter > 0 { + paging::map::(address, address, counter, PageTableFlags::empty()); + } + + unsafe { slice::from_raw_parts(sptr::from_exposed_addr(elf_start), elf_len) } +} + +pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { + let LoadedKernel { + load_info, + entry_point, + } = kernel_info; + + // determine boot stack address + let new_stack = + (unsafe { ptr::addr_of!(loader_end) }.addr() + 0x1000).align_up(Size4KiB::SIZE as usize); + + let cmdline_ptr = unsafe { + *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + CMD_LINE_PTR_OFFSET)) + }; + let cmdline_size: u32 = unsafe { + *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + CMD_LINE_SIZE_OFFSET)) + }; + + let command_line = if cmdline_size > 0 { + // Identity-map the command line. + let page_address = (cmdline_ptr as usize).align_down(Size4KiB::SIZE as usize); + paging::map::(page_address, page_address, 1, PageTableFlags::empty()); + + info!("Found command line at {:#x}", cmdline_ptr); + let slice = unsafe { + core::slice::from_raw_parts( + sptr::from_exposed_addr(cmdline_ptr), + cmdline_size.try_into().unwrap(), + ) + }; + + Some(core::str::from_utf8(slice).unwrap()) + } else { + None + }; + + let current_stack_address = new_stack as u64; + info!( + "Use kernel stack: [{:#x} - {:#x}]", + current_stack_address, + current_stack_address + KERNEL_STACK_SIZE + ); + + // map stack in the address space + paging::map::( + new_stack, + new_stack, + KERNEL_STACK_SIZE as usize / Size4KiB::SIZE as usize, + PageTableFlags::WRITABLE, + ); + + // clear stack + unsafe { + write_bytes( + sptr::from_exposed_addr_mut::(new_stack), + 0, + KERNEL_STACK_SIZE.try_into().unwrap(), + ); + } + + // Load the boot_param memory-map information + let linux_e820_entries = + unsafe { *(sptr::from_exposed_addr(boot_params + E820_ENTRIES_OFFSET)) }; + info!("Number of e820-entries: {}", linux_e820_entries); + + let mut found_entry = false; + let mut start_address: usize = 0; + let mut end_address: usize = 0; + + let e820_entries_address = unsafe { boot_params } + E820_TABLE_OFFSET; + + for index in 0..linux_e820_entries { + found_entry = true; + + //20: Size of one e820-Entry + let entry_address = e820_entries_address + (index as usize) * 20; + let entry_start = unsafe { sptr::from_exposed_addr::(entry_address).read_unaligned() }; + let entry_size = + unsafe { sptr::from_exposed_addr::(entry_address + 8).read_unaligned() }; + let entry_type: u32 = unsafe { sptr::from_exposed_addr::(entry_address + 16).read() }; + + info!( + "e820-Entry with index {}: Address 0x{:x}, Size 0x{:x}, Type 0x{:x}", + index, entry_start, entry_size, entry_type + ); + + let entry_end = entry_start + entry_size; + + if start_address == 0 { + start_address = entry_start as usize; + } + + if entry_end as usize > end_address { + end_address = entry_end as usize; + } + } + + // Identity-map the start of RAM + assert!(found_entry, "Could not find any free RAM areas!"); + + info!( + "Found available RAM: [0x{:x} - 0x{:x}]", + start_address, end_address + ); + + take_static::take_static! { + static RAW_BOOT_INFO: Option = None; + } + + let raw_boot_info = RAW_BOOT_INFO.take().unwrap(); + + let boot_info = BootInfo { + hardware_info: HardwareInfo { + phys_addr_range: start_address as u64..end_address as u64, + serial_port_base: SerialPortBase::new(SERIAL_IO_PORT), + device_tree: None, + }, + load_info, + platform_info: PlatformInfo::LinuxBootParams { + command_line, + boot_params_addr: (unsafe { boot_params } as u64).try_into().unwrap(), + }, + }; + + info!("boot_info = {boot_info:#?}"); + let boot_info_ptr = raw_boot_info.insert(RawBootInfo::from(boot_info)); + info!("boot_info at {boot_info_ptr:p}"); + + // Jump to the kernel entry point and provide the Multiboot information to it. + info!( + "Jumping to HermitCore Application Entry Point at {:#x}", + entry_point + ); + + #[allow(dead_code)] + const ENTRY_TYPE_CHECK: Entry = { + unsafe extern "C" fn entry_signature( + _raw_boot_info: &'static RawBootInfo, + _cpu_id: u32, + ) -> ! { + unimplemented!() + } + entry_signature + }; + + unsafe { + asm!( + "mov rsp, {stack_address}", + "jmp {entry}", + stack_address = in(reg) current_stack_address, + entry = in(reg) entry_point, + in("rdi") boot_info_ptr, + in("rsi") 0, + options(noreturn) + ) + } +} diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 50db3055..4d891459 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,544 +1,31 @@ -#[cfg(all(target_os = "none", not(feature = "fc")))] -mod fdt; -mod paging; -mod physicalmem; +cfg_if::cfg_if! { + if #[cfg(feature = "fc")] { + mod firecracker; + pub use self::firecracker::*; + } else if #[cfg(target_os = "none")] { + mod multiboot; + pub use self::multiboot::*; + } +} -use core::arch::asm; -#[cfg(all(target_os = "none", not(feature = "fc")))] -use core::mem; -use core::ptr; +mod console; #[cfg(target_os = "none")] -use core::ptr::write_bytes; +mod paging; #[cfg(target_os = "none")] -use core::slice; - -use align_address::Align; -#[cfg(all(target_os = "none", not(feature = "fc")))] -use hermit_entry::boot_info::DeviceTreeAddress; -use hermit_entry::boot_info::{BootInfo, HardwareInfo, PlatformInfo, RawBootInfo, SerialPortBase}; -use hermit_entry::elf::LoadedKernel; -#[cfg(all(target_os = "none", feature = "fc"))] -use hermit_entry::fc::{ - BOOT_FLAG_OFFSET, CMD_LINE_PTR_OFFSET, CMD_LINE_SIZE_OFFSET, E820_ENTRIES_OFFSET, - E820_TABLE_OFFSET, HDR_MAGIC_OFFSET, LINUX_KERNEL_BOOT_FLAG_MAGIC, LINUX_KERNEL_HRD_MAGIC, - LINUX_SETUP_HEADER_OFFSET, RAMDISK_IMAGE_OFFSET, RAMDISK_SIZE_OFFSET, -}; -use hermit_entry::Entry; -use log::info; -#[cfg(all(target_os = "none", not(feature = "fc")))] -use multiboot::information::MemoryManagement; -#[cfg(all(target_os = "none", not(feature = "fc")))] -use multiboot::information::{Multiboot, PAddr}; -use sptr::Strict; -use uart_16550::SerialPort; -use x86_64::structures::paging::{PageSize, PageTableFlags, Size2MiB, Size4KiB}; +mod physicalmem; -#[cfg(all(target_os = "none", not(feature = "fc")))] -use self::fdt::DeviceTree; -use self::physicalmem::PhysAlloc; +pub use console::Console; #[cfg(target_os = "none")] -extern "C" { - static loader_end: u8; - #[cfg(not(feature = "fc"))] - static mb_info: usize; - #[cfg(feature = "fc")] - static boot_params: usize; -} - -// CONSTANTS const KERNEL_STACK_SIZE: u64 = 32_768; +#[cfg(target_os = "none")] const SERIAL_IO_PORT: u16 = 0x3F8; -// VARIABLES -static mut COM1: SerialPort = unsafe { SerialPort::new(SERIAL_IO_PORT) }; - -#[cfg(all(target_os = "none", not(feature = "fc")))] -struct Mem; - -#[cfg(all(target_os = "none", not(feature = "fc")))] -impl MemoryManagement for Mem { - unsafe fn paddr_to_slice<'a>(&self, p: PAddr, sz: usize) -> Option<&'static [u8]> { - let ptr = sptr::from_exposed_addr(p as usize); - unsafe { Some(slice::from_raw_parts(ptr, sz)) } - } - - // If you only want to read fields, you can simply return `None`. - unsafe fn allocate(&mut self, _length: usize) -> Option<(PAddr, &mut [u8])> { - None - } - - unsafe fn deallocate(&mut self, addr: PAddr) { - if addr != 0 { - unimplemented!() - } - } -} - -#[cfg_attr(not(feature = "fc"), allow(bad_asm_style))] -mod entry { - #[cfg(not(feature = "fc"))] - core::arch::global_asm!(include_str!("entry.s")); - - #[cfg(feature = "fc")] - core::arch::global_asm!(include_str!("entry_fc.s")); -} - -// FUNCTIONS -pub fn message_output_init() { - unsafe { COM1.init() }; -} - -pub fn output_message_byte(byte: u8) { - unsafe { COM1.send(byte) }; -} - -#[cfg(target_os = "uefi")] -pub unsafe fn find_kernel() -> &'static [u8] { - &[1, 2, 3] -} - -#[cfg(target_os = "uefi")] -pub unsafe fn boot_kernel( - _elf_address: Option, - _virtual_address: u64, - _mem_size: u64, - _entry_point: u64, -) -> ! { - loop {} -} - -#[cfg(all(target_os = "none", feature = "fc"))] -pub fn find_kernel() -> &'static [u8] { - use core::cmp; - - paging::clean_up(); - - // Identity-map the Multiboot information. - unsafe { - assert!(boot_params > 0, "Could not find boot_params"); - info!("Found boot_params at 0x{:x}", boot_params); - } - let page_address = unsafe { boot_params }.align_down(Size4KiB::SIZE as usize); - paging::map::(page_address, page_address, 1, PageTableFlags::empty()); - - let linux_kernel_boot_flag_magic: u16 = unsafe { - *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + BOOT_FLAG_OFFSET)) - }; - let linux_kernel_header_magic = unsafe { - sptr::from_exposed_addr::(boot_params + LINUX_SETUP_HEADER_OFFSET + HDR_MAGIC_OFFSET) - .read_unaligned() - }; - if linux_kernel_boot_flag_magic == LINUX_KERNEL_BOOT_FLAG_MAGIC - && linux_kernel_header_magic == LINUX_KERNEL_HRD_MAGIC - { - info!("Found Linux kernel boot flag and header magic! Probably booting in firecracker."); - } else { - info!("Kernel boot flag and hdr magic have values 0x{:x} and 0x{:x} which does not align with the normal linux kernel values", - linux_kernel_boot_flag_magic, - linux_kernel_header_magic - ); - } - - // Load the boot_param memory-map information - let linux_e820_entries: u8 = - unsafe { *(sptr::from_exposed_addr(boot_params + E820_ENTRIES_OFFSET)) }; - info!("Number of e820-entries: {}", linux_e820_entries); - - let e820_entries_address = unsafe { boot_params } + E820_TABLE_OFFSET; - info!("e820-entry-table at 0x{:x}", e820_entries_address); - let page_address = e820_entries_address.align_down(Size4KiB::SIZE as usize); - - if !(unsafe { boot_params } >= page_address - && unsafe { boot_params } < page_address + Size4KiB::SIZE as usize) - { - paging::map::(page_address, page_address, 1, PageTableFlags::empty()); - } - - // Load the Hermit-ELF from the initrd supplied by Firecracker - let ramdisk_address: u32 = unsafe { - *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + RAMDISK_IMAGE_OFFSET)) - }; - let ramdisk_size: u32 = unsafe { - *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + RAMDISK_SIZE_OFFSET)) - }; - - info!( - "Initrd: Address 0x{:x}, Size 0x{:x}", - ramdisk_address, ramdisk_size - ); - - let elf_start = ramdisk_address as usize; - let elf_len = ramdisk_size as usize; - - let free_memory_address = unsafe { ptr::addr_of!(loader_end) } - .addr() - .align_up(Size2MiB::SIZE as usize); - // TODO: Workaround for https://github.com/hermitcore/loader/issues/96 - let free_memory_address = cmp::max(free_memory_address, 0x800000); - info!("Intialize PhysAlloc with {:#x}", free_memory_address); - // Memory after the highest end address is unused and available for the physical memory manager. - PhysAlloc::init(free_memory_address); - - assert!(ramdisk_address > 0); - info!("Found an ELF module at {:#x}", elf_start); - let page_address = elf_start.align_down(Size4KiB::SIZE as usize); - let counter = - (elf_start.align_up(Size2MiB::SIZE as usize) - page_address) / Size4KiB::SIZE as usize; - paging::map::(page_address, page_address, counter, PageTableFlags::empty()); - - // map also the rest of the module - let address = elf_start.align_up(Size2MiB::SIZE as usize); - let counter = ((elf_start + elf_len).align_up(Size2MiB::SIZE as usize) - address) - / Size2MiB::SIZE as usize; - if counter > 0 { - paging::map::(address, address, counter, PageTableFlags::empty()); - } - - unsafe { slice::from_raw_parts(sptr::from_exposed_addr(elf_start), elf_len) } -} - -#[cfg(all(target_os = "none", not(feature = "fc")))] -pub fn find_kernel() -> &'static [u8] { - use core::cmp; - - paging::clean_up(); - // Identity-map the Multiboot information. - unsafe { - assert!(mb_info > 0, "Could not find Multiboot information"); - info!("Found Multiboot information at {:#x}", mb_info); - } - let page_address = unsafe { mb_info.align_down(Size4KiB::SIZE as usize) }; - paging::map::(page_address, page_address, 1, PageTableFlags::empty()); - - let mut mem = Mem; - // Load the Multiboot information and identity-map the modules information. - let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() }; - let modules_address = multiboot - .modules() - .expect("Could not find a memory map in the Multiboot information") - .next() - .expect("Could not find first map address") - .start as usize; - let page_address = modules_address.align_down(Size4KiB::SIZE as usize); - paging::map::(page_address, page_address, 1, PageTableFlags::empty()); - - // Iterate through all modules. - // Collect the start address of the first module and the highest end address of all modules. - let modules = multiboot.modules().unwrap(); - let mut found_module = false; - let mut start_address = 0; - let mut end_address = 0; - - for m in modules { - found_module = true; - - if start_address == 0 { - start_address = m.start as usize; - } - - if m.end as usize > end_address { - end_address = m.end as usize; - } - } - - info!("Found module: [{:#x} - {:#x}]", start_address, end_address); - let elf_start = start_address; - let elf_len = end_address - start_address; - info!("Module length: {:#x}", elf_len); - - let free_memory_address = end_address.align_up(Size2MiB::SIZE as usize); - // TODO: Workaround for https://github.com/hermitcore/loader/issues/96 - let free_memory_address = cmp::max(free_memory_address, 0x800000); - // Memory after the highest end address is unused and available for the physical memory manager. - PhysAlloc::init(free_memory_address); - - // Identity-map the ELF header of the first module. - assert!( - found_module, - "Could not find a single module in the Multiboot information" - ); - assert!(start_address > 0); - info!("Found an ELF module at {:#x}", start_address); - let page_address = start_address.align_down(Size4KiB::SIZE as usize) + Size4KiB::SIZE as usize; - let counter = - (start_address.align_up(Size2MiB::SIZE as usize) - page_address) / Size4KiB::SIZE as usize; - paging::map::(page_address, page_address, counter, PageTableFlags::empty()); - - // map also the rest of the module - let address = start_address.align_up(Size2MiB::SIZE as usize); - let counter = - (end_address.align_up(Size2MiB::SIZE as usize) - address) / Size2MiB::SIZE as usize; - if counter > 0 { - paging::map::(address, address, counter, PageTableFlags::empty()); - } - - unsafe { slice::from_raw_parts(sptr::from_exposed_addr(elf_start), elf_len) } -} - -#[cfg(all(target_os = "none", feature = "fc"))] -pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { - let LoadedKernel { - load_info, - entry_point, - } = kernel_info; - - // determine boot stack address - let new_stack = - (unsafe { ptr::addr_of!(loader_end) }.addr() + 0x1000).align_up(Size4KiB::SIZE as usize); - - let cmdline_ptr = unsafe { - *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + CMD_LINE_PTR_OFFSET)) - }; - let cmdline_size: u32 = unsafe { - *(sptr::from_exposed_addr(boot_params + LINUX_SETUP_HEADER_OFFSET + CMD_LINE_SIZE_OFFSET)) - }; - - let command_line = if cmdline_size > 0 { - // Identity-map the command line. - let page_address = (cmdline_ptr as usize).align_down(Size4KiB::SIZE as usize); - paging::map::(page_address, page_address, 1, PageTableFlags::empty()); - - info!("Found command line at {:#x}", cmdline_ptr); - let slice = unsafe { - core::slice::from_raw_parts( - sptr::from_exposed_addr(cmdline_ptr), - cmdline_size.try_into().unwrap(), - ) - }; - - Some(core::str::from_utf8(slice).unwrap()) - } else { - None - }; - - let current_stack_address = new_stack as u64; - info!( - "Use kernel stack: [{:#x} - {:#x}]", - current_stack_address, - current_stack_address + KERNEL_STACK_SIZE - ); - - // map stack in the address space - paging::map::( - new_stack, - new_stack, - KERNEL_STACK_SIZE as usize / Size4KiB::SIZE as usize, - PageTableFlags::WRITABLE, - ); - - // clear stack - unsafe { - write_bytes( - sptr::from_exposed_addr_mut::(new_stack), - 0, - KERNEL_STACK_SIZE.try_into().unwrap(), - ); - } - - // Load the boot_param memory-map information - let linux_e820_entries = - unsafe { *(sptr::from_exposed_addr(boot_params + E820_ENTRIES_OFFSET)) }; - info!("Number of e820-entries: {}", linux_e820_entries); - - let mut found_entry = false; - let mut start_address: usize = 0; - let mut end_address: usize = 0; - - let e820_entries_address = unsafe { boot_params } + E820_TABLE_OFFSET; - - for index in 0..linux_e820_entries { - found_entry = true; - - //20: Size of one e820-Entry - let entry_address = e820_entries_address + (index as usize) * 20; - let entry_start = unsafe { sptr::from_exposed_addr::(entry_address).read_unaligned() }; - let entry_size = - unsafe { sptr::from_exposed_addr::(entry_address + 8).read_unaligned() }; - let entry_type: u32 = unsafe { sptr::from_exposed_addr::(entry_address + 16).read() }; - - info!( - "e820-Entry with index {}: Address 0x{:x}, Size 0x{:x}, Type 0x{:x}", - index, entry_start, entry_size, entry_type - ); - - let entry_end = entry_start + entry_size; - - if start_address == 0 { - start_address = entry_start as usize; - } - - if entry_end as usize > end_address { - end_address = entry_end as usize; - } - } - - // Identity-map the start of RAM - assert!(found_entry, "Could not find any free RAM areas!"); - - info!( - "Found available RAM: [0x{:x} - 0x{:x}]", - start_address, end_address - ); - - take_static::take_static! { - static RAW_BOOT_INFO: Option = None; - } - - let raw_boot_info = RAW_BOOT_INFO.take().unwrap(); - - let boot_info = BootInfo { - hardware_info: HardwareInfo { - phys_addr_range: start_address as u64..end_address as u64, - serial_port_base: SerialPortBase::new(SERIAL_IO_PORT), - device_tree: None, - }, - load_info, - platform_info: PlatformInfo::LinuxBootParams { - command_line, - boot_params_addr: (unsafe { boot_params } as u64).try_into().unwrap(), - }, - }; - - info!("boot_info = {boot_info:#?}"); - let boot_info_ptr = raw_boot_info.insert(RawBootInfo::from(boot_info)); - info!("boot_info at {boot_info_ptr:p}"); - - // Jump to the kernel entry point and provide the Multiboot information to it. - info!( - "Jumping to HermitCore Application Entry Point at {:#x}", - entry_point - ); - - #[allow(dead_code)] - const ENTRY_TYPE_CHECK: Entry = { - unsafe extern "C" fn entry_signature( - _raw_boot_info: &'static RawBootInfo, - _cpu_id: u32, - ) -> ! { - unimplemented!() - } - entry_signature - }; - - unsafe { - asm!( - "mov rsp, {stack_address}", - "jmp {entry}", - stack_address = in(reg) current_stack_address, - entry = in(reg) entry_point, - in("rdi") boot_info_ptr, - in("rsi") 0, - options(noreturn) - ) - } -} - -#[cfg(all(target_os = "none", not(feature = "fc")))] -pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { - let LoadedKernel { - load_info, - entry_point, - } = kernel_info; - - let mut mem = Mem; - let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() }; - - // determine boot stack address - let mut new_stack = unsafe { ptr::addr_of!(loader_end) } - .addr() - .align_up(Size4KiB::SIZE as usize); - - if new_stack + KERNEL_STACK_SIZE as usize > unsafe { mb_info } { - new_stack = (unsafe { mb_info } + mem::size_of::>()) - .align_up(Size4KiB::SIZE as usize); - } - - let command_line = multiboot.command_line(); - if let Some(command_line) = command_line { - let cmdline = command_line.as_ptr() as usize; - let cmdsize = command_line.len(); - if new_stack + KERNEL_STACK_SIZE as usize > cmdline { - new_stack = (cmdline + cmdsize).align_up(Size4KiB::SIZE as usize); - } - } - - let current_stack_address = new_stack as u64; - info!("Use stack address {:#x}", current_stack_address); - - // map stack in the address space - paging::map::( - new_stack, - new_stack, - KERNEL_STACK_SIZE as usize / Size4KiB::SIZE as usize, - PageTableFlags::WRITABLE, - ); - - // clear stack - unsafe { - write_bytes( - sptr::from_exposed_addr_mut::(new_stack), - 0, - KERNEL_STACK_SIZE.try_into().unwrap(), - ); - } - - let device_tree = DeviceTree::create().expect("Unable to create devicetree!"); - - take_static::take_static! { - static RAW_BOOT_INFO: Option = None; - } - - let raw_boot_info = RAW_BOOT_INFO.take().unwrap(); - - let boot_info = BootInfo { - hardware_info: HardwareInfo { - phys_addr_range: 0..0, - serial_port_base: SerialPortBase::new(SERIAL_IO_PORT), - device_tree: DeviceTreeAddress::new(device_tree.as_ptr() as u64), - }, - load_info, - platform_info: PlatformInfo::Multiboot { - command_line, - multiboot_info_addr: (unsafe { mb_info } as u64).try_into().unwrap(), - }, - }; - - info!("boot_info = {boot_info:#?}"); - let boot_info_ptr = raw_boot_info.insert(RawBootInfo::from(boot_info)); - info!("boot_info at {boot_info_ptr:p}"); - - // Jump to the kernel entry point and provide the Multiboot information to it. - info!( - "Jumping to HermitCore Application Entry Point at {:#x}", - entry_point - ); - - #[allow(dead_code)] - const ENTRY_TYPE_CHECK: Entry = { - unsafe extern "C" fn entry_signature( - _raw_boot_info: &'static RawBootInfo, - _cpu_id: u32, - ) -> ! { - unimplemented!() - } - entry_signature - }; - - unsafe { - asm!( - "mov rsp, {stack_address}", - "jmp {entry}", - stack_address = in(reg) current_stack_address, - entry = in(reg) entry_point, - in("rdi") boot_info_ptr, - in("rsi") 0, - options(noreturn) - ) - } -} - +#[cfg(target_os = "none")] unsafe fn map_memory(address: usize, memory_size: usize) -> usize { + use align_address::Align; + use x86_64::structures::paging::{PageSize, PageTableFlags, Size2MiB}; + let address = address.align_up(Size2MiB::SIZE as usize); let page_count = memory_size.align_up(Size2MiB::SIZE as usize) / Size2MiB::SIZE as usize; @@ -547,7 +34,13 @@ unsafe fn map_memory(address: usize, memory_size: usize) -> usize { address } +#[cfg(target_os = "none")] pub unsafe fn get_memory(memory_size: u64) -> u64 { + use align_address::Align; + use x86_64::structures::paging::{PageSize, Size2MiB}; + + use self::physicalmem::PhysAlloc; + let address = PhysAlloc::allocate((memory_size as usize).align_up(Size2MiB::SIZE as usize)); unsafe { map_memory(address, memory_size as usize) as u64 } } diff --git a/src/arch/x86_64/multiboot.rs b/src/arch/x86_64/multiboot.rs new file mode 100644 index 00000000..90987170 --- /dev/null +++ b/src/arch/x86_64/multiboot.rs @@ -0,0 +1,273 @@ +use alloc::format; +use core::arch::asm; +use core::ptr::write_bytes; +use core::{mem, ptr, slice}; + +use align_address::Align; +use hermit_entry::boot_info::{ + BootInfo, DeviceTreeAddress, HardwareInfo, PlatformInfo, RawBootInfo, SerialPortBase, +}; +use hermit_entry::elf::LoadedKernel; +use hermit_entry::Entry; +use log::info; +use multiboot::information::{MemoryManagement, MemoryType, Multiboot, PAddr}; +use sptr::Strict; +use vm_fdt::{Error as FdtError, FdtWriter}; +use x86_64::structures::paging::{PageSize, PageTableFlags, Size2MiB, Size4KiB}; + +use super::paging; +use super::physicalmem::PhysAlloc; +use crate::arch::x86_64::{KERNEL_STACK_SIZE, SERIAL_IO_PORT}; + +extern "C" { + static loader_end: u8; + static mb_info: usize; +} + +#[allow(bad_asm_style)] +mod entry { + core::arch::global_asm!(include_str!("entry.s")); +} + +struct Mem; + +impl MemoryManagement for Mem { + unsafe fn paddr_to_slice<'a>(&self, p: PAddr, sz: usize) -> Option<&'static [u8]> { + let ptr = sptr::from_exposed_addr(p as usize); + unsafe { Some(slice::from_raw_parts(ptr, sz)) } + } + + // If you only want to read fields, you can simply return `None`. + unsafe fn allocate(&mut self, _length: usize) -> Option<(PAddr, &mut [u8])> { + None + } + + unsafe fn deallocate(&mut self, addr: PAddr) { + if addr != 0 { + unimplemented!() + } + } +} + +pub struct DeviceTree; + +impl DeviceTree { + pub fn create() -> Result<&'static [u8], FdtError> { + let mut mem = Mem; + let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() }; + + let all_regions = multiboot + .memory_regions() + .expect("Could not find a memory map in the Multiboot information"); + let ram_regions = all_regions.filter(|m| m.memory_type() == MemoryType::Available); + + let mut fdt = FdtWriter::new()?; + + let root_node = fdt.begin_node("")?; + fdt.property_string("compatible", "linux,dummy-virt")?; + fdt.property_u32("#address-cells", 0x2)?; + fdt.property_u32("#size-cells", 0x2)?; + + if let Some(cmdline) = multiboot.command_line() { + let chosen_node = fdt.begin_node("chosen")?; + fdt.property_string("bootargs", cmdline)?; + fdt.end_node(chosen_node)?; + } + + for m in ram_regions { + let start_address = m.base_address(); + let length = m.length(); + + let memory_node = fdt.begin_node(format!("memory@{:x}", start_address).as_str())?; + fdt.property_string("device_type", "memory")?; + fdt.property_array_u64("reg", &[start_address, length])?; + fdt.end_node(memory_node)?; + } + + fdt.end_node(root_node)?; + + let fdt = fdt.finish()?; + + Ok(fdt.leak()) + } +} + +pub fn find_kernel() -> &'static [u8] { + use core::cmp; + + paging::clean_up(); + // Identity-map the Multiboot information. + unsafe { + assert!(mb_info > 0, "Could not find Multiboot information"); + info!("Found Multiboot information at {:#x}", mb_info); + } + let page_address = unsafe { mb_info.align_down(Size4KiB::SIZE as usize) }; + paging::map::(page_address, page_address, 1, PageTableFlags::empty()); + + let mut mem = Mem; + // Load the Multiboot information and identity-map the modules information. + let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() }; + let modules_address = multiboot + .modules() + .expect("Could not find a memory map in the Multiboot information") + .next() + .expect("Could not find first map address") + .start as usize; + let page_address = modules_address.align_down(Size4KiB::SIZE as usize); + paging::map::(page_address, page_address, 1, PageTableFlags::empty()); + + // Iterate through all modules. + // Collect the start address of the first module and the highest end address of all modules. + let modules = multiboot.modules().unwrap(); + let mut found_module = false; + let mut start_address = 0; + let mut end_address = 0; + + for m in modules { + found_module = true; + + if start_address == 0 { + start_address = m.start as usize; + } + + if m.end as usize > end_address { + end_address = m.end as usize; + } + } + + info!("Found module: [{:#x} - {:#x}]", start_address, end_address); + let elf_start = start_address; + let elf_len = end_address - start_address; + info!("Module length: {:#x}", elf_len); + + let free_memory_address = end_address.align_up(Size2MiB::SIZE as usize); + // TODO: Workaround for https://github.com/hermitcore/loader/issues/96 + let free_memory_address = cmp::max(free_memory_address, 0x800000); + // Memory after the highest end address is unused and available for the physical memory manager. + PhysAlloc::init(free_memory_address); + + // Identity-map the ELF header of the first module. + assert!( + found_module, + "Could not find a single module in the Multiboot information" + ); + assert!(start_address > 0); + info!("Found an ELF module at {:#x}", start_address); + let page_address = start_address.align_down(Size4KiB::SIZE as usize) + Size4KiB::SIZE as usize; + let counter = + (start_address.align_up(Size2MiB::SIZE as usize) - page_address) / Size4KiB::SIZE as usize; + paging::map::(page_address, page_address, counter, PageTableFlags::empty()); + + // map also the rest of the module + let address = start_address.align_up(Size2MiB::SIZE as usize); + let counter = + (end_address.align_up(Size2MiB::SIZE as usize) - address) / Size2MiB::SIZE as usize; + if counter > 0 { + paging::map::(address, address, counter, PageTableFlags::empty()); + } + + unsafe { slice::from_raw_parts(sptr::from_exposed_addr(elf_start), elf_len) } +} + +pub unsafe fn boot_kernel(kernel_info: LoadedKernel) -> ! { + let LoadedKernel { + load_info, + entry_point, + } = kernel_info; + + let mut mem = Mem; + let multiboot = unsafe { Multiboot::from_ptr(mb_info as u64, &mut mem).unwrap() }; + + // determine boot stack address + let mut new_stack = unsafe { ptr::addr_of!(loader_end) } + .addr() + .align_up(Size4KiB::SIZE as usize); + + if new_stack + KERNEL_STACK_SIZE as usize > unsafe { mb_info } { + new_stack = (unsafe { mb_info } + mem::size_of::>()) + .align_up(Size4KiB::SIZE as usize); + } + + let command_line = multiboot.command_line(); + if let Some(command_line) = command_line { + let cmdline = command_line.as_ptr() as usize; + let cmdsize = command_line.len(); + if new_stack + KERNEL_STACK_SIZE as usize > cmdline { + new_stack = (cmdline + cmdsize).align_up(Size4KiB::SIZE as usize); + } + } + + let current_stack_address = new_stack as u64; + info!("Use stack address {:#x}", current_stack_address); + + // map stack in the address space + paging::map::( + new_stack, + new_stack, + KERNEL_STACK_SIZE as usize / Size4KiB::SIZE as usize, + PageTableFlags::WRITABLE, + ); + + // clear stack + unsafe { + write_bytes( + sptr::from_exposed_addr_mut::(new_stack), + 0, + KERNEL_STACK_SIZE.try_into().unwrap(), + ); + } + + let device_tree = DeviceTree::create().expect("Unable to create devicetree!"); + + take_static::take_static! { + static RAW_BOOT_INFO: Option = None; + } + + let raw_boot_info = RAW_BOOT_INFO.take().unwrap(); + + let boot_info = BootInfo { + hardware_info: HardwareInfo { + phys_addr_range: 0..0, + serial_port_base: SerialPortBase::new(SERIAL_IO_PORT), + device_tree: DeviceTreeAddress::new(device_tree.as_ptr() as u64), + }, + load_info, + platform_info: PlatformInfo::Multiboot { + command_line, + multiboot_info_addr: (unsafe { mb_info } as u64).try_into().unwrap(), + }, + }; + + info!("boot_info = {boot_info:#?}"); + let boot_info_ptr = raw_boot_info.insert(RawBootInfo::from(boot_info)); + info!("boot_info at {boot_info_ptr:p}"); + + // Jump to the kernel entry point and provide the Multiboot information to it. + info!( + "Jumping to HermitCore Application Entry Point at {:#x}", + entry_point + ); + + #[allow(dead_code)] + const ENTRY_TYPE_CHECK: Entry = { + unsafe extern "C" fn entry_signature( + _raw_boot_info: &'static RawBootInfo, + _cpu_id: u32, + ) -> ! { + unimplemented!() + } + entry_signature + }; + + unsafe { + asm!( + "mov rsp, {stack_address}", + "jmp {entry}", + stack_address = in(reg) current_stack_address, + entry = in(reg) entry_point, + in("rdi") boot_info_ptr, + in("rsi") 0, + options(noreturn) + ) + } +} diff --git a/src/arch/x86_64/paging.rs b/src/arch/x86_64/paging.rs index 14253564..8a3e80c8 100644 --- a/src/arch/x86_64/paging.rs +++ b/src/arch/x86_64/paging.rs @@ -1,5 +1,6 @@ use core::fmt::Debug; +use log::warn; use x86_64::structures::paging::mapper::CleanUp; use x86_64::structures::paging::{ Mapper, Page, PageSize, PageTableFlags, PhysFrame, RecursivePageTable, @@ -25,7 +26,7 @@ where PhysFrame::range(start, end) }; - log::warn!( + warn!( "Mapping {count} {size} pages from {from_start:p}..{from_end:p} to {to_start:p}..{to_end:p}", count = (pages.end.start_address() - pages.start.start_address()) / S::SIZE, size = S::DEBUG_STR, diff --git a/src/console.rs b/src/console.rs deleted file mode 100644 index 61dd3e32..00000000 --- a/src/console.rs +++ /dev/null @@ -1,16 +0,0 @@ -use core::fmt; - -use one_shot_mutex::OneShotMutex; - -pub struct Console(()); - -impl fmt::Write for Console { - fn write_str(&mut self, s: &str) -> fmt::Result { - for byte in s.bytes() { - crate::arch::output_message_byte(byte); - } - Ok(()) - } -} - -pub static CONSOLE: OneShotMutex = OneShotMutex::new(Console(())); diff --git a/src/macros.rs b/src/macros.rs index 7ee46533..4b123083 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,16 +5,9 @@ /// [`std::print`]: https://doc.rust-lang.org/stable/std/macro.print.html #[macro_export] macro_rules! print { - ($($arg:tt)*) => { - #[cfg(target_os = "none")] - { - $crate::_print(::core::format_args!($($arg)*)); - } - #[cfg(target_os = "uefi")] - { - ::uefi_services::print!($($arg)*); - } - }; + ($($arg:tt)*) => {{ + $crate::_print(::core::format_args!($($arg)*)); + }}; } /// Prints to the standard output, with a newline. @@ -27,16 +20,9 @@ macro_rules! println { () => { $crate::print!("\n") }; - ($($arg:tt)*) => { - #[cfg(target_os = "none")] - { - $crate::_print(::core::format_args!("{}\n", format_args!($($arg)*))); - } - #[cfg(target_os = "uefi")] - { - ::uefi_services::println!($($arg)*); - } - }; + ($($arg:tt)*) => {{ + $crate::_print(::core::format_args!("{}\n", format_args!($($arg)*))); + }}; } /// Prints and returns the value of a given expression for quick and dirty diff --git a/src/main.rs b/src/main.rs index e464b188..681f752a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,17 +8,9 @@ #[macro_use] mod macros; -#[cfg(target_os = "none")] -mod allocator; mod arch; -#[cfg(target_os = "none")] -mod console; -#[cfg(target_os = "none")] mod log; -#[cfg(target_os = "none")] -mod none; -#[cfg(target_os = "uefi")] -mod uefi; +mod os; #[cfg(any( target_os = "uefi", @@ -26,10 +18,9 @@ mod uefi; ))] extern crate alloc; -#[cfg(target_os = "none")] #[doc(hidden)] fn _print(args: core::fmt::Arguments<'_>) { use core::fmt::Write; - console::CONSOLE.lock().write_fmt(args).unwrap(); + self::os::CONSOLE.lock().write_fmt(args).unwrap(); } diff --git a/src/os/mod.rs b/src/os/mod.rs new file mode 100644 index 00000000..6c1a4924 --- /dev/null +++ b/src/os/mod.rs @@ -0,0 +1,11 @@ +#![allow(unused_imports)] + +cfg_if::cfg_if! { + if #[cfg(target_os = "none")] { + mod none; + pub use self::none::*; + } else if #[cfg(target_os = "uefi")] { + mod uefi; + pub use self::uefi::*; + } +} diff --git a/src/allocator/bootstrap.rs b/src/os/none/allocator/bootstrap.rs similarity index 100% rename from src/allocator/bootstrap.rs rename to src/os/none/allocator/bootstrap.rs diff --git a/src/allocator/bump.rs b/src/os/none/allocator/bump.rs similarity index 100% rename from src/allocator/bump.rs rename to src/os/none/allocator/bump.rs diff --git a/src/allocator/mod.rs b/src/os/none/allocator/mod.rs similarity index 100% rename from src/allocator/mod.rs rename to src/os/none/allocator/mod.rs diff --git a/src/os/none/console.rs b/src/os/none/console.rs new file mode 100644 index 00000000..bf0d4b51 --- /dev/null +++ b/src/os/none/console.rs @@ -0,0 +1,31 @@ +use core::fmt; + +use one_shot_mutex::OneShotMutex; + +use crate::arch; + +pub struct Console { + console: Option, +} + +impl Console { + const fn new() -> Self { + Self { console: None } + } + + #[cfg(target_arch = "aarch64")] + pub fn get(&mut self) -> &mut arch::Console { + self.console.get_or_insert_with(arch::Console::default) + } +} + +impl fmt::Write for Console { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.console + .get_or_insert_with(arch::Console::default) + .write_bytes(s.as_bytes()); + Ok(()) + } +} + +pub static CONSOLE: OneShotMutex = OneShotMutex::new(Console::new()); diff --git a/src/none.rs b/src/os/none/mod.rs similarity index 88% rename from src/none.rs rename to src/os/none/mod.rs index 290534d2..eb66a2c2 100644 --- a/src/none.rs +++ b/src/os/none/mod.rs @@ -1,3 +1,6 @@ +mod allocator; +mod console; + use core::fmt::Write; use core::mem::MaybeUninit; use core::slice; @@ -5,7 +8,8 @@ use core::slice; use hermit_entry::elf::KernelObject; use log::info; -use crate::{arch, console}; +pub use self::console::CONSOLE; +use crate::arch; extern "C" { static loader_end: u8; @@ -16,7 +20,6 @@ extern "C" { /// (called from entry.asm or entry.rs) #[no_mangle] pub(crate) unsafe extern "C" fn loader_main() -> ! { - arch::message_output_init(); crate::log::init(); unsafe { @@ -44,7 +47,7 @@ pub(crate) unsafe extern "C" fn loader_main() -> ! { #[panic_handler] fn panic(info: &core::panic::PanicInfo<'_>) -> ! { // We can't use `println!` or related macros, because `_print` unwraps a result and might panic again - writeln!(console::CONSOLE.lock(), "[LOADER] {info}").ok(); + writeln!(crate::os::CONSOLE.lock(), "[LOADER] {info}").ok(); loop {} } diff --git a/src/os/uefi/console.rs b/src/os/uefi/console.rs new file mode 100644 index 00000000..36ddf26e --- /dev/null +++ b/src/os/uefi/console.rs @@ -0,0 +1,66 @@ +use core::ffi::c_void; +use core::fmt; +use core::ptr::NonNull; +use core::sync::atomic::{AtomicBool, Ordering}; + +use one_shot_mutex::OneShotMutex; +use uefi::table::boot::{EventType, Tpl}; +use uefi::table::{Boot, SystemTable}; +use uefi::Event; + +use crate::arch; + +pub enum Console { + None, + BootServices, + Native { console: arch::Console }, +} + +impl Console { + const fn new() -> Self { + Self::None + } + + fn exit_boot_services(&mut self) { + assert!(matches!(self, Self::BootServices { .. })); + *self = Self::Native { + console: arch::Console::default(), + }; + } + + fn init(&mut self) { + assert!(matches!(self, Console::None)); + unsafe { + uefi_services::system_table() + .boot_services() + .create_event( + EventType::SIGNAL_EXIT_BOOT_SERVICES, + Tpl::NOTIFY, + Some(exit_boot_services), + None, + ) + .unwrap(); + } + *self = Console::BootServices; + } +} + +impl fmt::Write for Console { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self { + Console::None => { + self.init(); + self.write_str(s)?; + } + Console::BootServices => uefi_services::system_table().stdout().write_str(s)?, + Console::Native { console } => console.write_bytes(s.as_bytes()), + } + Ok(()) + } +} + +unsafe extern "efiapi" fn exit_boot_services(_e: Event, _ctx: Option>) { + CONSOLE.lock().exit_boot_services(); +} + +pub static CONSOLE: OneShotMutex = OneShotMutex::new(Console::new()); diff --git a/src/uefi.rs b/src/os/uefi/mod.rs similarity index 85% rename from src/uefi.rs rename to src/os/uefi/mod.rs index 3daa60f3..4e3d1877 100644 --- a/src/uefi.rs +++ b/src/os/uefi/mod.rs @@ -1,10 +1,15 @@ +mod console; + use alloc::string::String; use alloc::vec::Vec; +use log::info; use qemu_exit::QEMUExit; use uefi::fs::{FileSystem, Path}; use uefi::prelude::*; +pub use self::console::CONSOLE; + fn read_app(bt: &BootServices) -> Vec { let fs = bt .get_image_file_system(bt.image_handle()) @@ -17,7 +22,7 @@ fn read_app(bt: &BootServices) -> Vec { .expect("should read file content"); let len = data.len(); - log::info!("Read Hermit application from \"{path}\" (size = {len} B)"); + info!("Read Hermit application from \"{path}\" (size = {len} B)"); data } @@ -26,6 +31,7 @@ fn read_app(bt: &BootServices) -> Vec { #[entry] fn loader_main(_handle: Handle, mut system_table: SystemTable) -> Status { uefi_services::init(&mut system_table).unwrap(); + crate::log::init(); let app = read_app(system_table.boot_services());