Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the multiprocessor wakeup mechanism. #225

Merged
merged 1 commit into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
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
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(())
}
Loading