Skip to content

Commit 8096343

Browse files
Hsy-IntelIsaacWoods
authored andcommitted
Implement the multiprocessor wakeup mechanism. (rust-osdev#225)
1 parent 250c8db commit 8096343

File tree

3 files changed

+131
-17
lines changed

3 files changed

+131
-17
lines changed

acpi/src/handler.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use core::{fmt, ops::Deref, pin::Pin, 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>()`
@@ -96,6 +100,15 @@ where
96100
}
97101
}
98102

103+
impl<H, T> DerefMut for PhysicalMapping<H, T>
104+
where
105+
H: AcpiHandler,
106+
{
107+
fn deref_mut(&mut self) -> &mut T {
108+
unsafe { self.virtual_start.as_mut() }
109+
}
110+
}
111+
99112
impl<H, T> Drop for PhysicalMapping<H, T>
100113
where
101114
H: AcpiHandler,

acpi/src/madt.rs

Lines changed: 55 additions & 14 deletions
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;
@@ -25,6 +26,7 @@ pub enum MadtError {
2526
InvalidLocalNmiLine,
2627
MpsIntiInvalidPolarity,
2728
MpsIntiInvalidTriggerMode,
29+
WakeupApsTimeout,
2830
}
2931

3032
/// Represents the MADT - this contains the MADT header fields. You can then iterate over a `Madt`
@@ -59,6 +61,18 @@ unsafe impl AcpiTable for Madt {
5961
}
6062

6163
impl Madt {
64+
pub fn get_mpwk_mailbox_addr(&self) -> Result<u64, AcpiError> {
65+
for entry in self.entries() {
66+
match entry {
67+
MadtEntry::MultiprocessorWakeup(entry) => {
68+
return Ok(entry.mailbox_address);
69+
}
70+
_ => {}
71+
}
72+
}
73+
Err(AcpiError::InvalidMadt(MadtError::UnexpectedEntry))
74+
}
75+
6276
#[cfg(feature = "allocator_api")]
6377
pub fn parse_interrupt_model_in<'a, A>(
6478
self: Pin<&Self>,
@@ -112,21 +126,18 @@ impl Madt {
112126
where
113127
A: core::alloc::Allocator + Clone,
114128
{
115-
use crate::{
116-
platform::{
117-
interrupt::{
118-
Apic,
119-
InterruptSourceOverride,
120-
IoApic,
121-
LocalInterruptLine,
122-
NmiLine,
123-
NmiProcessor,
124-
NmiSource,
125-
},
126-
Processor,
127-
ProcessorState,
129+
use crate::platform::{
130+
interrupt::{
131+
Apic,
132+
InterruptSourceOverride,
133+
IoApic,
134+
LocalInterruptLine,
135+
NmiLine,
136+
NmiProcessor,
137+
NmiSource,
128138
},
129-
AcpiError,
139+
Processor,
140+
ProcessorState,
130141
};
131142

132143
let mut local_apic_address = self.local_apic_address as u64;
@@ -641,6 +652,36 @@ pub struct MultiprocessorWakeupEntry {
641652
pub mailbox_address: u64,
642653
}
643654

655+
#[derive(Debug, PartialEq, Eq)]
656+
pub enum MpProtectedModeWakeupCommand {
657+
Noop = 0,
658+
Wakeup = 1,
659+
Sleep = 2,
660+
AcceptPages = 3,
661+
}
662+
663+
impl From<u16> for MpProtectedModeWakeupCommand {
664+
fn from(value: u16) -> Self {
665+
match value {
666+
0 => MpProtectedModeWakeupCommand::Noop,
667+
1 => MpProtectedModeWakeupCommand::Wakeup,
668+
2 => MpProtectedModeWakeupCommand::Sleep,
669+
3 => MpProtectedModeWakeupCommand::AcceptPages,
670+
_ => panic!("Invalid value for MpProtectedModeWakeupCommand"),
671+
}
672+
}
673+
}
674+
675+
#[repr(C)]
676+
pub struct MultiprocessorWakeupMailbox {
677+
pub command: u16,
678+
_reserved: u16,
679+
pub apic_id: u32,
680+
pub wakeup_vector: u64,
681+
pub reserved_for_os: [u64; 254],
682+
reserved_for_firmware: [u64; 256],
683+
}
684+
644685
#[cfg(feature = "allocator_api")]
645686
fn parse_mps_inti_flags(flags: u16) -> crate::AcpiResult<(Polarity, TriggerMode)> {
646687
let polarity = match flags.get_bits(0..2) {

acpi/src/platform/mod.rs

Lines changed: 62 additions & 2 deletions
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)