Skip to content

Commit c80ff08

Browse files
committed
Resubmission of #226
This moves the code_selector to the `EntryOptions` structure. While rebasing this, I also updated the `Debug` implementation to actually print out useful information instead of just hex codes. I left the type field as binary, as that's what the SDM does. Signed-off-by: Joe Richey <[email protected]>
1 parent 33e8c3b commit c80ff08

File tree

1 file changed

+84
-52
lines changed

1 file changed

+84
-52
lines changed

src/structures/idt.rs

+84-52
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use core::ops::Bound::{Excluded, Included, Unbounded};
1818
use core::ops::{Deref, Index, IndexMut, RangeBounds};
1919
use volatile::Volatile;
2020

21+
use super::gdt::SegmentSelector;
22+
2123
/// An Interrupt Descriptor Table with 256 entries.
2224
///
2325
/// The first 32 entries are used for CPU exceptions. These entries can be either accessed through
@@ -557,13 +559,11 @@ impl IndexMut<usize> for InterruptDescriptorTable {
557559

558560
/// An Interrupt Descriptor Table entry.
559561
///
560-
/// The generic parameter can either be `HandlerFunc` or `HandlerFuncWithErrCode`, depending
561-
/// on the interrupt vector.
562+
/// The generic parameter is some [`InterruptFn`], depending on the interrupt vector.
562563
#[derive(Clone, Copy)]
563564
#[repr(C)]
564565
pub struct Entry<F> {
565566
pointer_low: u16,
566-
gdt_selector: u16,
567567
options: EntryOptions,
568568
pointer_middle: u16,
569569
pointer_high: u32,
@@ -575,7 +575,6 @@ impl<T> fmt::Debug for Entry<T> {
575575
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
576576
f.debug_struct("Entry")
577577
.field("handler_addr", &format_args!("{:#x}", self.handler_addr()))
578-
.field("gdt_selector", &self.gdt_selector)
579578
.field("options", &self.options)
580579
.finish()
581580
}
@@ -584,7 +583,6 @@ impl<T> fmt::Debug for Entry<T> {
584583
impl<T> PartialEq for Entry<T> {
585584
fn eq(&self, other: &Self) -> bool {
586585
self.pointer_low == other.pointer_low
587-
&& self.gdt_selector == other.gdt_selector
588586
&& self.options == other.options
589587
&& self.pointer_middle == other.pointer_middle
590588
&& self.pointer_high == other.pointer_high
@@ -610,7 +608,6 @@ impl<F> Entry<F> {
610608
#[inline]
611609
pub const fn missing() -> Self {
612610
Entry {
613-
gdt_selector: 0,
614611
pointer_low: 0,
615612
pointer_middle: 0,
616613
pointer_high: 0,
@@ -620,93 +617,121 @@ impl<F> Entry<F> {
620617
}
621618
}
622619

623-
/// Set the handler address for the IDT entry and sets the present bit.
624-
///
625-
/// For the code selector field, this function uses the code segment selector currently
626-
/// active in the CPU.
620+
#[inline]
621+
fn handler_addr(&self) -> u64 {
622+
self.pointer_low as u64
623+
| (self.pointer_middle as u64) << 16
624+
| (self.pointer_high as u64) << 32
625+
}
626+
}
627+
628+
#[cfg(feature = "instructions")]
629+
impl<F: InterruptFn> Entry<F> {
630+
/// Sets the handler function for the IDT entry and sets the following defaults:
631+
/// - The code selector is the code segment currently active in the CPU
632+
/// - The present bit is set
633+
/// - Interrupts are disabled on handler invocation
634+
/// - The privilege level (DPL) is [`PrivilegeLevel::Ring0`]
635+
/// - No IST is configured (existing stack will be used)
627636
///
628637
/// The function returns a mutable reference to the entry's options that allows
629638
/// further customization.
630-
#[cfg(feature = "instructions")]
631-
#[inline]
632-
fn set_handler_addr(&mut self, addr: u64) -> &mut EntryOptions {
639+
pub fn set_handler_fn(&mut self, handler: F) -> &mut EntryOptions {
633640
use crate::instructions::segmentation;
634641

642+
let addr = handler.addr().as_u64();
635643
self.pointer_low = addr as u16;
636644
self.pointer_middle = (addr >> 16) as u16;
637645
self.pointer_high = (addr >> 32) as u32;
638646

639-
self.gdt_selector = segmentation::cs().0;
640-
647+
self.options = EntryOptions::minimal();
648+
// SAFETY: The current CS is a valid, long-mode code segment.
649+
unsafe { self.options.set_code_selector(segmentation::cs()) };
641650
self.options.set_present(true);
642651
&mut self.options
643652
}
653+
}
644654

645-
#[inline]
646-
fn handler_addr(&self) -> u64 {
647-
self.pointer_low as u64
648-
| (self.pointer_middle as u64) << 16
649-
| (self.pointer_high as u64) << 32
650-
}
655+
/// A trait for function pointers that can be used as interrupt handlers
656+
pub trait InterruptFn {
657+
/// The virtual address of this function pointer
658+
fn addr(self) -> VirtAddr;
651659
}
652660

653-
macro_rules! impl_set_handler_fn {
661+
macro_rules! impl_interrupt_fn {
654662
($h:ty) => {
655-
#[cfg(feature = "instructions")]
656-
impl Entry<$h> {
657-
/// Set the handler function for the IDT entry and sets the present bit.
658-
///
659-
/// For the code selector field, this function uses the code segment selector currently
660-
/// active in the CPU.
661-
///
662-
/// The function returns a mutable reference to the entry's options that allows
663-
/// further customization.
664-
#[inline]
665-
pub fn set_handler_fn(&mut self, handler: $h) -> &mut EntryOptions {
666-
self.set_handler_addr(handler as u64)
663+
#[cfg(target_pointer_width = "64")]
664+
impl InterruptFn for $h {
665+
fn addr(self) -> VirtAddr {
666+
VirtAddr::from_ptr(self as *const ())
667667
}
668668
}
669669
};
670670
}
671671

672-
impl_set_handler_fn!(HandlerFunc);
673-
impl_set_handler_fn!(HandlerFuncWithErrCode);
674-
impl_set_handler_fn!(PageFaultHandlerFunc);
675-
impl_set_handler_fn!(DivergingHandlerFunc);
676-
impl_set_handler_fn!(DivergingHandlerFuncWithErrCode);
672+
impl_interrupt_fn!(HandlerFunc);
673+
impl_interrupt_fn!(HandlerFuncWithErrCode);
674+
impl_interrupt_fn!(PageFaultHandlerFunc);
675+
impl_interrupt_fn!(DivergingHandlerFunc);
676+
impl_interrupt_fn!(DivergingHandlerFuncWithErrCode);
677677

678-
/// Represents the options field of an IDT entry.
679-
#[repr(transparent)]
678+
/// Represents the 4 non-offset bytes of an IDT entry.
679+
#[repr(C)]
680680
#[derive(Clone, Copy, PartialEq)]
681-
pub struct EntryOptions(u16);
681+
pub struct EntryOptions {
682+
cs: SegmentSelector,
683+
bits: u16,
684+
}
682685

683686
impl fmt::Debug for EntryOptions {
684687
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
685-
f.debug_tuple("EntryOptions")
686-
.field(&format_args!("{:#06x}", self.0))
688+
f.debug_struct("EntryOptions")
689+
.field("code_selector", &self.cs)
690+
.field("stack_index", &self.stack_index())
691+
.field("type", &format_args!("{:#04b}", self.bits.get_bits(8..12)))
692+
.field("privilege_level", &self.privilege_level())
693+
.field("present", &self.present())
687694
.finish()
688695
}
689696
}
690697

691698
impl EntryOptions {
692-
/// Creates a minimal options field with all the must-be-one bits set.
699+
/// Creates a minimal options field with all the must-be-one bits set. This
700+
/// means the CS selector, IST, and DPL field are all 0.
693701
#[inline]
694702
const fn minimal() -> Self {
695-
EntryOptions(0b1110_0000_0000)
703+
EntryOptions {
704+
cs: SegmentSelector(0),
705+
bits: 0b1110_0000_0000, // Default to a 64-bit Interrupt Gate
706+
}
707+
}
708+
709+
/// Set the code segment that will be used by this interrupt.
710+
///
711+
/// ## Safety
712+
/// This function is unsafe because the caller must ensure that the passed
713+
/// segment selector points to a valid, long-mode code segment.
714+
pub unsafe fn set_code_selector(&mut self, cs: SegmentSelector) -> &mut Self {
715+
self.cs = cs;
716+
self
696717
}
697718

698719
/// Set or reset the preset bit.
699720
#[inline]
700721
pub fn set_present(&mut self, present: bool) -> &mut Self {
701-
self.0.set_bit(15, present);
722+
self.bits.set_bit(15, present);
702723
self
703724
}
704725

726+
fn present(&self) -> bool {
727+
self.bits.get_bit(15)
728+
}
729+
705730
/// Let the CPU disable hardware interrupts when the handler is invoked. By default,
706731
/// interrupts are disabled on handler invocation.
707732
#[inline]
708733
pub fn disable_interrupts(&mut self, disable: bool) -> &mut Self {
709-
self.0.set_bit(8, !disable);
734+
self.bits.set_bit(8, !disable);
710735
self
711736
}
712737

@@ -716,10 +741,14 @@ impl EntryOptions {
716741
/// This function panics for a DPL > 3.
717742
#[inline]
718743
pub fn set_privilege_level(&mut self, dpl: PrivilegeLevel) -> &mut Self {
719-
self.0.set_bits(13..15, dpl as u16);
744+
self.bits.set_bits(13..15, dpl as u16);
720745
self
721746
}
722747

748+
fn privilege_level(&self) -> PrivilegeLevel {
749+
PrivilegeLevel::from_u16(self.bits.get_bits(13..15))
750+
}
751+
723752
/// Assigns a Interrupt Stack Table (IST) stack to this handler. The CPU will then always
724753
/// switch to the specified stack before the handler is invoked. This allows kernels to
725754
/// recover from corrupt stack pointers (e.g., on kernel stack overflow).
@@ -736,9 +765,13 @@ impl EntryOptions {
736765
pub unsafe fn set_stack_index(&mut self, index: u16) -> &mut Self {
737766
// The hardware IST index starts at 1, but our software IST index
738767
// starts at 0. Therefore we need to add 1 here.
739-
self.0.set_bits(0..3, index + 1);
768+
self.bits.set_bits(0..3, index + 1);
740769
self
741770
}
771+
772+
fn stack_index(&self) -> u16 {
773+
self.bits.get_bits(0..3) - 1
774+
}
742775
}
743776

744777
/// Wrapper type for the interrupt stack frame pushed by the CPU.
@@ -874,8 +907,7 @@ mod test {
874907

875908
foo(Entry::<HandlerFuncWithErrCode> {
876909
pointer_low: 0,
877-
gdt_selector: 0,
878-
options: EntryOptions(0),
910+
options: EntryOptions::minimal(),
879911
pointer_middle: 0,
880912
pointer_high: 0,
881913
reserved: 0,

0 commit comments

Comments
 (0)