Coverage Report

Created: 2026-02-14 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/x86-0.47.0/src/apic/mod.rs
Line
Count
Source
1
//! Register information and driver to program xAPIC, X2APIC and I/O APIC
2
3
use bit_field::BitField;
4
5
pub mod ioapic;
6
pub mod x2apic;
7
pub mod xapic;
8
9
/// Specify IPI Delivery Mode
10
#[allow(clippy::upper_case_acronyms)]
11
#[derive(Debug, Eq, PartialEq)]
12
#[repr(u64)]
13
pub 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)]
43
pub enum DestinationMode {
44
    Physical = 0,
45
    Logical = 1,
46
}
47
48
/// Specify Delivery Status
49
#[derive(Debug, Eq, PartialEq)]
50
#[repr(u64)]
51
pub enum DeliveryStatus {
52
    Idle = 0,
53
    SendPending = 1,
54
}
55
56
/// IPI Level
57
#[derive(Debug, Eq, PartialEq)]
58
#[repr(u64)]
59
pub enum Level {
60
    Deassert = 0,
61
    Assert = 1,
62
}
63
64
/// IPI Trigger Mode
65
#[derive(Debug, Eq, PartialEq)]
66
#[repr(u64)]
67
pub enum TriggerMode {
68
    Edge = 0,
69
    Level = 1,
70
}
71
72
/// IPI Destination Shorthand
73
#[derive(Debug, Eq, PartialEq)]
74
#[repr(u64)]
75
pub 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)]
84
pub struct Icr(u64);
85
86
impl Icr {
87
0
    fn id_to_xapic_destination(destination: ApicId) -> u64 {
88
        // XApic destination are encoded in bytes 56--63 in the Icr
89
0
        match destination {
90
0
            ApicId::XApic(d) => (d as u64) << 56,
91
0
            ApicId::X2Apic(_d) => {
92
0
                unreachable!("x2APIC IDs are not supported for xAPIC (use the x2APIC controller)")
93
            }
94
        }
95
0
    }
96
97
0
    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
0
        let d: u64 = match destination {
103
0
            ApicId::XApic(d) => d as u64,
104
0
            ApicId::X2Apic(d) => d as u64,
105
        };
106
107
0
        d << 32
108
0
    }
109
110
    #[allow(clippy::too_many_arguments)]
111
0
    fn new(
112
0
        dest_encoder: fn(ApicId) -> u64,
113
0
        vector: u8,
114
0
        destination: ApicId,
115
0
        destination_shorthand: DestinationShorthand,
116
0
        delivery_mode: DeliveryMode,
117
0
        destination_mode: DestinationMode,
118
0
        delivery_status: DeliveryStatus,
119
0
        level: Level,
120
0
        trigger_mode: TriggerMode,
121
0
    ) -> Icr {
122
0
        Icr(dest_encoder(destination)
123
0
            | (destination_shorthand as u64) << 18
124
0
            | (trigger_mode as u64) << 15
125
0
            | (level as u64) << 14
126
0
            | (delivery_status as u64) << 12
127
0
            | (destination_mode as u64) << 11
128
0
            | (delivery_mode as u64) << 8
129
0
            | (vector as u64))
130
0
    }
131
132
    /// Short-hand to create a Icr value that will work for an x2APIC controller.
133
    #[allow(clippy::too_many_arguments)]
134
0
    pub fn for_x2apic(
135
0
        vector: u8,
136
0
        destination: ApicId,
137
0
        destination_shorthand: DestinationShorthand,
138
0
        delivery_mode: DeliveryMode,
139
0
        destination_mode: DestinationMode,
140
0
        delivery_status: DeliveryStatus,
141
0
        level: Level,
142
0
        trigger_mode: TriggerMode,
143
0
    ) -> Icr {
144
0
        Icr::new(
145
0
            Icr::id_to_x2apic_destination,
146
0
            vector,
147
0
            destination,
148
0
            destination_shorthand,
149
0
            delivery_mode,
150
0
            destination_mode,
151
0
            delivery_status,
152
0
            level,
153
0
            trigger_mode,
154
        )
155
0
    }
156
157
    #[allow(clippy::too_many_arguments)]
158
0
    pub fn for_xapic(
159
0
        vector: u8,
160
0
        destination: ApicId,
161
0
        destination_shorthand: DestinationShorthand,
162
0
        delivery_mode: DeliveryMode,
163
0
        destination_mode: DestinationMode,
164
0
        delivery_status: DeliveryStatus,
165
0
        level: Level,
166
0
        trigger_mode: TriggerMode,
167
0
    ) -> Icr {
168
0
        Icr::new(
169
0
            Icr::id_to_xapic_destination,
170
0
            vector,
171
0
            destination,
172
0
            destination_shorthand,
173
0
            delivery_mode,
174
0
            destination_mode,
175
0
            delivery_status,
176
0
            level,
177
0
            trigger_mode,
178
        )
179
0
    }
180
181
    /// Get lower 32-bits of the Icr register.
182
0
    pub fn lower(&self) -> u32 {
183
0
        self.0 as u32
184
0
    }
185
186
    /// Get upper 32-bits of the Icr register.
187
0
    pub fn upper(&self) -> u32 {
188
0
        (self.0 >> 32) as u32
189
0
    }
190
}
191
192
/// Encodes the id of a core.
193
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
194
pub 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
201
impl 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
0
    pub fn x2apic_logical_id(&self) -> u32 {
208
0
        self.x2apic_logical_cluster_id() << 16 | 1 << self.x2apic_logical_cluster_address()
209
0
    }
210
211
    /// Returns the logical address relative to a cluster
212
    /// for a given APIC ID (assuming x2APIC addressing).
213
0
    pub fn x2apic_logical_cluster_address(&self) -> u32 {
214
0
        let d = match *self {
215
            // We support conversion for XApic IDs too because ACPI can
216
            // report <255 cores as XApic entries
217
0
            ApicId::XApic(id) => id as u32,
218
0
            ApicId::X2Apic(id) => id as u32,
219
        };
220
221
0
        d.get_bits(0..=3)
222
0
    }
223
224
    /// Returns the cluster ID a given APIC ID belongs to
225
    /// (assuming x2APIC addressing).
226
0
    pub fn x2apic_logical_cluster_id(&self) -> u32 {
227
0
        let d = match *self {
228
            // We support conversion for XApic IDs too because ACPI can
229
            // report <255 cores as XApic entries
230
0
            ApicId::XApic(id) => id as u32,
231
0
            ApicId::X2Apic(id) => id as u32,
232
        };
233
234
0
        d.get_bits(4..=19)
235
0
    }
236
}
237
238
#[allow(clippy::clippy::from_over_into)]
239
impl Into<usize> for ApicId {
240
0
    fn into(self) -> usize {
241
0
        match self {
242
0
            ApicId::XApic(id) => id as usize,
243
0
            ApicId::X2Apic(id) => id as usize,
244
        }
245
0
    }
246
}
247
248
/// Abstracts common interface of local APIC (x2APIC, xAPIC) hardware devices.
249
pub 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
}