use core::mem::size_of;
use core::ptr;
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::util::Once;
use crate::MAX_CPUS;
pub const INVALID_ID: u8 = 0xff;
pub static LAPIC_IDS: Once<[u8; MAX_CPUS]> = Once::new();
pub fn parse_apic_table(acpi: Acpi) -> (u8, usize) {
let madt = acpi.find(*b"APIC").unwrap().cast::<ApicTable>();
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 mut lapic_ids = [INVALID_ID; MAX_CPUS];
let mut ioapic = None;
for entry in madt.iter() {
match entry {
Entry::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:?}");
}
}
Entry::IOApic(entry) => {
if entry.global_int_base < IoApic::SLOT_MAX as _ && ioapic.is_none() {
ioapic = Some(entry);
} else {
serial!("ignore {entry:?}");
}
}
Entry::IntSource(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);
}
}
Entry::LApicAddr(entry) => {
LAPIC.base.store(entry.lapic_addr as _, Release);
}
Entry::Unknown(entry) => serial!("ignore {entry:?}"),
}
}
serial!("lapics: {lapic_ids:?}");
LAPIC_IDS.set(lapic_ids).unwrap();
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)]
struct ApicTable {
header: SysDescTable,
lapic_address: u32,
flags: ApicFlags,
}
impl AcpiTable for ApicTable {}
#[bitfield(u32)]
struct ApicFlags {
pcat_compat: bool,
#[bits(31)]
_p: u32,
}
impl ApicTable {
fn iter(&self) -> AcpiTableIter {
let start = ptr::from_ref(self).cast::<u8>();
AcpiTableIter {
start: unsafe { start.add(size_of::<Self>()) },
end: unsafe { start.add(self.header.length as usize) },
}
}
}
struct AcpiTableIter {
start: *const u8,
end: *const u8,
}
impl Iterator for AcpiTableIter {
type Item = Entry;
fn next(&mut self) -> Option<Self::Item> {
if self.start < self.end {
unsafe {
let entry = &*self.start.cast::<Header>();
let start = self.start.add(2);
self.start = self.start.add(entry.len as usize);
Some(match entry.ty {
0 => Entry::LApic(*start.cast()),
1 => Entry::IOApic(*start.cast()),
2 => Entry::IntSource(*start.cast()),
5 => Entry::LApicAddr(*start.cast()),
ty => Entry::Unknown(ty),
})
}
} else {
None
}
}
}
#[repr(C, packed)]
struct Header {
ty: u8,
len: u8,
}
#[derive(Debug)]
enum Entry {
LApic(LApicEntry),
IOApic(IOApicEntry),
IntSource(IntSourceEntry),
LApicAddr(LApicAddrEntry),
Unknown(u8),
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct LApicEntry {
pub apic_cid: u8,
pub apic_id: u8,
pub flags: LApicFlags,
}
#[bitfield(u32)]
pub struct LApicFlags {
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 IntSourceEntry {
pub bus: u8,
pub source: u8,
pub global_int: u32,
pub flags: u16,
}
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct LApicAddrEntry {
pub _reserved: u16,
pub lapic_addr: u64,
}