/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; |