use alloc::boxed::Box;
use core::cell::RefCell;
use bit_field::BitField;
use spin::Once;
use crate::cpu_local;
pub mod ioapic;
pub mod x2apic;
pub mod xapic;
cpu_local! {
static APIC_INSTANCE: RefCell<Option<Box<dyn Apic + 'static>>> = RefCell::new(None);
}
static APIC_TYPE: Once<ApicType> = Once::new();
pub fn with_borrow<R>(f: impl FnOnce(&mut (dyn Apic + 'static)) -> R) -> R {
let irq_guard = crate::trap::disable_local();
let apic_guard = APIC_INSTANCE.get_with(&irq_guard);
let mut apic_init_ref = apic_guard.borrow_mut();
let apic_ref = if let Some(apic_ref) = apic_init_ref.as_mut() {
apic_ref
} else {
*apic_init_ref = Some(match APIC_TYPE.get().unwrap() {
ApicType::XApic => {
let mut xapic = xapic::XApic::new().unwrap();
xapic.enable();
let version = xapic.version();
log::info!(
"xAPIC ID:{:x}, Version:{:x}, Max LVT:{:x}",
xapic.id(),
version & 0xff,
(version >> 16) & 0xff
);
Box::new(xapic)
}
ApicType::X2Apic => {
let mut x2apic = x2apic::X2Apic::new().unwrap();
x2apic.enable();
let version = x2apic.version();
log::info!(
"x2APIC ID:{:x}, Version:{:x}, Max LVT:{:x}",
x2apic.id(),
version & 0xff,
(version >> 16) & 0xff
);
Box::new(x2apic)
}
});
apic_init_ref.as_mut().unwrap()
};
let ret = f.call_once((apic_ref.as_mut(),));
ret
}
pub trait Apic: ApicTimer + Sync + Send {
fn id(&self) -> u32;
fn version(&self) -> u32;
fn eoi(&mut self);
unsafe fn send_ipi(&mut self, icr: Icr);
}
pub trait ApicTimer: Sync + Send {
fn set_timer_init_count(&mut self, value: u64);
fn timer_current_count(&self) -> u64;
fn set_lvt_timer(&mut self, value: u64);
fn set_timer_div_config(&mut self, div_config: DivideConfig);
}
enum ApicType {
XApic,
X2Apic,
}
pub struct Icr(u64);
impl Icr {
#[allow(clippy::too_many_arguments)]
pub fn new(
destination: ApicId,
destination_shorthand: DestinationShorthand,
trigger_mode: TriggerMode,
level: Level,
delivery_status: DeliveryStatus,
destination_mode: DestinationMode,
delivery_mode: DeliveryMode,
vector: u8,
) -> Self {
let dest = match destination {
ApicId::XApic(d) => (d as u64) << 56,
ApicId::X2Apic(d) => (d as u64) << 32,
};
Icr(dest
| (destination_shorthand as u64) << 18
| (trigger_mode as u64) << 15
| (level as u64) << 14
| (delivery_status as u64) << 12
| (destination_mode as u64) << 11
| (delivery_mode as u64) << 8
| (vector as u64))
}
pub fn lower(&self) -> u32 {
self.0 as u32
}
pub fn upper(&self) -> u32 {
(self.0 >> 32) as u32
}
}
pub enum ApicId {
XApic(u8),
X2Apic(u32),
}
impl ApicId {
pub fn x2apic_logical_id(&self) -> u32 {
self.x2apic_logical_cluster_id() << 16 | 1 << self.x2apic_logical_field_id()
}
pub fn x2apic_logical_cluster_id(&self) -> u32 {
let apic_id = match *self {
ApicId::XApic(id) => id as u32,
ApicId::X2Apic(id) => id,
};
apic_id.get_bits(4..=19)
}
pub fn x2apic_logical_field_id(&self) -> u32 {
let apic_id = match *self {
ApicId::XApic(id) => id as u32,
ApicId::X2Apic(id) => id,
};
apic_id.get_bits(0..=3)
}
}
impl From<u32> for ApicId {
fn from(value: u32) -> Self {
match APIC_TYPE.get().unwrap() {
ApicType::XApic => ApicId::XApic(value as u8),
ApicType::X2Apic => ApicId::X2Apic(value),
}
}
}
#[repr(u64)]
pub enum DestinationShorthand {
NoShorthand = 0b00,
#[allow(dead_code)]
MySelf = 0b01,
AllIncludingSelf = 0b10,
AllExcludingSelf = 0b11,
}
#[repr(u64)]
pub enum TriggerMode {
Edge = 0,
Level = 1,
}
#[repr(u64)]
pub enum Level {
Deassert = 0,
Assert = 1,
}
#[repr(u64)]
pub enum DeliveryStatus {
Idle = 0,
#[allow(dead_code)]
SendPending = 1,
}
#[repr(u64)]
pub enum DestinationMode {
Physical = 0,
#[allow(dead_code)]
Logical = 1,
}
#[repr(u64)]
pub enum DeliveryMode {
Fixed = 0b000,
#[allow(dead_code)]
LowestPriority = 0b001,
#[allow(dead_code)]
Smi = 0b010,
_Reserved = 0b011,
#[allow(dead_code)]
Nmi = 0b100,
Init = 0b101,
StartUp = 0b110,
}
#[derive(Debug)]
pub enum ApicInitError {
NoApic,
}
#[derive(Debug)]
#[repr(u32)]
#[allow(dead_code)]
pub enum DivideConfig {
Divide1 = 0b1011,
Divide2 = 0b0000,
Divide4 = 0b0001,
Divide8 = 0b0010,
Divide16 = 0b0011,
Divide32 = 0b1000,
Divide64 = 0b1001,
Divide128 = 0b1010,
}
pub fn init() -> Result<(), ApicInitError> {
crate::arch::x86::kernel::pic::disable_temp();
if x2apic::X2Apic::has_x2apic() {
log::info!("x2APIC found!");
APIC_TYPE.call_once(|| ApicType::X2Apic);
Ok(())
} else if xapic::XApic::has_xapic() {
log::info!("xAPIC found!");
APIC_TYPE.call_once(|| ApicType::XApic);
Ok(())
} else {
log::warn!("Neither x2APIC nor xAPIC found!");
Err(ApicInitError::NoApic)
}
}
pub fn exists() -> bool {
APIC_TYPE.is_completed()
}