Skip to content

Commit bc969d0

Browse files
authored
Implement the multiprocessor wakeup mechanism. (#225)
1 parent e9a5930 commit bc969d0

File tree

3 files changed

+131
-17
lines changed

3 files changed

+131
-17
lines changed

acpi/src/handler.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use core::{fmt, ops::Deref, ptr::NonNull};
1+
use core::{
2+
fmt,
3+
ops::{Deref, DerefMut},
4+
ptr::NonNull,
5+
};
26

37
/// Describes a physical mapping created by `AcpiHandler::map_physical_region` and unmapped by
48
/// `AcpiHandler::unmap_physical_region`. The region mapped must be at least `size_of::<T>()`
@@ -91,6 +95,15 @@ where
9195
}
9296
}
9397

98+
impl<H, T> DerefMut for PhysicalMapping<H, T>
99+
where
100+
H: AcpiHandler,
101+
{
102+
fn deref_mut(&mut self) -> &mut T {
103+
unsafe { self.virtual_start.as_mut() }
104+
}
105+
}
106+
94107
impl<H, T> Drop for PhysicalMapping<H, T>
95108
where
96109
H: AcpiHandler,

acpi/src/madt.rs

+55-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{
22
sdt::{ExtendedField, SdtHeader, Signature},
3+
AcpiError,
34
AcpiTable,
45
};
56
use bit_field::BitField;
@@ -21,6 +22,7 @@ pub enum MadtError {
2122
InvalidLocalNmiLine,
2223
MpsIntiInvalidPolarity,
2324
MpsIntiInvalidTriggerMode,
25+
WakeupApsTimeout,
2426
}
2527

2628
/// 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 {
4951
}
5052

5153
impl Madt {
54+
pub fn get_mpwk_mailbox_addr(&self) -> Result<u64, AcpiError> {
55+
for entry in self.entries() {
56+
match entry {
57+
MadtEntry::MultiprocessorWakeup(entry) => {
58+
return Ok(entry.mailbox_address);
59+
}
60+
_ => {}
61+
}
62+
}
63+
Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry))
64+
}
65+
5266
#[cfg(feature = "allocator_api")]
5367
pub fn parse_interrupt_model_in<'a, A>(
5468
&self,
@@ -102,21 +116,18 @@ impl Madt {
102116
where
103117
A: core::alloc::Allocator + Clone,
104118
{
105-
use crate::{
106-
platform::{
107-
interrupt::{
108-
Apic,
109-
InterruptSourceOverride,
110-
IoApic,
111-
LocalInterruptLine,
112-
NmiLine,
113-
NmiProcessor,
114-
NmiSource,
115-
},
116-
Processor,
117-
ProcessorState,
119+
use crate::platform::{
120+
interrupt::{
121+
Apic,
122+
InterruptSourceOverride,
123+
IoApic,
124+
LocalInterruptLine,
125+
NmiLine,
126+
NmiProcessor,
127+
NmiSource,
118128
},
119-
AcpiError,
129+
Processor,
130+
ProcessorState,
120131
};
121132

122133
let mut local_apic_address = self.local_apic_address as u64;
@@ -630,6 +641,36 @@ pub struct MultiprocessorWakeupEntry {
630641
pub mailbox_address: u64,
631642
}
632643

644+
#[derive(Debug, PartialEq, Eq)]
645+
pub enum MpProtectedModeWakeupCommand {
646+
Noop = 0,
647+
Wakeup = 1,
648+
Sleep = 2,
649+
AcceptPages = 3,
650+
}
651+
652+
impl From<u16> for MpProtectedModeWakeupCommand {
653+
fn from(value: u16) -> Self {
654+
match value {
655+
0 => MpProtectedModeWakeupCommand::Noop,
656+
1 => MpProtectedModeWakeupCommand::Wakeup,
657+
2 => MpProtectedModeWakeupCommand::Sleep,
658+
3 => MpProtectedModeWakeupCommand::AcceptPages,
659+
_ => panic!("Invalid value for MpProtectedModeWakeupCommand"),
660+
}
661+
}
662+
}
663+
664+
#[repr(C)]
665+
pub struct MultiprocessorWakeupMailbox {
666+
pub command: u16,
667+
_reserved: u16,
668+
pub apic_id: u32,
669+
pub wakeup_vector: u64,
670+
pub reserved_for_os: [u64; 254],
671+
reserved_for_firmware: [u64; 256],
672+
}
673+
633674
#[cfg(feature = "allocator_api")]
634675
fn parse_mps_inti_flags(flags: u16) -> crate::AcpiResult<(Polarity, TriggerMode)> {
635676
let polarity = match flags.get_bits(0..2) {

acpi/src/platform/mod.rs

+62-2
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ pub mod interrupt;
33
use crate::{
44
address::GenericAddress,
55
fadt::Fadt,
6-
madt::Madt,
6+
madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
77
AcpiError,
88
AcpiHandler,
99
AcpiResult,
1010
AcpiTables,
1111
ManagedSlice,
1212
PowerProfile,
1313
};
14-
use core::alloc::Allocator;
14+
use core::{alloc::Allocator, mem, ptr};
1515
use interrupt::InterruptModel;
1616

1717
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -132,3 +132,63 @@ where
132132
Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer })
133133
}
134134
}
135+
136+
/// Wakes up Application Processors (APs) using the Multiprocessor Wakeup Mailbox mechanism.
137+
///
138+
/// For Intel processors, the execution environment is:
139+
/// - Interrupts must be disabled.
140+
/// - RFLAGES.IF set to 0.
141+
/// - Long mode enabled.
142+
/// - Paging mode is enabled and physical memory for waking vector is identity mapped (virtual address equals physical address).
143+
/// - Waking vector must be contained within one physical page.
144+
/// - Selectors are set to flat and otherwise not used.
145+
pub fn wakeup_aps<H>(
146+
tables: &AcpiTables<H>,
147+
handler: H,
148+
apic_id: u32,
149+
wakeup_vector: u64,
150+
timeout_loops: u64,
151+
) -> Result<(), AcpiError>
152+
where
153+
H: AcpiHandler,
154+
{
155+
let madt = tables.find_table::<Madt>()?;
156+
let mailbox_addr = madt.get_mpwk_mailbox_addr()?;
157+
let mut mpwk_mapping = unsafe {
158+
handler.map_physical_region::<MultiprocessorWakeupMailbox>(
159+
mailbox_addr as usize,
160+
mem::size_of::<MultiprocessorWakeupMailbox>(),
161+
)
162+
};
163+
164+
// Reset command
165+
unsafe {
166+
ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Noop as u16);
167+
}
168+
169+
// Fill the mailbox
170+
mpwk_mapping.apic_id = apic_id;
171+
mpwk_mapping.wakeup_vector = wakeup_vector;
172+
unsafe {
173+
ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Wakeup as u16);
174+
}
175+
176+
// Wait to join
177+
let mut loops = 0;
178+
let mut command = MpProtectedModeWakeupCommand::Wakeup;
179+
while command != MpProtectedModeWakeupCommand::Noop {
180+
if loops >= timeout_loops {
181+
return Err(AcpiError::InvalidMadt(MadtError::WakeupApsTimeout));
182+
}
183+
// SAFETY: The caller must ensure that the provided `handler` correctly handles these
184+
// operations and that the specified `mailbox_addr` is valid.
185+
unsafe {
186+
command = ptr::read_volatile(&mpwk_mapping.command).into();
187+
}
188+
core::hint::spin_loop();
189+
loops += 1;
190+
}
191+
drop(mpwk_mapping);
192+
193+
Ok(())
194+
}

0 commit comments

Comments
 (0)