diff --git a/acpi/src/handler.rs b/acpi/src/handler.rs index 72adc0d..b3d9286 100644 --- a/acpi/src/handler.rs +++ b/acpi/src/handler.rs @@ -1,4 +1,8 @@ -use core::{fmt, ops::Deref, ptr::NonNull}; +use core::{ + fmt, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; /// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by /// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::()` @@ -91,6 +95,15 @@ where } } +impl DerefMut for PhysicalMapping +where + H: AcpiHandler, +{ + fn deref_mut(&mut self) -> &mut T { + unsafe { self.virtual_start.as_mut() } + } +} + impl Drop for PhysicalMapping where H: AcpiHandler, diff --git a/acpi/src/madt.rs b/acpi/src/madt.rs index 6a3f7b7..e9b0192 100644 --- a/acpi/src/madt.rs +++ b/acpi/src/madt.rs @@ -1,5 +1,6 @@ use crate::{ sdt::{ExtendedField, SdtHeader, Signature}, + AcpiError, AcpiTable, }; use bit_field::BitField; @@ -21,6 +22,7 @@ pub enum MadtError { InvalidLocalNmiLine, MpsIntiInvalidPolarity, MpsIntiInvalidTriggerMode, + WakeupApsTimeout, } /// Represents the MADT - this contains the MADT header fields. You can then iterate over a `Madt` @@ -49,6 +51,18 @@ unsafe impl AcpiTable for Madt { } impl Madt { + pub fn get_mpwk_mailbox_addr(&self) -> Result { + for entry in self.entries() { + match entry { + MadtEntry::MultiprocessorWakeup(entry) => { + return Ok(entry.mailbox_address); + } + _ => {} + } + } + Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry)) + } + #[cfg(feature = "allocator_api")] pub fn parse_interrupt_model_in<'a, A>( &self, @@ -102,21 +116,18 @@ impl Madt { where A: core::alloc::Allocator + Clone, { - use crate::{ - platform::{ - interrupt::{ - Apic, - InterruptSourceOverride, - IoApic, - LocalInterruptLine, - NmiLine, - NmiProcessor, - NmiSource, - }, - Processor, - ProcessorState, + use crate::platform::{ + interrupt::{ + Apic, + InterruptSourceOverride, + IoApic, + LocalInterruptLine, + NmiLine, + NmiProcessor, + NmiSource, }, - AcpiError, + Processor, + ProcessorState, }; let mut local_apic_address = self.local_apic_address as u64; @@ -630,6 +641,36 @@ pub struct MultiprocessorWakeupEntry { pub mailbox_address: u64, } +#[derive(Debug, PartialEq, Eq)] +pub enum MpProtectedModeWakeupCommand { + Noop = 0, + Wakeup = 1, + Sleep = 2, + AcceptPages = 3, +} + +impl From for MpProtectedModeWakeupCommand { + fn from(value: u16) -> Self { + match value { + 0 => MpProtectedModeWakeupCommand::Noop, + 1 => MpProtectedModeWakeupCommand::Wakeup, + 2 => MpProtectedModeWakeupCommand::Sleep, + 3 => MpProtectedModeWakeupCommand::AcceptPages, + _ => panic!("Invalid value for MpProtectedModeWakeupCommand"), + } + } +} + +#[repr(C)] +pub struct MultiprocessorWakeupMailbox { + pub command: u16, + _reserved: u16, + pub apic_id: u32, + pub wakeup_vector: u64, + pub reserved_for_os: [u64; 254], + reserved_for_firmware: [u64; 256], +} + #[cfg(feature = "allocator_api")] fn parse_mps_inti_flags(flags: u16) -> crate::AcpiResult<(Polarity, TriggerMode)> { let polarity = match flags.get_bits(0..2) { diff --git a/acpi/src/platform/mod.rs b/acpi/src/platform/mod.rs index c8f2221..4741543 100644 --- a/acpi/src/platform/mod.rs +++ b/acpi/src/platform/mod.rs @@ -3,7 +3,7 @@ pub mod interrupt; use crate::{ address::GenericAddress, fadt::Fadt, - madt::Madt, + madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox}, AcpiError, AcpiHandler, AcpiResult, @@ -11,7 +11,7 @@ use crate::{ ManagedSlice, PowerProfile, }; -use core::alloc::Allocator; +use core::{alloc::Allocator, mem, ptr}; use interrupt::InterruptModel; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -132,3 +132,55 @@ where Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer }) } } + +/// Wakes up Application Processors (APs) using the Multiprocessor Wakeup Mailbox mechanism. +/// +/// This function writes to a specific memory-mapped mailbox to signal APs to wake up and start +/// executing from a given wakeup vector. It waits for the APs to acknowledge the wakeup command +/// by monitoring the mailbox command field until it is reset to `Noop`. +pub fn wakeup_aps( + tables: &AcpiTables, + handler: H, + apic_id: u32, + wakeup_vector: u64, + timeout_loops: u64, +) -> Result<(), AcpiError> +where + H: AcpiHandler, +{ + let madt = tables.find_table::(); + let mailbox_addr = madt.unwrap().get_mpwk_mailbox_addr()?; + let mut mpwk_mapping = unsafe { + handler.map_physical_region::( + mailbox_addr as usize, + mem::size_of::(), + ) + }; + + // Reset command + mpwk_mapping.command = MpProtectedModeWakeupCommand::Noop as u16; + + // Fill the mailbox + mpwk_mapping.apic_id = apic_id; + mpwk_mapping.wakeup_vector = wakeup_vector; + mpwk_mapping.command = MpProtectedModeWakeupCommand::Wakeup as u16; + + // Wait to join + let mut loops = 0; + let mut command = MpProtectedModeWakeupCommand::Wakeup; + while command != MpProtectedModeWakeupCommand::Noop { + if loops >= timeout_loops { + return Err(AcpiError::InvalidMadt(MadtError::WakeupApsTimeout)); + } + // SAFETY: The caller must ensure that the provided `handler` correctly handles these + // operations and that the specified `mailbox_addr` is valid. + unsafe { + command = ptr::read_volatile(&mpwk_mapping.command).into(); + } + core::hint::spin_loop(); + loops += 1; + } + drop(mpwk_mapping); + + Ok(()) +}