1//! Multi-processor management protocols.
2//!
3//! On any system with more than one logical processor we can categorize them as:
4//!
5//! * BSP — bootstrap processor, executes modules that are necessary for booting the system
6//! * AP — application processor, any processor other than the bootstrap processor
7//!
8//! This module contains protocols that provide a generalized way of performing the following tasks on these logical processors:
9//!
10//! * retrieving information of multi-processor environment and MP-related status of specific processors
11//! * dispatching user-provided function to APs
12//! * maintaining MP-related processor status
13
14use crate::proto::unsafe_protocol;
15use crate::{Result, Status};
16use bitflags::bitflags;
17use core::ffi::c_void;
18use core::ptr;
19use core::time::Duration;
20
21/// Callback to be called on the AP.
22pub type Procedure = extern "efiapi" fn(*mut c_void);
23
24bitflags! {
25 /// Flags indicating if the processor is BSP or AP,
26 /// if the processor is enabled or disabled, and if
27 /// the processor is healthy.
28 #[derive(Default)]
29 struct StatusFlag: u32 {
30 /// Processor is playing the role of BSP.
31 const PROCESSOR_AS_BSP_BIT = 1;
32 /// Processor is enabled.
33 const PROCESSOR_ENABLED_BIT = 1 << 1;
34 /// Processor is healthy.
35 const PROCESSOR_HEALTH_STATUS_BIT = 1 << 2;
36 }
37}
38
39/// Information about number of logical processors on the platform.
40#[derive(Default, Debug)]
41pub struct ProcessorCount {
42 /// Total number of processors (including BSP).
43 pub total: usize,
44 /// Number of processors (including BSP) that are currently enabled.
45 pub enabled: usize,
46}
47
48/// Information about processor on the platform.
49#[repr(C)]
50#[derive(Default, Debug)]
51pub struct ProcessorInformation {
52 /// Unique processor ID determined by system hardware.
53 pub processor_id: u64,
54 /// Flags indicating BSP, enabled and healthy status.
55 status_flag: StatusFlag,
56 /// Physical location of the processor.
57 pub location: CpuPhysicalLocation,
58}
59
60impl ProcessorInformation {
61 /// Returns `true` if the processor is playing the role of BSP.
62 #[must_use]
63 pub const fn is_bsp(&self) -> bool {
64 self.status_flag.contains(StatusFlag::PROCESSOR_AS_BSP_BIT)
65 }
66
67 /// Returns `true` if the processor is enabled.
68 #[must_use]
69 pub const fn is_enabled(&self) -> bool {
70 self.status_flag.contains(StatusFlag::PROCESSOR_ENABLED_BIT)
71 }
72
73 /// Returns `true` if the processor is healthy.
74 #[must_use]
75 pub const fn is_healthy(&self) -> bool {
76 self.status_flag
77 .contains(StatusFlag::PROCESSOR_HEALTH_STATUS_BIT)
78 }
79}
80
81/// Information about physical location of the processor.
82#[repr(C)]
83#[derive(Default, Debug)]
84pub struct CpuPhysicalLocation {
85 /// Zero-based physical package number that identifies
86 /// the cartridge of the processor.
87 pub package: u32,
88 /// Zero-based physical core number within package of the processor.
89 pub core: u32,
90 /// Zero-based logical thread number within core of the processor.
91 pub thread: u32,
92}
93
94/// Protocol that provides services needed for multi-processor management.
95#[repr(C)]
96#[unsafe_protocol("3fdda605-a76e-4f46-ad29-12f4531b3d08")]
97pub struct MpServices {
98 get_number_of_processors: extern "efiapi" fn(
99 this: *const MpServices,
100 number_of_processors: *mut usize,
101 number_of_enabled_processors: *mut usize,
102 ) -> Status,
103 get_processor_info: extern "efiapi" fn(
104 this: *const MpServices,
105 processor_number: usize,
106 processor_info_buffer: *mut ProcessorInformation,
107 ) -> Status,
108 startup_all_aps: extern "efiapi" fn(
109 this: *const MpServices,
110 procedure: Procedure,
111 single_thread: bool,
112 wait_event: *mut c_void,
113 timeout_in_micro_seconds: usize,
114 procedure_argument: *mut c_void,
115 failed_cpu_list: *mut *mut usize,
116 ) -> Status,
117 startup_this_ap: extern "efiapi" fn(
118 this: *const MpServices,
119 procedure: Procedure,
120 processor_number: usize,
121 wait_event: *mut c_void,
122 timeout_in_micro_seconds: usize,
123 procedure_argument: *mut c_void,
124 finished: *mut bool,
125 ) -> Status,
126 switch_bsp: extern "efiapi" fn(
127 this: *const MpServices,
128 processor_number: usize,
129 enable_old_bsp: bool,
130 ) -> Status,
131 enable_disable_ap: extern "efiapi" fn(
132 this: *const MpServices,
133 processor_number: usize,
134 enable_ap: bool,
135 health_flag: *const u32,
136 ) -> Status,
137 who_am_i: extern "efiapi" fn(this: *const MpServices, processor_number: *mut usize) -> Status,
138}
139
140impl MpServices {
141 /// Retrieves the number of logical processors and the number of enabled logical processors in the system.
142 pub fn get_number_of_processors(&self) -> Result<ProcessorCount> {
143 let mut total: usize = 0;
144 let mut enabled: usize = 0;
145 (self.get_number_of_processors)(self, &mut total, &mut enabled)
146 .into_with_val(|| ProcessorCount { total, enabled })
147 }
148
149 /// Gets detailed information on the requested processor at the instant this call is made.
150 pub fn get_processor_info(&self, processor_number: usize) -> Result<ProcessorInformation> {
151 let mut pi: ProcessorInformation = Default::default();
152 (self.get_processor_info)(self, processor_number, &mut pi).into_with_val(|| pi)
153 }
154
155 /// Executes provided function on all APs in blocking mode.
156 pub fn startup_all_aps(
157 &self,
158 single_thread: bool,
159 procedure: Procedure,
160 procedure_argument: *mut c_void,
161 timeout: Option<Duration>,
162 ) -> Result {
163 let timeout_arg = match timeout {
164 Some(timeout) => timeout.as_micros().try_into().unwrap(),
165 None => 0,
166 };
167
168 (self.startup_all_aps)(
169 self,
170 procedure,
171 single_thread,
172 ptr::null_mut(),
173 timeout_arg,
174 procedure_argument,
175 ptr::null_mut(),
176 )
177 .into()
178 }
179
180 /// Executes provided function on a specific AP in blocking mode.
181 pub fn startup_this_ap(
182 &self,
183 processor_number: usize,
184 procedure: Procedure,
185 procedure_argument: *mut c_void,
186 timeout: Option<Duration>,
187 ) -> Result {
188 let timeout_arg = match timeout {
189 Some(timeout) => timeout.as_micros().try_into().unwrap(),
190 None => 0,
191 };
192
193 (self.startup_this_ap)(
194 self,
195 procedure,
196 processor_number,
197 ptr::null_mut(),
198 timeout_arg,
199 procedure_argument,
200 ptr::null_mut(),
201 )
202 .into()
203 }
204
205 /// Switches the requested AP to be the BSP from that point onward.
206 pub fn switch_bsp(&self, processor_number: usize, enable_old_bsp: bool) -> Result {
207 (self.switch_bsp)(self, processor_number, enable_old_bsp).into()
208 }
209
210 /// Enables or disables an AP from this point onward.
211 ///
212 /// The `healthy` argument can be used to specify the new health status of the AP.
213 pub fn enable_disable_ap(
214 &self,
215 processor_number: usize,
216 enable_ap: bool,
217 healthy: Option<bool>,
218 ) -> Result {
219 let health_flag_raw: u32;
220 let health_flag_ptr = match healthy {
221 Some(healthy) => {
222 let mut sf = StatusFlag::empty();
223 sf.set(StatusFlag::PROCESSOR_HEALTH_STATUS_BIT, healthy);
224 health_flag_raw = sf.bits();
225 &health_flag_raw
226 }
227 None => ptr::null(),
228 };
229 (self.enable_disable_ap)(self, processor_number, enable_ap, health_flag_ptr).into()
230 }
231
232 /// Gets the handle number of the caller processor.
233 pub fn who_am_i(&self) -> Result<usize> {
234 let mut processor_number: usize = 0;
235 (self.who_am_i)(self, &mut processor_number).into_with_val(|| processor_number)
236 }
237}
238