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