x86/apic/
ioapic.rs

1//! To control an I/O APIC.
2//!
3//! The IO APIC routes hardware interrupts to a local APIC.
4//!
5//! Figuring out which (bus,dev,fun,vector) maps to which I/O APIC
6//! entry can be a pain.
7
8use bit_field::BitField;
9use bitflags::bitflags;
10
11bitflags! {
12    /// The redirection table starts at REG_TABLE and uses
13    /// two registers to configure each interrupt.
14    /// The first (low) register in a pair contains configuration bits.
15    /// The second (high) register contains a bitmask telling which
16    /// CPUs can serve that interrupt.
17    struct RedirectionEntry: u32 {
18        /// Interrupt disabled
19        const DISABLED  = 0x00010000;
20        /// Level-triggered (vs edge)
21        const LEVEL     = 0x00008000;
22        /// Active low (vs high)
23        const ACTIVELOW = 0x00002000;
24        /// Destination is CPU id (vs APIC ID)
25        const LOGICAL   = 0x00000800;
26        /// None
27        const NONE		= 0x00000000;
28    }
29}
30
31pub struct IoApic {
32    reg: *mut u32,
33    data: *mut u32,
34}
35
36impl IoApic {
37    /// Instantiate a new IoApic.
38    ///
39    /// # Safety
40    /// `addr` must point to the base of the IoApic.
41    pub unsafe fn new(addr: usize) -> Self {
42        IoApic {
43            reg: addr as *mut u32,
44            data: (addr + 0x10) as *mut u32,
45        }
46    }
47    pub fn disable_all(&mut self) {
48        // Mark all interrupts edge-triggered, active high, disabled,
49        // and not routed to any CPUs.
50        for i in 0..self.supported_interrupts() {
51            self.write_irq(i, RedirectionEntry::DISABLED, 0);
52        }
53    }
54
55    unsafe fn read(&mut self, reg: u8) -> u32 {
56        self.reg.write_volatile(reg as u32);
57        self.data.read_volatile()
58    }
59
60    unsafe fn write(&mut self, reg: u8, data: u32) {
61        self.reg.write_volatile(reg as u32);
62        self.data.write_volatile(data);
63    }
64
65    fn write_irq(&mut self, irq: u8, flags: RedirectionEntry, dest: u8) {
66        unsafe {
67            self.write(REG_TABLE + 2 * irq, (T_IRQ0 + irq) as u32 | flags.bits());
68            self.write(REG_TABLE + 2 * irq + 1, (dest as u32) << 24);
69        }
70    }
71
72    pub fn enable(&mut self, irq: u8, cpunum: u8) {
73        // Mark interrupt edge-triggered, active high,
74        // enabled, and routed to the given cpunum,
75        // which happens to be that cpu's APIC ID.
76        self.write_irq(irq, RedirectionEntry::NONE, cpunum);
77    }
78
79    pub fn id(&mut self) -> u8 {
80        unsafe { self.read(REG_ID).get_bits(24..28) as u8 }
81    }
82
83    pub fn version(&mut self) -> u8 {
84        unsafe { self.read(REG_VER).get_bits(0..8) as u8 }
85    }
86
87    /// Number of supported interrupts by this IO APIC.
88    ///
89    /// Max Redirection Entry = "how many IRQs can this I/O APIC handle - 1"
90    /// The -1 is silly so we add one back to it.
91    pub fn supported_interrupts(&mut self) -> u8 {
92        unsafe { (self.read(REG_VER).get_bits(16..24) + 1) as u8 }
93    }
94}
95
96/// Register index: ID
97const REG_ID: u8 = 0x00;
98
99/// Register index: version
100const REG_VER: u8 = 0x01;
101
102/// Redirection table base
103const REG_TABLE: u8 = 0x10;
104
105const T_IRQ0: u8 = 32;