Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

idt: Fixup Options structure and cleanup set_handler_fn #226

Merged
merged 2 commits into from
Jan 12, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 58 additions & 39 deletions src/structures/idt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use core::marker::PhantomData;
use core::ops::Bound::{Excluded, Included, Unbounded};
use core::ops::{Deref, Index, IndexMut, RangeBounds};

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(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct Entry<F> {
pointer_low: u16,
gdt_selector: u16,
options: EntryOptions,
pointer_middle: u16,
pointer_high: u32,
Expand All @@ -590,7 +590,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 @@ -599,79 +598,99 @@ impl<F> Entry<F> {
phantom: PhantomData,
}
}
}

/// 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.
#[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
}
}

macro_rules! impl_set_handler_fn {
/// 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_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 Gate Descriptor.
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EntryOptions(u16);
pub struct EntryOptions {
cs: SegmentSelector,
bits: u16,
}

impl EntryOptions {
/// Creates a minimal options field with all the must-be-one bits set.
josephlr marked this conversation as resolved.
Show resolved Hide resolved
#[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. By default,
/// the current code segment is used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default, the current code segment is used.

This is not true, as minimal() uses 0 as default CS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I just removed this line as to avoid confusion.

Saying "By default" here is confusing, as it could mean the defaults from set_handler_fn or the defaults from minimal().

///
/// ## 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
}

/// 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 @@ -681,7 +700,7 @@ 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
}

Expand All @@ -701,7 +720,7 @@ 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
}
}
Expand Down