Skip to content

Commit 4fff735

Browse files
committed
Rename PlatformInfo to AcpiPlatform, bring wake_aps under it
1 parent 1998a26 commit 4fff735

File tree

1 file changed

+85
-100
lines changed

1 file changed

+85
-100
lines changed

src/platform/mod.rs

Lines changed: 85 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,91 @@ use crate::{
1919
use alloc::{alloc::Global, vec::Vec};
2020
use core::{alloc::Allocator, mem, ptr};
2121

22+
/// `AcpiPlatform` is a higher-level view of the ACPI tables that makes it easier to perform common
23+
/// tasks with ACPI. It requires allocator support.
24+
pub struct AcpiPlatform<H: AcpiHandler, A: Allocator = Global> {
25+
pub tables: AcpiTables<H>,
26+
pub power_profile: PowerProfile,
27+
pub interrupt_model: InterruptModel<A>,
28+
/// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
29+
/// interrupt model. That information is stored here, if present.
30+
pub processor_info: Option<ProcessorInfo<A>>,
31+
pub pm_timer: Option<PmTimer>,
32+
}
33+
34+
impl<H: AcpiHandler> AcpiPlatform<H, Global> {
35+
pub fn new(tables: AcpiTables<H>) -> Result<Self, AcpiError> {
36+
Self::new_in(tables, alloc::alloc::Global)
37+
}
38+
}
39+
40+
impl<H: AcpiHandler, A: Allocator + Clone> AcpiPlatform<H, A> {
41+
pub fn new_in(tables: AcpiTables<H>, allocator: A) -> Result<Self, AcpiError> {
42+
let Some(fadt) = tables.find_table::<Fadt>() else { Err(AcpiError::TableNotFound(Signature::FADT))? };
43+
let power_profile = fadt.power_profile();
44+
45+
let (interrupt_model, processor_info) = InterruptModel::new_in(&tables, allocator)?;
46+
let pm_timer = PmTimer::new(&fadt)?;
47+
48+
Ok(AcpiPlatform { tables, power_profile, interrupt_model, processor_info, pm_timer})
49+
}
50+
51+
/// Wake up all Application Processors (APs) using the Multiprocessor Wakeup Mailbox Mechanism.
52+
/// This may not be available on the platform you're running on.
53+
///
54+
/// On Intel processors, this will start the AP in long-mode, with interrupts disabled and a
55+
/// single page with the supplied waking vector identity-mapped (it is therefore advisable to
56+
/// align your waking vector to start at a page boundary and fit within one page).
57+
pub unsafe fn wake_aps(
58+
&self,
59+
apic_id: u32,
60+
wakeup_vector: u64,
61+
timeout_loops: u64,
62+
handler: H,
63+
) -> Result<(), AcpiError> {
64+
let Some(madt) = self.tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };
65+
let mailbox_addr = madt.get().get_mpwk_mailbox_addr()?;
66+
let mut mpwk_mapping = unsafe {
67+
handler.map_physical_region::<MultiprocessorWakeupMailbox>(
68+
mailbox_addr as usize,
69+
mem::size_of::<MultiprocessorWakeupMailbox>(),
70+
)
71+
};
72+
73+
// Reset command
74+
unsafe {
75+
ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Noop as u16);
76+
}
77+
78+
// Fill the mailbox
79+
mpwk_mapping.apic_id = apic_id;
80+
mpwk_mapping.wakeup_vector = wakeup_vector;
81+
unsafe {
82+
ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Wakeup as u16);
83+
}
84+
85+
// Wait to join
86+
// TODO: if we merge the handlers into one, we could use `stall` here.
87+
let mut loops = 0;
88+
let mut command = MpProtectedModeWakeupCommand::Wakeup;
89+
while command != MpProtectedModeWakeupCommand::Noop {
90+
if loops >= timeout_loops {
91+
return Err(AcpiError::InvalidMadt(MadtError::WakeupApsTimeout));
92+
}
93+
// SAFETY: The caller must ensure that the provided `handler` correctly handles these
94+
// operations and that the specified `mailbox_addr` is valid.
95+
unsafe {
96+
command = ptr::read_volatile(&mpwk_mapping.command).into();
97+
}
98+
core::hint::spin_loop();
99+
loops += 1;
100+
}
101+
drop(mpwk_mapping);
102+
103+
Ok(())
104+
}
105+
}
106+
22107
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23108
pub enum ProcessorState {
24109
/// A processor in this state is unusable, and you must not attempt to bring it up.
@@ -80,103 +165,3 @@ impl PmTimer {
80165
}
81166
}
82167
}
83-
84-
/// `PlatformInfo` allows the collection of some basic information about the platform from some of the fixed-size
85-
/// tables in a nice way. It requires access to the `FADT` and `MADT`. It is the easiest way to get information
86-
/// about the processors and interrupt controllers on a platform.
87-
#[derive(Debug, Clone)]
88-
pub struct PlatformInfo<A: Allocator = Global> {
89-
pub power_profile: PowerProfile,
90-
pub interrupt_model: InterruptModel<A>,
91-
/// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
92-
/// interrupt model. That information is stored here, if present.
93-
pub processor_info: Option<ProcessorInfo<A>>,
94-
pub pm_timer: Option<PmTimer>,
95-
/*
96-
* TODO: we could provide a nice view of the hardware register blocks in the FADT here.
97-
*/
98-
}
99-
100-
impl PlatformInfo<Global> {
101-
pub fn new<H>(tables: &AcpiTables<H>) -> Result<Self, AcpiError>
102-
where
103-
H: AcpiHandler,
104-
{
105-
Self::new_in(tables, alloc::alloc::Global)
106-
}
107-
}
108-
109-
impl<A: Allocator + Clone> PlatformInfo<A> {
110-
pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> Result<Self, AcpiError>
111-
where
112-
H: AcpiHandler,
113-
{
114-
let Some(fadt) = tables.find_table::<Fadt>() else { Err(AcpiError::TableNotFound(Signature::FADT))? };
115-
let power_profile = fadt.power_profile();
116-
117-
let (interrupt_model, processor_info) = InterruptModel::new_in(&tables, allocator)?;
118-
let pm_timer = PmTimer::new(&fadt)?;
119-
120-
Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer })
121-
}
122-
}
123-
124-
/// Wakes up Application Processors (APs) using the Multiprocessor Wakeup Mailbox mechanism.
125-
///
126-
/// For Intel processors, the execution environment is:
127-
/// - Interrupts must be disabled.
128-
/// - RFLAGES.IF set to 0.
129-
/// - Long mode enabled.
130-
/// - Paging mode is enabled and physical memory for waking vector is identity mapped (virtual address equals physical address).
131-
/// - Waking vector must be contained within one physical page.
132-
/// - Selectors are set to flat and otherwise not used.
133-
pub unsafe fn wakeup_aps<H>(
134-
tables: &AcpiTables<H>,
135-
handler: H,
136-
apic_id: u32,
137-
wakeup_vector: u64,
138-
timeout_loops: u64,
139-
) -> Result<(), AcpiError>
140-
where
141-
H: AcpiHandler,
142-
{
143-
let Some(madt) = tables.find_table::<Madt>() else { Err(AcpiError::TableNotFound(Signature::MADT))? };
144-
let mailbox_addr = madt.get().get_mpwk_mailbox_addr()?;
145-
let mut mpwk_mapping = unsafe {
146-
handler.map_physical_region::<MultiprocessorWakeupMailbox>(
147-
mailbox_addr as usize,
148-
mem::size_of::<MultiprocessorWakeupMailbox>(),
149-
)
150-
};
151-
152-
// Reset command
153-
unsafe {
154-
ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Noop as u16);
155-
}
156-
157-
// Fill the mailbox
158-
mpwk_mapping.apic_id = apic_id;
159-
mpwk_mapping.wakeup_vector = wakeup_vector;
160-
unsafe {
161-
ptr::write_volatile(&mut mpwk_mapping.command, MpProtectedModeWakeupCommand::Wakeup as u16);
162-
}
163-
164-
// Wait to join
165-
let mut loops = 0;
166-
let mut command = MpProtectedModeWakeupCommand::Wakeup;
167-
while command != MpProtectedModeWakeupCommand::Noop {
168-
if loops >= timeout_loops {
169-
return Err(AcpiError::InvalidMadt(MadtError::WakeupApsTimeout));
170-
}
171-
// SAFETY: The caller must ensure that the provided `handler` correctly handles these
172-
// operations and that the specified `mailbox_addr` is valid.
173-
unsafe {
174-
command = ptr::read_volatile(&mpwk_mapping.command).into();
175-
}
176-
core::hint::spin_loop();
177-
loops += 1;
178-
}
179-
drop(mpwk_mapping);
180-
181-
Ok(())
182-
}

0 commit comments

Comments
 (0)