Skip to content

Commit 993d1b9

Browse files
authored
feat(hal-x86_64): add LocalApic::check_error (#504)
The local APIC has an ESR (Error Status Register) that can be read to get error bits. This is particularly useful in a handler for the local APIC error interrupt. We don't currently provide an API for looking at this register, so this PR adds one.
1 parent fb4f514 commit 993d1b9

File tree

1 file changed

+128
-2
lines changed

1 file changed

+128
-2
lines changed

hal-x86_64/src/interrupt/apic/local.rs

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub use self::register::ErrorStatus;
12
use super::{PinPolarity, TriggerMode};
23
use crate::{
34
cpu::{local, FeatureNotSupported, Msr},
@@ -331,6 +332,46 @@ impl LocalApic {
331332
self.register(register::END_OF_INTERRUPT).write(0);
332333
}
333334

335+
/// Reads the error stauts register (`ESR`) of the local APIC.
336+
///
337+
/// Calling this method resets the value of the error status register. Any
338+
/// currently set error bits are cleared. If the same error bits are present
339+
/// in a subsequent call to `LocalApic::check_error`, they represent *new*
340+
/// instances of the same error.
341+
///
342+
/// # Returns
343+
///
344+
/// If any error bits are set, this method returns
345+
/// [`Err`]`(`[`ErrorStatus`]`)`. Otherwise, if no error bits are present,
346+
/// this method returns [`Ok`]`()`.
347+
pub fn check_error(&self) -> Result<(), register::ErrorStatus> {
348+
let esr = unsafe {
349+
let mut reg = self.register(register::ERROR_STATUS);
350+
351+
// Per the Intel SDM, Vol 3A, Ch 7, Section 12.5.3, "Error
352+
// Handling":
353+
//
354+
// > The ESR is a write/read register. Before attempt to read from
355+
// > the ESR, software should first write to it. (The value written
356+
// > does not affect the values read subsequently; only zero may be
357+
// > written in x2APIC mode.) This write clears any previously
358+
// > logged errors and updates the ESR with any errors detected
359+
// > since the last write to the ESR. This write also rearms the
360+
// > APIC error interrupt triggering mechanism.
361+
//
362+
// So, first write a zero.
363+
reg.write(register::ErrorStatus::default());
364+
reg.read()
365+
};
366+
367+
// Return the ESR value if error bits are set.
368+
if esr.is_error() {
369+
Err(esr)
370+
} else {
371+
Ok(())
372+
}
373+
}
374+
334375
unsafe fn write_register<T, A>(&self, register: LocalApicRegister<T, A>, value: T)
335376
where
336377
LocalApicRegister<T, A>: RegisterAccess<Target = T, Access = A>,
@@ -517,8 +558,8 @@ pub mod register {
517558

518559
/// Error Status Register (ESR)
519560
///
520-
/// **Access**: read-only
521-
ERROR_STATUS = 0x280, ReadOnly;
561+
/// **Access**: read/write
562+
ERROR_STATUS<ErrorStatus> = 0x280, ReadWrite;
522563

523564
/// LVT Corrected Machine Check Interrupt (CMCI) Register
524565
///
@@ -575,6 +616,91 @@ pub mod register {
575616
TscDeadline = 0b10,
576617
}
577618
}
619+
620+
bitfield! {
621+
/// Value of the Error Status Register (ESR).
622+
///
623+
/// See Intel SDM Vol. 3A, Ch. 7, Section 12.5.3, "Error Handling".
624+
#[derive(Default)]
625+
pub struct ErrorStatus<u32> {
626+
/// Set when the local APIC detects a checksum error for a message
627+
/// that it sent on the APIC bus.
628+
///
629+
/// Used only on P6 family and Pentium processors.
630+
pub const SEND_CHECKSUM_ERROR: bool;
631+
632+
/// Set when the local APIC detects a checksum error for a message
633+
/// that it received on the APIC bus.
634+
///
635+
/// Used only on P6 family and Pentium processors.
636+
pub const RECV_CHECKSUM_ERROR: bool;
637+
638+
/// Set when the local APIC detects that a message it sent was not
639+
/// accepted by any APIC on the APIC bus
640+
///
641+
/// Used only on P6 family and Pentium processors.
642+
pub const SEND_ACCEPT_ERROR: bool;
643+
644+
/// Set when the local APIC detects that a message it received was
645+
/// not accepted by any APIC on the APIC bus.
646+
///
647+
/// Used only on P6 family and Pentium processors.
648+
pub const RECV_ACCEPT_ERROR: bool;
649+
650+
/// Set when the local APIC detects an attempt to send an IPI with
651+
/// the lowest-priority delivery mode and the local APIC does not
652+
/// support the sending of such IPIs. This bit is used on some Intel
653+
/// Core and Intel Xeon processors.
654+
pub const REDIRECTABLE_IPI: bool;
655+
656+
/// Set when the local APIC detects an illegal vector (one in the
657+
/// range 0 to 15) in the message that it is sending. This occurs as
658+
/// the result of a write to the ICR (in both xAPIC and x2APIC
659+
/// modes) or to SELF IPI register (x2APIC mode only) with an
660+
/// illegal vector.
661+
///
662+
/// If the local APIC does not support the sending of
663+
/// lowest-priority IPIs and software writes the ICR to send a
664+
/// lowest-priority IPI with an illegal vector, the local APIC sets
665+
/// only the “redirectable IPI” error bit. The interrupt is not
666+
/// processed and hence the “Send Illegal Vector” bit is not set in
667+
/// the ESR.
668+
pub const SEND_ILLEGAL_VECTOR: bool;
669+
670+
/// Set when the local APIC detects an illegal vector (one in the
671+
/// range 0 to 15) in an interrupt message it receives or in an
672+
/// interrupt generated locally from the local vector table or via a
673+
/// self IPI. Such interrupts are not delivered to the processor;
674+
/// the local APIC will never set an IRR bit in the range 0 to 15.
675+
pub const RECV_ILLEGAL_VECTOR: bool;
676+
677+
/// Set when the local APIC is in xAPIC mode and software attempts
678+
/// to access a register that is reserved in the processor's
679+
/// local-APIC register-address space; see Table 10-1. (The
680+
/// local-APIC register-address spacemprises the 4 KBytes at the
681+
/// physical address specified in the `IA32_APIC_BASE` MSR.) Used only
682+
/// on Intel Core, Intel Atom, Pentium 4, Intel Xeon, and P6 family
683+
/// processors.
684+
///
685+
/// In x2APIC mode, software accesses the APIC registers using the
686+
/// `RDMSR` and `WRMSR` instructions. Use of one of these
687+
/// instructions to access a reserved register cause a
688+
/// general-protection exception (see Section 10.12.1.3). They do
689+
/// not set the “Illegal Register Access” bit in the ESR.
690+
pub const ILLEGAL_REGISTER_ACCESS: bool;
691+
}
692+
693+
}
694+
695+
impl ErrorStatus {
696+
/// Returns `true` if an error is present, or `false` if no error
697+
/// bits are set.
698+
pub fn is_error(&self) -> bool {
699+
// Mask out the reserved bits, just in case they have values in the,
700+
// (they shouldn't, per the SDM, but...who knows!)
701+
self.bits() & 0b1111_1111 != 0
702+
}
703+
}
578704
}
579705

580706
#[cfg(test)]

0 commit comments

Comments
 (0)