Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
Signed-off-by: Martin Kröning <martin.kroening@eonerc.rwth-aachen.de>
  • Loading branch information
mkroening committed Apr 27, 2024
1 parent b0a557f commit 3e95c63
Show file tree
Hide file tree
Showing 9 changed files with 656 additions and 119 deletions.
141 changes: 140 additions & 1 deletion src/arch/x86_64/kernel/acpi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ impl AcpiSdtHeader {
fn signature(&self) -> &str {
str::from_utf8(&self.signature).unwrap()
}
pub fn header_start_address(&self) -> usize {
self as *const _ as usize
}
}

/// A convenience structure to work with an ACPI table.
Expand Down Expand Up @@ -307,6 +310,16 @@ 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, ()> {
// For UEFI Systems, RSDP detection takes place in the bootloader and the tables are already mapped so we only need to return a proper reference to the table
if crate::arch::kernel::is_uefi() {
let rsdp = crate::arch::kernel::get_rsdp_addr();
trace!("RSDP detected successfully at {rsdp:#x?}");
let rsdp = unsafe { &*(rsdp as *const AcpiRsdp) };
if &rsdp.signature != b"RSD PTR " {
panic!("RSDP Address not valid!");
}
return Ok(rsdp);
}
// Get the address of the EBDA.
let frame =
PhysFrame::<BasePageSize>::containing_address(x86_64::PhysAddr::new(EBDA_PTR_LOCATION.0));
Expand Down Expand Up @@ -410,7 +423,26 @@ fn parse_fadt(fadt: AcpiTable<'_>) {
} else {
PhysAddr(fadt_table.dsdt.into())
};
let dsdt = AcpiTable::map(dsdt_address);
let dsdt = if !crate::arch::kernel::is_uefi() {
AcpiTable::map(dsdt_address)
} else {
// For UEFI Systems, the tables are already mapped so we only need to return a proper reference to the table
let table = unsafe { (dsdt_address.0 as *const AcpiSdtHeader).as_ref().unwrap() };
let mut length = table.length as u64;
let res = length % BasePageSize::SIZE;
//ACPI tables are 4KiB aligned, so the length can span the entire pagetable
//-> necessary since the tables are dropped (and virtual memory is deallocated) after use
//somehow, this table is larger than 4KiB, so we bumb it up to the entire page
if res != 0 {
length += 0x1000 - res;
}

AcpiTable {
header: table,
allocated_virtual_address: VirtAddr(dsdt_address.0),
allocated_length: length as usize,
}
};

// Check it.
assert!(
Expand Down Expand Up @@ -521,3 +553,110 @@ pub fn init() {
}
}
}

pub fn init_uefi() {
// Retrieve the RSDP and get a pointer to either the XSDT (64-bit) or RSDT (32-bit), whichever is available.
// Both are called RSDT in the following.
let rsdp = detect_acpi().expect("Hermit requires an ACPI-compliant system");
let rsdt_physical_address = if rsdp.revision >= 2 {
PhysAddr(rsdp.xsdt_physical_address)
} else {
PhysAddr(rsdp.rsdt_physical_address.into())
};

//Load RSDT
let rsdt = &unsafe { *(rsdt_physical_address.0 as *const AcpiSdtHeader) };

// The RSDT contains pointers to all available ACPI tables.
// Iterate through them.
let mut current_address = rsdt_physical_address.0 as usize + mem::size_of::<AcpiRsdp>();
trace!("RSDT start address at {current_address:#x}");
let nr_entries =
(rsdt.length as usize - mem::size_of::<AcpiRsdp>()) / mem::size_of::<AcpiSdtHeader>();
let end_addr = current_address + nr_entries * mem::size_of::<AcpiSdtHeader>();
while current_address < end_addr {
trace!("current_address: {current_address:#x}");

// Depending on the RSDP revision, either an XSDT or an RSDT has been chosen above.
// The XSDT contains 64-bit pointers whereas the RSDT has 32-bit pointers.
let table_physical_address = if rsdp.revision >= 2 {
let address = PhysAddr(unsafe { ptr::read_unaligned(current_address as *const u64) });
current_address += mem::size_of::<u64>();
address
} else {
let address =
PhysAddr((unsafe { ptr::read_unaligned(current_address as *const u32) }).into());
current_address += mem::size_of::<u32>();
address
};

let table = unsafe {
(table_physical_address.0 as *const AcpiSdtHeader)
.as_ref()
.unwrap()
};

let signature = table.signature();

debug!("Found ACPI table: {signature:#?}");

if signature == "APIC" {
// This is a "Multiple APIC Descriptor Table" (MADT) aka "APIC Table"
// Check and save the entire APIC table for the get_apic_table() call
assert!(
verify_checksum(table.header_start_address(), table.length as usize).is_ok(),
"MADT at {table_physical_address:#x} has invalid checksum"
);
//ACPI tables are 4KiB aligned, so the length can span the entire pagetable
//-> necessary since the tables are dropped (and virtual memory is deallocated) after use
let mut length = table.length as u64;
if length < 0x1000 {
length = 0x1000;
}

let madt: AcpiTable<'static> = AcpiTable {
header: table,
allocated_virtual_address: VirtAddr(table_physical_address.0),
allocated_length: length as usize,
};
MADT.set(madt).unwrap();
} else if signature == "FACP" {
// This is the "Fixed ACPI Description Table" (FADT) aka "Fixed ACPI Control Pointer" (FACP)
// Check and parse this table for the poweroff() call
assert!(
verify_checksum(table.header_start_address(), table.length as usize).is_ok(),
"FADT at {table_physical_address:#x} has invalid checksum"
);
//ACPI tables are 4KiB aligned, so the length can span the entire pagetable
//-> necessary since the tables are dropped (and virtual memory is deallocated) after use
let mut length = table.length as u64;
if length < 0x1000 {
length = 0x1000;
}
let fadt: AcpiTable<'static> = AcpiTable {
header: table,
allocated_virtual_address: VirtAddr(table_physical_address.0),
allocated_length: length as usize,
};

parse_fadt(fadt);
} else if signature == "SSDT" {
assert!(
verify_checksum(table.header_start_address(), table.length as usize).is_ok(),
"SSDT at {table_physical_address:p} has invalid checksum"
);

let mut length = table.length as u64;
if length < 0x1000 {
length = 0x1000;
}

let ssdt: AcpiTable<'static> = AcpiTable {
header: table,
allocated_virtual_address: VirtAddr(table_physical_address.0),
allocated_length: length as usize,
};
parse_ssdt(ssdt);
}
}
}
87 changes: 59 additions & 28 deletions src/arch/x86_64/kernel/apic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 !crate::kernel::is_uefi() {
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::<BasePageSize>(ioapic_address, phys_addr, 1, flags);
let mut flags = PageTableEntryFlags::empty();
flags.device().writable().execute_disable();
paging::map::<BasePageSize>(ioapic_address, phys_addr, 1, flags);
} else {
// 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();
}
}

#[cfg(not(feature = "acpi"))]
Expand Down Expand Up @@ -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 crate::kernel::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::<BasePageSize>(local_apic_address, local_apic_physical_address, 1, flags);
let mut flags = PageTableEntryFlags::empty();
flags.device().writable().execute_disable();
paging::map::<BasePageSize>(local_apic_address, local_apic_physical_address, 1, flags);
}
}

// Set gates to ISRs for the APIC interrupts we are going to enable.
Expand Down Expand Up @@ -696,6 +708,8 @@ pub fn init_next_processor_variables() {
pub fn boot_application_processors() {
use core::hint;

use x86_64::structures::paging::Translate;

use super::{raw_boot_info, start};

let smp_boot_code = include_bytes!(concat!(core::env!("OUT_DIR"), "/boot.bin"));
Expand All @@ -706,20 +720,37 @@ pub fn boot_application_processors() {
"SMP Boot Code is larger than a page"
);
debug!("SMP boot code is {} bytes long", smp_boot_code.len());
// We can only allocate full pages of physmem

// 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::<BasePageSize>(
SMP_BOOT_CODE_ADDRESS,
PhysAddr(SMP_BOOT_CODE_ADDRESS.as_u64()),
1,
flags,
);
if crate::kernel::is_uefi() {
// Since UEFI already provides identity-mapped pagetables, we only have to check whether the physical address 0x8000 really is mapped to the virtual address 0x8000
unsafe {
let pt = crate::arch::mm::paging::identity_mapped_page_table();
let virt_addr = 0x8000;
let phys_addr = pt
.translate_addr(x86_64::VirtAddr::new(virt_addr))
.unwrap()
.as_u64();
assert_eq!(
phys_addr, virt_addr,
"0x8000 is not identity-mapped for SMP boot code!"
)
}
} 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::<BasePageSize>(
SMP_BOOT_CODE_ADDRESS,
PhysAddr(SMP_BOOT_CODE_ADDRESS.as_u64()),
1,
flags,
);
}
unsafe {
ptr::copy_nonoverlapping(
smp_boot_code.as_ptr(),
Expand Down
22 changes: 21 additions & 1 deletion src/arch/x86_64/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,26 @@ pub fn get_image_size() -> usize {
(range.end - range.start) as usize
}

pub fn get_start() -> usize {
boot_info().hardware_info.phys_addr_range.start as usize
}

pub fn get_limit() -> usize {
boot_info().hardware_info.phys_addr_range.end as usize
}

pub fn is_uefi() -> bool {
let fdt_addr = get_fdt().unwrap();
let fdt = unsafe { fdt::Fdt::from_ptr(fdt_addr as *const u8).unwrap() };
fdt.root().compatible().first() == "hermit,uefi"
}

pub fn get_rsdp_addr() -> u64 {
let fdt_addr = get_fdt().unwrap();
let fdt = unsafe { fdt::Fdt::from_ptr(fdt_addr as *const u8).unwrap() };
fdt.find_node("/hermit,rsdp").unwrap().reg().unwrap().next().unwrap().starting_address as u64
}

pub fn get_mbinfo() -> Option<NonZeroU64> {
match boot_info().platform_info {
PlatformInfo::Multiboot {
Expand Down Expand Up @@ -184,7 +200,11 @@ pub fn boot_processor_init() {
}
if !env::is_uhyve() {
#[cfg(feature = "acpi")]
acpi::init();
if is_uefi() {
acpi::init_uefi();
} else {
acpi::init();
}
}

apic::init();
Expand Down
57 changes: 30 additions & 27 deletions src/arch/x86_64/kernel/scheduler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,29 +116,30 @@ impl TaskStacks {
let mut flags = PageTableEntryFlags::empty();
flags.normal().writable().execute_disable();

// map IST1 into the address space
crate::arch::mm::paging::map::<BasePageSize>(
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::<BasePageSize>(
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::<BasePageSize>(
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,
);
// For UEFI systems, the entire memory is already mapped, just clear the stack for safety
if !crate::kernel::is_uefi() {
// map IST1 into the address space
crate::arch::mm::paging::map::<BasePageSize>(
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::<BasePageSize>(
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::<BasePageSize>(
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 {
Expand Down Expand Up @@ -225,10 +226,12 @@ impl Drop for TaskStacks {
stacks.total_size >> 10,
);

crate::arch::mm::paging::unmap::<BasePageSize>(
stacks.virt_addr,
stacks.total_size / BasePageSize::SIZE as usize + 4,
);
if !crate::kernel::is_uefi() {
crate::arch::mm::paging::unmap::<BasePageSize>(
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,
Expand Down
Loading

0 comments on commit 3e95c63

Please sign in to comment.