2023-08-01 11:09:56 +03:00
|
|
|
//! x86-64 Local APIC driver implementation
|
2025-02-10 18:07:43 +02:00
|
|
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
2023-08-31 13:40:17 +03:00
|
|
|
|
2023-12-09 15:27:09 +02:00
|
|
|
use abi::error::Error;
|
2024-12-10 11:52:26 +02:00
|
|
|
use alloc::{sync::Arc, vec, vec::Vec};
|
2023-08-31 13:40:17 +03:00
|
|
|
use device_api::{
|
2024-12-10 11:52:26 +02:00
|
|
|
device::Device,
|
2023-12-09 15:27:09 +02:00
|
|
|
interrupt::{
|
2025-02-05 21:45:48 +02:00
|
|
|
InterruptAffinity, InterruptHandler, IpiDeliveryTarget, IpiMessage, IrqVector,
|
2024-02-07 13:06:17 +02:00
|
|
|
LocalInterruptController, MessageInterruptController, MsiInfo,
|
2023-12-09 15:27:09 +02:00
|
|
|
},
|
2023-08-31 13:40:17 +03:00
|
|
|
};
|
2024-10-12 20:43:16 +03:00
|
|
|
use kernel_arch_x86::registers::MSR_IA32_APIC_BASE;
|
|
|
|
use kernel_arch_x86_64::{mem::table::L3, LocalApicInterface, CPU_COUNT};
|
2024-02-12 12:09:53 +02:00
|
|
|
use libk::arch::Cpu;
|
2024-07-25 11:58:47 +03:00
|
|
|
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo, table::EntryLevelExt};
|
2024-02-05 12:59:23 +02:00
|
|
|
use libk_util::{
|
2025-02-10 18:07:43 +02:00
|
|
|
sync::{spin_rwlock::IrqSafeRwLock, IrqGuard},
|
2024-02-05 12:59:23 +02:00
|
|
|
OneTimeInit,
|
2023-12-10 18:52:33 +02:00
|
|
|
};
|
2023-08-01 11:09:56 +03:00
|
|
|
use tock_registers::{
|
|
|
|
interfaces::{ReadWriteable, Readable, Writeable},
|
|
|
|
register_bitfields, register_structs,
|
|
|
|
registers::{ReadOnly, ReadWrite, WriteOnly},
|
|
|
|
};
|
|
|
|
|
2024-02-14 11:04:29 +02:00
|
|
|
use crate::arch::x86_64::apic::APIC_MSI_OFFSET;
|
2023-08-01 11:09:56 +03:00
|
|
|
|
2023-08-31 13:40:17 +03:00
|
|
|
use super::{
|
|
|
|
APIC_IPI_VECTOR, APIC_LINT0_VECTOR, APIC_LINT1_VECTOR, APIC_SPURIOUS_VECTOR, APIC_TIMER_VECTOR,
|
2023-12-11 21:13:33 +02:00
|
|
|
MAX_MSI_VECTORS,
|
2023-08-31 13:40:17 +03:00
|
|
|
};
|
2023-08-01 11:09:56 +03:00
|
|
|
|
2025-02-09 16:52:36 +02:00
|
|
|
const TIMER_INTERVAL: u32 = 15000;
|
2023-08-01 11:09:56 +03:00
|
|
|
|
|
|
|
/// 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 [
|
2023-08-05 16:32:12 +03:00
|
|
|
Vector OFFSET(0) NUMBITS(8) [],
|
|
|
|
SoftwareEnable OFFSET(8) NUMBITS(1) [],
|
2023-08-01 11:09:56 +03:00
|
|
|
],
|
|
|
|
TimerLocalVectorEntry [
|
|
|
|
Vector OFFSET(0) NUMBITS(8) [],
|
|
|
|
Mask OFFSET(16) NUMBITS(1) [
|
|
|
|
Masked = 1,
|
|
|
|
Unmasked = 0
|
|
|
|
],
|
|
|
|
TimerMode OFFSET(17) NUMBITS(1) [
|
|
|
|
Periodic = 1,
|
|
|
|
OneShot = 0
|
|
|
|
]
|
2023-08-03 18:49:29 +03:00
|
|
|
],
|
|
|
|
LocalVectorEntry [
|
|
|
|
Vector OFFSET(0) NUMBITS(8) [],
|
|
|
|
Mask OFFSET(16) NUMBITS(1) [
|
|
|
|
Masked = 1,
|
|
|
|
Unmasked = 0,
|
|
|
|
],
|
2023-08-05 16:32:12 +03:00
|
|
|
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,
|
2023-08-03 18:49:29 +03:00
|
|
|
],
|
2023-08-05 16:32:12 +03:00
|
|
|
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) []
|
|
|
|
],
|
2023-08-01 11:09:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
register_structs! {
|
|
|
|
#[allow(non_snake_case, missing_docs)]
|
|
|
|
Regs {
|
|
|
|
(0x00 => _0),
|
|
|
|
(0x20 => Id: ReadOnly<u32, Id::Register>),
|
|
|
|
(0x24 => _1),
|
2023-08-05 16:32:12 +03:00
|
|
|
(0x80 => TaskPriorityRegister: ReadWrite<u32>),
|
|
|
|
(0x84 => _13),
|
2023-08-01 11:09:56 +03:00
|
|
|
(0xB0 => EndOfInterrupt: WriteOnly<u32>),
|
|
|
|
(0xB4 => _2),
|
|
|
|
(0xF0 => SpuriousVector: ReadWrite<u32, SpuriousVector::Register>),
|
|
|
|
(0xF4 => _3),
|
2023-08-05 16:32:12 +03:00
|
|
|
(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),
|
2023-08-01 11:09:56 +03:00
|
|
|
(0x320 => TimerLocalVectorEntry: ReadWrite<u32, TimerLocalVectorEntry::Register>),
|
2023-08-05 16:32:12 +03:00
|
|
|
(0x324 => _7),
|
2023-08-03 18:49:29 +03:00
|
|
|
(0x350 => LInt0: ReadWrite<u32, LocalVectorEntry::Register>),
|
2023-08-05 16:32:12 +03:00
|
|
|
(0x354 => _8),
|
2023-08-03 18:49:29 +03:00
|
|
|
(0x360 => LInt1: ReadWrite<u32, LocalVectorEntry::Register>),
|
2023-08-05 16:32:12 +03:00
|
|
|
(0x364 => _9),
|
2023-08-01 11:09:56 +03:00
|
|
|
(0x380 => TimerInitCount: ReadWrite<u32>),
|
2023-08-05 16:32:12 +03:00
|
|
|
(0x384 => _10),
|
2023-08-01 11:09:56 +03:00
|
|
|
(0x390 => TimerCurrentCount: ReadOnly<u32>),
|
2023-08-05 16:32:12 +03:00
|
|
|
(0x394 => _11),
|
2023-08-01 11:09:56 +03:00
|
|
|
(0x3E0 => TimerDivideConfig: ReadWrite<u32>),
|
2023-08-05 16:32:12 +03:00
|
|
|
(0x3E4 => _12),
|
2023-08-01 11:09:56 +03:00
|
|
|
(0x530 => @END),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-02-10 18:07:43 +02:00
|
|
|
#[derive(Clone)]
|
|
|
|
struct MsiVector {
|
|
|
|
logical: usize,
|
|
|
|
handler: Arc<dyn InterruptHandler>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MsiVectorTable {
|
|
|
|
// MSI_MAX_VECTORS * vectors per row
|
|
|
|
msi_vectors: IrqSafeRwLock<Vec<Vec<MsiVector>>>,
|
|
|
|
last_msi_allocated: AtomicUsize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MsiVectorTable {
|
|
|
|
pub fn new(physical_vectors: usize) -> Self {
|
|
|
|
let rows = vec![Vec::new(); physical_vectors];
|
|
|
|
|
|
|
|
Self {
|
|
|
|
msi_vectors: IrqSafeRwLock::new(rows),
|
|
|
|
last_msi_allocated: AtomicUsize::new(0),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn map(
|
|
|
|
&self,
|
|
|
|
apic_id: usize,
|
|
|
|
range: &mut [MsiInfo],
|
|
|
|
handler: Arc<dyn InterruptHandler>,
|
|
|
|
) -> Result<(), Error> {
|
|
|
|
if range.is_empty() {
|
|
|
|
return Err(Error::InvalidArgument);
|
|
|
|
}
|
|
|
|
|
|
|
|
let seq = self
|
|
|
|
.last_msi_allocated
|
|
|
|
.fetch_add(range.len(), Ordering::AcqRel);
|
|
|
|
let mut rows = self.msi_vectors.write();
|
|
|
|
|
|
|
|
let physical_start = seq % rows.len();
|
|
|
|
let physical_end = seq.wrapping_add(range.len()) % rows.len();
|
|
|
|
if range.len() == 1 {
|
|
|
|
log::info!("Map MSI {:?} -> {}", handler.display_name(), physical_start);
|
|
|
|
} else {
|
|
|
|
log::info!(
|
|
|
|
"Map MSI {:?} -> {:?}",
|
|
|
|
handler.display_name(),
|
|
|
|
physical_start..physical_end - 1
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (logical, info) in range.iter_mut().enumerate() {
|
|
|
|
let physical = logical.wrapping_add(seq) % rows.len();
|
|
|
|
rows[physical].push(MsiVector {
|
|
|
|
logical,
|
|
|
|
handler: handler.clone(),
|
|
|
|
});
|
|
|
|
|
|
|
|
let address = 0xFEE00000 | (apic_id << 12);
|
|
|
|
let value = 32 + APIC_MSI_OFFSET + physical as u32;
|
|
|
|
|
|
|
|
*info = MsiInfo {
|
|
|
|
vector: logical,
|
|
|
|
affinity: InterruptAffinity::Specific(0),
|
|
|
|
address,
|
|
|
|
value,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn invoke(&self, physical_vector: usize) {
|
|
|
|
let rows = self.msi_vectors.read();
|
|
|
|
let row = &rows[physical_vector];
|
|
|
|
|
|
|
|
for col in row.iter() {
|
|
|
|
col.handler.clone().handle_irq(IrqVector::Msi(col.logical));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
/// Per-processor local APIC interface
|
|
|
|
pub struct LocalApic {
|
2023-09-13 18:21:45 +03:00
|
|
|
regs: DeviceMemoryIo<'static, Regs>,
|
2023-12-11 21:13:33 +02:00
|
|
|
id: u32,
|
2025-02-10 18:07:43 +02:00
|
|
|
msi_table: MsiVectorTable,
|
2023-08-01 11:09:56 +03:00
|
|
|
}
|
|
|
|
|
2023-08-21 17:26:44 +03:00
|
|
|
unsafe impl Send for LocalApic {}
|
|
|
|
unsafe impl Sync for LocalApic {}
|
|
|
|
|
|
|
|
impl Device for LocalApic {
|
2024-12-10 11:52:26 +02:00
|
|
|
fn display_name(&self) -> &str {
|
2023-08-21 17:26:44 +03:00
|
|
|
"Local APIC"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-08 15:50:25 +02:00
|
|
|
impl LocalApicInterface for LocalApic {
|
2024-12-05 19:25:18 +02:00
|
|
|
#[inline]
|
2024-02-08 15:50:25 +02:00
|
|
|
fn clear_interrupt(&self) {
|
|
|
|
self.regs.EndOfInterrupt.set(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe fn wakeup_cpu(&self, apic_id: u32, entry_vector: PhysicalAddress) {
|
2024-11-30 23:51:02 +02:00
|
|
|
log::info!("Waking up apic{}, entry = {:#x}", apic_id, entry_vector);
|
2024-02-08 15:50:25 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-09 15:27:09 +02:00
|
|
|
impl MessageInterruptController for LocalApic {
|
|
|
|
fn handle_msi(&self, vector: usize) {
|
2025-02-10 18:07:43 +02:00
|
|
|
self.msi_table.invoke(vector);
|
2023-12-09 15:27:09 +02:00
|
|
|
}
|
|
|
|
|
2023-12-11 21:13:33 +02:00
|
|
|
fn register_msi_range(
|
2025-02-05 21:45:48 +02:00
|
|
|
self: Arc<Self>,
|
2023-12-11 21:13:33 +02:00
|
|
|
range: &mut [MsiInfo],
|
2024-12-10 11:52:26 +02:00
|
|
|
handler: Arc<dyn InterruptHandler>,
|
2023-12-11 21:13:33 +02:00
|
|
|
) -> Result<(), Error> {
|
|
|
|
let _guard = IrqGuard::acquire();
|
2025-02-10 18:07:43 +02:00
|
|
|
self.msi_table.map(self.id as usize, range, handler)
|
2023-12-09 15:27:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-21 17:26:44 +03:00
|
|
|
impl LocalInterruptController for LocalApic {
|
2024-02-07 13:06:17 +02:00
|
|
|
fn send_ipi(&self, target: IpiDeliveryTarget, msg: IpiMessage) -> Result<(), Error> {
|
2023-08-31 13:40:17 +03:00
|
|
|
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 => {
|
2023-12-22 12:36:42 +02:00
|
|
|
let local = Cpu::local();
|
|
|
|
let local_id = local.id() as usize;
|
|
|
|
|
2023-08-31 13:40:17 +03:00
|
|
|
for i in 0..CPU_COUNT.load(Ordering::Acquire) {
|
2023-12-22 12:36:42 +02:00
|
|
|
if i != local_id {
|
2023-08-31 13:40:17 +03:00
|
|
|
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(())
|
|
|
|
}
|
2024-11-05 22:00:10 +02:00
|
|
|
IpiDeliveryTarget::ThisCpu => unimplemented!(),
|
|
|
|
IpiDeliveryTarget::Specific(_) => unimplemented!(),
|
2023-08-31 13:40:17 +03:00
|
|
|
}
|
2023-08-21 17:26:44 +03:00
|
|
|
}
|
|
|
|
|
2023-12-09 15:27:09 +02:00
|
|
|
unsafe fn init_ap(&self) -> Result<(), Error> {
|
2024-11-05 22:00:10 +02:00
|
|
|
unreachable!()
|
2023-08-21 17:26:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-01 11:09:56 +03:00
|
|
|
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 {
|
2024-02-03 20:44:04 +02:00
|
|
|
let regs = DeviceMemoryIo::<Regs>::map(Self::base(), Default::default()).unwrap();
|
2023-08-01 11:09:56 +03:00
|
|
|
|
|
|
|
let id = regs.Id.read(Id::ApicId);
|
|
|
|
|
|
|
|
if Self::is_bootstrap_cpu() {
|
|
|
|
BSP_APIC_ID.init(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
Self::enable();
|
2023-08-05 16:32:12 +03:00
|
|
|
// 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);
|
2023-08-01 11:09:56 +03:00
|
|
|
|
|
|
|
// Enable timer
|
2025-02-09 16:52:36 +02:00
|
|
|
regs.TimerDivideConfig.set(0x2);
|
2023-08-05 16:32:12 +03:00
|
|
|
regs.TimerInitCount.set(TIMER_INTERVAL);
|
2023-08-01 11:09:56 +03:00
|
|
|
|
2023-08-05 16:32:12 +03:00
|
|
|
// Configure local interrupt vectors
|
2023-08-01 11:09:56 +03:00
|
|
|
regs.TimerLocalVectorEntry.write(
|
|
|
|
TimerLocalVectorEntry::Vector.val(APIC_TIMER_VECTOR + 32)
|
|
|
|
+ TimerLocalVectorEntry::Mask::Unmasked
|
|
|
|
+ TimerLocalVectorEntry::TimerMode::Periodic,
|
|
|
|
);
|
2023-08-05 16:32:12 +03:00
|
|
|
// 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),
|
|
|
|
);
|
2023-08-01 11:09:56 +03:00
|
|
|
|
2025-02-10 18:07:43 +02:00
|
|
|
let msi_table = MsiVectorTable::new(MAX_MSI_VECTORS as _);
|
2023-12-11 21:13:33 +02:00
|
|
|
|
2023-12-09 15:27:09 +02:00
|
|
|
Self {
|
2023-12-11 21:13:33 +02:00
|
|
|
id,
|
2023-12-09 15:27:09 +02:00
|
|
|
regs,
|
2025-02-10 18:07:43 +02:00
|
|
|
msi_table,
|
2023-12-09 15:27:09 +02:00
|
|
|
}
|
2023-08-01 11:09:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-09-13 18:21:45 +03:00
|
|
|
fn base() -> PhysicalAddress {
|
2024-07-25 11:58:47 +03:00
|
|
|
PhysicalAddress::from_u64(MSR_IA32_APIC_BASE.read_base())
|
2023-08-01 11:09:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn is_bootstrap_cpu() -> bool {
|
|
|
|
MSR_IA32_APIC_BASE.read(MSR_IA32_APIC_BASE::BootstrapCpuCore) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn enable() {
|
2023-08-05 16:32:12 +03:00
|
|
|
MSR_IA32_APIC_BASE.modify(
|
|
|
|
MSR_IA32_APIC_BASE::ApicEnable::SET + MSR_IA32_APIC_BASE::ExtendedEnable::CLEAR,
|
|
|
|
);
|
2023-08-01 11:09:56 +03:00
|
|
|
}
|
|
|
|
}
|