use core::mem::size_of;
use core::sync::atomic::Ordering::Release;
use bitfield_struct::bitfield;
use super::ioapic::{IoApic, IOAPIC};
use super::lapic::LAPIC;
use super::pic;
use crate::arch::acpi::{Acpi, AcpiTable, SysDescTable};
use crate::MAX_CPUS;
pub const INVALID_ID: u8 = 0xff;
pub static mut LAPIC_IDS: [u8; MAX_CPUS] = [INVALID_ID; MAX_CPUS];
pub fn parse_apic_table(acpi: Acpi) -> (u8, usize) {
let madt = acpi.find(*b"APIC").unwrap().cast::<MulAcpiTable>();
let flags = madt.flags;
LAPIC.base.store(madt.lapic_address as _, Release);
if flags.pcat_compat() {
serial!("disable pic");
pic::disable();
}
let mut lapics = 0;
let lapic_ids = unsafe { &mut LAPIC_IDS };
let mut ioapic = None;
for entry in madt.iter() {
match entry {
ApicEntry::LApic(entry) => {
let flags = entry.flags;
if flags.enabled() && entry.apic_id != INVALID_ID && lapics < lapic_ids.len() {
lapic_ids[lapics] = entry.apic_id;
lapics += 1;
} else {
serial!("ignore {entry:?}");
}
}
ApicEntry::IOApic(entry) => {
if entry.global_int_base < IoApic::SLOT_MAX as _ && ioapic.is_none() {
ioapic = Some(entry)
} else {
serial!("ignore {entry:?}");
}
}
ApicEntry::IntSrcOverride(entry) => {
if entry.bus == 0 {
let global_int = entry.global_int;
let source = entry.source as usize;
serial!("Override interrupt {source} <- {global_int}");
IOAPIC.set_override(source, global_int as _);
} else {
serial!("invalid apic bus {}", entry.bus);
}
}
ApicEntry::LApicAddrOverride(entry) => {
LAPIC.base.store(entry.lapic_addr as _, Release);
}
_ => {
serial!("ignore {entry:?}")
}
}
}
serial!("lapics: {lapic_ids:?}");
let ioapic = ioapic.expect("Not IOAPIC found!");
let ioapic_addr = ioapic.ioapic_addr as *mut u32;
serial!("init ioapic {} @ {ioapic_addr:?}", ioapic.ioapic_id);
IOAPIC.base.store(ioapic_addr, Release);
(ioapic.ioapic_id, lapics)
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct MulAcpiTable {
pub header: SysDescTable,
pub lapic_address: u32,
pub flags: MulAcpiTableF,
}
impl AcpiTable for MulAcpiTable {}
#[bitfield(u32)]
pub struct MulAcpiTableF {
pub pcat_compat: bool,
#[bits(31)]
_p: u32,
}
impl MulAcpiTable {
pub fn iter(&self) -> MulAcpiTableIter {
let start = self as *const _ as usize;
MulAcpiTableIter {
start: start + size_of::<Self>(),
end: start + self.header.length as usize,
}
}
}
pub struct MulAcpiTableIter {
start: usize,
end: usize,
}
impl Iterator for MulAcpiTableIter {
type Item = ApicEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.start < self.end {
#[repr(C, packed)]
struct Header {
ty: u8,
len: u8,
}
let entry = unsafe { &*(self.start as *const Header) };
let start = (self.start + 2) as *const ();
self.start += entry.len as usize;
Some(match entry.ty {
0 => ApicEntry::LApic(unsafe { *start.cast() }),
1 => ApicEntry::IOApic(unsafe { *start.cast() }),
2 => ApicEntry::IntSrcOverride(unsafe { *start.cast() }),
5 => ApicEntry::LApicAddrOverride(unsafe { *start.cast() }),
ty => ApicEntry::Unknown(ty),
})
} else {
None
}
}
}
#[derive(Debug)]
pub enum ApicEntry {
LApic(LApicEntry),
IOApic(IOApicEntry),
IntSrcOverride(IntSrcOverrideEntry),
LApicAddrOverride(LApicAddrOverrideEntry),
Unknown(u8),
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct LApicEntry {
pub apic_cid: u8,
pub apic_id: u8,
pub flags: ApicEntryLApicF,
}
#[bitfield(u32)]
pub struct ApicEntryLApicF {
pub enabled: bool,
#[bits(31)]
__: u32,
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct IOApicEntry {
pub ioapic_id: u8,
pub _reserved: u8,
pub ioapic_addr: u32,
pub global_int_base: u32,
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct IntSrcOverrideEntry {
pub bus: u8,
pub source: u8,
pub global_int: u32,
pub flags: u16,
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct LApicAddrOverrideEntry {
pub _reserved: u16,
pub lapic_addr: u64,
}