use crate::proto::Protocol;
use crate::{unsafe_guid, Result, Status};
use bitflags::bitflags;
use core::convert::TryInto;
use core::ffi::c_void;
use core::ptr;
use core::time::Duration;
pub type Procedure = extern "efiapi" fn(*mut c_void);
bitflags! {
#[derive(Default)]
struct StatusFlag: u32 {
const PROCESSOR_AS_BSP_BIT = 1;
const PROCESSOR_ENABLED_BIT = 1 << 1;
const PROCESSOR_HEALTH_STATUS_BIT = 1 << 2;
}
}
#[derive(Default, Debug)]
pub struct ProcessorCount {
pub total: usize,
pub enabled: usize,
}
#[repr(C)]
#[derive(Default, Debug)]
pub struct ProcessorInformation {
pub processor_id: u64,
status_flag: StatusFlag,
pub location: CpuPhysicalLocation,
}
impl ProcessorInformation {
pub fn is_bsp(&self) -> bool {
self.status_flag.contains(StatusFlag::PROCESSOR_AS_BSP_BIT)
}
pub fn is_enabled(&self) -> bool {
self.status_flag.contains(StatusFlag::PROCESSOR_ENABLED_BIT)
}
pub fn is_healthy(&self) -> bool {
self.status_flag
.contains(StatusFlag::PROCESSOR_HEALTH_STATUS_BIT)
}
}
#[repr(C)]
#[derive(Default, Debug)]
pub struct CpuPhysicalLocation {
pub package: u32,
pub core: u32,
pub thread: u32,
}
#[repr(C)]
#[unsafe_guid("3fdda605-a76e-4f46-ad29-12f4531b3d08")]
#[derive(Protocol)]
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 {
pub fn get_number_of_processors(&self) -> Result<ProcessorCount> {
let mut total: usize = 0;
let mut enabled: usize = 0;
(self.get_number_of_processors)(self, &mut total, &mut enabled)
.into_with_val(|| ProcessorCount { total, enabled })
}
pub fn get_processor_info(&self, processor_number: usize) -> Result<ProcessorInformation> {
let mut pi: ProcessorInformation = Default::default();
(self.get_processor_info)(self, processor_number, &mut pi).into_with_val(|| pi)
}
pub fn startup_all_aps(
&self,
single_thread: bool,
procedure: Procedure,
procedure_argument: *mut c_void,
timeout: Option<Duration>,
) -> Result {
let timeout_arg = match timeout {
Some(timeout) => timeout.as_micros().try_into().unwrap(),
None => 0,
};
(self.startup_all_aps)(
self,
procedure,
single_thread,
ptr::null_mut(),
timeout_arg,
procedure_argument,
ptr::null_mut(),
)
.into()
}
pub fn startup_this_ap(
&self,
processor_number: usize,
procedure: Procedure,
procedure_argument: *mut c_void,
timeout: Option<Duration>,
) -> Result {
let timeout_arg = match timeout {
Some(timeout) => timeout.as_micros().try_into().unwrap(),
None => 0,
};
(self.startup_this_ap)(
self,
procedure,
processor_number,
ptr::null_mut(),
timeout_arg,
procedure_argument,
ptr::null_mut(),
)
.into()
}
pub fn switch_bsp(&self, processor_number: usize, enable_old_bsp: bool) -> Result {
(self.switch_bsp)(self, processor_number, enable_old_bsp).into()
}
pub fn enable_disable_ap(
&self,
processor_number: usize,
enable_ap: bool,
healthy: Option<bool>,
) -> 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).into()
}
pub fn who_am_i(&self) -> Result<usize> {
let mut processor_number: usize = 0;
(self.who_am_i)(self, &mut processor_number).into_with_val(|| processor_number)
}
}