Support SPCR and clean up device memory allocation

This commit is contained in:
Jeremy Soller 2024-10-31 10:53:58 -06:00
parent e19c1404f7
commit 4db9673e2a
No known key found for this signature in database
GPG Key ID: D02FD439211AF56F
10 changed files with 218 additions and 109 deletions

View File

@ -1,5 +1,5 @@
use alloc::boxed::Box;
use core::{mem, ptr};
use core::mem;
use super::{find_sdt, sdt::Sdt};
use crate::{
@ -58,10 +58,9 @@ impl Gtdt {
unsafe { IRQ_CHIP.irq_enable(gsiv as u32) };
}
pub fn new(sdt: &'static Sdt) -> Option<Gtdt> {
pub fn new(sdt: &'static Sdt) -> Option<&'static Gtdt> {
if &sdt.signature == b"GTDT" && sdt.length as usize >= mem::size_of::<Gtdt>() {
let s = unsafe { ptr::read((sdt as *const Sdt) as *const Gtdt) };
Some(s)
Some(unsafe { &*((sdt as *const Sdt) as *const Gtdt) })
} else {
None
}

View File

@ -2,22 +2,9 @@ use core::{mem, ptr};
use core::ptr::{read_volatile, write_volatile};
use crate::{
memory::{Frame, KernelMapper},
paging::{entry::EntryFlags, PageFlags, PhysicalAddress},
};
use crate::memory::{map_device_memory, PhysicalAddress, PAGE_SIZE};
use super::{find_sdt, sdt::Sdt, ACPI_TABLE};
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct GenericAddressStructure {
_address_space: u8,
_bit_width: u8,
_bit_offset: u8,
_access_size: u8,
pub address: u64,
}
use super::{find_sdt, sdt::Sdt, GenericAddressStructure, ACPI_TABLE};
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
@ -56,8 +43,16 @@ impl Hpet {
pub fn new(sdt: &'static Sdt) -> Option<Hpet> {
if &sdt.signature == b"HPET" && sdt.length as usize >= mem::size_of::<Hpet>() {
let s = unsafe { ptr::read((sdt as *const Sdt) as *const Hpet) };
unsafe { s.base_address.init(&mut KernelMapper::lock()) };
Some(s)
if s.base_address.address_space == 0 {
unsafe { s.map() };
Some(s)
} else {
log::warn!(
"HPET has unsupported address space {}",
s.base_address.address_space
);
None
}
} else {
None
}
@ -66,14 +61,18 @@ impl Hpet {
//TODO: x86 use assumes only one HPET and only one GenericAddressStructure
#[cfg(target_arch = "x86")]
impl GenericAddressStructure {
pub unsafe fn init(&self, mapper: &mut KernelMapper) {
use crate::paging::{Page, VirtualAddress};
impl Hpet {
pub unsafe fn map(&self) {
use crate::{
memory::{Frame, KernelMapper},
paging::{entry::EntryFlags, Page, VirtualAddress},
};
use rmm::PageFlags;
let frame = Frame::containing(PhysicalAddress::new(self.address as usize));
let frame = Frame::containing(PhysicalAddress::new(self.base_address.address as usize));
let page = Page::containing_address(VirtualAddress::new(crate::HPET_OFFSET));
mapper
KernelMapper::lock()
.get_mut()
.expect(
"KernelMapper locked re-entrant while mapping memory for GenericAddressStructure",
@ -99,31 +98,23 @@ impl GenericAddressStructure {
}
#[cfg(not(target_arch = "x86"))]
impl GenericAddressStructure {
pub unsafe fn init(&self, mapper: &mut KernelMapper) {
let frame = Frame::containing(PhysicalAddress::new(self.address as usize));
let (_, result) = mapper
.get_mut()
.expect(
"KernelMapper locked re-entrant while mapping memory for GenericAddressStructure",
)
.map_linearly(
frame.base(),
PageFlags::new()
.write(true)
.custom_flag(EntryFlags::NO_CACHE.bits(), true),
)
.expect("failed to map memory for GenericAddressStructure");
result.flush();
impl Hpet {
pub unsafe fn map(&self) {
map_device_memory(
PhysicalAddress::new(self.base_address.address as usize),
PAGE_SIZE,
);
}
pub unsafe fn read_u64(&self, offset: usize) -> u64 {
read_volatile((self.address as usize + offset + crate::PHYS_OFFSET) as *const u64)
read_volatile(
(self.base_address.address as usize + offset + crate::PHYS_OFFSET) as *const u64,
)
}
pub unsafe fn write_u64(&mut self, offset: usize, value: u64) {
write_volatile(
(self.address as usize + offset + crate::PHYS_OFFSET) as *mut u64,
(self.base_address.address as usize + offset + crate::PHYS_OFFSET) as *mut u64,
value,
);
}

View File

@ -1,5 +1,4 @@
use alloc::{boxed::Box, vec::Vec};
use rmm::{Arch, PageFlags};
use super::{Madt, MadtEntry};
use crate::{
@ -7,28 +6,10 @@ use crate::{
gic::{GenericInterruptController, GicCpuIf, GicDistIf},
gicv3::{GicV3, GicV3CpuIf},
},
dtb::irqchip::{InterruptController, IrqChipItem, IRQ_CHIP},
memory::{Frame, KernelMapper, PhysicalAddress},
paging::{entry::EntryFlags, RmmA},
dtb::irqchip::{IrqChipItem, IRQ_CHIP},
memory::{map_device_memory, PhysicalAddress, PAGE_SIZE},
};
unsafe fn map_gic_page(phys: PhysicalAddress) {
let frame = Frame::containing(phys);
let (_, result) = KernelMapper::lock()
.get_mut()
.expect("KernelMapper locked re-entrant while mapping memory for GIC")
.map_linearly(
frame.base(),
PageFlags::new()
.write(true)
.custom_flag(EntryFlags::NO_CACHE.bits(), true),
)
.expect("failed to map memory for GIC");
result.flush();
}
fn add_irqchip(irqchip: Box<dyn InterruptController>) {}
pub(super) fn init(madt: Madt) {
let mut gicd_opt = None;
let mut giccs = Vec::new();
@ -55,8 +36,8 @@ pub(super) fn init(madt: Madt) {
let mut gic_dist_if = GicDistIf::default();
unsafe {
let phys = PhysicalAddress::new(gicd.physical_base_address as usize);
map_gic_page(phys);
gic_dist_if.init(RmmA::phys_to_virt(phys).data());
let virt = map_device_memory(phys, PAGE_SIZE);
gic_dist_if.init(virt.data());
};
log::info!("{:#x?}", gic_dist_if);
match gicd.gic_version {
@ -65,8 +46,8 @@ pub(super) fn init(madt: Madt) {
let mut gic_cpu_if = GicCpuIf::default();
unsafe {
let phys = PhysicalAddress::new(gicc.physical_base_address as usize);
map_gic_page(phys);
gic_cpu_if.init(RmmA::phys_to_virt(phys).data())
let virt = map_device_memory(phys, PAGE_SIZE);
gic_cpu_if.init(virt.data())
};
log::info!("{:#x?}", gic_cpu_if);
let gic = GenericInterruptController {

View File

@ -89,7 +89,10 @@ pub(super) fn init(madt: Madt) {
};
AP_READY.store(false, Ordering::SeqCst);
print!(" AP {} APIC {}:", ap_local_apic.processor, ap_local_apic.id);
print!(
" AP {} APIC {}:",
ap_local_apic.processor, ap_local_apic.id
);
// Send INIT IPI
{

View File

@ -23,6 +23,8 @@ mod rsdp;
mod rsdt;
mod rxsdt;
pub mod sdt;
#[cfg(target_arch = "aarch64")]
mod spcr;
mod xsdt;
unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::paging::PageMapper) {
@ -64,6 +66,16 @@ pub fn get_sdt(sdt_address: usize, mapper: &mut KernelMapper) -> &'static Sdt {
sdt
}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct GenericAddressStructure {
pub address_space: u8,
pub bit_width: u8,
pub bit_offset: u8,
pub access_size: u8,
pub address: u64,
}
pub enum RxsdtEnum {
Rsdt(Rsdt),
Xsdt(Xsdt),
@ -145,6 +157,9 @@ pub unsafe fn init(already_supplied_rsdp: Option<*const u8>) {
}
}
//TODO: support this on any arch
#[cfg(target_arch = "aarch64")]
spcr::Spcr::init();
// TODO: Enumerate processors in userspace, and then provide an ACPI-independent interface
// to initialize enumerated processors to userspace?
Madt::init();

117
src/acpi/spcr.rs Normal file
View File

@ -0,0 +1,117 @@
use core::mem;
use super::{find_sdt, sdt::Sdt, GenericAddressStructure};
use crate::{
device::{
serial::{SerialKind, COM1},
uart_pl011,
},
memory::{map_device_memory, PhysicalAddress, PAGE_SIZE},
};
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct Spcr {
pub header: Sdt,
pub interface_type: u8,
_reserved: [u8; 3],
pub base_address: GenericAddressStructure,
pub interrupt_type: u8,
pub irq: u8,
pub gsiv: u32,
pub configured_baud_rate: u8,
pub parity: u8,
pub stop_bits: u8,
pub flow_control: u8,
pub terminal_type: u8,
pub language: u8,
pub pci_device_id: u16,
pub pci_vendor_id: u16,
pub pci_bus: u8,
pub pci_device: u8,
pub pci_function: u8,
pub pci_flags: u32,
pub pci_segment: u8,
/*TODO: these fields are optional based on the table revision
pub uart_clock_frequency: u32,
pub precise_baud_rate: u32,
pub namespace_string_length: u16,
pub namespace_string_offset: u16,
*/
// namespace_string
}
impl Spcr {
pub fn init() {
let spcr_sdt = find_sdt("SPCR");
let spcr = if spcr_sdt.len() == 1 {
match Spcr::new(spcr_sdt[0]) {
Some(spcr) => spcr,
None => {
log::warn!("Failed to parse SPCR");
return;
}
}
} else {
log::warn!("Unable to find SPCR");
return;
};
if spcr.base_address.address == 0 {
// Serial disabled
return;
}
if spcr.header.revision >= 2 {
match spcr.interface_type {
3 => {
// PL011
if spcr.base_address.address_space == 0
&& spcr.base_address.bit_width == 32
&& spcr.base_address.bit_offset == 0
&& spcr.base_address.access_size == 3
{
let virt = unsafe {
map_device_memory(
PhysicalAddress::new(spcr.base_address.address as usize),
PAGE_SIZE,
)
};
let serial_port = uart_pl011::SerialPort::new(virt.data(), false);
*COM1.lock() = Some(SerialKind::Pl011(serial_port))
} else {
log::warn!(
"SPCR unsuppoted address for PL011 {:#x?}",
spcr.base_address
);
}
}
//TODO: support more types!
unsupported => {
log::warn!(
"SPCR revision {} unsupported interface type {}",
spcr.header.revision,
unsupported
);
}
}
} else if spcr.header.revision == 1 {
match spcr.interface_type {
//TODO: support more types!
unsupported => {
log::warn!("SPCR revision 1 unsupported interface type {}", unsupported);
}
}
} else {
log::warn!("SPCR unsupported revision {}", spcr.header.revision);
}
}
pub fn new(sdt: &'static Sdt) -> Option<&'static Spcr> {
if &sdt.signature == b"SPCR" && sdt.length as usize >= mem::size_of::<Spcr>() {
Some(unsafe { &*((sdt as *const Sdt) as *const Spcr) })
} else {
None
}
}
}

View File

@ -102,18 +102,6 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! {
// Try to find serial port prior to logging
if let Ok(dtb) = &dtb_res {
device::serial::init_early(dtb);
} else {
/*
//TODO: This is for QEMU debugging when using ACPI
use crate::device::{
serial::{SerialKind, COM1},
uart_pl011,
};
let mut serial_port =
uart_pl011::SerialPort::new(crate::PHYS_OFFSET + 0x9000000, false);
serial_port.init(false);
*COM1.lock() = Some(SerialKind::Pl011(serial_port))
*/
}
// Initialize logger
@ -174,9 +162,6 @@ pub unsafe extern "C" fn kstart(args_ptr: *const KernelArgs) -> ! {
register_bootloader_areas(args.areas_base, args.areas_size);
if let Ok(dtb) = &dtb_res {
register_dev_memory_ranges(dtb);
} else {
//TODO: THIS IS JUST FOR QEMU SERIAL WHEN ACPI
register_memory_region(0x9000000, 0x1000, BootloaderMemoryKind::Device);
}
register_memory_region(

View File

@ -25,13 +25,12 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool {
// Disable HPET
{
let mut config_word = hpet.base_address.read_u64(GENERAL_CONFIG_OFFSET);
let mut config_word = hpet.read_u64(GENERAL_CONFIG_OFFSET);
config_word &= !(LEG_RT_CNF | ENABLE_CNF);
hpet.base_address
.write_u64(GENERAL_CONFIG_OFFSET, config_word);
hpet.write_u64(GENERAL_CONFIG_OFFSET, config_word);
}
let capability = hpet.base_address.read_u64(CAPABILITY_OFFSET);
let capability = hpet.read_u64(CAPABILITY_OFFSET);
if capability & LEG_RT_CAP == 0 {
log::warn!("HPET missing capability LEG_RT_CAP");
return false;
@ -40,29 +39,26 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool {
let period_fs = capability >> 32;
let divisor = (pit::RATE as u64 * 1_000_000) / period_fs;
let t0_capabilities = hpet.base_address.read_u64(T0_CONFIG_CAPABILITY_OFFSET);
let t0_capabilities = hpet.read_u64(T0_CONFIG_CAPABILITY_OFFSET);
if t0_capabilities & PER_INT_CAP == 0 {
log::warn!("HPET T0 missing capability PER_INT_CAP");
return false;
}
let counter = hpet.base_address.read_u64(MAIN_COUNTER_OFFSET);
let counter = hpet.read_u64(MAIN_COUNTER_OFFSET);
let t0_config_word: u64 = TN_VAL_SET_CNF | TN_TYPE_CNF | TN_INT_ENB_CNF;
hpet.base_address
.write_u64(T0_CONFIG_CAPABILITY_OFFSET, t0_config_word);
hpet.write_u64(T0_CONFIG_CAPABILITY_OFFSET, t0_config_word);
// set accumulator value
hpet.base_address
.write_u64(T0_COMPARATOR_OFFSET, counter + divisor);
hpet.write_u64(T0_COMPARATOR_OFFSET, counter + divisor);
// set interval
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, divisor);
hpet.write_u64(T0_COMPARATOR_OFFSET, divisor);
// Enable interrupts from the HPET
{
let mut config_word: u64 = hpet.base_address.read_u64(GENERAL_CONFIG_OFFSET);
let mut config_word: u64 = hpet.read_u64(GENERAL_CONFIG_OFFSET);
config_word |= LEG_RT_CNF | ENABLE_CNF;
hpet.base_address
.write_u64(GENERAL_CONFIG_OFFSET, config_word);
hpet.write_u64(GENERAL_CONFIG_OFFSET, config_word);
}
println!("HPET After Init");
@ -74,7 +70,7 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool {
pub unsafe fn debug(hpet: &mut Hpet) {
println!("HPET @ {:#x}", { hpet.base_address.address });
let capability = hpet.base_address.read_u64(CAPABILITY_OFFSET);
let capability = hpet.read_u64(CAPABILITY_OFFSET);
{
println!(" caps: {:#x}", capability);
println!(" clock period: {}", (capability >> 32) as u32);
@ -88,16 +84,16 @@ pub unsafe fn debug(hpet: &mut Hpet) {
println!(" revision: {}", capability as u8);
}
let config_word = hpet.base_address.read_u64(GENERAL_CONFIG_OFFSET);
let config_word = hpet.read_u64(GENERAL_CONFIG_OFFSET);
println!(" config: {:#x}", config_word);
let interrupt_status = hpet.base_address.read_u64(GENERAL_INTERRUPT_OFFSET);
let interrupt_status = hpet.read_u64(GENERAL_INTERRUPT_OFFSET);
println!(" interrupt status: {:#x}", interrupt_status);
let counter = hpet.base_address.read_u64(MAIN_COUNTER_OFFSET);
let counter = hpet.read_u64(MAIN_COUNTER_OFFSET);
println!(" counter: {:#x}", counter);
let t0_capabilities = hpet.base_address.read_u64(T0_CONFIG_CAPABILITY_OFFSET);
let t0_capabilities = hpet.read_u64(T0_CONFIG_CAPABILITY_OFFSET);
println!(" T0 caps: {:#x}", t0_capabilities);
println!(
" interrupt routing: {:#x}",
@ -105,6 +101,6 @@ pub unsafe fn debug(hpet: &mut Hpet) {
);
println!(" flags: {:#x}", t0_capabilities as u16);
let t0_comparator = hpet.base_address.read_u64(T0_COMPARATOR_OFFSET);
let t0_comparator = hpet.read_u64(T0_COMPARATOR_OFFSET);
println!(" T0 comparator: {:#x}", t0_comparator);
}

View File

@ -19,11 +19,11 @@ fn hpet_or_pit() -> u128 {
//TODO: improve performance
// Current count
let counter = unsafe { hpet.base_address.read_u64(hpet::MAIN_COUNTER_OFFSET) };
let counter = unsafe { hpet.read_u64(hpet::MAIN_COUNTER_OFFSET) };
// Comparator holds next interrupt count
let comparator = unsafe { hpet.base_address.read_u64(hpet::T0_COMPARATOR_OFFSET) };
let comparator = unsafe { hpet.read_u64(hpet::T0_COMPARATOR_OFFSET) };
// Get period in femtoseconds
let capability = unsafe { hpet.base_address.read_u64(hpet::CAPABILITY_OFFSET) };
let capability = unsafe { hpet.read_u64(hpet::CAPABILITY_OFFSET) };
// There seems to be a bug in qemu on macos that causes the calculation to produce 0 for
// period_fs and hence a divide by zero calculating the divisor - workaround it while we

View File

@ -20,7 +20,7 @@ use crate::{
memory::{AccessMode, PfError},
},
kernel_executable_offsets::{__usercopy_end, __usercopy_start},
paging::Page,
paging::{entry::EntryFlags, Page, PageFlags},
syscall::error::{Error, ENOMEM},
};
use rmm::{BumpAllocator, FrameAllocator, FrameCount, FrameUsage, TableKind, VirtualAddress};
@ -233,6 +233,28 @@ pub unsafe fn deallocate_frame(frame: Frame) {
deallocate_p2frame(frame, 0)
}
// Helper function for quickly mapping device memory
pub unsafe fn map_device_memory(addr: PhysicalAddress, len: usize) -> VirtualAddress {
let mut mapper_lock = KernelMapper::lock();
let mapper = mapper_lock
.get_mut()
.expect("KernelMapper mapper locked re-entrant in map_device_memory");
let base = PhysicalAddress::new(crate::paging::round_down_pages(addr.data()));
let aligned_len = crate::paging::round_up_pages(len + (addr.data() - base.data()));
for page_idx in 0..aligned_len / crate::memory::PAGE_SIZE {
let (_, flush) = mapper
.map_linearly(
base.add(page_idx * crate::memory::PAGE_SIZE),
PageFlags::new()
.write(true)
.custom_flag(EntryFlags::NO_CACHE.bits(), true),
)
.expect("failed to linearly map SDT");
flush.flush();
}
RmmA::phys_to_virt(addr)
}
const ORDER_COUNT: u32 = 11;
const MAX_ORDER: u32 = ORDER_COUNT - 1;