diff --git a/Cargo.lock b/Cargo.lock index d23373f06d..5a0454c37f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,17 @@ dependencies = [ "tock-registers", ] +[[package]] +name = "acpi" +version = "4.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654f48ab3178632ea535be1765073b990895cb62f70a7e5671975d7150c26d15" +dependencies = [ + "bit_field", + "log", + "rsdp", +] + [[package]] name = "allocator" version = "0.1.0" @@ -20,6 +31,19 @@ dependencies = [ "slab_allocator", ] +[[package]] +name = "aml" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4f8cba7d4260ea05671dda81029f6f718b54402a4ec926a0d9a41bdbb96b415" +dependencies = [ + "bit_field", + "bitvec", + "byteorder", + "log", + "spinning_top", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -279,6 +303,8 @@ name = "axhal" version = "0.1.0" dependencies = [ "aarch64-cpu", + "acpi", + "aml", "arm_gic", "arm_pl011", "axalloc", @@ -458,6 +484,18 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "buddy_system_allocator" version = "0.9.0" @@ -771,6 +809,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "getrandom" version = "0.2.10" @@ -1202,6 +1246,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1289,6 +1339,15 @@ dependencies = [ "embedded-hal", ] +[[package]] +name = "rsdp" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66d3add2fc55ef37511bcf81a08ee7a09eff07b23aae38b06a29024a38c604b1" +dependencies = [ + "log", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1449,6 +1508,15 @@ dependencies = [ "kernel_guard", ] +[[package]] +name = "spinning_top" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1489,6 +1557,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.6.0" @@ -1820,6 +1894,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x2apic" version = "0.4.2" diff --git a/modules/axconfig/src/platform/pc-x86.toml b/modules/axconfig/src/platform/pc-x86.toml index d06e5ce352..b3886c7b89 100644 --- a/modules/axconfig/src/platform/pc-x86.toml +++ b/modules/axconfig/src/platform/pc-x86.toml @@ -21,6 +21,7 @@ mmio-regions = [ ["0xfec0_0000", "0x1000"], # IO APIC ["0xfed0_0000", "0x1000"], # HPET ["0xfee0_0000", "0x1000"], # Local APIC + ["0xe_0000", "0x2_0000"], # ACPI ] # VirtIO MMIO regions with format (`base_paddr`, `size`). virtio-mmio-regions = [] diff --git a/modules/axdriver/src/bus/pci.rs b/modules/axdriver/src/bus/pci.rs index f47b2757e6..78f057c858 100644 --- a/modules/axdriver/src/bus/pci.rs +++ b/modules/axdriver/src/bus/pci.rs @@ -84,7 +84,14 @@ fn config_pci_device( impl AllDevices { pub(crate) fn probe_bus_devices(&mut self) { - let base_vaddr = phys_to_virt(axconfig::PCI_ECAM_BASE.into()); + cfg_if::cfg_if! { + if #[cfg(all(target_arch = "x86_64",feature = "virtio"))] { + let pci_ecam_base = axhal::pci::get_ecam_address().unwrap(); + } else { + let pci_ecam_base = axconfig::PCI_ECAM_BASE.into(); + } + } + let base_vaddr = phys_to_virt(pci_ecam_base); let mut root = unsafe { PciRoot::new(base_vaddr.as_mut_ptr(), Cam::Ecam) }; // PCI 32-bit MMIO space diff --git a/modules/axhal/Cargo.toml b/modules/axhal/Cargo.toml index f30079a50c..5de30e580c 100644 --- a/modules/axhal/Cargo.toml +++ b/modules/axhal/Cargo.toml @@ -50,6 +50,8 @@ x86 = "0.52" x86_64 = "0.14" x2apic = "0.4" raw-cpuid = "11.0" +acpi = "4.1.1" +aml = "0.16.4" [target.'cfg(any(target_arch = "riscv32", target_arch = "riscv64"))'.dependencies] riscv = "0.10" diff --git a/modules/axhal/src/arch/x86_64/mod.rs b/modules/axhal/src/arch/x86_64/mod.rs index 5a51ac620b..ebbf794d29 100644 --- a/modules/axhal/src/arch/x86_64/mod.rs +++ b/modules/axhal/src/arch/x86_64/mod.rs @@ -2,7 +2,7 @@ mod context; mod gdt; mod idt; -#[cfg(target_os = "none")] +// #[cfg(target_os = "none")] mod trap; use core::arch::asm; @@ -14,6 +14,7 @@ use x86_64::instructions::interrupts; pub use self::context::{ExtendedState, FxsaveArea, TaskContext, TrapFrame}; pub use self::gdt::GdtStruct; pub use self::idt::IdtStruct; +pub use self::trap::{IRQ_VECTOR_END, IRQ_VECTOR_START}; pub use x86_64::structures::tss::TaskStateSegment; /// Allows the current CPU to respond to interrupts. diff --git a/modules/axhal/src/arch/x86_64/trap.rs b/modules/axhal/src/arch/x86_64/trap.rs index 121a6864f9..5c35add6b3 100644 --- a/modules/axhal/src/arch/x86_64/trap.rs +++ b/modules/axhal/src/arch/x86_64/trap.rs @@ -4,8 +4,10 @@ use super::context::TrapFrame; core::arch::global_asm!(include_str!("trap.S")); -const IRQ_VECTOR_START: u8 = 0x20; -const IRQ_VECTOR_END: u8 = 0xff; +/// start value of irq vector +pub const IRQ_VECTOR_START: u8 = 0x20; +/// end value of irq vector +pub const IRQ_VECTOR_END: u8 = 0xff; #[no_mangle] fn x86_trap_handler(tf: &mut TrapFrame) { diff --git a/modules/axhal/src/lib.rs b/modules/axhal/src/lib.rs index f8201362fd..31aecff098 100644 --- a/modules/axhal/src/lib.rs +++ b/modules/axhal/src/lib.rs @@ -77,3 +77,11 @@ pub use self::platform::platform_init; #[cfg(feature = "smp")] pub use self::platform::platform_init_secondary; + +/// PCI related operations +pub mod pci { + #[cfg(all(target_arch = "x86_64", feature = "axalloc"))] + pub use super::platform::acpi::get_ecam_address; + #[cfg(all(target_arch = "x86_64", feature = "irq", feature = "axalloc"))] + pub use super::platform::acpi::get_pci_irq_vector; +} diff --git a/modules/axhal/src/platform/dummy/mod.rs b/modules/axhal/src/platform/dummy/mod.rs index 6b19c097d8..f43dcdc6f5 100644 --- a/modules/axhal/src/platform/dummy/mod.rs +++ b/modules/axhal/src/platform/dummy/mod.rs @@ -91,3 +91,16 @@ pub fn platform_init() {} /// Initializes the platform devices for secondary CPUs. #[cfg(feature = "smp")] pub fn platform_init_secondary() {} + +pub mod acpi { + #[cfg(feature = "irq")] + pub fn get_pci_irq_vector(bus: u8, device: u8, function: u8) -> Option { + None + } + + use crate::mem::PhysAddr; + /// Get PCIe ECAM space physical address. + pub fn get_ecam_address() -> Option { + None + } +} diff --git a/modules/axhal/src/platform/pc_x86/acpi.rs b/modules/axhal/src/platform/pc_x86/acpi.rs new file mode 100644 index 0000000000..1b2241e132 --- /dev/null +++ b/modules/axhal/src/platform/pc_x86/acpi.rs @@ -0,0 +1,332 @@ +extern crate alloc; + +use alloc::boxed::Box; +use alloc::format; +use core::ptr::NonNull; + +use acpi::{AcpiTables, PhysicalMapping}; +use aml::pci_routing::{IrqDescriptor, PciRoutingTable, Pin}; +use aml::{AmlContext, AmlName, DebugVerbosity}; + +use crate::mem::phys_to_virt; +use lazy_init::LazyInit; +use memory_addr::PhysAddr; + +#[cfg(feature = "irq")] +use crate::platform::irq::irq_to_vector; + +#[derive(Clone)] +struct LocalAcpiHandler; + +impl acpi::AcpiHandler for LocalAcpiHandler { + unsafe fn map_physical_region( + &self, + physical_address: usize, + size: usize, + ) -> PhysicalMapping { + let vaddr = phys_to_virt(PhysAddr::from(physical_address)).as_mut_ptr(); + PhysicalMapping::new( + physical_address, + NonNull::new_unchecked(vaddr as *mut T), + size, + size, + self.clone(), + ) + } + fn unmap_physical_region(_region: &PhysicalMapping) {} +} + +struct LocalAmlHandler; + +impl aml::Handler for LocalAmlHandler { + fn read_u8(&self, address: usize) -> u8 { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_ptr(); + unsafe { vaddr.read_volatile() } + } + + fn read_u16(&self, address: usize) -> u16 { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_ptr() as *const u16; + unsafe { vaddr.read_volatile() } + } + + fn read_u32(&self, address: usize) -> u32 { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_ptr() as *const u32; + unsafe { vaddr.read_volatile() } + } + + fn read_u64(&self, address: usize) -> u64 { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_ptr() as *const u64; + unsafe { vaddr.read_volatile() } + } + + fn write_u8(&mut self, address: usize, value: u8) { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_mut_ptr(); + unsafe { vaddr.write_volatile(value) } + } + + fn write_u16(&mut self, address: usize, value: u16) { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_mut_ptr() as *mut u16; + unsafe { vaddr.write_volatile(value) } + } + + fn write_u32(&mut self, address: usize, value: u32) { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_mut_ptr() as *mut u32; + unsafe { vaddr.write_volatile(value) } + } + + fn write_u64(&mut self, address: usize, value: u64) { + let vaddr = phys_to_virt(PhysAddr::from(address)).as_mut_ptr() as *mut u64; + unsafe { vaddr.write_volatile(value) } + } + + fn read_io_u8(&self, port: u16) -> u8 { + unsafe { x86::io::inb(port) } + } + + fn read_io_u16(&self, port: u16) -> u16 { + unsafe { x86::io::inw(port) } + } + + fn read_io_u32(&self, port: u16) -> u32 { + unsafe { x86::io::inl(port) } + } + + fn write_io_u8(&self, port: u16, value: u8) { + unsafe { + x86::io::outb(port, value); + } + } + + fn write_io_u16(&self, port: u16, value: u16) { + unsafe { + x86::io::outw(port, value); + } + } + + fn write_io_u32(&self, port: u16, value: u32) { + unsafe { + x86::io::outl(port, value); + } + } + + fn read_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u8 { + let paddr = unsafe { + ACPI.get_pci_config_regions_addr(segment, bus, device, function) + .unwrap() + }; + let vaddr = phys_to_virt(PhysAddr::from(paddr as usize)).as_ptr(); + let address = unsafe { vaddr.add(offset as usize) }; + unsafe { address.read_volatile() } + } + + fn read_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u16 { + let paddr = unsafe { + ACPI.get_pci_config_regions_addr(segment, bus, device, function) + .unwrap() + }; + let vaddr = phys_to_virt(PhysAddr::from(paddr as usize)).as_ptr() as *const u16; + let address = unsafe { vaddr.add(offset as usize) }; + unsafe { address.read_volatile() } + } + + fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u32 { + let paddr = unsafe { + ACPI.get_pci_config_regions_addr(segment, bus, device, function) + .unwrap() + }; + let vaddr = phys_to_virt(PhysAddr::from(paddr as usize)).as_ptr() as *const u32; + let address = unsafe { vaddr.add(offset as usize) }; + unsafe { address.read_volatile() } + } + + fn write_pci_u8( + &self, + segment: u16, + bus: u8, + device: u8, + function: u8, + offset: u16, + value: u8, + ) { + let paddr = unsafe { + ACPI.get_pci_config_regions_addr(segment, bus, device, function) + .unwrap() + }; + let vaddr = phys_to_virt(PhysAddr::from(paddr as usize)).as_mut_ptr(); + let address = unsafe { vaddr.add(offset as usize) }; + unsafe { address.write_volatile(value) } + } + + fn write_pci_u16( + &self, + segment: u16, + bus: u8, + device: u8, + function: u8, + offset: u16, + value: u16, + ) { + let paddr = unsafe { + ACPI.get_pci_config_regions_addr(segment, bus, device, function) + .unwrap() + }; + let vaddr = phys_to_virt(PhysAddr::from(paddr as usize)).as_mut_ptr() as *mut u16; + let address = unsafe { vaddr.add(offset as usize) }; + unsafe { address.write_volatile(value) } + } + + fn write_pci_u32( + &self, + segment: u16, + bus: u8, + device: u8, + function: u8, + offset: u16, + value: u32, + ) { + let paddr = unsafe { + ACPI.get_pci_config_regions_addr(segment, bus, device, function) + .unwrap() + }; + let vaddr = phys_to_virt(PhysAddr::from(paddr as usize)).as_mut_ptr() as *mut u32; + let address = unsafe { vaddr.add(offset as usize) }; + unsafe { address.write_volatile(value) } + } +} + +struct Acpi { + rsdp: AcpiTables, + aml_context: AmlContext, +} + +/// irq model used in ACPI +#[allow(dead_code)] +enum X86IrqModel { + /// PIC model + Pic, + /// APIC model + Apic, +} + +impl Acpi { + pub unsafe fn new() -> Self { + Acpi { + rsdp: AcpiTables::search_for_rsdp_bios(LocalAcpiHandler).unwrap(), + aml_context: AmlContext::new(Box::new(LocalAmlHandler), DebugVerbosity::None), + } + } + + fn init(&mut self) -> bool { + let dsdt = self.rsdp.dsdt.as_ref().unwrap(); + let paddr = PhysAddr::from(dsdt.address); + let vaddr = phys_to_virt(paddr).as_mut_ptr(); + let slice = unsafe { core::slice::from_raw_parts_mut(vaddr, dsdt.length as usize) }; + if self.aml_context.parse_table(slice).is_err() { + return false; + } + self.set_irq_model(X86IrqModel::Apic) + } + + /// Set IRQ model that ACPI uses by invoking ACPI global method _PIC. + /// + /// This method changes the routing tables (PIC or APIC) to return when calling _PRT methods. + /// Since this method changes ACPI state, it could lead to concurrent problem. + /// But currently it is only invoked in init thus runs by primary cpu only. + /// We may need a lock for ACPI in the future as more ACPI state altering method implemented. + fn set_irq_model(&mut self, irq_model: X86IrqModel) -> bool { + let value = match irq_model { + X86IrqModel::Pic => 0, + X86IrqModel::Apic => 1, + }; + let mut arg = aml::value::Args::EMPTY; + if arg.store_arg(0, aml::AmlValue::Integer(value)).is_err() { + return false; + } + let result = self + .aml_context + .invoke_method(&AmlName::from_str("\\_PIC").unwrap(), arg); + if let Err(err) = result { + error!("set_irq_model failed:{:#?}", err); + return false; + } + true + } + + /// Get PCI IRQ by invoking device _PRT method. + /// + /// Each PCI bus that ACPI provides interrupt routing information for appears as a device + /// in the ACPI namespace. + /// Each of these devices contains a _PRT method that returns an array of objects describing + /// the interrupt routing for slots on that PCI bus. + #[allow(dead_code)] + fn get_pci_irq_desc(&mut self, bus: u8, device: u8, function: u8) -> Option { + match AmlName::from_str(format!("\\_SB.PCI{bus_id}._PRT", bus_id = bus).as_str()) { + Ok(prt_path) => { + match PciRoutingTable::from_prt_path(&prt_path, &mut self.aml_context) { + Ok(table) => { + if let Ok(irq_descriptor) = table.route( + device as u16, + function as u16, + Pin::IntA, + &mut self.aml_context, + ) { + Some(irq_descriptor) + } else { + None + } + } + Err(_) => None, + } + } + Err(_) => None, + } + } + + /// Get base physical address of the PCIe ECAM space from ACPI MCFG table. + /// + /// Currently the ACPI crate does not export MCFG internal structure, thus we can not get ECAM + /// space address directly. This method get configuration space address of bdf(0:0:0) instead. + fn get_ecam_address(&mut self) -> Option { + if let Ok(config) = acpi::mcfg::PciConfigRegions::new(&self.rsdp) { + return Some(config.physical_address(0, 0, 0, 0).unwrap()); + } + None + } + + /// Get PCIe configuration space physical address of device function. + fn get_pci_config_regions_addr( + &mut self, + segment_group_no: u16, + bus: u8, + device: u8, + function: u8, + ) -> Option { + if let Ok(config) = acpi::mcfg::PciConfigRegions::new(&self.rsdp) { + return config.physical_address(segment_group_no, bus, device, function); + } + None + } +} + +static mut ACPI: LazyInit = LazyInit::new(); + +pub(crate) fn init() { + unsafe { + let mut acpi = Acpi::new(); + acpi.init(); + ACPI.init_by(acpi); + } +} + +/// Get PCI IRQ and map it to vector used in OS. +/// Temporarily allow unused here because irq support for virtio hasn't ready yet. +#[cfg(feature = "irq")] +pub fn get_pci_irq_vector(bus: u8, device: u8, function: u8) -> Option { + unsafe { ACPI.get_pci_irq_desc(bus, device, function) } + .map(|irq_desc| irq_to_vector(irq_desc.irq as u8)) +} + +/// Get PCIe ECAM space physical address. +pub fn get_ecam_address() -> Option { + unsafe { ACPI.get_ecam_address() }.map(|ecam_addr| PhysAddr::from(ecam_addr as usize)) +} diff --git a/modules/axhal/src/platform/pc_x86/apic.rs b/modules/axhal/src/platform/pc_x86/apic.rs index eb4e3e5930..45c595066b 100644 --- a/modules/axhal/src/platform/pc_x86/apic.rs +++ b/modules/axhal/src/platform/pc_x86/apic.rs @@ -10,6 +10,24 @@ use x86_64::instructions::port::Port; use self::vectors::*; use crate::mem::phys_to_virt; +#[cfg(feature = "irq")] +use crate::platform::pc_x86::current_cpu_id; +#[cfg(feature = "irq")] +use x2apic::ioapic::{IrqFlags, IrqMode}; + +#[cfg(feature = "irq")] +use crate::arch::IRQ_VECTOR_START; +/// map external IRQ to vector +#[cfg(feature = "irq")] +pub fn irq_to_vector(irq: u8) -> usize { + (irq + IRQ_VECTOR_START) as usize +} +/// map vector to external IRQ +#[cfg(feature = "irq")] +pub fn vector_to_irq(vector: usize) -> u8 { + vector as u8 - IRQ_VECTOR_START +} + pub(super) mod vectors { pub const APIC_TIMER_VECTOR: u8 = 0xf0; pub const APIC_SPURIOUS_VECTOR: u8 = 0xf1; @@ -33,22 +51,40 @@ static IO_APIC: LazyInit> = LazyInit::new(); pub fn set_enable(vector: usize, enabled: bool) { // should not affect LAPIC interrupts if vector < APIC_TIMER_VECTOR as _ { + let irq = vector_to_irq(vector); unsafe { if enabled { - IO_APIC.lock().enable_irq(vector as u8); + IO_APIC.lock().enable_irq(irq as u8); } else { - IO_APIC.lock().disable_irq(vector as u8); + IO_APIC.lock().disable_irq(irq as u8); } } } } +/// Program IO_APIC in order to route IO_APIC IRQ to vector. +#[cfg(feature = "irq")] +fn ioapic_redirect(vector: usize) { + let mut ioapic = IO_APIC.lock(); + let irq = vector_to_irq(vector); + let mut table_entry = unsafe { ioapic.table_entry(irq) }; + table_entry.set_vector(vector as u8); + table_entry.set_mode(IrqMode::Fixed); + let irq_flag = table_entry.flags() - IrqFlags::MASKED; + table_entry.set_flags(irq_flag); + table_entry.set_dest(current_cpu_id() as u8); + unsafe { ioapic.set_table_entry(irq, table_entry) }; +} + /// Registers an IRQ handler for the given IRQ. /// /// It also enables the IRQ if the registration succeeds. It returns `false` if /// the registration failed. #[cfg(feature = "irq")] pub fn register_handler(vector: usize, handler: crate::irq::IrqHandler) -> bool { + if vector < APIC_TIMER_VECTOR as usize && vector >= IRQ_VECTOR_START as usize { + ioapic_redirect(vector); + } crate::irq::register_handler_common(vector, handler) } diff --git a/modules/axhal/src/platform/pc_x86/mod.rs b/modules/axhal/src/platform/pc_x86/mod.rs index ba3ce43630..640b6290c8 100644 --- a/modules/axhal/src/platform/pc_x86/mod.rs +++ b/modules/axhal/src/platform/pc_x86/mod.rs @@ -15,6 +15,9 @@ pub mod irq { pub use super::apic::*; } +#[cfg(feature = "axalloc")] +pub mod acpi; + pub mod console { pub use super::uart16550::*; } @@ -58,6 +61,8 @@ unsafe extern "C" fn rust_entry_secondary(magic: usize) { pub fn platform_init() { self::apic::init_primary(); self::time::init_primary(); + #[cfg(feature = "axalloc")] + self::acpi::init(); } /// Initializes the platform devices for secondary CPUs.