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}