Skip to content

Commit

Permalink
Implement the multiprocessor wakeup mechanism. (#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hsy-Intel authored Nov 11, 2024
1 parent e9a5930 commit bc969d0
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 17 deletions.
15 changes: 14 additions & 1 deletion acpi/src/handler.rs
Original file line number Diff line number Diff line change
@@ -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::<T>()`
Expand Down Expand Up @@ -91,6 +95,15 @@ where
}
}

impl<H, T> DerefMut for PhysicalMapping<H, T>
where
H: AcpiHandler,
{
fn deref_mut(&mut self) -> &mut T {
unsafe { self.virtual_start.as_mut() }
}
}

impl<H, T> Drop for PhysicalMapping<H, T>
where
H: AcpiHandler,
Expand Down
69 changes: 55 additions & 14 deletions acpi/src/madt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
sdt::{ExtendedField, SdtHeader, Signature},
AcpiError,
AcpiTable,
};
use bit_field::BitField;
Expand All @@ -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`
Expand Down Expand Up @@ -49,6 +51,18 @@ unsafe impl AcpiTable for Madt {
}

impl Madt {
pub fn get_mpwk_mailbox_addr(&self) -> Result<u64, AcpiError> {
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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<u16> 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) {
Expand Down
64 changes: 62 additions & 2 deletions acpi/src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ pub mod interrupt;
use crate::{
address::GenericAddress,
fadt::Fadt,
madt::Madt,
madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
AcpiError,
AcpiHandler,
AcpiResult,
AcpiTables,
ManagedSlice,
PowerProfile,
};
use core::alloc::Allocator;
use core::{alloc::Allocator, mem, ptr};
use interrupt::InterruptModel;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -132,3 +132,63 @@ where
Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer })
}
}

/// Wakes up Application Processors (APs) using the Multiprocessor Wakeup Mailbox mechanism.
///
/// For Intel processors, the execution environment is:
/// - Interrupts must be disabled.
/// - RFLAGES.IF set to 0.
/// - Long mode enabled.
/// - Paging mode is enabled and physical memory for waking vector is identity mapped (virtual address equals physical address).
/// - Waking vector must be contained within one physical page.
/// - Selectors are set to flat and otherwise not used.
pub fn wakeup_aps<H>(
tables: &AcpiTables<H>,
handler: H,
apic_id: u32,
wakeup_vector: u64,
timeout_loops: u64,
) -> Result<(), AcpiError>
where
H: AcpiHandler,
{
let madt = tables.find_table::<Madt>()?;
let mailbox_addr = madt.get_mpwk_mailbox_addr()?;
let mut mpwk_mapping = unsafe {
handler.map_physical_region::<MultiprocessorWakeupMailbox>(
mailbox_addr as usize,
mem::size_of::<MultiprocessorWakeupMailbox>(),
)
};

// Reset command
unsafe {
ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Noop as u16);
}

// Fill the mailbox
mpwk_mapping.apic_id = apic_id;
mpwk_mapping.wakeup_vector = wakeup_vector;
unsafe {
ptr::write_volatile(&mut 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(())
}

0 comments on commit bc969d0

Please sign in to comment.