Coverage Report

Created: 2025-07-14 07:05

/rust/registry/src/index.crates.io-6f17d22bba15001f/x86-0.47.0/src/apic/ioapic.rs
Line
Count
Source (jump to first uncovered line)
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
8
use bit_field::BitField;
9
use bitflags::bitflags;
10
11
bitflags! {
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
31
pub struct IoApic {
32
    reg: *mut u32,
33
    data: *mut u32,
34
}
35
36
impl IoApic {
37
    /// Instantiate a new IoApic.
38
    ///
39
    /// # Safety
40
    /// `addr` must point to the base of the IoApic.
41
0
    pub unsafe fn new(addr: usize) -> Self {
42
0
        IoApic {
43
0
            reg: addr as *mut u32,
44
0
            data: (addr + 0x10) as *mut u32,
45
0
        }
46
0
    }
47
0
    pub fn disable_all(&mut self) {
48
        // Mark all interrupts edge-triggered, active high, disabled,
49
        // and not routed to any CPUs.
50
0
        for i in 0..self.supported_interrupts() {
51
0
            self.write_irq(i, RedirectionEntry::DISABLED, 0);
52
0
        }
53
0
    }
54
55
0
    unsafe fn read(&mut self, reg: u8) -> u32 {
56
0
        self.reg.write_volatile(reg as u32);
57
0
        self.data.read_volatile()
58
0
    }
59
60
0
    unsafe fn write(&mut self, reg: u8, data: u32) {
61
0
        self.reg.write_volatile(reg as u32);
62
0
        self.data.write_volatile(data);
63
0
    }
64
65
0
    fn write_irq(&mut self, irq: u8, flags: RedirectionEntry, dest: u8) {
66
0
        unsafe {
67
0
            self.write(REG_TABLE + 2 * irq, (T_IRQ0 + irq) as u32 | flags.bits());
68
0
            self.write(REG_TABLE + 2 * irq + 1, (dest as u32) << 24);
69
0
        }
70
0
    }
71
72
0
    pub fn enable(&mut self, irq: u8, cpunum: u8) {
73
0
        // Mark interrupt edge-triggered, active high,
74
0
        // enabled, and routed to the given cpunum,
75
0
        // which happens to be that cpu's APIC ID.
76
0
        self.write_irq(irq, RedirectionEntry::NONE, cpunum);
77
0
    }
78
79
0
    pub fn id(&mut self) -> u8 {
80
0
        unsafe { self.read(REG_ID).get_bits(24..28) as u8 }
81
0
    }
82
83
0
    pub fn version(&mut self) -> u8 {
84
0
        unsafe { self.read(REG_VER).get_bits(0..8) as u8 }
85
0
    }
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
0
    pub fn supported_interrupts(&mut self) -> u8 {
92
0
        unsafe { (self.read(REG_VER).get_bits(16..24) + 1) as u8 }
93
0
    }
94
}
95
96
/// Register index: ID
97
const REG_ID: u8 = 0x00;
98
99
/// Register index: version
100
const REG_VER: u8 = 0x01;
101
102
/// Redirection table base
103
const REG_TABLE: u8 = 0x10;
104
105
const T_IRQ0: u8 = 32;