acpi/platform/
mod.rs

1pub mod interrupt;
2
3use crate::{
4    address::GenericAddress,
5    fadt::Fadt,
6    madt::{Madt, MadtError, MpProtectedModeWakeupCommand, MultiprocessorWakeupMailbox},
7    AcpiError,
8    AcpiHandler,
9    AcpiResult,
10    AcpiTables,
11    ManagedSlice,
12    PowerProfile,
13};
14use core::{alloc::Allocator, mem, ptr};
15use interrupt::InterruptModel;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum ProcessorState {
19    /// A processor in this state is unusable, and you must not attempt to bring it up.
20    Disabled,
21
22    /// A processor waiting for a SIPI (Startup Inter-processor Interrupt) is currently not active,
23    /// but may be brought up.
24    WaitingForSipi,
25
26    /// A Running processor is currently brought up and running code.
27    Running,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
31pub struct Processor {
32    /// Corresponds to the `_UID` object of the processor's `Device`, or the `ProcessorId` field of the `Processor`
33    /// object, in AML.
34    pub processor_uid: u32,
35    /// The ID of the local APIC of the processor. Will be less than `256` if the APIC is being used, but can be
36    /// greater than this if the X2APIC is being used.
37    pub local_apic_id: u32,
38
39    /// The state of this processor. Check that the processor is not `Disabled` before attempting to bring it up!
40    pub state: ProcessorState,
41
42    /// Whether this processor is the Bootstrap Processor (BSP), or an Application Processor (AP).
43    /// When the bootloader is entered, the BSP is the only processor running code. To run code on
44    /// more than one processor, you need to "bring up" the APs.
45    pub is_ap: bool,
46}
47
48#[derive(Debug, Clone)]
49pub struct ProcessorInfo<'a, A>
50where
51    A: Allocator,
52{
53    pub boot_processor: Processor,
54    /// Application processors should be brought up in the order they're defined in this list.
55    pub application_processors: ManagedSlice<'a, Processor, A>,
56}
57
58impl<'a, A> ProcessorInfo<'a, A>
59where
60    A: Allocator,
61{
62    pub(crate) fn new(boot_processor: Processor, application_processors: ManagedSlice<'a, Processor, A>) -> Self {
63        Self { boot_processor, application_processors }
64    }
65}
66
67/// Information about the ACPI Power Management Timer (ACPI PM Timer).
68#[derive(Debug, Clone)]
69pub struct PmTimer {
70    /// A generic address to the register block of ACPI PM Timer.
71    pub base: GenericAddress,
72    /// This field is `true` if the hardware supports 32-bit timer, and `false` if the hardware supports 24-bit timer.
73    pub supports_32bit: bool,
74}
75
76impl PmTimer {
77    pub fn new(fadt: &Fadt) -> Result<Option<PmTimer>, AcpiError> {
78        match fadt.pm_timer_block()? {
79            Some(base) => Ok(Some(PmTimer { base, supports_32bit: { fadt.flags }.pm_timer_is_32_bit() })),
80            None => Ok(None),
81        }
82    }
83}
84
85/// `PlatformInfo` allows the collection of some basic information about the platform from some of the fixed-size
86/// tables in a nice way. It requires access to the `FADT` and `MADT`. It is the easiest way to get information
87/// about the processors and interrupt controllers on a platform.
88#[derive(Debug, Clone)]
89pub struct PlatformInfo<'a, A>
90where
91    A: Allocator,
92{
93    pub power_profile: PowerProfile,
94    pub interrupt_model: InterruptModel<'a, A>,
95    /// On `x86_64` platforms that support the APIC, the processor topology must also be inferred from the
96    /// interrupt model. That information is stored here, if present.
97    pub processor_info: Option<ProcessorInfo<'a, A>>,
98    pub pm_timer: Option<PmTimer>,
99    /*
100     * TODO: we could provide a nice view of the hardware register blocks in the FADT here.
101     */
102}
103
104#[cfg(feature = "alloc")]
105impl PlatformInfo<'_, alloc::alloc::Global> {
106    pub fn new<H>(tables: &AcpiTables<H>) -> AcpiResult<Self>
107    where
108        H: AcpiHandler,
109    {
110        Self::new_in(tables, alloc::alloc::Global)
111    }
112}
113
114impl<A> PlatformInfo<'_, A>
115where
116    A: Allocator + Clone,
117{
118    pub fn new_in<H>(tables: &AcpiTables<H>, allocator: A) -> AcpiResult<Self>
119    where
120        H: AcpiHandler,
121    {
122        let fadt = tables.find_table::<Fadt>()?;
123        let power_profile = fadt.power_profile();
124
125        let madt = tables.find_table::<Madt>();
126        let (interrupt_model, processor_info) = match madt {
127            Ok(madt) => madt.get().parse_interrupt_model_in(allocator)?,
128            Err(_) => (InterruptModel::Unknown, None),
129        };
130        let pm_timer = PmTimer::new(&fadt)?;
131
132        Ok(PlatformInfo { power_profile, interrupt_model, processor_info, pm_timer })
133    }
134}
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.
145pub 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>
152where
153    H: AcpiHandler,
154{
155    let madt = tables.find_table::<Madt>()?;
156    let mailbox_addr = madt.get().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}