mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 05:16:47 +00:00
Support sending IPI using APIC
Co-authored-by: Chuandong Li <lichuand@pku.edu.cn>
This commit is contained in:
parent
fef8eebadc
commit
0cf954801d
@ -4,6 +4,7 @@
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use bit_field::BitField;
|
||||
use log::info;
|
||||
use spin::Once;
|
||||
|
||||
@ -14,6 +15,7 @@ pub mod x2apic;
|
||||
pub mod xapic;
|
||||
|
||||
pub static APIC_INSTANCE: Once<Arc<SpinLock<dyn Apic + 'static>>> = Once::new();
|
||||
static APIC_TYPE: Once<ApicType> = Once::new();
|
||||
|
||||
pub trait Apic: ApicTimer + Sync + Send {
|
||||
fn id(&self) -> u32;
|
||||
@ -22,6 +24,9 @@ pub trait Apic: ApicTimer + Sync + Send {
|
||||
|
||||
/// End of Interrupt, this function will inform APIC that this interrupt has been processed.
|
||||
fn eoi(&mut self);
|
||||
|
||||
/// Send a general inter-processor interrupt.
|
||||
unsafe fn send_ipi(&mut self, icr: Icr);
|
||||
}
|
||||
|
||||
pub trait ApicTimer: Sync + Send {
|
||||
@ -43,6 +48,191 @@ pub trait ApicTimer: Sync + Send {
|
||||
fn set_timer_div_config(&mut self, div_config: DivideConfig);
|
||||
}
|
||||
|
||||
enum ApicType {
|
||||
XApic,
|
||||
X2Apic,
|
||||
}
|
||||
|
||||
/// The inter-processor interrupt control register.
|
||||
///
|
||||
/// ICR is a 64-bit local APIC register that allows software running on the
|
||||
/// porcessor to specify and send IPIs to other porcessors in the system.
|
||||
/// To send an IPI, software must set up the ICR to indicate the type of IPI
|
||||
/// message to be sent and the destination processor or processors. (All fields
|
||||
/// of the ICR are read-write by software with the exception of the delivery
|
||||
/// status field, which is read-only.)
|
||||
///
|
||||
/// The act of writing to the low doubleword of the ICR causes the IPI to be
|
||||
/// sent. Therefore, in xapic mode, high doubleword of the ICR needs to be written
|
||||
/// first and then the low doubleword to ensure the correct interrupt is sent.
|
||||
///
|
||||
/// The ICR consists of the following fields:
|
||||
/// - **Bit 0-7** Vector :The vector number of the interrupt being sent.
|
||||
/// - **Bit 8-10** Delivery Mode :Specifies the type of IPI to be sent.
|
||||
/// - **Bit 11** Destination Mode :Selects either physical or logical destination mode.
|
||||
/// - **Bit 12** Delivery Status(RO) :Indicates the IPI delivery status.
|
||||
/// - **Bit 13** Reserved
|
||||
/// - **Bit 14** Level :Only set 1 for the INIT level de-assert delivery mode.
|
||||
/// - **Bit 15** Trigger Mode :Selects level or edge trigger mode.
|
||||
/// - **Bit 16-17** Reserved
|
||||
/// - **Bit 18-19** Destination Shorthand :Indicates destination set.
|
||||
/// - **Bit 20-55** Reserved
|
||||
/// - **Bit 56-63** Destination Field :Specifies the target processor or processors.
|
||||
pub struct Icr(u64);
|
||||
|
||||
impl Icr {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
destination: ApicId,
|
||||
destination_shorthand: DestinationShorthand,
|
||||
trigger_mode: TriggerMode,
|
||||
level: Level,
|
||||
delivery_status: DeliveryStatus,
|
||||
destination_mode: DestinationMode,
|
||||
delivery_mode: DeliveryMode,
|
||||
vector: u8,
|
||||
) -> Self {
|
||||
let dest = match destination {
|
||||
ApicId::XApic(d) => (d as u64) << 56,
|
||||
ApicId::X2Apic(d) => (d as u64) << 32,
|
||||
};
|
||||
Icr(dest
|
||||
| (destination_shorthand as u64) << 18
|
||||
| (trigger_mode as u64) << 15
|
||||
| (level as u64) << 14
|
||||
| (delivery_status as u64) << 12
|
||||
| (destination_mode as u64) << 11
|
||||
| (delivery_mode as u64) << 8
|
||||
| (vector as u64))
|
||||
}
|
||||
|
||||
/// Returns the lower 32 bits of the ICR.
|
||||
pub fn lower(&self) -> u32 {
|
||||
self.0 as u32
|
||||
}
|
||||
|
||||
/// Returns the higher 32 bits of the ICR.
|
||||
pub fn upper(&self) -> u32 {
|
||||
(self.0 >> 32) as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// The core identifier. ApicId can be divided into Physical ApicId and Logical ApicId.
|
||||
/// The Physical ApicId is the value read from the LAPIC ID Register, while the Logical ApicId has different
|
||||
/// encoding modes in XApic and X2Apic.
|
||||
pub enum ApicId {
|
||||
XApic(u8),
|
||||
X2Apic(u32),
|
||||
}
|
||||
|
||||
impl ApicId {
|
||||
/// Returns the logical x2apic ID.
|
||||
///
|
||||
/// In x2APIC mode, the 32-bit logical x2APIC ID, which can be read from
|
||||
/// LDR, is derived from the 32-bit local x2APIC ID:
|
||||
/// Logical x2APIC ID = [(x2APIC ID[19:4] << 16) | (1 << x2APIC ID[3:0])]
|
||||
pub fn x2apic_logical_id(&self) -> u32 {
|
||||
self.x2apic_logical_cluster_id() << 16 | 1 << self.x2apic_logical_field_id()
|
||||
}
|
||||
|
||||
/// Returns the logical x2apic cluster ID.
|
||||
///
|
||||
/// Logical cluster ID = x2APIC ID[19:4]
|
||||
pub fn x2apic_logical_cluster_id(&self) -> u32 {
|
||||
let apic_id = match *self {
|
||||
ApicId::XApic(id) => id as u32,
|
||||
ApicId::X2Apic(id) => id,
|
||||
};
|
||||
apic_id.get_bits(4..=19)
|
||||
}
|
||||
|
||||
/// Returns the logical x2apic field ID.
|
||||
///
|
||||
/// Specifically, the 16-bit logical ID sub-field is derived by the lowest
|
||||
/// 4 bits of the x2APIC ID, i.e.,
|
||||
/// Logical field ID = x2APIC ID[3:0].
|
||||
pub fn x2apic_logical_field_id(&self) -> u32 {
|
||||
let apic_id = match *self {
|
||||
ApicId::XApic(id) => id as u32,
|
||||
ApicId::X2Apic(id) => id,
|
||||
};
|
||||
apic_id.get_bits(0..=3)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for ApicId {
|
||||
fn from(value: u32) -> Self {
|
||||
match APIC_TYPE.get().unwrap() {
|
||||
ApicType::XApic => ApicId::XApic(value as u8),
|
||||
ApicType::X2Apic => ApicId::X2Apic(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates whether a shorthand notation is used to specify the destination of
|
||||
/// the interrupt and, if so, which shorthand is used. Destination shorthands are
|
||||
/// used in place of the 8-bit destination field, and can be sent by software
|
||||
/// using a single write to the low doubleword of the ICR.
|
||||
///
|
||||
/// Shorthands are defined for the following cases: software self interrupt, IPIs
|
||||
/// to all processors in the system including the sender, IPIs to all processors
|
||||
/// in the system excluding the sender.
|
||||
#[repr(u64)]
|
||||
pub enum DestinationShorthand {
|
||||
NoShorthand = 0b00,
|
||||
MySelf = 0b01,
|
||||
AllIncludingSelf = 0b10,
|
||||
AllExcludingSelf = 0b11,
|
||||
}
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum TriggerMode {
|
||||
Egde = 0,
|
||||
Level = 1,
|
||||
}
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum Level {
|
||||
Deassert = 0,
|
||||
Assert = 1,
|
||||
}
|
||||
|
||||
/// Indicates the IPI delivery status (read only), as follows:
|
||||
/// **0 (Idle)** Indicates that this local APIC has completed sending any previous IPIs.
|
||||
/// **1 (Send Pending)** Indicates that this local APIC has not completed sending the last IPI.
|
||||
#[repr(u64)]
|
||||
pub enum DeliveryStatus {
|
||||
Idle = 0,
|
||||
SendPending = 1,
|
||||
}
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum DestinationMode {
|
||||
Physical = 0,
|
||||
Logical = 1,
|
||||
}
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum DeliveryMode {
|
||||
/// Delivers the interrupt specified in the vector field to the target processor or processors.
|
||||
Fixed = 0b000,
|
||||
/// Same as fixed mode, except that the interrupt is delivered to the processor executing at
|
||||
/// the lowest priority among the set of processors specified in the destination field. The
|
||||
/// ability for a processor to send a lowest priority IPI is model specific and should be
|
||||
/// avoided by BIOS and operating system software.
|
||||
LowestPriority = 0b001,
|
||||
/// Non-Maskable Interrupt
|
||||
Smi = 0b010,
|
||||
_Reserved = 0b011,
|
||||
/// System Management Interrupt
|
||||
Nmi = 0b100,
|
||||
/// Delivers an INIT request to the target processor or processors, which causes them to
|
||||
/// perform an initialization.
|
||||
Init = 0b101,
|
||||
/// Start-up Interrupt
|
||||
StrartUp = 0b110,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ApicInitError {
|
||||
/// No x2APIC or xAPIC found.
|
||||
@ -74,6 +264,7 @@ pub fn init() -> Result<(), ApicInitError> {
|
||||
(version >> 16) & 0xff
|
||||
);
|
||||
APIC_INSTANCE.call_once(|| Arc::new(SpinLock::new(x2apic)));
|
||||
APIC_TYPE.call_once(|| ApicType::X2Apic);
|
||||
Ok(())
|
||||
} else if let Some(mut xapic) = xapic::XApic::new() {
|
||||
xapic.enable();
|
||||
@ -85,9 +276,10 @@ pub fn init() -> Result<(), ApicInitError> {
|
||||
(version >> 16) & 0xff
|
||||
);
|
||||
APIC_INSTANCE.call_once(|| Arc::new(SpinLock::new(xapic)));
|
||||
APIC_TYPE.call_once(|| ApicType::XApic);
|
||||
Ok(())
|
||||
} else {
|
||||
log::warn!("Not found x2APIC or xAPIC");
|
||||
log::warn!("Neither x2APIC nor xAPIC found!");
|
||||
Err(ApicInitError::NoApic)
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
use x86::msr::{
|
||||
rdmsr, wrmsr, IA32_APIC_BASE, IA32_X2APIC_APICID, IA32_X2APIC_CUR_COUNT, IA32_X2APIC_DIV_CONF,
|
||||
IA32_X2APIC_EOI, IA32_X2APIC_INIT_COUNT, IA32_X2APIC_LVT_TIMER, IA32_X2APIC_SIVR,
|
||||
IA32_X2APIC_VERSION,
|
||||
IA32_X2APIC_EOI, IA32_X2APIC_ESR, IA32_X2APIC_ICR, IA32_X2APIC_INIT_COUNT,
|
||||
IA32_X2APIC_LVT_TIMER, IA32_X2APIC_SIVR, IA32_X2APIC_VERSION,
|
||||
};
|
||||
|
||||
use super::ApicTimer;
|
||||
@ -66,6 +66,20 @@ impl super::Apic for X2Apic {
|
||||
wrmsr(IA32_X2APIC_EOI, 0);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn send_ipi(&mut self, icr: super::Icr) {
|
||||
wrmsr(IA32_X2APIC_ESR, 0);
|
||||
wrmsr(IA32_X2APIC_ICR, icr.0);
|
||||
loop {
|
||||
let icr = rdmsr(IA32_X2APIC_ICR);
|
||||
if (icr >> 12 & 0x1) == 0 {
|
||||
break;
|
||||
}
|
||||
if rdmsr(IA32_X2APIC_ESR) > 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApicTimer for X2Apic {
|
||||
|
@ -74,6 +74,24 @@ impl super::Apic for XApic {
|
||||
fn eoi(&mut self) {
|
||||
self.write(xapic::XAPIC_EOI, 0);
|
||||
}
|
||||
|
||||
unsafe fn send_ipi(&mut self, icr: super::Icr) {
|
||||
self.write(xapic::XAPIC_ESR, 0);
|
||||
// The upper 32 bits of ICR must be written into XAPIC_ICR1 first,
|
||||
// because writing into XAPIC_ICR0 will trigger the action of
|
||||
// interrupt sending.
|
||||
self.write(xapic::XAPIC_ICR1, icr.upper());
|
||||
self.write(xapic::XAPIC_ICR0, icr.lower());
|
||||
loop {
|
||||
let icr = self.read(xapic::XAPIC_ICR0);
|
||||
if (icr >> 12 & 0x1) == 0 {
|
||||
break;
|
||||
}
|
||||
if self.read(xapic::XAPIC_ESR) > 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApicTimer for XApic {
|
||||
|
Loading…
x
Reference in New Issue
Block a user