hal_x86_64/interrupt/apic/local.rs
1//! Local APIC
2pub use self::register::{ErrorStatus, Version};
3use self::register::{LvtTimer, TimerMode};
4use super::{PinPolarity, TriggerMode};
5use crate::{
6 cpu::{local, FeatureNotSupported, Msr},
7 mm::{self, page, size::Size4Kb, PhysPage, VirtPage},
8 time::{Duration, InvalidDuration},
9};
10use core::{
11 cell::{Cell, RefCell},
12 convert::TryInto,
13 marker::PhantomData,
14 num::NonZeroU32,
15};
16use hal_core::{PAddr, VAddr};
17use mycelium_util::fmt;
18use raw_cpuid::CpuId;
19use register::TimerDivisor;
20use volatile::{access, Volatile};
21
22/// Local APIC state.
23///
24///# Notes on Mutability
25///
26/// In a multi-core x86_64 system, each CPU core has its own local APIC.
27/// Therefore, the Mycelium HAL's [`interrupt::Controller`] type uses
28/// [core-local storage] to store a reference to each CPU core's local APIC that
29/// can be accessed only by that core. The value in core-local storage is a
30/// [`RefCell`]`<`[`Option`]`<`[`LocalApic`]`>>`, as the local APIC must be
31/// initialized on a per-core basis before it can be used.
32///
33/// Note that the [`RefCell`] must *only* be [borrowed
34/// mutably](RefCell::borrow_mut) in order to *initialize* the `LocalApic`
35/// value. Once the local APIC has been initialized, it may receive interrupts,
36/// and may also be accessed by interrupt *handlers* in order to send an
37/// end-of-interrupt. This means that mutably borrowing the `RefCell` once
38/// interrupts are enabled may result in a panic if an ISR attempts to borrow
39/// the local APIC immutably to send an EOI while the local APIC is borrowed
40/// mutably.
41///
42/// Therefore, all local APIC operations that mutate state, such as setting the
43/// timer calibration, must use interior mutability, so that an interrupt
44/// handler may access the local APIC's state. Interior mutability need not be
45/// thread-safe, however, as the local APIC state is only accessed from the core
46/// that owns it.
47///
48/// [`interrupt::Controller`]: crate::interrupt::Controller
49/// [core-local storage]: crate::cpu::local
50#[derive(Debug)]
51pub struct LocalApic {
52 msr: Msr,
53 base: VAddr,
54 timer_calibration: Cell<Option<TimerCalibration>>,
55}
56
57#[derive(Copy, Clone, Debug, Eq, PartialEq)]
58struct TimerCalibration {
59 frequency_hz: u32,
60 divisor: register::TimerDivisor,
61}
62
63/// Represents a register in the local APIC's configuration area.
64#[derive(Debug)]
65pub struct LocalApicRegister<T = u32, A = access::ReadWrite> {
66 offset: usize,
67 name: &'static str,
68 _ty: PhantomData<fn(T, A)>,
69}
70
71#[derive(Debug)]
72pub(in crate::interrupt) struct Handle(local::LocalKey<RefCell<Option<LocalApic>>>);
73
74pub trait RegisterAccess {
75 type Access;
76 type Target;
77 fn volatile(
78 ptr: &'static mut Self::Target,
79 ) -> Volatile<&'static mut Self::Target, Self::Access>;
80}
81
82#[derive(Debug, Eq, PartialEq, thiserror::Error)]
83pub enum LocalApicError {
84 /// The system is configured to use the PIC interrupt model rather than the
85 /// APIC interrupt model.
86 #[error("interrupt model is PIC, not APIC")]
87 NoApic,
88
89 /// The local APIC is uninitialized.
90 #[error("the local APIC has not been initialized on this core")]
91 Uninitialized,
92}
93
94#[derive(Debug, Eq, PartialEq, thiserror::Error)]
95pub enum TimerError {
96 #[error(transparent)]
97 InvalidDuration(#[from] InvalidDuration),
98 #[error("local APIC timer has not been calibrated on this core")]
99 NotCalibrated,
100}
101
102impl LocalApic {
103 const BASE_PADDR_MASK: u64 = 0xffff_ffff_f000;
104
105 /// Try to construct a `LocalApic`.
106 ///
107 /// # Arguments
108 ///
109 /// - `pagectrl`: a [page mapper](page::Map) used to ensure that the local
110 /// APIC's memory-mapped register page is mapped and writable.
111 /// - `frame_alloc`: a [frame allocator](page::Alloc) used to allocate page
112 /// frame(s) while mapping the MMIO register page.
113 ///
114 /// # Returns
115 ///
116 /// - [`Ok`]`(LocalApic)` if this CPU supports the APIC interrupt model.
117 /// - [`Err`]`(`[`FeatureNotSupported`]`)` if this CPU does not support APIC
118 /// interrupt handling.
119 pub fn try_new<A>(
120 pagectrl: &mut impl page::Map<Size4Kb, A>,
121 frame_alloc: &A,
122 ) -> Result<Self, FeatureNotSupported>
123 where
124 A: page::Alloc<Size4Kb>,
125 {
126 if !super::is_supported() {
127 return Err(FeatureNotSupported::new("APIC interrupt model"));
128 }
129
130 let msr = Msr::ia32_apic_base();
131 let base_paddr = PAddr::from_u64(msr.read() & Self::BASE_PADDR_MASK);
132 let base = mm::kernel_vaddr_of(base_paddr);
133 tracing::debug!(?base, "found local APIC base address");
134 assert_ne!(base, VAddr::from_u64(0));
135
136 unsafe {
137 // ensure the local APIC's MMIO page is mapped and writable.
138 let virt = VirtPage::<Size4Kb>::containing_fixed(base);
139 let phys = PhysPage::<Size4Kb>::containing_fixed(base_paddr);
140 tracing::debug!(?virt, ?phys, "mapping local APIC MMIO page...");
141 pagectrl
142 .map_page(virt, phys, frame_alloc)
143 .set_writable(true)
144 .commit();
145 tracing::debug!("mapped local APIC MMIO page");
146 }
147
148 Ok(Self {
149 msr,
150 base,
151 timer_calibration: Cell::new(None),
152 })
153 }
154
155 #[must_use]
156 #[inline]
157 pub fn new<A>(pagectrl: &mut impl page::Map<Size4Kb, A>, frame_alloc: &A) -> Self
158 where
159 A: page::Alloc<Size4Kb>,
160 {
161 Self::try_new(pagectrl, frame_alloc).unwrap()
162 }
163
164 pub fn enable(&self, spurious_vector: u8) {
165 use register::Version;
166 /// Writing this to the IA32_APIC_BASE MSR enables the local APIC.
167 const MSR_ENABLE: u64 = 0x800;
168 /// Bit 8 in the spurious interrupt vector register enables the APIC.
169 const SPURIOUS_VECTOR_ENABLE_BIT: u32 = 1 << 8;
170
171 // Write the enable bit to the MSR
172 unsafe {
173 self.msr.update(|base| base | MSR_ENABLE);
174 }
175
176 // Enable the APIC by writing the spurious vector to the APIC's
177 // SPURIOUS_VECTOR register.
178 let value = spurious_vector as u32 | SPURIOUS_VECTOR_ENABLE_BIT;
179 unsafe { self.register(register::SPURIOUS_VECTOR).write(value) }
180
181 let version = self.version();
182 tracing::info!(
183 base = ?self.base,
184 spurious_vector,
185 version = ?version.get(Version::VERSION),
186 max_lvt_entry = ?version.get(Version::MAX_LVT),
187 supports_eoi_suppression = ?version.get(Version::EOI_SUPPRESSION),
188 "local APIC enabled",
189 );
190 }
191
192 pub fn calibrate_timer(&self, divisor: TimerDivisor) {
193 if let Some(prev) = self.timer_calibration.get() {
194 if prev.divisor == divisor {
195 tracing::info!(
196 ?divisor,
197 "local APIC timer already calibrated with this divisor"
198 );
199 return;
200 }
201 }
202 self.timer_calibration.set(Some(TimerCalibration {
203 frequency_hz: self.calibrate_frequency_hz(divisor),
204 divisor,
205 }));
206 }
207
208 /// Reads the local APIC's version register.
209 ///
210 /// The returned [`Version`] struct indicates the version of the local APIC,
211 /// the maximum LVT entry index, and whether or not EOI suppression is
212 /// supported.
213 pub fn version(&self) -> register::Version {
214 unsafe { self.register(register::VERSION) }.read()
215 }
216
217 fn calibrate_frequency_hz(&self, divisor: TimerDivisor) -> u32 {
218 // How sloppy do we expect the PIT frequency calibration to be?
219 // If the delta between the CPUID frequency and the frequency we
220 // determined using PIT calibration is > this value, we'll yell about
221 // it.
222 const PIT_SLOPPINESS: u32 = 1000;
223
224 // Start out by calibrating the APIC frequency using the PIT.
225 let pit_frequency_hz = self.calibrate_frequency_hz_pit(divisor);
226
227 // See if we can get something from CPUID.
228 let Some(cpuid_frequency_hz) = Self::calibrate_frequency_hz_cpuid(divisor) else {
229 tracing::info!(
230 pit.frequency_hz = pit_frequency_hz,
231 "CPUID does not indicate APIC timer frequency; using PIT \
232 calibration only"
233 );
234 return pit_frequency_hz;
235 };
236
237 // Cross-check the PIT calibration result and CPUID value.
238 let distance = pit_frequency_hz.abs_diff(cpuid_frequency_hz);
239 if distance > PIT_SLOPPINESS {
240 tracing::warn!(
241 pit.frequency_hz = pit_frequency_hz,
242 cpuid.frequency_hz = cpuid_frequency_hz,
243 distance,
244 ?divisor,
245 "APIC timer frequency from PIT calibration differs substantially \
246 from CPUID!"
247 );
248 }
249
250 cpuid_frequency_hz
251 }
252
253 /// Calibrate the timer frequency using the PIT.
254 #[inline(always)]
255 fn calibrate_frequency_hz_pit(&self, divisor: TimerDivisor) -> u32 {
256 tracing::debug!(?divisor, "calibrating APIC timer frequency using PIT...");
257 const ATTEMPTS: usize = 5;
258
259 // lock the PIT now, before actually starting the timer IRQ, so that we
260 // don't include any time spent waiting for the PIT lock.
261 //
262 // since we only run this code on startup, before any other cores have
263 // been started, this probably never actually waits for a lock. but...we
264 // should do it the right way, anyway.
265 let mut pit = crate::time::PIT.lock();
266
267 let mut sum = 0;
268 let mut min = u32::MAX;
269 let mut max = 0;
270 for attempt in 1..ATTEMPTS + 1 {
271 unsafe {
272 // start the timer
273
274 // set timer divisor to 16
275 self.write_register(register::TIMER_DIVISOR, divisor);
276 self.register(register::LVT_TIMER).update(|lvt_timer| {
277 lvt_timer
278 .set(LvtTimer::MODE, TimerMode::OneShot)
279 .set(LvtTimer::MASKED, false);
280 });
281 // set initial count to -1
282 self.write_register(register::TIMER_INITIAL_COUNT, -1i32 as u32);
283 }
284
285 // use the PIT to sleep for 10ms
286 pit.sleep_blocking(Duration::from_millis(10))
287 .expect("the PIT should be able to send a 10ms interrupt...");
288
289 unsafe {
290 // stop the timer
291 self.register(register::LVT_TIMER).update(|lvt_timer| {
292 lvt_timer.set(LvtTimer::MASKED, true);
293 });
294 }
295
296 let elapsed_ticks = unsafe { self.register(register::TIMER_CURRENT_COUNT).read() };
297 // since we slept for ten milliseconds, each tick is 10 kHz. we don't
298 // need to account for the divisor since we ran the timer at that
299 // divisor already.
300 let ticks_per_10ms = (-1i32 as u32).wrapping_sub(elapsed_ticks);
301 // convert the frequency to Hz.
302 let frequency_hz = ticks_per_10ms * 100;
303 // TODO(eliza): throw out attempts that differ too substantially
304 // from the min/max?
305 sum += frequency_hz;
306 min = core::cmp::min(min, frequency_hz);
307 max = core::cmp::max(max, frequency_hz);
308 tracing::trace!(attempt, frequency_hz, sum, min, max);
309 }
310 let frequency_hz = sum / ATTEMPTS as u32;
311 tracing::debug!(
312 ?divisor,
313 frequency_hz,
314 min_hz = min,
315 max_hz = max,
316 "calibrated local APIC timer using PIT"
317 );
318 frequency_hz
319 }
320
321 #[inline(always)]
322 fn calibrate_frequency_hz_cpuid(divisor: TimerDivisor) -> Option<u32> {
323 let cpuid = CpuId::new();
324 if let Some(freq_khz) = cpuid.get_hypervisor_info().and_then(|hypervisor| {
325 tracing::trace!("CPUID contains hypervisor info");
326 let freq = hypervisor.apic_frequency();
327 tracing::trace!(hypervisor.apic_frequency_khz = ?freq);
328 NonZeroU32::new(freq?)
329 }) {
330 // the hypervisor info CPUID leaf expresses the frequency in kHz,
331 // and the frequency is not divided by the target timer divisor.
332 let frequency_hz = (freq_khz.get() * 1000) / divisor.into_divisor();
333 tracing::debug!(
334 ?divisor,
335 frequency_hz,
336 "determined APIC timer frequency from CPUID hypervisor info"
337 );
338 return Some(frequency_hz);
339 }
340
341 // XXX ELIZA THIS IS TSC FREQUENCY, SO IDK IF THAT'S RIGHT?
342 /*
343 if let Some(undivided_freq_hz) = cpuid.get_tsc_info().and_then(|tsc| {
344 tracing::trace!("CPUID contains TSC info");
345 let freq = tsc.nominal_frequency();
346 NonZeroU32::new(freq)
347 }) {
348 // divide by the target timer divisor.
349 let frequency_hz = undivided_freq_hz.get() / Self::TIMER_DIVISOR;
350 tracing::debug!(
351 frequency_hz,
352 "determined APIC frequency from CPUID TSC info"
353 );
354 return frequency_hz;
355 }
356 */
357
358 // CPUID didn't help, so fall back to calibrating the APIC frequency
359 // using the PIT.
360 None
361 }
362
363 pub fn interrupt_in(&self, duration: Duration, vector: u8) -> Result<(), TimerError> {
364 let TimerCalibration {
365 frequency_hz,
366 divisor,
367 } = self
368 .timer_calibration
369 .get()
370 .ok_or(TimerError::NotCalibrated)?;
371 let duration_ms: u32 = duration.as_millis().try_into().map_err(|_| {
372 InvalidDuration::new(
373 duration,
374 "local APIC oneshot timer duration exceeds a `u32`",
375 )
376 })?;
377 let ticks = duration_ms
378 .checked_mul(frequency_hz / 1000)
379 .ok_or_else(|| {
380 InvalidDuration::new(
381 duration,
382 "local APIC oneshot timer duration requires a number of ticks that exceed a `u32`",
383 )
384 })?;
385 unsafe {
386 self.configure_timer(divisor, TimerMode::OneShot, vector, ticks);
387 }
388
389 Ok(())
390 }
391
392 #[tracing::instrument(
393 level = tracing::Level::DEBUG,
394 name = "LocalApic::start_periodic_timer",
395 skip(self, interval),
396 fields(?interval, vector),
397 err
398 )]
399 pub fn start_periodic_timer(&self, interval: Duration, vector: u8) -> Result<(), TimerError> {
400 let TimerCalibration {
401 frequency_hz,
402 divisor,
403 } = self
404 .timer_calibration
405 .get()
406 .ok_or(TimerError::NotCalibrated)?;
407 let ticks_per_ms = frequency_hz / 1000;
408 tracing::trace!(
409 frequency_hz,
410 ticks_per_ms,
411 ?divisor,
412 "starting local APIC timer"
413 );
414 let interval_ms: u32 = interval.as_millis().try_into().map_err(|_| {
415 InvalidDuration::new(
416 interval,
417 "local APIC periodic timer interval exceeds a `u32`",
418 )
419 })?;
420 let ticks_per_interval = interval_ms.checked_mul(ticks_per_ms).ok_or_else(|| {
421 InvalidDuration::new(
422 interval,
423 "local APIC periodic timer interval requires a number of ticks that exceed a `u32`",
424 )
425 })?;
426
427 unsafe {
428 self.configure_timer(divisor, TimerMode::Periodic, vector, ticks_per_interval);
429 }
430
431 tracing::info!(
432 ?interval,
433 frequency_hz,
434 ticks_per_ms,
435 vector,
436 "started local APIC timer"
437 );
438
439 Ok(())
440 }
441
442 #[inline(always)]
443 unsafe fn configure_timer(
444 &self,
445 divisor: TimerDivisor,
446 mode: TimerMode,
447 vector: u8,
448 initial_count: u32,
449 ) {
450 // Set the divisor configuration, update the LVT entry, and set the
451 // initial count. Per the OSDev Wiki, we must do this in this specific
452 // order (divisor, then unmask the LVT entry, then set the initial
453 // count), or else we may miss IRQs or have other issues. I'm not sure
454 // why this is, but see:
455 // https://wiki.osdev.org/APIC_Timer#Enabling_APIC_Timer
456 self.register(register::TIMER_DIVISOR).write(divisor);
457 self.register(register::LVT_TIMER).update(|lvt_timer| {
458 lvt_timer
459 .set(LvtTimer::VECTOR, vector)
460 .set(LvtTimer::MODE, mode)
461 .set(LvtTimer::MASKED, false);
462 });
463 self.register(register::TIMER_INITIAL_COUNT)
464 .write(initial_count)
465 }
466
467 /// Sends an End of Interrupt (EOI) to the local APIC.
468 ///
469 /// This should be called by an interrupt handler after handling a local
470 /// APIC interrupt.
471 ///
472 /// # Safety
473 ///
474 /// This should only be called when an interrupt has been triggered by this
475 /// local APIC.
476 pub unsafe fn end_interrupt(&self) {
477 // Write a 0 to the EOI register.
478 self.register(register::END_OF_INTERRUPT).write(0);
479 }
480
481 /// Reads the error stauts register (`ESR`) of the local APIC.
482 ///
483 /// Calling this method resets the value of the error status register. Any
484 /// currently set error bits are cleared. If the same error bits are present
485 /// in a subsequent call to `LocalApic::check_error`, they represent *new*
486 /// instances of the same error.
487 ///
488 /// # Returns
489 ///
490 /// If any error bits are set, this method returns
491 /// [`Err`]`(`[`ErrorStatus`]`)`. Otherwise, if no error bits are present,
492 /// this method returns [`Ok`]`()`.
493 pub fn check_error(&self) -> Result<(), register::ErrorStatus> {
494 let esr = unsafe {
495 let mut reg = self.register(register::ERROR_STATUS);
496
497 // Per the Intel SDM, Vol 3A, Ch 7, Section 12.5.3, "Error
498 // Handling":
499 //
500 // > The ESR is a write/read register. Before attempt to read from
501 // > the ESR, software should first write to it. (The value written
502 // > does not affect the values read subsequently; only zero may be
503 // > written in x2APIC mode.) This write clears any previously
504 // > logged errors and updates the ESR with any errors detected
505 // > since the last write to the ESR. This write also rearms the
506 // > APIC error interrupt triggering mechanism.
507 //
508 // So, first write a zero.
509 reg.write(register::ErrorStatus::default());
510 reg.read()
511 };
512
513 // Return the ESR value if error bits are set.
514 if esr.is_error() {
515 Err(esr)
516 } else {
517 Ok(())
518 }
519 }
520
521 unsafe fn write_register<T, A>(&self, register: LocalApicRegister<T, A>, value: T)
522 where
523 LocalApicRegister<T, A>: RegisterAccess<Target = T, Access = A>,
524 A: access::Writable,
525 T: Copy + fmt::Debug + 'static,
526 {
527 tracing::trace!(%register, write = ?value);
528 self.register(register).write(value);
529 }
530
531 #[must_use]
532 unsafe fn register<T, A>(
533 &self,
534 register: LocalApicRegister<T, A>,
535 ) -> Volatile<&'static mut T, A>
536 where
537 LocalApicRegister<T, A>: RegisterAccess<Target = T, Access = A>,
538 {
539 let addr = self.base + register.offset;
540 assert!(
541 addr.is_aligned(16usize),
542 "Local APIC memory-mapped registers must be 16-byte aligned!"
543 );
544 let reference = &mut *addr.as_mut_ptr::<T>();
545 LocalApicRegister::<T, A>::volatile(reference)
546 }
547}
548
549impl Handle {
550 pub(in crate::interrupt) const fn new() -> Self {
551 Self(local::LocalKey::new(|| RefCell::new(None)))
552 }
553
554 pub(in crate::interrupt) fn with<T>(
555 &self,
556 f: impl FnOnce(&LocalApic) -> T,
557 ) -> Result<T, LocalApicError> {
558 self.0.with(|apic| {
559 Ok(f(apic
560 .borrow()
561 .as_ref()
562 .ok_or(LocalApicError::Uninitialized)?))
563 })
564 }
565
566 pub(in crate::interrupt) unsafe fn initialize<A>(
567 &self,
568 frame_alloc: &A,
569 pagectrl: &mut impl page::Map<Size4Kb, A>,
570 spurious_vector: u8,
571 ) where
572 A: page::Alloc<Size4Kb>,
573 {
574 self.0.with(|slot| {
575 let mut slot = slot.borrow_mut();
576 if slot.is_some() {
577 // already initialized, bail.
578 return;
579 }
580 let apic = LocalApic::new(pagectrl, frame_alloc);
581 apic.enable(spurious_vector);
582 *slot = Some(apic);
583 })
584 }
585}
586
587pub mod register {
588 use super::*;
589 use mycelium_util::bits::{bitfield, enum_from_bits};
590 use volatile::access::*;
591
592 impl<T> RegisterAccess for LocalApicRegister<T, ReadOnly> {
593 type Access = ReadOnly;
594 type Target = T;
595 fn volatile(
596 ptr: &'static mut Self::Target,
597 ) -> Volatile<&'static mut Self::Target, Self::Access> {
598 Volatile::new_read_only(ptr)
599 }
600 }
601
602 impl<T> RegisterAccess for LocalApicRegister<T, ReadWrite> {
603 type Access = ReadWrite;
604 type Target = T;
605 fn volatile(
606 ptr: &'static mut Self::Target,
607 ) -> Volatile<&'static mut Self::Target, Self::Access> {
608 Volatile::new(ptr)
609 }
610 }
611
612 impl<T> RegisterAccess for LocalApicRegister<T, WriteOnly> {
613 type Access = WriteOnly;
614 type Target = T;
615 fn volatile(
616 ptr: &'static mut Self::Target,
617 ) -> Volatile<&'static mut Self::Target, Self::Access> {
618 Volatile::new_write_only(ptr)
619 }
620 }
621
622 impl<T, A> fmt::Display for LocalApicRegister<T, A> {
623 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624 let Self { name, offset, _ty } = self;
625 write!(f, "{name} ({offset:#x})")
626 }
627 }
628
629 macro_rules! registers {
630 ( $( $(#[$m:meta])* $NAME:ident$(<$T:ty>)? = $offset:literal, $access:ident);+ $(;)? ) => {
631 $(
632 registers! {@ $(#[$m])* $NAME$(<$T>)? = $offset, $access }
633 )+
634 };
635 (@ $(#[$m:meta])* $NAME:ident = $offset:literal, $access:ident) => {
636 registers!{@ $(#[$m])* $NAME<u32> = $offset, $access }
637 };
638 (@ $(#[$m:meta])* $NAME:ident<$T:ty> = $offset:literal, $access:ident )=> {
639 $(#[$m])*
640 pub const $NAME: LocalApicRegister<$T, $access> = LocalApicRegister {
641 offset: $offset,
642 name: stringify!($NAME),
643 _ty: PhantomData,
644 };
645 };
646 }
647
648 registers! {
649
650 /// Local APIC ID
651 ///
652 /// **Access**: read/write
653 ID = 0x020, ReadWrite;
654
655 /// Local APIC version
656 ///
657 /// **Access**: read-only
658 VERSION<Version> = 0x030, ReadOnly;
659
660 /// Task Priority Register (TPR)
661 ///
662 /// **Access**: read/write
663 TASK_PRIORITY = 0x080, ReadWrite;
664
665 /// Arbitration Priority Register (APR)
666 ///
667 /// **Access**: read-only
668 ARBITRATION_PRIORITY = 0x090, ReadOnly;
669
670 /// Processor Priority Register (APR)
671 ///
672 /// **Access**: read-only
673 PROCESSOR_PRIORITY = 0x0a0, ReadOnly;
674
675 /// End of Interrupt (EOI) Register
676 ///
677 /// **Access**: write-only
678 END_OF_INTERRUPT = 0x0b0, WriteOnly;
679
680 /// Remote Read Register (RRD)
681 ///
682 /// **Access**: read-only
683 REMOTE_READ = 0x0c0, ReadOnly;
684
685 /// Logical Destination Register
686 ///
687 /// **Access**: read/write
688 LOGICAL_DEST = 0x0d0, ReadWrite;
689
690 /// Destination Format Register
691 ///
692 /// **Access**: read/write
693 DEST_FORMAT = 0x0e0, ReadWrite;
694
695 /// Spurious Interrupt Vector Register
696 ///
697 /// **Access**: read/write
698 SPURIOUS_VECTOR = 0x0f0, ReadWrite;
699
700 /// In-Service Register (ISR) 0
701 ///
702 /// **Access**: read-only
703 IN_SERVICE_0 = 0x100, ReadOnly;
704
705 /// Error Status Register (ESR)
706 ///
707 /// **Access**: read/write
708 ERROR_STATUS<ErrorStatus> = 0x280, ReadWrite;
709
710 /// LVT Corrected Machine Check Interrupt (CMCI) Register
711 ///
712 /// *Access**: read/write
713 LVT_CMCI = 0x2f0, ReadWrite;
714
715 ICR_LOW = 0x300, ReadWrite;
716 ICR_HIGH = 0x310, ReadWrite;
717
718 LVT_TIMER<LvtTimer> = 0x320, ReadWrite;
719 LVT_THERMAL<LvtEntry> = 0x330, ReadWrite;
720 LVT_PERF<LvtEntry> = 0x340, ReadWrite;
721 LVT_LINT0<LvtEntry> = 0x350, ReadWrite;
722 LVT_LINT1<LvtEntry> = 0x360, ReadWrite;
723 LVT_ERROR<LvtEntry> = 0x370, ReadWrite;
724
725 TIMER_INITIAL_COUNT = 0x380, ReadWrite;
726 TIMER_CURRENT_COUNT = 0x390, ReadOnly;
727 TIMER_DIVISOR<TimerDivisor> = 0x3e0, ReadWrite;
728 }
729
730 bitfield! {
731 /// Value of the `VERSION` register in the local APIC.
732 pub struct Version<u32> {
733 /// The version numbers of the local APIC:
734 /// - 0XH: 82489DX discrete APIC.
735 /// -10H - 15H: Integrated APIC.
736 /// Other values reserved.
737 pub const VERSION: u8;
738 const _RESERVED_0 = 8;
739 /// The maximum number of LVT entries in the local APIC, minus one.
740 ///
741 /// For the Pentium 4 and Intel Xeon processors (which
742 /// have 6 LVT entries), the value returned in the Max LVT field is
743 /// 5; for the P6 family processors (which have 5 LVT entries), the
744 /// value returned is 4; for the Pentium processor (which has 4 LVT
745 /// entries), the value returned is 3. For processors based on the
746 /// Nehalem microarchitecture (which has 7 LVT entries) and onward,
747 /// the value returned is 6.
748 pub const MAX_LVT: u8;
749 /// Indicates whether software can inhibit the broadcast of EOI
750 /// message by setting bit 12 of the Spurious Interrupt Vector
751 /// Register; see Section 12.8.5 and Section 12.9. in Ch. 7 of Vol.
752 /// 3A of the Intel SDM for details.
753 pub const EOI_SUPPRESSION: bool;
754 }
755 }
756
757 bitfield! {
758 pub struct LvtTimer<u32> {
759 pub const VECTOR: u8;
760 const _RESERVED_0 = 4;
761 pub const SEND_PENDING: bool;
762 const _RESERVED_1 = 3;
763 pub const MASKED: bool;
764 pub const MODE: TimerMode;
765 }
766 }
767
768 bitfield! {
769 pub struct LvtEntry<u32> {
770 pub const VECTOR: u8;
771 const _RESERVED_0 = 2;
772 pub const NMI: bool;
773 pub const SEND_PENDING: bool;
774 pub const POLARITY: PinPolarity;
775 pub const REMOTE_IRR: bool;
776 pub const TRIGGER: TriggerMode;
777 pub const MASKED: bool;
778 }
779 }
780
781 enum_from_bits! {
782 #[derive(Debug, Eq, PartialEq)]
783 pub enum TimerMode<u8> {
784 /// One-shot mode, program count-down value in an initial-count register.
785 OneShot = 0b00,
786 /// Periodic mode, program interval value in an initial-count register.
787 Periodic = 0b01,
788 /// TSC-Deadline mode, program target value in IA32_TSC_DEADLINE MSR.
789 TscDeadline = 0b10,
790 }
791 }
792
793 enum_from_bits! {
794 /// Configures the LVT time divisor.
795 #[derive(Debug, Eq, PartialEq)]
796 pub enum TimerDivisor<u32> {
797 By1 = 0b1011,
798 By2 = 0b0000,
799 By4 = 0b0001,
800 By8 = 0b0010,
801 By16 = 0b0011,
802 By32 = 0b1000,
803 By64 = 0b1001,
804 By128 = 0b1010,
805 }
806 }
807 impl TimerDivisor {
808 /// Returns the numeric value of the divisor configuration.
809 pub(super) fn into_divisor(self) -> u32 {
810 match self {
811 TimerDivisor::By1 => 1,
812 TimerDivisor::By2 => 2,
813 TimerDivisor::By4 => 4,
814 TimerDivisor::By8 => 8,
815 TimerDivisor::By16 => 16,
816 TimerDivisor::By32 => 32,
817 TimerDivisor::By64 => 64,
818 TimerDivisor::By128 => 128,
819 }
820 }
821 }
822
823 bitfield! {
824 /// Value of the Error Status Register (ESR).
825 ///
826 /// See Intel SDM Vol. 3A, Ch. 7, Section 12.5.3, "Error Handling".
827 #[derive(Default)]
828 pub struct ErrorStatus<u32> {
829 /// Set when the local APIC detects a checksum error for a message
830 /// that it sent on the APIC bus.
831 ///
832 /// Used only on P6 family and Pentium processors.
833 pub const SEND_CHECKSUM_ERROR: bool;
834
835 /// Set when the local APIC detects a checksum error for a message
836 /// that it received on the APIC bus.
837 ///
838 /// Used only on P6 family and Pentium processors.
839 pub const RECV_CHECKSUM_ERROR: bool;
840
841 /// Set when the local APIC detects that a message it sent was not
842 /// accepted by any APIC on the APIC bus
843 ///
844 /// Used only on P6 family and Pentium processors.
845 pub const SEND_ACCEPT_ERROR: bool;
846
847 /// Set when the local APIC detects that a message it received was
848 /// not accepted by any APIC on the APIC bus.
849 ///
850 /// Used only on P6 family and Pentium processors.
851 pub const RECV_ACCEPT_ERROR: bool;
852
853 /// Set when the local APIC detects an attempt to send an IPI with
854 /// the lowest-priority delivery mode and the local APIC does not
855 /// support the sending of such IPIs. This bit is used on some Intel
856 /// Core and Intel Xeon processors.
857 pub const REDIRECTABLE_IPI: bool;
858
859 /// Set when the local APIC detects an illegal vector (one in the
860 /// range 0 to 15) in the message that it is sending. This occurs as
861 /// the result of a write to the ICR (in both xAPIC and x2APIC
862 /// modes) or to SELF IPI register (x2APIC mode only) with an
863 /// illegal vector.
864 ///
865 /// If the local APIC does not support the sending of
866 /// lowest-priority IPIs and software writes the ICR to send a
867 /// lowest-priority IPI with an illegal vector, the local APIC sets
868 /// only the “redirectable IPI” error bit. The interrupt is not
869 /// processed and hence the “Send Illegal Vector” bit is not set in
870 /// the ESR.
871 pub const SEND_ILLEGAL_VECTOR: bool;
872
873 /// Set when the local APIC detects an illegal vector (one in the
874 /// range 0 to 15) in an interrupt message it receives or in an
875 /// interrupt generated locally from the local vector table or via a
876 /// self IPI. Such interrupts are not delivered to the processor;
877 /// the local APIC will never set an IRR bit in the range 0 to 15.
878 pub const RECV_ILLEGAL_VECTOR: bool;
879
880 /// Set when the local APIC is in xAPIC mode and software attempts
881 /// to access a register that is reserved in the processor's
882 /// local-APIC register-address space; see Table 10-1. (The
883 /// local-APIC register-address spacemprises the 4 KBytes at the
884 /// physical address specified in the `IA32_APIC_BASE` MSR.) Used only
885 /// on Intel Core, Intel Atom, Pentium 4, Intel Xeon, and P6 family
886 /// processors.
887 ///
888 /// In x2APIC mode, software accesses the APIC registers using the
889 /// `RDMSR` and `WRMSR` instructions. Use of one of these
890 /// instructions to access a reserved register cause a
891 /// general-protection exception (see Section 10.12.1.3). They do
892 /// not set the “Illegal Register Access” bit in the ESR.
893 pub const ILLEGAL_REGISTER_ACCESS: bool;
894 }
895
896 }
897
898 impl ErrorStatus {
899 /// Returns `true` if an error is present, or `false` if no error
900 /// bits are set.
901 pub fn is_error(&self) -> bool {
902 // Mask out the reserved bits, just in case they have values in the,
903 // (they shouldn't, per the SDM, but...who knows!)
904 self.bits() & 0b1111_1111 != 0
905 }
906 }
907}
908
909#[cfg(test)]
910mod tests {
911 use super::*;
912
913 #[test]
914 fn lvt_entry_is_valid() {
915 register::LvtEntry::assert_valid();
916 }
917
918 #[test]
919 fn lvt_timer_is_valid() {
920 register::LvtTimer::assert_valid();
921 }
922
923 #[test]
924 fn lvt_timer_offsets() {
925 assert_eq!(
926 register::LvtTimer::VECTOR.least_significant_index(),
927 0,
928 "vector LSB"
929 );
930 assert_eq!(
931 register::LvtTimer::VECTOR.most_significant_index(),
932 8,
933 "vector MSB"
934 );
935 assert_eq!(
936 register::LvtTimer::SEND_PENDING.least_significant_index(),
937 12,
938 "send pending"
939 );
940 assert_eq!(
941 register::LvtTimer::MASKED.least_significant_index(),
942 16,
943 "masked MSB"
944 );
945 assert_eq!(
946 register::LvtTimer::MODE.least_significant_index(),
947 17,
948 "mode LSB"
949 );
950 }
951}