Dedup the apic code between x86 and x86_64
This commit is contained in:
@@ -1,441 +0,0 @@
|
||||
use core::{fmt, ptr};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use spin::Mutex;
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
use crate::acpi::madt::{self, Madt, MadtEntry, MadtIntSrcOverride, MadtIoApic};
|
||||
|
||||
use crate::{
|
||||
arch::interrupt::irq,
|
||||
memory::Frame,
|
||||
paging::{
|
||||
entry::EntryFlags, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch,
|
||||
VirtualAddress,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{super::cpuid::cpuid, pic};
|
||||
|
||||
pub struct IoApicRegs {
|
||||
pointer: *const u32,
|
||||
}
|
||||
impl IoApicRegs {
|
||||
fn ioregsel(&self) -> *const u32 {
|
||||
self.pointer
|
||||
}
|
||||
fn iowin(&self) -> *const u32 {
|
||||
// offset 0x10
|
||||
unsafe { self.pointer.offset(4) }
|
||||
}
|
||||
fn write_ioregsel(&mut self, value: u32) {
|
||||
unsafe { ptr::write_volatile::<u32>(self.ioregsel() as *mut u32, value) }
|
||||
}
|
||||
fn read_iowin(&self) -> u32 {
|
||||
unsafe { ptr::read_volatile::<u32>(self.iowin()) }
|
||||
}
|
||||
fn write_iowin(&mut self, value: u32) {
|
||||
unsafe { ptr::write_volatile::<u32>(self.iowin() as *mut u32, value) }
|
||||
}
|
||||
fn read_reg(&mut self, reg: u8) -> u32 {
|
||||
self.write_ioregsel(reg.into());
|
||||
self.read_iowin()
|
||||
}
|
||||
fn write_reg(&mut self, reg: u8, value: u32) {
|
||||
self.write_ioregsel(reg.into());
|
||||
self.write_iowin(value);
|
||||
}
|
||||
pub fn read_ioapicid(&mut self) -> u32 {
|
||||
self.read_reg(0x00)
|
||||
}
|
||||
pub fn write_ioapicid(&mut self, value: u32) {
|
||||
self.write_reg(0x00, value);
|
||||
}
|
||||
pub fn read_ioapicver(&mut self) -> u32 {
|
||||
self.read_reg(0x01)
|
||||
}
|
||||
pub fn read_ioapicarb(&mut self) -> u32 {
|
||||
self.read_reg(0x02)
|
||||
}
|
||||
pub fn read_ioredtbl(&mut self, idx: u8) -> u64 {
|
||||
assert!(idx < 24);
|
||||
let lo = self.read_reg(0x10 + idx * 2);
|
||||
let hi = self.read_reg(0x10 + idx * 2 + 1);
|
||||
|
||||
u64::from(lo) | (u64::from(hi) << 32)
|
||||
}
|
||||
pub fn write_ioredtbl(&mut self, idx: u8, value: u64) {
|
||||
assert!(idx < 24);
|
||||
|
||||
let lo = value as u32;
|
||||
let hi = (value >> 32) as u32;
|
||||
|
||||
self.write_reg(0x10 + idx * 2, lo);
|
||||
self.write_reg(0x10 + idx * 2 + 1, hi);
|
||||
}
|
||||
|
||||
pub fn max_redirection_table_entries(&mut self) -> u8 {
|
||||
let ver = self.read_ioapicver();
|
||||
((ver & 0x00FF_0000) >> 16) as u8
|
||||
}
|
||||
pub fn id(&mut self) -> u8 {
|
||||
let id_reg = self.read_ioapicid();
|
||||
((id_reg & 0x0F00_0000) >> 24) as u8
|
||||
}
|
||||
}
|
||||
pub struct IoApic {
|
||||
regs: Mutex<IoApicRegs>,
|
||||
gsi_start: u32,
|
||||
count: u8,
|
||||
}
|
||||
impl IoApic {
|
||||
pub fn new(regs_base: *const u32, gsi_start: u32) -> Self {
|
||||
let mut regs = IoApicRegs { pointer: regs_base };
|
||||
let count = regs.max_redirection_table_entries();
|
||||
|
||||
Self {
|
||||
regs: Mutex::new(regs),
|
||||
gsi_start,
|
||||
count,
|
||||
}
|
||||
}
|
||||
/// Map an interrupt vector to a physical local APIC ID of a processor (thus physical mode).
|
||||
pub fn map(&self, idx: u8, info: MapInfo) {
|
||||
self.regs.lock().write_ioredtbl(idx, info.as_raw())
|
||||
}
|
||||
pub fn set_mask(&self, gsi: u32, mask: bool) {
|
||||
let idx = (gsi - self.gsi_start) as u8;
|
||||
let mut guard = self.regs.lock();
|
||||
|
||||
let mut reg = guard.read_ioredtbl(idx);
|
||||
reg &= !(1 << 16);
|
||||
reg |= u64::from(mask) << 16;
|
||||
guard.write_ioredtbl(idx, reg);
|
||||
}
|
||||
}
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ApicTriggerMode {
|
||||
Edge = 0,
|
||||
Level = 1,
|
||||
}
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ApicPolarity {
|
||||
ActiveHigh = 0,
|
||||
ActiveLow = 1,
|
||||
}
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DestinationMode {
|
||||
Physical = 0,
|
||||
Logical = 1,
|
||||
}
|
||||
#[repr(u8)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum DeliveryMode {
|
||||
Fixed = 0b000,
|
||||
LowestPriority = 0b001,
|
||||
Smi = 0b010,
|
||||
Nmi = 0b100,
|
||||
Init = 0b101,
|
||||
ExtInt = 0b111,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct MapInfo {
|
||||
pub dest: u8,
|
||||
pub mask: bool,
|
||||
pub trigger_mode: ApicTriggerMode,
|
||||
pub polarity: ApicPolarity,
|
||||
pub dest_mode: DestinationMode,
|
||||
pub delivery_mode: DeliveryMode,
|
||||
pub vector: u8,
|
||||
}
|
||||
|
||||
impl MapInfo {
|
||||
pub fn as_raw(&self) -> u64 {
|
||||
assert!(self.vector >= 0x20);
|
||||
assert!(self.vector <= 0xFE);
|
||||
|
||||
// TODO: Check for reserved fields.
|
||||
|
||||
(u64::from(self.dest) << 56)
|
||||
| (u64::from(self.mask) << 16)
|
||||
| ((self.trigger_mode as u64) << 15)
|
||||
| ((self.polarity as u64) << 13)
|
||||
| ((self.dest_mode as u64) << 11)
|
||||
| ((self.delivery_mode as u64) << 8)
|
||||
| u64::from(self.vector)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for IoApic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
struct RedirTable<'a>(&'a Mutex<IoApicRegs>);
|
||||
|
||||
impl<'a> fmt::Debug for RedirTable<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut guard = self.0.lock();
|
||||
|
||||
let count = guard.max_redirection_table_entries();
|
||||
f.debug_list()
|
||||
.entries((0..count).map(|i| guard.read_ioredtbl(i)))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
f.debug_struct("IoApic")
|
||||
.field("redir_table", &RedirTable(&self.regs))
|
||||
.field("gsi_start", &self.gsi_start)
|
||||
.field("count", &self.count)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum TriggerMode {
|
||||
ConformsToSpecs,
|
||||
Edge,
|
||||
Level,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Polarity {
|
||||
ConformsToSpecs,
|
||||
ActiveHigh,
|
||||
ActiveLow,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Override {
|
||||
bus_irq: u8,
|
||||
gsi: u32,
|
||||
|
||||
trigger_mode: TriggerMode,
|
||||
polarity: Polarity,
|
||||
}
|
||||
|
||||
// static mut because only the AP initializes the I/O Apic, and when that is done, it's solely
|
||||
// accessed immutably.
|
||||
static mut IOAPICS: Option<Vec<IoApic>> = None;
|
||||
|
||||
// static mut for the same reason as above
|
||||
static mut SRC_OVERRIDES: Option<Vec<Override>> = None;
|
||||
|
||||
pub fn ioapics() -> &'static [IoApic] {
|
||||
unsafe { IOAPICS.as_ref().map_or(&[], |vector| &vector[..]) }
|
||||
}
|
||||
pub fn src_overrides() -> &'static [Override] {
|
||||
unsafe { SRC_OVERRIDES.as_ref().map_or(&[], |vector| &vector[..]) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
pub unsafe fn handle_ioapic(mapper: &mut KernelMapper, madt_ioapic: &'static MadtIoApic) {
|
||||
// map the I/O APIC registers
|
||||
|
||||
let frame = Frame::containing_address(PhysicalAddress::new(madt_ioapic.address as usize));
|
||||
let page = Page::containing_address(VirtualAddress::new(crate::IOAPIC_OFFSET));
|
||||
|
||||
assert!(mapper.translate(page.start_address()).is_none());
|
||||
|
||||
mapper
|
||||
.get_mut()
|
||||
.expect("expected KernelMapper not to be locked re-entrant while mapping I/O APIC memory")
|
||||
.map_phys(
|
||||
page.start_address(),
|
||||
frame.start_address(),
|
||||
PageFlags::new()
|
||||
.write(true)
|
||||
.custom_flag(EntryFlags::NO_CACHE.bits(), true),
|
||||
)
|
||||
.expect("failed to map I/O APIC")
|
||||
.flush();
|
||||
|
||||
let ioapic_registers = page.start_address().data() as *const u32;
|
||||
let ioapic = IoApic::new(ioapic_registers, madt_ioapic.gsi_base);
|
||||
|
||||
assert_eq!(
|
||||
ioapic.regs.lock().id(),
|
||||
madt_ioapic.id,
|
||||
"mismatched ACPI MADT I/O APIC ID, and the ID reported by the I/O APIC"
|
||||
);
|
||||
|
||||
IOAPICS.get_or_insert_with(Vec::new).push(ioapic);
|
||||
}
|
||||
#[cfg(feature = "acpi")]
|
||||
pub unsafe fn handle_src_override(src_override: &'static MadtIntSrcOverride) {
|
||||
let flags = src_override.flags;
|
||||
|
||||
let polarity_raw = (flags & 0x0003) as u8;
|
||||
let trigger_mode_raw = ((flags & 0x000C) >> 2) as u8;
|
||||
|
||||
let polarity = match polarity_raw {
|
||||
0b00 => Polarity::ConformsToSpecs,
|
||||
0b01 => Polarity::ActiveHigh,
|
||||
0b10 => return, // reserved
|
||||
0b11 => Polarity::ActiveLow,
|
||||
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let trigger_mode = match trigger_mode_raw {
|
||||
0b00 => TriggerMode::ConformsToSpecs,
|
||||
0b01 => TriggerMode::Edge,
|
||||
0b10 => return, // reserved
|
||||
0b11 => TriggerMode::Level,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let over = Override {
|
||||
bus_irq: src_override.irq_source,
|
||||
gsi: src_override.gsi_base,
|
||||
polarity,
|
||||
trigger_mode,
|
||||
};
|
||||
SRC_OVERRIDES.get_or_insert_with(Vec::new).push(over);
|
||||
}
|
||||
|
||||
pub unsafe fn init(active_table: &mut KernelMapper) {
|
||||
let bsp_apic_id = cpuid()
|
||||
.unwrap()
|
||||
.get_feature_info()
|
||||
.unwrap()
|
||||
.initial_local_apic_id(); // TODO: remove unwraps
|
||||
|
||||
// search the madt for all IOAPICs.
|
||||
#[cfg(feature = "acpi")]
|
||||
{
|
||||
let madt: &'static Madt = match madt::MADT.as_ref() {
|
||||
Some(m) => m,
|
||||
// TODO: Parse MP tables too.
|
||||
None => return,
|
||||
};
|
||||
if madt.flags & madt::FLAG_PCAT != 0 {
|
||||
pic::disable();
|
||||
}
|
||||
|
||||
// find all I/O APICs (usually one).
|
||||
|
||||
for entry in madt.iter() {
|
||||
match entry {
|
||||
MadtEntry::IoApic(ioapic) => handle_ioapic(active_table, ioapic),
|
||||
MadtEntry::IntSrcOverride(src_override) => handle_src_override(src_override),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
println!(
|
||||
"I/O APICs: {:?}, overrides: {:?}",
|
||||
ioapics(),
|
||||
src_overrides()
|
||||
);
|
||||
|
||||
// map the legacy PC-compatible IRQs (0-15) to 32-47, just like we did with 8259 PIC (if it
|
||||
// wouldn't have been disabled due to this I/O APIC)
|
||||
for legacy_irq in 0..=15 {
|
||||
let (gsi, trigger_mode, polarity) = match get_override(legacy_irq) {
|
||||
Some(over) => (over.gsi, over.trigger_mode, over.polarity),
|
||||
None => {
|
||||
if src_overrides()
|
||||
.iter()
|
||||
.any(|over| over.gsi == u32::from(legacy_irq) && over.bus_irq != legacy_irq)
|
||||
&& !src_overrides()
|
||||
.iter()
|
||||
.any(|over| over.bus_irq == legacy_irq)
|
||||
{
|
||||
// there's an IRQ conflict, making this legacy IRQ inaccessible.
|
||||
continue;
|
||||
}
|
||||
(
|
||||
legacy_irq.into(),
|
||||
TriggerMode::ConformsToSpecs,
|
||||
Polarity::ConformsToSpecs,
|
||||
)
|
||||
}
|
||||
};
|
||||
let apic = match find_ioapic(gsi) {
|
||||
Some(ioapic) => ioapic,
|
||||
None => {
|
||||
println!("Unable to find a suitable APIC for legacy IRQ {} (GSI {}). It will not be mapped.", legacy_irq, gsi);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let redir_tbl_index = (gsi - apic.gsi_start) as u8;
|
||||
|
||||
let map_info = MapInfo {
|
||||
// only send to the BSP
|
||||
dest: bsp_apic_id,
|
||||
dest_mode: DestinationMode::Physical,
|
||||
delivery_mode: DeliveryMode::Fixed,
|
||||
mask: false,
|
||||
polarity: match polarity {
|
||||
Polarity::ActiveHigh => ApicPolarity::ActiveHigh,
|
||||
Polarity::ActiveLow => ApicPolarity::ActiveLow,
|
||||
Polarity::ConformsToSpecs => ApicPolarity::ActiveHigh,
|
||||
},
|
||||
trigger_mode: match trigger_mode {
|
||||
TriggerMode::Edge => ApicTriggerMode::Edge,
|
||||
TriggerMode::Level => ApicTriggerMode::Level,
|
||||
TriggerMode::ConformsToSpecs => ApicTriggerMode::Edge,
|
||||
},
|
||||
vector: 32 + legacy_irq,
|
||||
};
|
||||
apic.map(redir_tbl_index, map_info);
|
||||
}
|
||||
println!(
|
||||
"I/O APICs: {:?}, overrides: {:?}",
|
||||
ioapics(),
|
||||
src_overrides()
|
||||
);
|
||||
irq::set_irq_method(irq::IrqMethod::Apic);
|
||||
|
||||
// tell the firmware that we're using APIC rather than the default 8259 PIC.
|
||||
|
||||
// FIXME: With ACPI moved to userspace, we should instead allow userspace to check whether the
|
||||
// IOAPIC has been initialized, and then subsequently let some ACPI driver call the AML from
|
||||
// userspace.
|
||||
|
||||
/*#[cfg(feature = "acpi")]
|
||||
{
|
||||
let method = {
|
||||
let namespace_guard = crate::acpi::ACPI_TABLE.namespace.read();
|
||||
if let Some(value) = namespace_guard.as_ref().unwrap().get("\\_PIC") {
|
||||
value.get_as_method().ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(m) = method {
|
||||
m.execute("\\_PIC".into(), vec!(crate::acpi::aml::AmlValue::Integer(1)));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
fn get_override(irq: u8) -> Option<&'static Override> {
|
||||
src_overrides().iter().find(|over| over.bus_irq == irq)
|
||||
}
|
||||
fn resolve(irq: u8) -> u32 {
|
||||
get_override(irq).map_or(u32::from(irq), |over| over.gsi)
|
||||
}
|
||||
fn find_ioapic(gsi: u32) -> Option<&'static IoApic> {
|
||||
ioapics()
|
||||
.iter()
|
||||
.find(|apic| gsi >= apic.gsi_start && gsi < apic.gsi_start + u32::from(apic.count))
|
||||
}
|
||||
|
||||
pub unsafe fn mask(irq: u8) {
|
||||
let gsi = resolve(irq);
|
||||
let apic = match find_ioapic(gsi) {
|
||||
Some(a) => a,
|
||||
None => return,
|
||||
};
|
||||
apic.set_mask(gsi, true);
|
||||
}
|
||||
pub unsafe fn unmask(irq: u8) {
|
||||
let gsi = resolve(irq);
|
||||
let apic = match find_ioapic(gsi) {
|
||||
Some(a) => a,
|
||||
None => return,
|
||||
};
|
||||
apic.set_mask(gsi, false);
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
pub use crate::arch::x86_shared::device::*;
|
||||
pub mod ioapic;
|
||||
pub mod local_apic;
|
||||
|
||||
use crate::paging::KernelMapper;
|
||||
|
||||
|
||||
@@ -1,251 +0,0 @@
|
||||
use core::{
|
||||
ptr::{read_volatile, write_volatile},
|
||||
sync::atomic::{self, AtomicU64},
|
||||
};
|
||||
use x86::msr::*;
|
||||
|
||||
use crate::paging::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch};
|
||||
|
||||
use super::super::cpuid::cpuid;
|
||||
|
||||
pub static mut LOCAL_APIC: LocalApic = LocalApic {
|
||||
address: 0,
|
||||
x2: false,
|
||||
};
|
||||
|
||||
pub unsafe fn init(active_table: &mut KernelMapper) {
|
||||
LOCAL_APIC.init(active_table);
|
||||
}
|
||||
|
||||
pub unsafe fn init_ap() {
|
||||
LOCAL_APIC.init_ap();
|
||||
}
|
||||
|
||||
/// Local APIC
|
||||
pub struct LocalApic {
|
||||
pub address: usize,
|
||||
pub x2: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct NoFreqInfo;
|
||||
|
||||
static BSP_APIC_ID: AtomicU64 = AtomicU64::new(0xFFFF_FFFF_FFFF_FFFF);
|
||||
|
||||
#[no_mangle]
|
||||
pub fn bsp_apic_id() -> Option<u32> {
|
||||
let value = BSP_APIC_ID.load(atomic::Ordering::SeqCst);
|
||||
if value <= u64::from(u32::max_value()) {
|
||||
Some(value as u32)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalApic {
|
||||
unsafe fn init(&mut self, mapper: &mut KernelMapper) {
|
||||
let mapper = mapper
|
||||
.get_mut()
|
||||
.expect("expected KernelMapper not to be locked re-entrant while initializing LAPIC");
|
||||
|
||||
let physaddr = PhysicalAddress::new(rdmsr(IA32_APIC_BASE) as usize & 0xFFFF_0000);
|
||||
let virtaddr = RmmA::phys_to_virt(physaddr);
|
||||
|
||||
self.address = virtaddr.data();
|
||||
self.x2 = cpuid().map_or(false, |cpuid| {
|
||||
cpuid
|
||||
.get_feature_info()
|
||||
.map_or(false, |feature_info| feature_info.has_x2apic())
|
||||
});
|
||||
|
||||
if !self.x2 {
|
||||
log::info!("Detected xAPIC at {:#x}", physaddr.data());
|
||||
if let Some((_entry, _, flush)) = mapper.unmap_phys(virtaddr, true) {
|
||||
// Unmap xAPIC page if already mapped
|
||||
flush.flush();
|
||||
}
|
||||
mapper
|
||||
.map_phys(virtaddr, physaddr, PageFlags::new().write(true))
|
||||
.expect("failed to map local APIC memory")
|
||||
.flush();
|
||||
} else {
|
||||
log::info!("Detected x2APIC");
|
||||
}
|
||||
|
||||
self.init_ap();
|
||||
BSP_APIC_ID.store(u64::from(self.id()), atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
unsafe fn init_ap(&mut self) {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_APIC_BASE, rdmsr(IA32_APIC_BASE) | 1 << 10);
|
||||
wrmsr(IA32_X2APIC_SIVR, 0x100);
|
||||
} else {
|
||||
self.write(0xF0, 0x100);
|
||||
}
|
||||
self.setup_error_int();
|
||||
|
||||
//self.setup_timer();
|
||||
}
|
||||
|
||||
unsafe fn read(&self, reg: u32) -> u32 {
|
||||
read_volatile((self.address + reg as usize) as *const u32)
|
||||
}
|
||||
|
||||
unsafe fn write(&mut self, reg: u32, value: u32) {
|
||||
write_volatile((self.address + reg as usize) as *mut u32, value);
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u32 {
|
||||
if self.x2 {
|
||||
unsafe { rdmsr(IA32_X2APIC_APICID) as u32 }
|
||||
} else {
|
||||
unsafe { self.read(0x20) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn version(&self) -> u32 {
|
||||
if self.x2 {
|
||||
unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 }
|
||||
} else {
|
||||
unsafe { self.read(0x30) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icr(&self) -> u64 {
|
||||
if self.x2 {
|
||||
unsafe { rdmsr(IA32_X2APIC_ICR) }
|
||||
} else {
|
||||
unsafe { (self.read(0x310) as u64) << 32 | self.read(0x300) as u64 }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_icr(&mut self, value: u64) {
|
||||
if self.x2 {
|
||||
unsafe {
|
||||
wrmsr(IA32_X2APIC_ICR, value);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
const PENDING: u32 = 1 << 12;
|
||||
while self.read(0x300) & PENDING == PENDING {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.write(0x310, (value >> 32) as u32);
|
||||
self.write(0x300, value as u32);
|
||||
while self.read(0x300) & PENDING == PENDING {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ipi(&mut self, apic_id: usize) {
|
||||
let mut icr = 0x4040;
|
||||
if self.x2 {
|
||||
icr |= (apic_id as u64) << 32;
|
||||
} else {
|
||||
icr |= (apic_id as u64) << 56;
|
||||
}
|
||||
self.set_icr(icr);
|
||||
}
|
||||
// Not used just yet, but allows triggering an NMI to another processor.
|
||||
pub fn ipi_nmi(&mut self, apic_id: u32) {
|
||||
let shift = if self.x2 { 32 } else { 56 };
|
||||
self.set_icr((u64::from(apic_id) << shift) | (1 << 14) | (0b100 << 8));
|
||||
}
|
||||
|
||||
pub unsafe fn eoi(&mut self) {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_X2APIC_EOI, 0);
|
||||
} else {
|
||||
self.write(0xB0, 0);
|
||||
}
|
||||
}
|
||||
/// Reads the Error Status Register.
|
||||
pub unsafe fn esr(&mut self) -> u32 {
|
||||
if self.x2 {
|
||||
// update the ESR to the current state of the local apic.
|
||||
wrmsr(IA32_X2APIC_ESR, 0);
|
||||
// read the updated value
|
||||
rdmsr(IA32_X2APIC_ESR) as u32
|
||||
} else {
|
||||
self.write(0x280, 0);
|
||||
self.read(0x280)
|
||||
}
|
||||
}
|
||||
pub unsafe fn lvt_timer(&mut self) -> u32 {
|
||||
if self.x2 {
|
||||
rdmsr(IA32_X2APIC_LVT_TIMER) as u32
|
||||
} else {
|
||||
self.read(0x320)
|
||||
}
|
||||
}
|
||||
pub unsafe fn set_lvt_timer(&mut self, value: u32) {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_X2APIC_LVT_TIMER, u64::from(value));
|
||||
} else {
|
||||
self.write(0x320, value);
|
||||
}
|
||||
}
|
||||
pub unsafe fn init_count(&mut self) -> u32 {
|
||||
if self.x2 {
|
||||
rdmsr(IA32_X2APIC_INIT_COUNT) as u32
|
||||
} else {
|
||||
self.read(0x380)
|
||||
}
|
||||
}
|
||||
pub unsafe fn set_init_count(&mut self, initial_count: u32) {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_X2APIC_INIT_COUNT, u64::from(initial_count));
|
||||
} else {
|
||||
self.write(0x380, initial_count);
|
||||
}
|
||||
}
|
||||
pub unsafe fn cur_count(&mut self) -> u32 {
|
||||
if self.x2 {
|
||||
rdmsr(IA32_X2APIC_CUR_COUNT) as u32
|
||||
} else {
|
||||
self.read(0x390)
|
||||
}
|
||||
}
|
||||
pub unsafe fn div_conf(&mut self) -> u32 {
|
||||
if self.x2 {
|
||||
rdmsr(IA32_X2APIC_DIV_CONF) as u32
|
||||
} else {
|
||||
self.read(0x3E0)
|
||||
}
|
||||
}
|
||||
pub unsafe fn set_div_conf(&mut self, div_conf: u32) {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_X2APIC_DIV_CONF, u64::from(div_conf));
|
||||
} else {
|
||||
self.write(0x3E0, div_conf);
|
||||
}
|
||||
}
|
||||
pub unsafe fn lvt_error(&mut self) -> u32 {
|
||||
if self.x2 {
|
||||
rdmsr(IA32_X2APIC_LVT_ERROR) as u32
|
||||
} else {
|
||||
self.read(0x370)
|
||||
}
|
||||
}
|
||||
pub unsafe fn set_lvt_error(&mut self, lvt_error: u32) {
|
||||
if self.x2 {
|
||||
wrmsr(IA32_X2APIC_LVT_ERROR, u64::from(lvt_error));
|
||||
} else {
|
||||
self.write(0x370, lvt_error);
|
||||
}
|
||||
}
|
||||
unsafe fn setup_error_int(&mut self) {
|
||||
let vector = 49u32;
|
||||
self.set_lvt_error(vector);
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum LvtTimerMode {
|
||||
OneShot = 0b00,
|
||||
Periodic = 0b01,
|
||||
TscDeadline = 0b10,
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
pub use crate::arch::x86_shared::device::*;
|
||||
pub mod ioapic;
|
||||
pub mod local_apic;
|
||||
|
||||
use crate::paging::KernelMapper;
|
||||
|
||||
|
||||
@@ -12,7 +12,8 @@ use crate::{
|
||||
paging::{entry::EntryFlags, KernelMapper, Page, PageFlags, PhysicalAddress, RmmA, RmmArch},
|
||||
};
|
||||
|
||||
use super::{super::cpuid::cpuid, pic};
|
||||
use crate::arch::cpuid::cpuid;
|
||||
use super::pic;
|
||||
|
||||
pub struct IoApicRegs {
|
||||
pointer: *const u32,
|
||||
@@ -4,9 +4,9 @@ use core::{
|
||||
};
|
||||
use x86::msr::*;
|
||||
|
||||
use crate::paging::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch, VirtualAddress};
|
||||
use crate::paging::{KernelMapper, PageFlags, PhysicalAddress, RmmA, RmmArch};
|
||||
|
||||
use super::super::cpuid::cpuid;
|
||||
use crate::arch::cpuid::cpuid;
|
||||
|
||||
pub static mut LOCAL_APIC: LocalApic = LocalApic {
|
||||
address: 0,
|
||||
@@ -36,7 +36,7 @@ static BSP_APIC_ID: AtomicU32 = AtomicU32::new(u32::max_value());
|
||||
pub fn bsp_apic_id() -> Option<u32> {
|
||||
let value = BSP_APIC_ID.load(atomic::Ordering::SeqCst);
|
||||
if value < u32::max_value() {
|
||||
Some(value as u32)
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@@ -49,7 +49,7 @@ impl LocalApic {
|
||||
.expect("expected KernelMapper not to be locked re-entrant while initializing LAPIC");
|
||||
|
||||
let physaddr = PhysicalAddress::new(rdmsr(IA32_APIC_BASE) as usize & 0xFFFF_0000);
|
||||
let virtaddr = VirtualAddress::new(crate::LAPIC_OFFSET);
|
||||
let virtaddr = RmmA::phys_to_virt(physaddr);
|
||||
|
||||
self.address = virtaddr.data();
|
||||
self.x2 = cpuid().map_or(false, |cpuid| {
|
||||
@@ -1,6 +1,8 @@
|
||||
pub mod cpu;
|
||||
#[cfg(feature = "acpi")]
|
||||
pub mod hpet;
|
||||
pub mod ioapic;
|
||||
pub mod local_apic;
|
||||
pub mod pic;
|
||||
pub mod pit;
|
||||
pub mod rtc;
|
||||
|
||||
Reference in New Issue
Block a user