//! Multi-processor management protocols. //! //! On any system with more than one logical processor we can categorize them as: //! //! * BSP — bootstrap processor, executes modules that are necessary for booting the system //! * AP — application processor, any processor other than the bootstrap processor //! //! This module contains protocols that provide a generalized way of performing the following tasks on these logical processors: //! //! * retrieving information of multi-processor environment and MP-related status of specific processors //! * dispatching user-provided function to APs //! * maintaining MP-related processor status use crate::data_types::Event; use crate::proto::unsafe_protocol; use crate::{Result, Status, StatusExt}; use bitflags::bitflags; use core::ffi::c_void; use core::ptr; use core::time::Duration; /// Callback to be called on the AP. pub type Procedure = extern "efiapi" fn(*mut c_void); bitflags! { /// Flags indicating if the processor is BSP or AP, /// if the processor is enabled or disabled, and if /// the processor is healthy. #[repr(transparent)] #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] struct StatusFlag: u32 { /// Processor is playing the role of BSP. const PROCESSOR_AS_BSP_BIT = 1; /// Processor is enabled. const PROCESSOR_ENABLED_BIT = 1 << 1; /// Processor is healthy. const PROCESSOR_HEALTH_STATUS_BIT = 1 << 2; } } /// Information about number of logical processors on the platform. #[derive(Default, Debug)] pub struct ProcessorCount { /// Total number of processors (including BSP). pub total: usize, /// Number of processors (including BSP) that are currently enabled. pub enabled: usize, } /// Information about processor on the platform. #[repr(C)] #[derive(Default, Debug)] pub struct ProcessorInformation { /// Unique processor ID determined by system hardware. pub processor_id: u64, /// Flags indicating BSP, enabled and healthy status. status_flag: StatusFlag, /// Physical location of the processor. pub location: CpuPhysicalLocation, } impl ProcessorInformation { /// Returns `true` if the processor is playing the role of BSP. #[must_use] pub const fn is_bsp(&self) -> bool { self.status_flag.contains(StatusFlag::PROCESSOR_AS_BSP_BIT) } /// Returns `true` if the processor is enabled. #[must_use] pub const fn is_enabled(&self) -> bool { self.status_flag.contains(StatusFlag::PROCESSOR_ENABLED_BIT) } /// Returns `true` if the processor is healthy. #[must_use] pub const fn is_healthy(&self) -> bool { self.status_flag .contains(StatusFlag::PROCESSOR_HEALTH_STATUS_BIT) } } /// Information about physical location of the processor. #[repr(C)] #[derive(Default, Debug)] pub struct CpuPhysicalLocation { /// Zero-based physical package number that identifies /// the cartridge of the processor. pub package: u32, /// Zero-based physical core number within package of the processor. pub core: u32, /// Zero-based logical thread number within core of the processor. pub thread: u32, } /// Protocol that provides services needed for multi-processor management. #[derive(Debug)] #[repr(C)] #[unsafe_protocol("3fdda605-a76e-4f46-ad29-12f4531b3d08")] pub struct MpServices { get_number_of_processors: extern "efiapi" fn( this: *const MpServices, number_of_processors: *mut usize, number_of_enabled_processors: *mut usize, ) -> Status, get_processor_info: extern "efiapi" fn( this: *const MpServices, processor_number: usize, processor_info_buffer: *mut ProcessorInformation, ) -> Status, startup_all_aps: extern "efiapi" fn( this: *const MpServices, procedure: Procedure, single_thread: bool, wait_event: *mut c_void, timeout_in_micro_seconds: usize, procedure_argument: *mut c_void, failed_cpu_list: *mut *mut usize, ) -> Status, startup_this_ap: extern "efiapi" fn( this: *const MpServices, procedure: Procedure, processor_number: usize, wait_event: *mut c_void, timeout_in_micro_seconds: usize, procedure_argument: *mut c_void, finished: *mut bool, ) -> Status, switch_bsp: extern "efiapi" fn( this: *const MpServices, processor_number: usize, enable_old_bsp: bool, ) -> Status, enable_disable_ap: extern "efiapi" fn( this: *const MpServices, processor_number: usize, enable_ap: bool, health_flag: *const u32, ) -> Status, who_am_i: extern "efiapi" fn(this: *const MpServices, processor_number: *mut usize) -> Status, } impl MpServices { /// Retrieves the number of logical processors and the number of enabled logical processors in the system. pub fn get_number_of_processors(&self) -> Result { let mut total: usize = 0; let mut enabled: usize = 0; (self.get_number_of_processors)(self, &mut total, &mut enabled) .to_result_with_val(|| ProcessorCount { total, enabled }) } /// Gets detailed information on the requested processor at the instant this call is made. pub fn get_processor_info(&self, processor_number: usize) -> Result { let mut pi: ProcessorInformation = Default::default(); (self.get_processor_info)(self, processor_number, &mut pi).to_result_with_val(|| pi) } /// Executes provided function on all APs. pub fn startup_all_aps( &self, single_thread: bool, procedure: Procedure, procedure_argument: *mut c_void, event: Option, timeout: Option, ) -> Result { let timeout_arg = match timeout { Some(timeout) => timeout.as_micros().try_into().unwrap(), None => 0, }; let event_arg = match event { Some(event) => event.as_ptr(), None => ptr::null_mut(), }; (self.startup_all_aps)( self, procedure, single_thread, event_arg, timeout_arg, procedure_argument, ptr::null_mut(), ) .to_result() } /// Executes provided function on a specific AP in blocking mode. pub fn startup_this_ap( &self, processor_number: usize, procedure: Procedure, procedure_argument: *mut c_void, event: Option, timeout: Option, ) -> Result { let timeout_arg = match timeout { Some(timeout) => timeout.as_micros().try_into().unwrap(), None => 0, }; let event_arg = match event { Some(event) => event.as_ptr(), None => ptr::null_mut(), }; (self.startup_this_ap)( self, procedure, processor_number, event_arg, timeout_arg, procedure_argument, ptr::null_mut(), ) .to_result() } /// Switches the requested AP to be the BSP from that point onward. pub fn switch_bsp(&self, processor_number: usize, enable_old_bsp: bool) -> Result { (self.switch_bsp)(self, processor_number, enable_old_bsp).to_result() } /// Enables or disables an AP from this point onward. /// /// The `healthy` argument can be used to specify the new health status of the AP. pub fn enable_disable_ap( &self, processor_number: usize, enable_ap: bool, healthy: Option, ) -> Result { let health_flag_raw: u32; let health_flag_ptr = match healthy { Some(healthy) => { let mut sf = StatusFlag::empty(); sf.set(StatusFlag::PROCESSOR_HEALTH_STATUS_BIT, healthy); health_flag_raw = sf.bits(); &health_flag_raw } None => ptr::null(), }; (self.enable_disable_ap)(self, processor_number, enable_ap, health_flag_ptr).to_result() } /// Gets the handle number of the caller processor. pub fn who_am_i(&self) -> Result { let mut processor_number: usize = 0; (self.who_am_i)(self, &mut processor_number).to_result_with_val(|| processor_number) } }