use core::fmt;
use core::intrinsics::{volatile_load, volatile_store};
use bit_field::BitField;
use super::*;
use crate::msr::{rdmsr, wrmsr, IA32_APIC_BASE, IA32_TSC_DEADLINE};
pub const XAPIC_ID: u32 = 0x020;
pub const XAPIC_VERSION: u32 = 0x030;
pub const XAPIC_TPR: u32 = 0x080;
pub const XAPIC_PPR: u32 = 0x0A0;
pub const XAPIC_EOI: u32 = 0x0B0;
pub const XAPIC_LDR: u32 = 0x0D0;
pub const XAPIC_SVR: u32 = 0x0F0;
pub const XAPIC_ISR0: u32 = 0x100;
pub const XAPIC_ISR1: u32 = 0x110;
pub const XAPIC_ISR2: u32 = 0x120;
pub const XAPIC_ISR3: u32 = 0x130;
pub const XAPIC_ISR4: u32 = 0x140;
pub const XAPIC_ISR5: u32 = 0x150;
pub const XAPIC_ISR6: u32 = 0x160;
pub const XAPIC_ISR7: u32 = 0x170;
pub const XAPIC_TMR0: u32 = 0x180;
pub const XAPIC_TMR1: u32 = 0x190;
pub const XAPIC_TMR2: u32 = 0x1A0;
pub const XAPIC_TMR3: u32 = 0x1B0;
pub const XAPIC_TMR4: u32 = 0x1C0;
pub const XAPIC_TMR5: u32 = 0x1D0;
pub const XAPIC_TMR6: u32 = 0x1E0;
pub const XAPIC_TMR7: u32 = 0x1F0;
pub const XAPIC_IRR0: u32 = 0x200;
pub const XAPIC_IRR1: u32 = 0x210;
pub const XAPIC_IRR2: u32 = 0x220;
pub const XAPIC_IRR3: u32 = 0x230;
pub const XAPIC_IRR4: u32 = 0x240;
pub const XAPIC_IRR5: u32 = 0x250;
pub const XAPIC_IRR6: u32 = 0x260;
pub const XAPIC_IRR7: u32 = 0x270;
pub const XAPIC_ESR: u32 = 0x280;
pub const XAPIC_LVT_CMCI: u32 = 0x2F0;
pub const XAPIC_ICR0: u32 = 0x300;
pub const XAPIC_ICR1: u32 = 0x310;
pub const XAPIC_LVT_TIMER: u32 = 0x320;
pub const XAPIC_LVT_THERMAL: u32 = 0x330;
pub const XAPIC_LVT_PMI: u32 = 0x340;
pub const XAPIC_LVT_LINT0: u32 = 0x350;
pub const XAPIC_LVT_LINT1: u32 = 0x360;
pub const XAPIC_LVT_ERROR: u32 = 0x370;
pub const XAPIC_TIMER_INIT_COUNT: u32 = 0x380;
pub const XAPIC_TIMER_CURRENT_COUNT: u32 = 0x390;
pub const XAPIC_TIMER_DIV_CONF: u32 = 0x3E0;
#[derive(Copy, Clone)]
#[allow(dead_code, non_camel_case_types)]
enum ApicRegister {
XAPIC_ID = XAPIC_ID as isize,
XAPIC_VERSION = XAPIC_VERSION as isize,
XAPIC_TPR = XAPIC_TPR as isize,
XAPIC_PPR = XAPIC_PPR as isize,
XAPIC_EOI = XAPIC_EOI as isize,
XAPIC_LDR = XAPIC_LDR as isize,
XAPIC_SVR = XAPIC_SVR as isize,
XAPIC_ISR0 = XAPIC_ISR0 as isize,
XAPIC_ISR1 = XAPIC_ISR1 as isize,
XAPIC_ISR2 = XAPIC_ISR2 as isize,
XAPIC_ISR3 = XAPIC_ISR3 as isize,
XAPIC_ISR4 = XAPIC_ISR4 as isize,
XAPIC_ISR5 = XAPIC_ISR5 as isize,
XAPIC_ISR6 = XAPIC_ISR6 as isize,
XAPIC_ISR7 = XAPIC_ISR7 as isize,
XAPIC_TMR0 = XAPIC_TMR0 as isize,
XAPIC_TMR1 = XAPIC_TMR1 as isize,
XAPIC_TMR2 = XAPIC_TMR2 as isize,
XAPIC_TMR3 = XAPIC_TMR3 as isize,
XAPIC_TMR4 = XAPIC_TMR4 as isize,
XAPIC_TMR5 = XAPIC_TMR5 as isize,
XAPIC_TMR6 = XAPIC_TMR6 as isize,
XAPIC_TMR7 = XAPIC_TMR7 as isize,
XAPIC_IRR0 = XAPIC_IRR0 as isize,
XAPIC_IRR1 = XAPIC_IRR1 as isize,
XAPIC_IRR2 = XAPIC_IRR2 as isize,
XAPIC_IRR3 = XAPIC_IRR3 as isize,
XAPIC_IRR4 = XAPIC_IRR4 as isize,
XAPIC_IRR5 = XAPIC_IRR5 as isize,
XAPIC_IRR6 = XAPIC_IRR6 as isize,
XAPIC_IRR7 = XAPIC_IRR7 as isize,
XAPIC_ESR = XAPIC_ESR as isize,
XAPIC_LVT_CMCI = XAPIC_LVT_CMCI as isize,
XAPIC_ICR0 = XAPIC_ICR0 as isize,
XAPIC_ICR1 = XAPIC_ICR1 as isize,
XAPIC_LVT_TIMER = XAPIC_LVT_TIMER as isize,
XAPIC_LVT_THERMAL = XAPIC_LVT_THERMAL as isize,
XAPIC_LVT_PMI = XAPIC_LVT_PMI as isize,
XAPIC_LVT_LINT0 = XAPIC_LVT_LINT0 as isize,
XAPIC_LVT_LINT1 = XAPIC_LVT_LINT1 as isize,
XAPIC_LVT_ERROR = XAPIC_LVT_ERROR as isize,
XAPIC_TIMER_INIT_COUNT = XAPIC_TIMER_INIT_COUNT as isize,
XAPIC_TIMER_CURRENT_COUNT = XAPIC_TIMER_CURRENT_COUNT as isize,
XAPIC_TIMER_DIV_CONF = XAPIC_TIMER_DIV_CONF as isize,
}
pub struct XAPIC {
mmio_region: &'static mut [u32],
base: u64,
}
impl fmt::Debug for XAPIC {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("XAPIC")
.field("XAPIC_ID", &self.read(ApicRegister::XAPIC_ID))
.field("XAPIC_VERSION", &self.read(ApicRegister::XAPIC_VERSION))
.field("XAPIC_TPR", &self.read(ApicRegister::XAPIC_TPR))
.field("XAPIC_PPR", &self.read(ApicRegister::XAPIC_PPR))
.field("XAPIC_EOI", &self.read(ApicRegister::XAPIC_EOI))
.field("XAPIC_LDR", &self.read(ApicRegister::XAPIC_LDR))
.field("XAPIC_SVR", &self.read(ApicRegister::XAPIC_SVR))
.field("XAPIC_ISR0", &self.read(ApicRegister::XAPIC_ISR0))
.field("XAPIC_ISR1", &self.read(ApicRegister::XAPIC_ISR1))
.field("XAPIC_ISR2", &self.read(ApicRegister::XAPIC_ISR2))
.field("XAPIC_ISR3", &self.read(ApicRegister::XAPIC_ISR3))
.field("XAPIC_ISR4", &self.read(ApicRegister::XAPIC_ISR4))
.field("XAPIC_ISR5", &self.read(ApicRegister::XAPIC_ISR5))
.field("XAPIC_ISR6", &self.read(ApicRegister::XAPIC_ISR6))
.field("XAPIC_ISR7", &self.read(ApicRegister::XAPIC_ISR7))
.field("XAPIC_TMR0", &self.read(ApicRegister::XAPIC_TMR0))
.field("XAPIC_TMR1", &self.read(ApicRegister::XAPIC_TMR1))
.field("XAPIC_TMR2", &self.read(ApicRegister::XAPIC_TMR2))
.field("XAPIC_TMR3", &self.read(ApicRegister::XAPIC_TMR3))
.field("XAPIC_TMR4", &self.read(ApicRegister::XAPIC_TMR4))
.field("XAPIC_TMR5", &self.read(ApicRegister::XAPIC_TMR5))
.field("XAPIC_TMR6", &self.read(ApicRegister::XAPIC_TMR6))
.field("XAPIC_TMR7", &self.read(ApicRegister::XAPIC_TMR7))
.field("XAPIC_IRR0", &self.read(ApicRegister::XAPIC_IRR0))
.field("XAPIC_IRR1", &self.read(ApicRegister::XAPIC_IRR1))
.field("XAPIC_IRR2", &self.read(ApicRegister::XAPIC_IRR2))
.field("XAPIC_IRR3", &self.read(ApicRegister::XAPIC_IRR3))
.field("XAPIC_IRR4", &self.read(ApicRegister::XAPIC_IRR4))
.field("XAPIC_IRR5", &self.read(ApicRegister::XAPIC_IRR5))
.field("XAPIC_IRR6", &self.read(ApicRegister::XAPIC_IRR6))
.field("XAPIC_IRR7", &self.read(ApicRegister::XAPIC_IRR7))
.field("XAPIC_ESR", &self.read(ApicRegister::XAPIC_ESR))
.field("XAPIC_LVT_CMCI", &self.read(ApicRegister::XAPIC_LVT_CMCI))
.field("XAPIC_ICR0", &self.read(ApicRegister::XAPIC_ICR0))
.field("XAPIC_ICR1", &self.read(ApicRegister::XAPIC_ICR1))
.field("XAPIC_LVT_TIMER", &self.read(ApicRegister::XAPIC_LVT_TIMER))
.field(
"XAPIC_LVT_THERMAL",
&self.read(ApicRegister::XAPIC_LVT_THERMAL),
)
.field("XAPIC_LVT_PMI", &self.read(ApicRegister::XAPIC_LVT_PMI))
.field("XAPIC_LVT_LINT0", &self.read(ApicRegister::XAPIC_LVT_LINT0))
.field("XAPIC_LVT_LINT1", &self.read(ApicRegister::XAPIC_LVT_LINT1))
.field("XAPIC_LVT_ERROR", &self.read(ApicRegister::XAPIC_LVT_ERROR))
.field(
"XAPIC_TIMER_INIT_COUNT",
&self.read(ApicRegister::XAPIC_TIMER_INIT_COUNT),
)
.field(
"XAPIC_TIMER_CURRENT_COUNT",
&self.read(ApicRegister::XAPIC_TIMER_CURRENT_COUNT),
)
.field(
"XAPIC_TIMER_DIV_CONF",
&self.read(ApicRegister::XAPIC_TIMER_DIV_CONF),
)
.finish()
}
}
impl XAPIC {
pub fn new(apic_region: &'static mut [u32]) -> XAPIC {
unsafe {
XAPIC {
mmio_region: apic_region,
base: rdmsr(IA32_APIC_BASE),
}
}
}
pub fn attach(&mut self) {
unsafe {
self.base = rdmsr(IA32_APIC_BASE);
self.base.set_bit(11, true);
wrmsr(IA32_APIC_BASE, self.base);
let svr: u32 = 1 << 8 | 15;
self.write(ApicRegister::XAPIC_SVR, svr);
}
}
pub fn detach(&mut self) {
unsafe {
self.base = rdmsr(IA32_APIC_BASE);
self.base.set_bit(11, false);
wrmsr(IA32_APIC_BASE, self.base);
}
}
fn read(&self, offset: ApicRegister) -> u32 {
assert!(offset as usize % 4 == 0);
let index = offset as usize / 4;
unsafe { volatile_load(&self.mmio_region[index]) }
}
fn write(&mut self, offset: ApicRegister, val: u32) {
assert!(offset as usize % 4 == 0);
let index = offset as usize / 4;
unsafe { volatile_store(&mut self.mmio_region[index], val) }
}
}
impl ApicControl for XAPIC {
fn bsp(&self) -> bool {
(self.base & (1 << 8)) > 0
}
fn id(&self) -> u32 {
self.read(ApicRegister::XAPIC_ID)
}
fn logical_id(&self) -> u32 {
self.read(ApicRegister::XAPIC_LDR)
}
fn version(&self) -> u32 {
self.read(ApicRegister::XAPIC_VERSION)
}
fn eoi(&mut self) {
self.write(ApicRegister::XAPIC_EOI, 0);
}
fn tsc_enable(&mut self, vector: u8) {
let mut lvt: u32 = self.read(ApicRegister::XAPIC_LVT_TIMER);
lvt &= !0xff;
lvt |= vector as u32;
lvt.set_bit(16, false);
lvt.set_bit(17, false);
lvt.set_bit(18, true);
self.write(ApicRegister::XAPIC_LVT_TIMER, lvt);
}
fn tsc_set(&self, value: u64) {
unsafe {
wrmsr(IA32_TSC_DEADLINE, value);
}
}
unsafe fn ipi_init(&mut self, core: ApicId) {
let icr = Icr::for_xapic(
0,
core,
DestinationShorthand::NoShorthand,
DeliveryMode::Init,
DestinationMode::Physical,
DeliveryStatus::Idle,
Level::Assert,
TriggerMode::Level,
);
self.send_ipi(icr);
}
unsafe fn ipi_init_deassert(&mut self) {
let icr = Icr::for_xapic(
0,
ApicId::XApic(0),
DestinationShorthand::AllIncludingSelf,
DeliveryMode::Init,
DestinationMode::Physical,
DeliveryStatus::Idle,
Level::Deassert,
TriggerMode::Level,
);
self.send_ipi(icr);
}
unsafe fn ipi_startup(&mut self, core: ApicId, start_page: u8) {
let icr = Icr::for_xapic(
start_page,
core,
DestinationShorthand::NoShorthand,
DeliveryMode::StartUp,
DestinationMode::Physical,
DeliveryStatus::Idle,
Level::Assert,
TriggerMode::Edge,
);
self.send_ipi(icr);
}
unsafe fn send_ipi(&mut self, icr: Icr) {
self.write(ApicRegister::XAPIC_ESR, 0);
self.write(ApicRegister::XAPIC_ESR, 0);
self.write(ApicRegister::XAPIC_ICR1, icr.upper());
self.write(ApicRegister::XAPIC_ICR0, icr.lower());
loop {
let icr = self.read(ApicRegister::XAPIC_ICR0);
if (icr >> 12 & 0x1) == 0 {
break;
}
if self.read(ApicRegister::XAPIC_ESR) > 0 {
break;
}
}
}
}