From ceff58e8134c424fd0a60d7af11a6ea4a13d0d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Kr=C3=B6ning?= Date: Mon, 28 Oct 2024 16:15:19 +0100 Subject: [PATCH] feat: UEFI MVP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sarah Simon Signed-off-by: Martin Kröning --- .github/workflows/ci.yml | 11 ++++ src/arch/x86_64/kernel/acpi.rs | 36 +++++++++++- src/arch/x86_64/kernel/apic.rs | 81 +++++++++++++++++---------- src/arch/x86_64/kernel/scheduler.rs | 55 +++++++++--------- src/arch/x86_64/mm/paging.rs | 87 +++++++++++++++++++++++++---- src/arch/x86_64/mm/physicalmem.rs | 46 ++++++++++----- src/arch/x86_64/mm/virtualmem.rs | 29 ++++++++-- src/drivers/pci.rs | 8 ++- src/env.rs | 16 ++++++ src/mm/mod.rs | 8 ++- xtask/src/ci/qemu.rs | 70 ++++++++++++++++++----- 11 files changed, 342 insertions(+), 105 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b322788fc..f21b5cca6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -182,6 +182,13 @@ jobs: run: | gh release download v1.4 --repo riscv-software-src/opensbi --pattern 'opensbi-*-rv-bin.tar.xz' tar -xvf opensbi-*-rv-bin.tar.xz opensbi-1.4-rv-bin/share/opensbi/lp64/generic/firmware/fw_jump.bin + - name: Download OVMF + run: | + sudo apt-get update + sudo apt-get install ovmf + mkdir -p edk2-stable202408-r1-bin/x64 + cp /usr/share/OVMF/OVMF_CODE.fd edk2-stable202408-r1-bin/x64/code.fd + cp /usr/share/OVMF/OVMF_VARS.fd edk2-stable202408-r1-bin/x64/vars.fd - name: Install Firecracker run: | # https://github.com/firecracker-microvm/firecracker/blob/v1.5.1/docs/getting-started.md#getting-a-firecracker-binary @@ -204,6 +211,8 @@ jobs: - run: cargo +stable install cargo-careful if: matrix.profile == 'dev' - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package hello_world + - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package hello_world --uefi + if: matrix.arch == 'x86_64' - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package wasmtime-demo --features ci if: matrix.arch == 'x86_64' - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package hello_world --no-default-features --microvm @@ -222,6 +231,8 @@ jobs: if: matrix.arch != 'aarch64' # https://github.com/hermit-os/kernel/issues/1286 continue-on-error: ${{ matrix.arch == 'riscv64' }} + - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package rusty_demo --smp 4 --uefi + if: matrix.arch == 'x86_64' - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package rftrace-example --virtiofsd if: matrix.arch == 'x86_64' - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package httpd --features ci,hermit/dhcpv4 --netdev virtio-net-pci diff --git a/src/arch/x86_64/kernel/acpi.rs b/src/arch/x86_64/kernel/acpi.rs index 64988dfe56..a732b87720 100644 --- a/src/arch/x86_64/kernel/acpi.rs +++ b/src/arch/x86_64/kernel/acpi.rs @@ -9,6 +9,7 @@ use crate::arch::x86_64::mm::paging::{ BasePageSize, PageSize, PageTableEntryFlags, PageTableEntryFlagsExt, }; use crate::arch::x86_64::mm::{paging, virtualmem, PhysAddr, VirtAddr}; +use crate::env; /// Memory at this physical address is supposed to contain a pointer to the Extended BIOS Data Area (EBDA). const EBDA_PTR_LOCATION: PhysAddr = PhysAddr(0x0000_040E); @@ -98,6 +99,24 @@ pub struct AcpiTable<'a> { impl AcpiTable<'_> { fn map(physical_address: PhysAddr) -> Self { + if env::is_uefi() { + // For UEFI Systems, the tables are already mapped so we only need to return a proper reference to the table + let allocated_virtual_address = VirtAddr(physical_address.0); + let header = unsafe { + allocated_virtual_address + .as_ptr::() + .as_ref() + .unwrap() + }; + let allocated_length = usize::try_from(header.length).unwrap(); + + return Self { + header, + allocated_virtual_address, + allocated_length, + }; + } + let mut flags = PageTableEntryFlags::empty(); flags.normal().read_only().execute_disable(); @@ -152,7 +171,9 @@ impl AcpiTable<'_> { impl Drop for AcpiTable<'_> { fn drop(&mut self) { - virtualmem::deallocate(self.allocated_virtual_address, self.allocated_length); + if !env::is_uefi() { + virtualmem::deallocate(self.allocated_virtual_address, self.allocated_length); + } } } @@ -308,6 +329,19 @@ fn detect_rsdp(start_address: PhysAddr, end_address: PhysAddr) -> Result<&'stati /// Detects ACPI support of the computer system. /// Returns a reference to the ACPI RSDP within the Ok() if successful or an empty Err() on failure. fn detect_acpi() -> Result<&'static AcpiRsdp, ()> { + if let Some(rsdp) = env::rsdp() { + trace!("RSDP detected successfully at {rsdp:#x?}"); + let rsdp = unsafe { + ptr::with_exposed_provenance::(rsdp.get()) + .as_ref() + .unwrap() + }; + if &rsdp.signature != b"RSD PTR " { + panic!("RSDP Address not valid!"); + } + return Ok(rsdp); + } + // Get the address of the EBDA. let frame = PhysFrame::::containing_address(x86_64::PhysAddr::new(EBDA_PTR_LOCATION.0)); diff --git a/src/arch/x86_64/kernel/apic.rs b/src/arch/x86_64/kernel/apic.rs index 829802e686..528694a231 100644 --- a/src/arch/x86_64/kernel/apic.rs +++ b/src/arch/x86_64/kernel/apic.rs @@ -259,13 +259,18 @@ pub fn local_apic_id_count() -> u32 { } fn init_ioapic_address(phys_addr: PhysAddr) { - let ioapic_address = virtualmem::allocate(BasePageSize::SIZE as usize).unwrap(); - IOAPIC_ADDRESS.set(ioapic_address).unwrap(); - debug!("Mapping IOAPIC at {phys_addr:p} to virtual address {ioapic_address:p}",); + if env::is_uefi() { + // UEFI systems have already id mapped everything, so we can just set the physical address as the virtual one + IOAPIC_ADDRESS.set(VirtAddr(phys_addr.as_u64())).unwrap(); + } else { + let ioapic_address = virtualmem::allocate(BasePageSize::SIZE as usize).unwrap(); + IOAPIC_ADDRESS.set(ioapic_address).unwrap(); + debug!("Mapping IOAPIC at {phys_addr:p} to virtual address {ioapic_address:p}",); - let mut flags = PageTableEntryFlags::empty(); - flags.device().writable().execute_disable(); - paging::map::(ioapic_address, phys_addr, 1, flags); + let mut flags = PageTableEntryFlags::empty(); + flags.device().writable().execute_disable(); + paging::map::(ioapic_address, phys_addr, 1, flags); + } } #[cfg(not(feature = "acpi"))] @@ -468,16 +473,23 @@ pub fn init() { if !processor::supports_x2apic() { // We use the traditional xAPIC mode available on all x86-64 CPUs. // It uses a mapped page for communication. - let local_apic_address = virtualmem::allocate(BasePageSize::SIZE as usize).unwrap(); - LOCAL_APIC_ADDRESS.set(local_apic_address).unwrap(); - debug!( - "Mapping Local APIC at {:p} to virtual address {:p}", - local_apic_physical_address, local_apic_address - ); + if env::is_uefi() { + //already id mapped in UEFI systems, just use the physical address as virtual one + LOCAL_APIC_ADDRESS + .set(VirtAddr(local_apic_physical_address.as_u64())) + .unwrap(); + } else { + let local_apic_address = virtualmem::allocate(BasePageSize::SIZE as usize).unwrap(); + LOCAL_APIC_ADDRESS.set(local_apic_address).unwrap(); + debug!( + "Mapping Local APIC at {:p} to virtual address {:p}", + local_apic_physical_address, local_apic_address + ); - let mut flags = PageTableEntryFlags::empty(); - flags.device().writable().execute_disable(); - paging::map::(local_apic_address, local_apic_physical_address, 1, flags); + let mut flags = PageTableEntryFlags::empty(); + flags.device().writable().execute_disable(); + paging::map::(local_apic_address, local_apic_physical_address, 1, flags); + } } // Set gates to ISRs for the APIC interrupts we are going to enable. @@ -697,6 +709,8 @@ pub fn init_next_processor_variables() { pub fn boot_application_processors() { use core::hint; + use x86_64::structures::paging::Translate; + use super::start; let smp_boot_code = include_bytes!(concat!(core::env!("OUT_DIR"), "/boot.bin")); @@ -708,19 +722,30 @@ pub fn boot_application_processors() { ); debug!("SMP boot code is {} bytes long", smp_boot_code.len()); - // Identity-map the boot code page and copy over the code. - debug!( - "Mapping SMP boot code to physical and virtual address {:p}", - SMP_BOOT_CODE_ADDRESS - ); - let mut flags = PageTableEntryFlags::empty(); - flags.normal().writable(); - paging::map::( - SMP_BOOT_CODE_ADDRESS, - PhysAddr(SMP_BOOT_CODE_ADDRESS.as_u64()), - 1, - flags, - ); + if env::is_uefi() { + // Since UEFI already provides identity-mapped pagetables, we only have to sanity-check the identity mapping + let pt = unsafe { crate::arch::mm::paging::identity_mapped_page_table() }; + let virt_addr = SMP_BOOT_CODE_ADDRESS.0; + let phys_addr = pt + .translate_addr(x86_64::VirtAddr::new(virt_addr)) + .unwrap() + .as_u64(); + assert_eq!(phys_addr, virt_addr) + } else { + // Identity-map the boot code page and copy over the code. + debug!( + "Mapping SMP boot code to physical and virtual address {:p}", + SMP_BOOT_CODE_ADDRESS + ); + let mut flags = PageTableEntryFlags::empty(); + flags.normal().writable(); + paging::map::( + SMP_BOOT_CODE_ADDRESS, + PhysAddr(SMP_BOOT_CODE_ADDRESS.as_u64()), + 1, + flags, + ); + } unsafe { ptr::copy_nonoverlapping( smp_boot_code.as_ptr(), diff --git a/src/arch/x86_64/kernel/scheduler.rs b/src/arch/x86_64/kernel/scheduler.rs index b35215d52e..1de51e63f7 100644 --- a/src/arch/x86_64/kernel/scheduler.rs +++ b/src/arch/x86_64/kernel/scheduler.rs @@ -115,29 +115,32 @@ impl TaskStacks { let mut flags = PageTableEntryFlags::empty(); flags.normal().writable().execute_disable(); - // map IST1 into the address space - crate::arch::mm::paging::map::( - virt_addr + BasePageSize::SIZE, - phys_addr, - IST_SIZE / BasePageSize::SIZE as usize, - flags, - ); + // For UEFI systems, the entire memory is already mapped, just clear the stack for safety + if !env::is_uefi() { + // map IST1 into the address space + crate::arch::mm::paging::map::( + virt_addr + BasePageSize::SIZE, + phys_addr, + IST_SIZE / BasePageSize::SIZE as usize, + flags, + ); - // map kernel stack into the address space - crate::arch::mm::paging::map::( - virt_addr + IST_SIZE + 2 * BasePageSize::SIZE, - phys_addr + IST_SIZE, - DEFAULT_STACK_SIZE / BasePageSize::SIZE as usize, - flags, - ); + // map kernel stack into the address space + crate::arch::mm::paging::map::( + virt_addr + IST_SIZE + 2 * BasePageSize::SIZE, + phys_addr + IST_SIZE, + DEFAULT_STACK_SIZE / BasePageSize::SIZE as usize, + flags, + ); - // map user stack into the address space - crate::arch::mm::paging::map::( - virt_addr + IST_SIZE + DEFAULT_STACK_SIZE + 3 * BasePageSize::SIZE, - phys_addr + IST_SIZE + DEFAULT_STACK_SIZE, - user_stack_size / BasePageSize::SIZE as usize, - flags, - ); + // map user stack into the address space + crate::arch::mm::paging::map::( + virt_addr + IST_SIZE + DEFAULT_STACK_SIZE + 3 * BasePageSize::SIZE, + phys_addr + IST_SIZE + DEFAULT_STACK_SIZE, + user_stack_size / BasePageSize::SIZE as usize, + flags, + ); + } // clear user stack unsafe { @@ -224,10 +227,12 @@ impl Drop for TaskStacks { stacks.total_size >> 10, ); - crate::arch::mm::paging::unmap::( - stacks.virt_addr, - stacks.total_size / BasePageSize::SIZE as usize + 4, - ); + if !env::is_uefi() { + crate::arch::mm::paging::unmap::( + stacks.virt_addr, + stacks.total_size / BasePageSize::SIZE as usize + 4, + ); + } crate::arch::mm::virtualmem::deallocate( stacks.virt_addr, stacks.total_size + 4 * BasePageSize::SIZE as usize, diff --git a/src/arch/x86_64/mm/paging.rs b/src/arch/x86_64/mm/paging.rs index 9999ffccb0..7096a28f39 100644 --- a/src/arch/x86_64/mm/paging.rs +++ b/src/arch/x86_64/mm/paging.rs @@ -2,17 +2,18 @@ use core::fmt::Debug; use core::ptr; use x86_64::instructions::tlb; -use x86_64::registers::control::Cr2; +use x86_64::registers::control::{Cr0, Cr0Flags, Cr2, Cr3}; #[cfg(feature = "common-os")] use x86_64::registers::segmentation::SegmentSelector; pub use x86_64::structures::idt::InterruptStackFrame as ExceptionStackFrame; use x86_64::structures::idt::PageFaultErrorCode; use x86_64::structures::paging::frame::PhysFrameRange; -use x86_64::structures::paging::mapper::{TranslateResult, UnmapError}; +use x86_64::structures::paging::mapper::{MappedFrame, TranslateResult, UnmapError}; use x86_64::structures::paging::page::PageRange; pub use x86_64::structures::paging::PageTableFlags as PageTableEntryFlags; use x86_64::structures::paging::{ - Mapper, Page, PageTableIndex, PhysFrame, RecursivePageTable, Size2MiB, Translate, + Mapper, OffsetPageTable, Page, PageTable, PageTableIndex, PhysFrame, RecursivePageTable, + Size2MiB, Size4KiB, Translate, }; use crate::arch::x86_64::kernel::processor; @@ -91,6 +92,7 @@ pub use x86_64::structures::paging::{ PageSize, Size1GiB as HugePageSize, Size2MiB as LargePageSize, Size4KiB as BasePageSize, }; +/// Returns a recursive page table mapping, its last entry is mapped to the table itself unsafe fn recursive_page_table() -> RecursivePageTable<'static> { let level_4_table_addr = 0xFFFF_FFFF_FFFF_F000; let level_4_table_ptr = ptr::with_exposed_provenance_mut(level_4_table_addr); @@ -100,6 +102,17 @@ unsafe fn recursive_page_table() -> RecursivePageTable<'static> { } } +/// Returns a mapping of the physical memory where physical address is equal to the virtual address (no offset) +pub unsafe fn identity_mapped_page_table() -> OffsetPageTable<'static> { + let level_4_table_addr = Cr3::read().0.start_address().as_u64(); + let level_4_table_ptr = + ptr::with_exposed_provenance_mut::(level_4_table_addr.try_into().unwrap()); + unsafe { + let level_4_table = level_4_table_ptr.as_mut().unwrap(); + OffsetPageTable::new(level_4_table, x86_64::addr::VirtAddr::new(0x0)) + } +} + /// Translate a virtual memory address to a physical one. pub fn virtual_to_physical(virtual_address: VirtAddr) -> Option { let virtual_address = x86_64::VirtAddr::new(virtual_address.0); @@ -140,6 +153,7 @@ pub fn map( ) where S: PageSize + Debug, RecursivePageTable<'static>: Mapper, + OffsetPageTable<'static>: Mapper, { let pages = { let start = Page::::containing_address(x86_64::VirtAddr::new(virtual_address.0)); @@ -183,7 +197,11 @@ pub fn map( unmapped } - let unmapped = unsafe { map_pages(&mut recursive_page_table(), pages, frames, flags) }; + let unmapped = if env::is_uefi() { + unsafe { map_pages(&mut identity_mapped_page_table(), pages, frames, flags) } + } else { + unsafe { map_pages(&mut recursive_page_table(), pages, frames, flags) } + }; if unmapped { #[cfg(feature = "smp")] @@ -197,6 +215,7 @@ pub fn map_heap(virt_addr: VirtAddr, count: usize) -> Result<(), usize> where S: PageSize + Debug, RecursivePageTable<'static>: Mapper, + OffsetPageTable<'static>: Mapper, { let flags = { let mut flags = PageTableEntryFlags::empty(); @@ -302,7 +321,55 @@ pub(crate) extern "x86-interrupt" fn page_fault_handler( scheduler::abort(); } -pub fn init() {} +pub fn init() { + make_p4_writable(); +} + +fn make_p4_writable() { + debug!("Making P4 table writable"); + + if !env::is_uefi() { + return; + } + + let mut pt = unsafe { identity_mapped_page_table() }; + + let p4_page = { + let (p4_frame, _) = Cr3::read_raw(); + let p4_addr = x86_64::VirtAddr::new(p4_frame.start_address().as_u64()); + Page::::from_start_address(p4_addr).unwrap() + }; + + let TranslateResult::Mapped { frame, flags, .. } = pt.translate(p4_page.start_address()) else { + unreachable!() + }; + + let make_writable = || unsafe { + let flags = flags | PageTableEntryFlags::WRITABLE; + match frame { + MappedFrame::Size1GiB(_) => pt.set_flags_p3_entry(p4_page, flags).unwrap().ignore(), + MappedFrame::Size2MiB(_) => pt.set_flags_p2_entry(p4_page, flags).unwrap().ignore(), + MappedFrame::Size4KiB(_) => pt.update_flags(p4_page, flags).unwrap().ignore(), + } + }; + + unsafe fn without_protect(f: F) -> R + where + F: FnOnce() -> R, + { + let cr0 = Cr0::read(); + if cr0.contains(Cr0Flags::WRITE_PROTECT) { + unsafe { Cr0::write(cr0 - Cr0Flags::WRITE_PROTECT) } + } + let ret = f(); + if cr0.contains(Cr0Flags::WRITE_PROTECT) { + unsafe { Cr0::write(cr0) } + } + ret + } + + unsafe { without_protect(make_writable) } +} pub fn init_page_tables() { if env::is_uhyve() { @@ -374,9 +441,7 @@ unsafe fn print_page_table_entries(page_table_indices: &[PageTableIndex]) { let mut pt = recursive_page_table.level_4_table(); // Identity mapped - // let level_4_table_addr = Cr3::read().0.start_address().as_u64(); - // let level_4_table_ptr = - // ptr::with_exposed_provenance::(level_4_table_addr.try_into().unwrap()); + // let identity_mapped_page_table = unsafe { identity_mapped_page_table() }; // let pt = identity_mapped_page_table.level_4_table(); for (i, page_table_index) in page_table_indices.iter().copied().enumerate() { @@ -424,10 +489,8 @@ pub(crate) unsafe fn print_page_tables(levels: usize) { let pt = recursive_page_table.level_4_table(); // Identity mapped - //let level_4_table_addr = Cr3::read().0.start_address().as_u64(); - //let level_4_table_ptr = - // ptr::with_exposed_provenance::(level_4_table_addr.try_into().unwrap()); - //let pt = unsafe { &*level_4_table_ptr }; + // let identity_mapped_page_table = unsafe { identity_mapped_page_table() }; + // let pt = identity_mapped_page_table.level_4_table(); print(pt, 4, 5 - levels); } diff --git a/src/arch/x86_64/mm/physicalmem.rs b/src/arch/x86_64/mm/physicalmem.rs index 51a3334caa..659b1a34ca 100644 --- a/src/arch/x86_64/mm/physicalmem.rs +++ b/src/arch/x86_64/mm/physicalmem.rs @@ -26,28 +26,44 @@ fn detect_from_fdt() -> Result<(), ()> { let mut found_ram = false; - for m in all_regions { - let start_address = m.starting_address as u64; - let size = m.size.unwrap() as u64; - let end_address = start_address + size; - - if end_address <= mm::kernel_end_address().as_u64() { - continue; - } - + if env::is_uefi() { + let biggest_region = all_regions.max_by_key(|m| m.size.unwrap()).unwrap(); found_ram = true; - let start_address = if start_address <= mm::kernel_start_address().as_u64() { - mm::kernel_end_address() - } else { - VirtAddr(start_address) - }; + let range = PageRange::from_start_len( + biggest_region.starting_address.addr(), + biggest_region.size.unwrap(), + ) + .unwrap(); - let range = PageRange::new(start_address.as_usize(), end_address as usize).unwrap(); TOTAL_MEMORY.fetch_add(range.len().get(), Ordering::Relaxed); unsafe { PHYSICAL_FREE_LIST.lock().deallocate(range).unwrap(); } + } else { + for m in all_regions { + let start_address = m.starting_address as u64; + let size = m.size.unwrap() as u64; + let end_address = start_address + size; + + if end_address <= mm::kernel_end_address().as_u64() { + continue; + } + + found_ram = true; + + let start_address = if start_address <= mm::kernel_start_address().as_u64() { + mm::kernel_end_address() + } else { + VirtAddr(start_address) + }; + + let range = PageRange::new(start_address.as_usize(), end_address as usize).unwrap(); + TOTAL_MEMORY.fetch_add(range.len().get(), Ordering::Relaxed); + unsafe { + PHYSICAL_FREE_LIST.lock().deallocate(range).unwrap(); + } + } } if found_ram { diff --git a/src/arch/x86_64/mm/virtualmem.rs b/src/arch/x86_64/mm/virtualmem.rs index 390946f45e..be675b62ec 100644 --- a/src/arch/x86_64/mm/virtualmem.rs +++ b/src/arch/x86_64/mm/virtualmem.rs @@ -3,17 +3,34 @@ use hermit_sync::InterruptTicketMutex; use crate::arch::x86_64::mm::paging::{BasePageSize, PageSize}; use crate::arch::x86_64::mm::VirtAddr; -use crate::mm; +use crate::{env, mm}; static KERNEL_FREE_LIST: InterruptTicketMutex> = InterruptTicketMutex::new(FreeList::new()); pub fn init() { - let range = PageRange::new( - mm::kernel_end_address().as_usize(), - kernel_heap_end().as_usize(), - ) - .unwrap(); + let range = if env::is_uefi() { + let fdt = env::fdt().unwrap(); + + let biggest_region = fdt + .find_all_nodes("/memory") + .map(|m| m.reg().unwrap().next().unwrap()) + .max_by_key(|m| m.size.unwrap()) + .unwrap(); + + PageRange::from_start_len( + biggest_region.starting_address.addr(), + biggest_region.size.unwrap(), + ) + .unwrap() + } else { + PageRange::new( + mm::kernel_end_address().as_usize(), + kernel_heap_end().as_usize(), + ) + .unwrap() + }; + unsafe { KERNEL_FREE_LIST.lock().deallocate(range).unwrap(); } diff --git a/src/drivers/pci.rs b/src/drivers/pci.rs index 9c299d4224..07a9b10318 100644 --- a/src/drivers/pci.rs +++ b/src/drivers/pci.rs @@ -41,6 +41,7 @@ use crate::drivers::virtio::transport::pci::VirtioDriver; use crate::drivers::vsock::VirtioVsockDriver; #[allow(unused_imports)] use crate::drivers::{Driver, InterruptHandlerQueue}; +use crate::env; use crate::init_cell::InitCell; pub(crate) static PCI_DEVICES: InitCell>> = @@ -165,8 +166,11 @@ impl PciDevice { // We therefore do not need to reserve any additional memory in our kernel. // Map bar into RW^X virtual memory let physical_address = address; - let virtual_address = - crate::mm::map(PhysAddr::from(physical_address), size, true, true, no_cache); + let virtual_address = if env::is_uefi() { + VirtAddr::from(address) + } else { + crate::mm::map(PhysAddr::from(physical_address), size, true, true, no_cache) + }; Some((virtual_address, size)) } diff --git a/src/env.rs b/src/env.rs index 13fba8d62f..e710ffed04 100644 --- a/src/env.rs +++ b/src/env.rs @@ -47,6 +47,10 @@ pub fn is_uhyve() -> bool { matches!(boot_info().platform_info, PlatformInfo::Uhyve { .. }) } +pub fn is_uefi() -> bool { + fdt().is_some_and(|fdt| fdt.root().compatible().first() == "hermit,uefi") +} + pub fn fdt() -> Option> { boot_info().hardware_info.device_tree.map(|fdt| { let ptr = ptr::with_exposed_provenance(fdt.get().try_into().unwrap()); @@ -54,6 +58,18 @@ pub fn fdt() -> Option> { }) } +/// Returns the RSDP physical address if available. +#[cfg(all(target_arch = "x86_64", feature = "acpi"))] +pub fn rsdp() -> Option> { + let rsdp = fdt()? + .find_node("/hermit,rsdp")? + .reg()? + .next()? + .starting_address + .addr(); + core::num::NonZero::new(rsdp) +} + pub fn fdt_args() -> Option<&'static str> { fdt().and_then(|fdt| fdt.chosen().bootargs()) } diff --git a/src/mm/mod.rs b/src/mm/mod.rs index cda24d91ca..5d56defdd5 100644 --- a/src/mm/mod.rs +++ b/src/mm/mod.rs @@ -70,8 +70,12 @@ pub(crate) fn init() { let has_1gib_pages = arch::processor::supports_1gib_pages(); let has_2mib_pages = arch::processor::supports_2mib_pages(); - let min_mem = - kernel_addr_range.end.as_usize() - env::get_ram_address().as_usize() + reserved_space; + let min_mem = if env::is_uefi() { + // On UEFI, the given memory is guaranteed free memory and the kernel is located before the given memory + reserved_space + } else { + kernel_addr_range.end.as_usize() - env::get_ram_address().as_usize() + reserved_space + }; info!("Minimum memory size: {}", min_mem >> 20); let avail_mem = total_mem .checked_sub(min_mem) diff --git a/xtask/src/ci/qemu.rs b/xtask/src/ci/qemu.rs index 774e98b63c..d7cc56be5a 100644 --- a/xtask/src/ci/qemu.rs +++ b/xtask/src/ci/qemu.rs @@ -30,6 +30,10 @@ pub struct Qemu { #[arg(long)] microvm: bool, + /// Enable UEFI. + #[arg(long)] + uefi: bool, + /// Enable a network device. #[arg(long)] netdev: Option, @@ -85,7 +89,7 @@ impl Qemu { let qemu = cmd!(sh, "{program} {arg...}") .args(&["-display", "none"]) .args(&["-serial", "stdio"]) - .args(&["-kernel", format!("hermit-loader-{arch}").as_ref()]) + .args(self.image_args()?) .args(self.machine_args()) .args(self.cpu_args()) .args(&["-smp", &self.smp.to_string()]) @@ -140,6 +144,48 @@ impl Qemu { Ok(()) } + fn image_args(&self) -> Result> { + let arch = self.build.cargo_build.artifact.arch.name(); + let exe_suffix = if self.uefi { ".efi" } else { "" }; + let loader = format!("hermit-loader-{arch}{exe_suffix}"); + + let image_args = if self.uefi { + let sh = crate::sh()?; + sh.create_dir("target/esp/efi/boot")?; + sh.copy_file(loader, "target/esp/efi/boot/bootx64.efi")?; + sh.copy_file(self.build.image(), "target/esp/efi/boot/hermit-app")?; + + vec![ + "-drive".to_string(), + "if=pflash,format=raw,readonly=on,file=edk2-stable202408-r1-bin/x64/code.fd" + .to_string(), + "-drive".to_string(), + "if=pflash,format=raw,readonly=on,file=edk2-stable202408-r1-bin/x64/vars.fd" + .to_string(), + "-drive".to_string(), + "format=raw,file=fat:rw:target/esp".to_string(), + ] + } else { + let mut image_args = vec!["-kernel".to_string(), loader]; + match self.build.cargo_build.artifact.arch { + Arch::X86_64 | Arch::Riscv64 => { + image_args.push("-initrd".to_string()); + image_args.push(self.build.image().into_os_string().into_string().unwrap()); + } + Arch::Aarch64 => { + image_args.push("-device".to_string()); + image_args.push(format!( + "guest-loader,addr=0x48000000,initrd={}", + self.build.image().display() + )); + } + } + image_args + }; + + Ok(image_args) + } + fn machine_args(&self) -> Vec { if self.microvm { let frequency = get_frequency(); @@ -186,8 +232,6 @@ impl Qemu { }; cpu_args.push("-device".to_string()); cpu_args.push("isa-debug-exit,iobase=0xf4,iosize=0x04".to_string()); - cpu_args.push("-initrd".to_string()); - cpu_args.push(self.build.image().into_os_string().into_string().unwrap()); cpu_args } Arch::Aarch64 => { @@ -197,22 +241,14 @@ impl Qemu { vec!["-cpu".to_string(), "cortex-a72".to_string()] }; cpu_args.push("-semihosting".to_string()); - cpu_args.push("-device".to_string()); - cpu_args.push(format!( - "guest-loader,addr=0x48000000,initrd={}", - self.build.image().display() - )); cpu_args } Arch::Riscv64 => { - let mut cpu_args = if self.accel { + if self.accel { todo!() } else { vec!["-cpu".to_string(), "rv64".to_string()] - }; - cpu_args.push("-initrd".to_string()); - cpu_args.push(self.build.image().into_os_string().into_string().unwrap()); - cpu_args + } } } } @@ -222,7 +258,13 @@ impl Qemu { && self.build.package == "hello_world" { return match self.build.cargo_build.artifact.arch { - Arch::X86_64 => 32, + Arch::X86_64 => { + if self.uefi { + 64 + } else { + 32 + } + } Arch::Aarch64 => 144, Arch::Riscv64 => 40, };