1 pub mod interrupt;
2
3 use 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 };
14 use core::{alloc::Allocator, mem, ptr};
15 use interrupt::InterruptModel;
16
17 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
18 pub 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)]
31 pub 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)]
49 pub struct ProcessorInfo<'a, A>
50 where
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
58 impl<'a, A> ProcessorInfo<'a, A>
59 where
60 A: Allocator,
61 {
new(boot_processor: Processor, application_processors: ManagedSlice<'a, Processor, A>) -> Self62 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)]
69 pub 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
76 impl PmTimer {
new(fadt: &Fadt) -> Result<Option<PmTimer>, AcpiError>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)]
89 pub struct PlatformInfo<'a, A>
90 where
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")]
105 impl PlatformInfo<'_, alloc::alloc::Global> {
new<H>(tables: &AcpiTables<H>) -> AcpiResult<Self> where H: AcpiHandler,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
114 impl<A> PlatformInfo<'_, A>
115 where
116 A: Allocator + Clone,
117 {
new_in<H>(tables: &AcpiTables<H>, allocator: A) -> AcpiResult<Self> where H: AcpiHandler,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.
wakeup_aps<H>( tables: &AcpiTables<H>, handler: H, apic_id: u32, wakeup_vector: u64, timeout_loops: u64, ) -> Result<(), AcpiError> where H: AcpiHandler,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().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 }
195