Skip to content

Commit

Permalink
Resubmission of #226
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
josephlr committed Jun 4, 2021
1 parent 33e8c3b commit c80ff08
Showing 1 changed file with 84 additions and 52 deletions.
136 changes: 84 additions & 52 deletions src/structures/idt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use core::ops::Bound::{Excluded, Included, Unbounded};
use core::ops::{Deref, Index, IndexMut, RangeBounds};
use volatile::Volatile;

use super::gdt::SegmentSelector;

/// An Interrupt Descriptor Table with 256 entries.
///
/// The first 32 entries are used for CPU exceptions. These entries can be either accessed through
Expand Down Expand Up @@ -557,13 +559,11 @@ impl IndexMut<usize> for InterruptDescriptorTable {

/// An Interrupt Descriptor Table entry.
///
/// The generic parameter can either be `HandlerFunc` or `HandlerFuncWithErrCode`, depending
/// on the interrupt vector.
/// The generic parameter is some [`InterruptFn`], depending on the interrupt vector.
#[derive(Clone, Copy)]
#[repr(C)]
pub struct Entry<F> {
pointer_low: u16,
gdt_selector: u16,
options: EntryOptions,
pointer_middle: u16,
pointer_high: u32,
Expand All @@ -575,7 +575,6 @@ impl<T> fmt::Debug for Entry<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Entry")
.field("handler_addr", &format_args!("{:#x}", self.handler_addr()))
.field("gdt_selector", &self.gdt_selector)
.field("options", &self.options)
.finish()
}
Expand All @@ -584,7 +583,6 @@ impl<T> fmt::Debug for Entry<T> {
impl<T> PartialEq for Entry<T> {
fn eq(&self, other: &Self) -> bool {
self.pointer_low == other.pointer_low
&& self.gdt_selector == other.gdt_selector
&& self.options == other.options
&& self.pointer_middle == other.pointer_middle
&& self.pointer_high == other.pointer_high
Expand All @@ -610,7 +608,6 @@ impl<F> Entry<F> {
#[inline]
pub const fn missing() -> Self {
Entry {
gdt_selector: 0,
pointer_low: 0,
pointer_middle: 0,
pointer_high: 0,
Expand All @@ -620,93 +617,121 @@ impl<F> Entry<F> {
}
}

/// Set the handler address for the IDT entry and sets the present bit.
///
/// For the code selector field, this function uses the code segment selector currently
/// active in the CPU.
#[inline]
fn handler_addr(&self) -> u64 {
self.pointer_low as u64
| (self.pointer_middle as u64) << 16
| (self.pointer_high as u64) << 32
}
}

#[cfg(feature = "instructions")]
impl<F: InterruptFn> Entry<F> {
/// Sets the handler function for the IDT entry and sets the following defaults:
/// - The code selector is the code segment currently active in the CPU
/// - The present bit is set
/// - Interrupts are disabled on handler invocation
/// - The privilege level (DPL) is [`PrivilegeLevel::Ring0`]
/// - No IST is configured (existing stack will be used)
///
/// The function returns a mutable reference to the entry's options that allows
/// further customization.
#[cfg(feature = "instructions")]
#[inline]
fn set_handler_addr(&mut self, addr: u64) -> &mut EntryOptions {
pub fn set_handler_fn(&mut self, handler: F) -> &mut EntryOptions {
use crate::instructions::segmentation;

let addr = handler.addr().as_u64();
self.pointer_low = addr as u16;
self.pointer_middle = (addr >> 16) as u16;
self.pointer_high = (addr >> 32) as u32;

self.gdt_selector = segmentation::cs().0;

self.options = EntryOptions::minimal();
// SAFETY: The current CS is a valid, long-mode code segment.
unsafe { self.options.set_code_selector(segmentation::cs()) };
self.options.set_present(true);
&mut self.options
}
}

#[inline]
fn handler_addr(&self) -> u64 {
self.pointer_low as u64
| (self.pointer_middle as u64) << 16
| (self.pointer_high as u64) << 32
}
/// A trait for function pointers that can be used as interrupt handlers
pub trait InterruptFn {
/// The virtual address of this function pointer
fn addr(self) -> VirtAddr;
}

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

impl_set_handler_fn!(HandlerFunc);
impl_set_handler_fn!(HandlerFuncWithErrCode);
impl_set_handler_fn!(PageFaultHandlerFunc);
impl_set_handler_fn!(DivergingHandlerFunc);
impl_set_handler_fn!(DivergingHandlerFuncWithErrCode);
impl_interrupt_fn!(HandlerFunc);
impl_interrupt_fn!(HandlerFuncWithErrCode);
impl_interrupt_fn!(PageFaultHandlerFunc);
impl_interrupt_fn!(DivergingHandlerFunc);
impl_interrupt_fn!(DivergingHandlerFuncWithErrCode);

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

impl fmt::Debug for EntryOptions {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("EntryOptions")
.field(&format_args!("{:#06x}", self.0))
f.debug_struct("EntryOptions")
.field("code_selector", &self.cs)
.field("stack_index", &self.stack_index())
.field("type", &format_args!("{:#04b}", self.bits.get_bits(8..12)))
.field("privilege_level", &self.privilege_level())
.field("present", &self.present())
.finish()
}
}

impl EntryOptions {
/// Creates a minimal options field with all the must-be-one bits set.
/// Creates a minimal options field with all the must-be-one bits set. This
/// means the CS selector, IST, and DPL field are all 0.
#[inline]
const fn minimal() -> Self {
EntryOptions(0b1110_0000_0000)
EntryOptions {
cs: SegmentSelector(0),
bits: 0b1110_0000_0000, // Default to a 64-bit Interrupt Gate
}
}

/// Set the code segment that will be used by this interrupt.
///
/// ## Safety
/// This function is unsafe because the caller must ensure that the passed
/// segment selector points to a valid, long-mode code segment.
pub unsafe fn set_code_selector(&mut self, cs: SegmentSelector) -> &mut Self {
self.cs = cs;
self
}

/// Set or reset the preset bit.
#[inline]
pub fn set_present(&mut self, present: bool) -> &mut Self {
self.0.set_bit(15, present);
self.bits.set_bit(15, present);
self
}

fn present(&self) -> bool {
self.bits.get_bit(15)
}

/// Let the CPU disable hardware interrupts when the handler is invoked. By default,
/// interrupts are disabled on handler invocation.
#[inline]
pub fn disable_interrupts(&mut self, disable: bool) -> &mut Self {
self.0.set_bit(8, !disable);
self.bits.set_bit(8, !disable);
self
}

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

fn privilege_level(&self) -> PrivilegeLevel {
PrivilegeLevel::from_u16(self.bits.get_bits(13..15))
}

/// Assigns a Interrupt Stack Table (IST) stack to this handler. The CPU will then always
/// switch to the specified stack before the handler is invoked. This allows kernels to
/// recover from corrupt stack pointers (e.g., on kernel stack overflow).
Expand All @@ -736,9 +765,13 @@ impl EntryOptions {
pub unsafe fn set_stack_index(&mut self, index: u16) -> &mut Self {
// The hardware IST index starts at 1, but our software IST index
// starts at 0. Therefore we need to add 1 here.
self.0.set_bits(0..3, index + 1);
self.bits.set_bits(0..3, index + 1);
self
}

fn stack_index(&self) -> u16 {
self.bits.get_bits(0..3) - 1
}
}

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

foo(Entry::<HandlerFuncWithErrCode> {
pointer_low: 0,
gdt_selector: 0,
options: EntryOptions(0),
options: EntryOptions::minimal(),
pointer_middle: 0,
pointer_high: 0,
reserved: 0,
Expand Down

0 comments on commit c80ff08

Please sign in to comment.