• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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