364 lines
10 KiB
Rust

//! x86-64 Local APIC driver implementation
use core::sync::atomic::Ordering;
use abi::error::Error;
use alloc::vec::Vec;
use device_api::{
interrupt::{
IpiDeliveryTarget, LocalInterruptController, MessageInterruptController, MsiHandler,
MsiInfo,
},
Device,
};
use kernel_util::{sync::IrqSafeSpinlock, util::OneTimeInit};
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use crate::{
arch::{
x86_64::{
apic::APIC_MSI_OFFSET, mem::table::L3, registers::MSR_IA32_APIC_BASE, smp::CPU_COUNT,
},
CpuMessage,
},
mem::{
address::{FromRaw, IntoRaw},
device::DeviceMemoryIo,
PhysicalAddress,
},
task::Cpu,
};
use super::{
APIC_IPI_VECTOR, APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR,
};
const TIMER_INTERVAL: u32 = 150000;
/// When initialized, contains the Local APIC ID of the bootstrap processor
pub static BSP_APIC_ID: OneTimeInit<u32> = OneTimeInit::new();
register_bitfields! {
u32,
Id [
ApicId OFFSET(24) NUMBITS(8) []
],
SpuriousVector [
Vector OFFSET(0) NUMBITS(8) [],
SoftwareEnable OFFSET(8) NUMBITS(1) [],
],
TimerLocalVectorEntry [
Vector OFFSET(0) NUMBITS(8) [],
Mask OFFSET(16) NUMBITS(1) [
Masked = 1,
Unmasked = 0
],
TimerMode OFFSET(17) NUMBITS(1) [
Periodic = 1,
OneShot = 0
]
],
LocalVectorEntry [
Vector OFFSET(0) NUMBITS(8) [],
Mask OFFSET(16) NUMBITS(1) [
Masked = 1,
Unmasked = 0,
],
DeliveryMode OFFSET(8) NUMBITS(3) [
Nmi = 4,
ExtINT = 7
],
],
ICR0 [
Vector OFFSET(0) NUMBITS(8) [],
Destination OFFSET(8) NUMBITS(3) [
Normal = 1,
Lowest = 2,
SMI = 3,
NMI = 4,
INIT = 5,
SIPI = 6
],
DeliveryStatus OFFSET(12) NUMBITS(1) [],
INIT0 OFFSET(14) NUMBITS(1) [
Deassert = 0,
Assert = 1,
],
INIT1 OFFSET(15) NUMBITS(1) [
Deassert = 1,
Assert = 0,
],
DestinationType OFFSET(18) NUMBITS(3) [
Physical = 0,
This = 1,
All = 2,
AllExceptThis = 3,
]
],
ICR1 [
PhysicalDestination OFFSET(24) NUMBITS(4) []
],
}
register_structs! {
#[allow(non_snake_case, missing_docs)]
Regs {
(0x00 => _0),
(0x20 => Id: ReadOnly<u32, Id::Register>),
(0x24 => _1),
(0x80 => TaskPriorityRegister: ReadWrite<u32>),
(0x84 => _13),
(0xB0 => EndOfInterrupt: WriteOnly<u32>),
(0xB4 => _2),
(0xF0 => SpuriousVector: ReadWrite<u32, SpuriousVector::Register>),
(0xF4 => _3),
(0x100 => ISR0: ReadOnly<u32>),
(0x104 => _14),
(0x280 => ErrorStatus: ReadOnly<u32>),
(0x284 => _4),
(0x300 => ICR0: ReadWrite<u32, ICR0::Register>),
(0x304 => _5),
(0x310 => ICR1: ReadWrite<u32, ICR1::Register>),
(0x314 => _6),
(0x320 => TimerLocalVectorEntry: ReadWrite<u32, TimerLocalVectorEntry::Register>),
(0x324 => _7),
(0x350 => LInt0: ReadWrite<u32, LocalVectorEntry::Register>),
(0x354 => _8),
(0x360 => LInt1: ReadWrite<u32, LocalVectorEntry::Register>),
(0x364 => _9),
(0x380 => TimerInitCount: ReadWrite<u32>),
(0x384 => _10),
(0x390 => TimerCurrentCount: ReadOnly<u32>),
(0x394 => _11),
(0x3E0 => TimerDivideConfig: ReadWrite<u32>),
(0x3E4 => _12),
(0x530 => @END),
}
}
/// Per-processor local APIC interface
pub struct LocalApic {
regs: DeviceMemoryIo<'static, Regs>,
msi_vectors: IrqSafeSpinlock<Vec<&'static dyn MsiHandler>>,
}
unsafe impl Send for LocalApic {}
unsafe impl Sync for LocalApic {}
impl Device for LocalApic {
fn display_name(&self) -> &'static str {
"Local APIC"
}
}
impl MessageInterruptController for LocalApic {
fn handle_msi(&self, vector: usize) {
// TODO this is ugly
let mut i = 0;
loop {
let table = self.msi_vectors.lock();
let Some(&handler) = table.get(i) else {
break;
};
drop(table);
if handler.handle_msi(vector) {
break;
}
i += 1;
}
}
fn register_msi(&self, handler: &'static dyn MsiHandler) -> Result<MsiInfo, Error> {
// TODO only 1 ISR vector allocated for MSIs
let vector = 0;
let mut table = self.msi_vectors.lock();
table.push(handler);
// TODO magic numbers
let apic_vector = 32 + APIC_MSI_OFFSET + vector;
let value = apic_vector;
let address = Self::base();
Ok(MsiInfo {
address: address.into_raw(),
value,
vector: vector as _,
})
}
}
impl LocalInterruptController for LocalApic {
type IpiMessage = CpuMessage;
fn send_ipi(&self, target: IpiDeliveryTarget, msg: Self::IpiMessage) -> Result<(), Error> {
while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
core::hint::spin_loop();
}
// TODO use NMI or regular interrupt depending on severity of the message
match target {
IpiDeliveryTarget::OtherCpus => {
let local = Cpu::local_id();
for i in 0..CPU_COUNT.load(Ordering::Acquire) {
if i != local as usize {
Cpu::push_ipi_queue(i as u32, msg);
}
}
self.regs.ICR1.write(ICR1::PhysicalDestination.val(0));
self.regs.ICR0.write(
ICR0::Vector.val(APIC_IPI_VECTOR + 32)
+ ICR0::Destination::NMI
+ ICR0::DestinationType::AllExceptThis,
);
Ok(())
}
IpiDeliveryTarget::ThisCpu => todo!(),
IpiDeliveryTarget::Specific(_) => todo!(),
}
}
unsafe fn init_ap(&self) -> Result<(), Error> {
todo!()
}
}
impl LocalApic {
/// Constructs a new instance of Local APIC.
///
/// # Safety
///
/// Only meant to be called once per processor during their init.
pub unsafe fn new() -> Self {
let regs = DeviceMemoryIo::<Regs>::map(Self::base()).unwrap();
let id = regs.Id.read(Id::ApicId);
if Self::is_bootstrap_cpu() {
BSP_APIC_ID.init(id);
}
Self::enable();
// Configure spurious interrupt handler
regs.SpuriousVector.write(
SpuriousVector::SoftwareEnable::SET
+ SpuriousVector::Vector.val(APIC_SPURIOUS_VECTOR + 32),
);
// Configure task priority register
regs.TaskPriorityRegister.set(0);
// Enable timer
regs.TimerDivideConfig.set(0x3);
regs.TimerInitCount.set(TIMER_INTERVAL);
// Configure local interrupt vectors
regs.TimerLocalVectorEntry.write(
TimerLocalVectorEntry::Vector.val(APIC_TIMER_VECTOR + 32)
+ TimerLocalVectorEntry::Mask::Unmasked
+ TimerLocalVectorEntry::TimerMode::Periodic,
);
// LINT0 unmasked, leave LINT1 masked
regs.LInt0.write(
LocalVectorEntry::Mask::Unmasked
+ LocalVectorEntry::Vector.val(APIC_LINT0_VECTOR + 32)
+ LocalVectorEntry::DeliveryMode::ExtINT,
);
regs.LInt1.write(
LocalVectorEntry::Mask::Masked + LocalVectorEntry::Vector.val(APIC_LINT1_VECTOR + 32),
);
Self {
regs,
msi_vectors: IrqSafeSpinlock::new(Vec::new()),
}
}
/// Signals local APIC that we've handled the IRQ
pub fn clear_interrupt(&self) {
self.regs.EndOfInterrupt.set(0);
}
/// Performs an application processor startup sequence.
///
/// # Safety
///
/// Unsafe: only meant to be called by the BSP during SMP init.
pub unsafe fn wakeup_cpu(&self, apic_id: u32, entry_vector: PhysicalAddress) {
infoln!("Waking up apic{}, entry = {:#x}", apic_id, entry_vector);
while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
core::hint::spin_loop();
}
let entry_vector = entry_vector.page_index::<L3>();
// INIT assert
self.regs.ICR1.write(ICR1::PhysicalDestination.val(apic_id));
self.regs.ICR0.write(
ICR0::Destination::INIT
+ ICR0::DestinationType::Physical
+ ICR0::INIT0::Assert
+ ICR0::INIT1::Assert,
);
while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
core::hint::spin_loop();
}
// INIT deassert
self.regs.ICR1.write(ICR1::PhysicalDestination.val(apic_id));
self.regs.ICR0.write(
ICR0::Destination::INIT
+ ICR0::DestinationType::Physical
+ ICR0::INIT0::Deassert
+ ICR0::INIT1::Deassert,
);
while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
core::hint::spin_loop();
}
// Send another SIPI type IPI because the spec says so
self.regs.ICR1.write(ICR1::PhysicalDestination.val(apic_id));
self.regs.ICR0.write(
ICR0::Vector.val(entry_vector as u32)
+ ICR0::Destination::SIPI
+ ICR0::DestinationType::Physical,
);
while self.regs.ICR0.matches_all(ICR0::DeliveryStatus::SET) {
core::hint::spin_loop();
}
}
#[inline]
fn base() -> PhysicalAddress {
PhysicalAddress::from_raw(MSR_IA32_APIC_BASE.read_base())
}
#[inline]
fn is_bootstrap_cpu() -> bool {
MSR_IA32_APIC_BASE.read(MSR_IA32_APIC_BASE::BootstrapCpuCore) != 0
}
#[inline]
fn enable() {
MSR_IA32_APIC_BASE.modify(
MSR_IA32_APIC_BASE::ApicEnable::SET + MSR_IA32_APIC_BASE::ExtendedEnable::CLEAR,
);
}
}