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}