Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 7 additions & 86 deletions src/arch/x86_64/kernel/acpi.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
use core::{mem, ptr, slice, str};

use align_address::Align;
use free_list::{PageLayout, PageRange};
use hermit_sync::OnceCell;
use memory_addresses::{PhysAddr, VirtAddr};
use x86_64::instructions::port::Port;
use x86_64::structures::paging::PhysFrame;

use crate::arch::x86_64::mm::paging;
use crate::arch::x86_64::mm::paging::{
BasePageSize, PageSize, PageTableEntryFlags, PageTableEntryFlagsExt,
};
use crate::arch::x86_64::mm::paging::{BasePageSize, PageSize};
use crate::env;
use crate::mm::virtualmem::KERNEL_FREE_LIST;
use crate::mm::device_alloc::DeviceAlloc;

/// Memory at this physical address is supposed to contain a pointer to the Extended BIOS Data Area (EBDA).
const EBDA_PTR_LOCATION: PhysAddr = PhysAddr::new(0x0000_040e);
Expand Down Expand Up @@ -97,79 +93,19 @@ impl AcpiSdtHeader {
}

/// A convenience structure to work with an ACPI table.
/// Maps a single table to memory and frees the memory when a variable of this structure goes out of scope.
#[derive(Debug)]
pub struct AcpiTable<'a> {
header: &'a AcpiSdtHeader,
allocated_virtual_address: VirtAddr,
allocated_length: usize,
}

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::new(physical_address.as_u64());
let header = unsafe {
allocated_virtual_address
.as_ptr::<AcpiSdtHeader>()
.as_ref()
.unwrap()
};
let allocated_length = usize::try_from(header.length).unwrap();

return Self {
header,
allocated_virtual_address,
allocated_length,
};
}
fn map(phys_addr: PhysAddr) -> Self {
// We expect physical devices to be already mapped according to `DeviceAlloc`.
let ptr = DeviceAlloc.ptr_from::<AcpiSdtHeader>(phys_addr);

let mut flags = PageTableEntryFlags::empty();
flags.normal().read_only().execute_disable();

// Allocate two 4 KiB pages for the table and map it.
// This guarantees that we can access at least the "length" field of the table header when its physical address
// crosses a page boundary.
let mut allocated_length = 2 * BasePageSize::SIZE as usize;
let mut count = allocated_length / BasePageSize::SIZE as usize;

let physical_map_address = physical_address.align_down(BasePageSize::SIZE);
let offset = (physical_address - physical_map_address) as usize;
let layout = PageLayout::from_size(allocated_length).unwrap();
let page_range = KERNEL_FREE_LIST.lock().allocate(layout).unwrap();
let mut virtual_address = VirtAddr::from(page_range.start());
paging::map::<BasePageSize>(virtual_address, physical_map_address, count, flags);

// Get a pointer to the header and query the table length.
let mut header_ptr: *const AcpiSdtHeader = (virtual_address + offset).as_ptr();
let table_length = unsafe { (*header_ptr).length } as usize;

// Remap if the length exceeds what we've allocated.
if table_length > allocated_length - offset {
let range =
PageRange::from_start_len(virtual_address.as_usize(), allocated_length).unwrap();
unsafe {
KERNEL_FREE_LIST.lock().deallocate(range).unwrap();
}

allocated_length = (table_length + offset).align_up(BasePageSize::SIZE as usize);
count = allocated_length / BasePageSize::SIZE as usize;
let header = unsafe { ptr.as_ref().unwrap() };

let layout = PageLayout::from_size(allocated_length).unwrap();
let page_range = KERNEL_FREE_LIST.lock().allocate(layout).unwrap();
virtual_address = VirtAddr::from(page_range.start());
paging::map::<BasePageSize>(virtual_address, physical_map_address, count, flags);

header_ptr = (virtual_address + offset).as_ptr();
}

// Return the table.
Self {
header: unsafe { &*header_ptr },
allocated_virtual_address: virtual_address,
allocated_length,
}
Self { header }
}

pub fn header_start_address(&self) -> usize {
Expand All @@ -185,21 +121,6 @@ impl AcpiTable<'_> {
}
}

impl Drop for AcpiTable<'_> {
fn drop(&mut self) {
if !env::is_uefi() {
let range = PageRange::from_start_len(
self.allocated_virtual_address.as_usize(),
self.allocated_length,
)
.unwrap();
unsafe {
KERNEL_FREE_LIST.lock().deallocate(range).unwrap();
}
}
}
}

/// The ACPI Generic Address Structure (GAS).
/// Described in ACPI Specification 6.2 A, 5.2.3.2 Generic Address Structure.
#[repr(C, packed)]
Expand Down
Loading