x86/apic/
mod.rs

1//! Register information and driver to program xAPIC, X2APIC and I/O APIC
2
3use bit_field::BitField;
4
5pub mod ioapic;
6pub mod x2apic;
7pub mod xapic;
8
9/// Specify IPI Delivery Mode
10#[allow(clippy::upper_case_acronyms)]
11#[derive(Debug, Eq, PartialEq)]
12#[repr(u64)]
13pub enum DeliveryMode {
14    /// Delivers the interrupt specified in the vector field to the target processor or processors.
15    Fixed = 0b000,
16    /// Same as fixed mode, except that the interrupt is delivered to the processor executing at the
17    /// lowest priority among the set of processors specified in the destination field. The ability
18    /// for a processor to send a lowest priority IPI is model specific and should be avoided by
19    /// BIOS and operating system software.
20    LowestPriority = 0b001,
21    /// Delivers an SMI interrupt to the target processor or processors.
22    /// The vector field must be programmed to 00H for future compatibility.
23    SMI = 0b010,
24    /// Reserved
25    _Reserved = 0b11,
26    /// Delivers an NMI interrupt to the target processor or processors.
27    /// The vector information is ignored.
28    NMI = 0b100,
29    /// Delivers an INIT request to the target processor or processors, which causes them to perform an INIT.
30    Init = 0b101,
31    /// Sends a special start-up IPI (called a SIPI) to the target processor or processors.
32    /// The vector typically points to a start-up routine that is part of the
33    /// BIOS boot-strap code (see Section 8.4, Multiple-Processor (MP) Initialization). I
34    /// PIs sent with this delivery mode are not automatically retried if the source
35    /// APIC is unable to deliver it. It is up to the software to deter- mine if the
36    /// SIPI was not successfully delivered and to reissue the SIPI if necessary.
37    StartUp = 0b110,
38}
39
40/// Specify IPI Destination Mode.
41#[derive(Debug, Eq, PartialEq)]
42#[repr(u64)]
43pub enum DestinationMode {
44    Physical = 0,
45    Logical = 1,
46}
47
48/// Specify Delivery Status
49#[derive(Debug, Eq, PartialEq)]
50#[repr(u64)]
51pub enum DeliveryStatus {
52    Idle = 0,
53    SendPending = 1,
54}
55
56/// IPI Level
57#[derive(Debug, Eq, PartialEq)]
58#[repr(u64)]
59pub enum Level {
60    Deassert = 0,
61    Assert = 1,
62}
63
64/// IPI Trigger Mode
65#[derive(Debug, Eq, PartialEq)]
66#[repr(u64)]
67pub enum TriggerMode {
68    Edge = 0,
69    Level = 1,
70}
71
72/// IPI Destination Shorthand
73#[derive(Debug, Eq, PartialEq)]
74#[repr(u64)]
75pub enum DestinationShorthand {
76    NoShorthand = 0b00,
77    Myself = 0b01,
78    AllIncludingSelf = 0b10,
79    AllExcludingSelf = 0b11,
80}
81
82/// Abstract the IPI control register
83#[derive(Debug, Eq, PartialEq)]
84pub struct Icr(u64);
85
86impl Icr {
87    fn id_to_xapic_destination(destination: ApicId) -> u64 {
88        // XApic destination are encoded in bytes 56--63 in the Icr
89        match destination {
90            ApicId::XApic(d) => (d as u64) << 56,
91            ApicId::X2Apic(_d) => {
92                unreachable!("x2APIC IDs are not supported for xAPIC (use the x2APIC controller)")
93            }
94        }
95    }
96
97    fn id_to_x2apic_destination(destination: ApicId) -> u64 {
98        // whereas, X2Apic destinations are encoded in bytes 32--63 in the Icr
99        // The ACPI tables will can name the first 255 processors
100        // with xAPIC IDs and no x2APIC entry exists in SRAT
101        // However, the IDs should be compatible (I hope)
102        let d: u64 = match destination {
103            ApicId::XApic(d) => d as u64,
104            ApicId::X2Apic(d) => d as u64,
105        };
106
107        d << 32
108    }
109
110    #[allow(clippy::too_many_arguments)]
111    fn new(
112        dest_encoder: fn(ApicId) -> u64,
113        vector: u8,
114        destination: ApicId,
115        destination_shorthand: DestinationShorthand,
116        delivery_mode: DeliveryMode,
117        destination_mode: DestinationMode,
118        delivery_status: DeliveryStatus,
119        level: Level,
120        trigger_mode: TriggerMode,
121    ) -> Icr {
122        Icr(dest_encoder(destination)
123            | (destination_shorthand as u64) << 18
124            | (trigger_mode as u64) << 15
125            | (level as u64) << 14
126            | (delivery_status as u64) << 12
127            | (destination_mode as u64) << 11
128            | (delivery_mode as u64) << 8
129            | (vector as u64))
130    }
131
132    /// Short-hand to create a Icr value that will work for an x2APIC controller.
133    #[allow(clippy::too_many_arguments)]
134    pub fn for_x2apic(
135        vector: u8,
136        destination: ApicId,
137        destination_shorthand: DestinationShorthand,
138        delivery_mode: DeliveryMode,
139        destination_mode: DestinationMode,
140        delivery_status: DeliveryStatus,
141        level: Level,
142        trigger_mode: TriggerMode,
143    ) -> Icr {
144        Icr::new(
145            Icr::id_to_x2apic_destination,
146            vector,
147            destination,
148            destination_shorthand,
149            delivery_mode,
150            destination_mode,
151            delivery_status,
152            level,
153            trigger_mode,
154        )
155    }
156
157    #[allow(clippy::too_many_arguments)]
158    pub fn for_xapic(
159        vector: u8,
160        destination: ApicId,
161        destination_shorthand: DestinationShorthand,
162        delivery_mode: DeliveryMode,
163        destination_mode: DestinationMode,
164        delivery_status: DeliveryStatus,
165        level: Level,
166        trigger_mode: TriggerMode,
167    ) -> Icr {
168        Icr::new(
169            Icr::id_to_xapic_destination,
170            vector,
171            destination,
172            destination_shorthand,
173            delivery_mode,
174            destination_mode,
175            delivery_status,
176            level,
177            trigger_mode,
178        )
179    }
180
181    /// Get lower 32-bits of the Icr register.
182    pub fn lower(&self) -> u32 {
183        self.0 as u32
184    }
185
186    /// Get upper 32-bits of the Icr register.
187    pub fn upper(&self) -> u32 {
188        (self.0 >> 32) as u32
189    }
190}
191
192/// Encodes the id of a core.
193#[derive(Debug, Eq, PartialEq, Copy, Clone)]
194pub enum ApicId {
195    /// A core destination encoded as an xAPIC ID.
196    XApic(u8),
197    /// A core destination encoded as an x2APIC ID.
198    X2Apic(u32),
199}
200
201impl ApicId {
202    /// Returns the Logical x2APIC ID.
203    ///
204    /// In x2APIC mode, the 32-bit logical x2APIC ID, which can be read from LDR,
205    /// is derived from the 32-bit local x2APIC ID:
206    /// Logical x2APIC ID = [(x2APIC ID[19:4] « 16) | (1 « x2APIC ID[3:0])]
207    pub fn x2apic_logical_id(&self) -> u32 {
208        self.x2apic_logical_cluster_id() << 16 | 1 << self.x2apic_logical_cluster_address()
209    }
210
211    /// Returns the logical address relative to a cluster
212    /// for a given APIC ID (assuming x2APIC addressing).
213    pub fn x2apic_logical_cluster_address(&self) -> u32 {
214        let d = match *self {
215            // We support conversion for XApic IDs too because ACPI can
216            // report <255 cores as XApic entries
217            ApicId::XApic(id) => id as u32,
218            ApicId::X2Apic(id) => id as u32,
219        };
220
221        d.get_bits(0..=3)
222    }
223
224    /// Returns the cluster ID a given APIC ID belongs to
225    /// (assuming x2APIC addressing).
226    pub fn x2apic_logical_cluster_id(&self) -> u32 {
227        let d = match *self {
228            // We support conversion for XApic IDs too because ACPI can
229            // report <255 cores as XApic entries
230            ApicId::XApic(id) => id as u32,
231            ApicId::X2Apic(id) => id as u32,
232        };
233
234        d.get_bits(4..=19)
235    }
236}
237
238#[allow(clippy::clippy::from_over_into)]
239impl Into<usize> for ApicId {
240    fn into(self) -> usize {
241        match self {
242            ApicId::XApic(id) => id as usize,
243            ApicId::X2Apic(id) => id as usize,
244        }
245    }
246}
247
248/// Abstracts common interface of local APIC (x2APIC, xAPIC) hardware devices.
249pub trait ApicControl {
250    /// Is a bootstrap processor?
251    fn bsp(&self) -> bool;
252
253    /// Return APIC ID.
254    fn id(&self) -> u32;
255
256    /// Returns the logical APIC ID.
257    fn logical_id(&self) -> u32;
258
259    /// Read APIC version
260    fn version(&self) -> u32;
261
262    /// End Of Interrupt -- Acknowledge interrupt delivery.
263    fn eoi(&mut self);
264
265    /// Enable TSC deadline timer.
266    fn tsc_enable(&mut self, vector: u8);
267
268    /// Set TSC deadline value.
269    fn tsc_set(&self, value: u64);
270
271    /// Send a INIT IPI to a core.
272    ///
273    /// # Safety
274    /// Should only be used to reset or boot a new core.
275    unsafe fn ipi_init(&mut self, core: ApicId);
276
277    /// Deassert INIT IPI.
278    ///
279    /// # Safety
280    /// Should only be used to reset or boot a new core.
281    unsafe fn ipi_init_deassert(&mut self);
282
283    /// Send a STARTUP IPI to a core.
284    ///
285    /// # Safety
286    /// Should only be used to reset or boot a new core.
287    unsafe fn ipi_startup(&mut self, core: ApicId, start_page: u8);
288
289    /// Send a generic IPI.
290    ///
291    /// # Safety
292    /// Interrupts one or multiple cores.
293    unsafe fn send_ipi(&mut self, icr: Icr);
294}