aboutsummaryrefslogtreecommitdiff
path: root/src/apic/ioapic.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/apic/ioapic.rs')
-rw-r--r--src/apic/ioapic.rs101
1 files changed, 101 insertions, 0 deletions
diff --git a/src/apic/ioapic.rs b/src/apic/ioapic.rs
new file mode 100644
index 0000000..5fad92f
--- /dev/null
+++ b/src/apic/ioapic.rs
@@ -0,0 +1,101 @@
+//! To control an I/O APIC.
+//!
+//! The IO APIC routes hardware interrupts to a local APIC.
+//!
+//! Figuring out which (bus,dev,fun,vector) maps to which I/O APIC
+//! entry can be a pain.
+
+use bit_field::BitField;
+use bitflags::bitflags;
+
+bitflags! {
+ /// The redirection table starts at REG_TABLE and uses
+ /// two registers to configure each interrupt.
+ /// The first (low) register in a pair contains configuration bits.
+ /// The second (high) register contains a bitmask telling which
+ /// CPUs can serve that interrupt.
+ struct RedirectionEntry: u32 {
+ /// Interrupt disabled
+ const DISABLED = 0x00010000;
+ /// Level-triggered (vs edge)
+ const LEVEL = 0x00008000;
+ /// Active low (vs high)
+ const ACTIVELOW = 0x00002000;
+ /// Destination is CPU id (vs APIC ID)
+ const LOGICAL = 0x00000800;
+ /// None
+ const NONE = 0x00000000;
+ }
+}
+
+pub struct IoApic {
+ reg: *mut u32,
+ data: *mut u32,
+}
+
+impl IoApic {
+ pub unsafe fn new(addr: usize) -> Self {
+ IoApic {
+ reg: addr as *mut u32,
+ data: (addr + 0x10) as *mut u32,
+ }
+ }
+ pub fn disable_all(&mut self) {
+ // Mark all interrupts edge-triggered, active high, disabled,
+ // and not routed to any CPUs.
+ for i in 0..self.supported_interrupts() {
+ self.write_irq(i, RedirectionEntry::DISABLED, 0);
+ }
+ }
+
+ unsafe fn read(&mut self, reg: u8) -> u32 {
+ self.reg.write_volatile(reg as u32);
+ self.data.read_volatile()
+ }
+
+ unsafe fn write(&mut self, reg: u8, data: u32) {
+ self.reg.write_volatile(reg as u32);
+ self.data.write_volatile(data);
+ }
+
+ fn write_irq(&mut self, irq: u8, flags: RedirectionEntry, dest: u8) {
+ unsafe {
+ self.write(REG_TABLE + 2 * irq, (T_IRQ0 + irq) as u32 | flags.bits());
+ self.write(REG_TABLE + 2 * irq + 1, (dest as u32) << 24);
+ }
+ }
+
+ pub fn enable(&mut self, irq: u8, cpunum: u8) {
+ // Mark interrupt edge-triggered, active high,
+ // enabled, and routed to the given cpunum,
+ // which happens to be that cpu's APIC ID.
+ self.write_irq(irq, RedirectionEntry::NONE, cpunum);
+ }
+
+ pub fn id(&mut self) -> u8 {
+ unsafe { self.read(REG_ID).get_bits(24..28) as u8 }
+ }
+
+ pub fn version(&mut self) -> u8 {
+ unsafe { self.read(REG_VER).get_bits(0..8) as u8 }
+ }
+
+ /// Number of supported interrupts by this IO APIC.
+ ///
+ /// Max Redirection Entry = "how many IRQs can this I/O APIC handle - 1"
+ /// The -1 is silly so we add one back to it.
+ pub fn supported_interrupts(&mut self) -> u8 {
+ unsafe { (self.read(REG_VER).get_bits(16..24) + 1) as u8 }
+ }
+}
+
+/// Register index: ID
+const REG_ID: u8 = 0x00;
+
+/// Register index: version
+const REG_VER: u8 = 0x01;
+
+/// Redirection table base
+const REG_TABLE: u8 = 0x10;
+
+const T_IRQ0: u8 = 32;