From 885977a21fb1b1f2b6efa9ce1b998fe4d3c92dce Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Fri, 4 Jun 2021 09:30:51 +0200 Subject: [PATCH 01/64] replace software_interrupt! macro with generic function (#259) This is a *breaking change*, also add note about safety. --- src/instructions/interrupts.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index f1d171cf8..b9b41a714 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -149,14 +149,14 @@ pub fn int3() { /// Generate a software interrupt by invoking the `int` instruction. /// -/// This currently needs to be a macro because the `int` argument needs to be an -/// immediate. This macro will be replaced by a generic function when support for -/// const generics is implemented in Rust. +/// ## Safety +/// +/// Invoking an arbitrary interrupt is unsafe. It can cause your system to +/// crash if you invoke a double-fault (#8) or machine-check (#18) exception. +/// It can also cause memory/register corruption depending on the interrupt +/// implementation (if it expects values/pointers to be passed in registers). #[cfg(feature = "inline_asm")] #[cfg_attr(docsrs, doc(cfg(any(feature = "nightly", feature = "inline_asm"))))] -#[macro_export] -macro_rules! software_interrupt { - ($x:expr) => {{ - asm!("int {id}", id = const $x, options(nomem, nostack)); - }}; +pub unsafe fn software_interrupt() { + asm!("int {num}", num = const NUM, options(nomem, nostack)); } From 33e8c3be28c753ba1f088ac463e4bbbf8ce745b8 Mon Sep 17 00:00:00 2001 From: Joseph Richey Date: Fri, 4 Jun 2021 01:31:52 -0700 Subject: [PATCH 02/64] software_interrupt: Add additional testing (#260) Followup to #259. Code previously merged as part of #227. Signed-off-by: Joe Richey --- testing/Cargo.toml | 2 +- ...int_exception.rs => interrupt_handling.rs} | 39 ++++++++++++++++--- 2 files changed, 35 insertions(+), 6 deletions(-) rename testing/tests/{breakpoint_exception.rs => interrupt_handling.rs} (55%) diff --git a/testing/Cargo.toml b/testing/Cargo.toml index fe08cc67c..7a32657c1 100644 --- a/testing/Cargo.toml +++ b/testing/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Philipp Oppermann "] edition = "2018" [[test]] -name = "breakpoint_exception" +name = "interrupt_handling" harness = false [[test]] diff --git a/testing/tests/breakpoint_exception.rs b/testing/tests/interrupt_handling.rs similarity index 55% rename from testing/tests/breakpoint_exception.rs rename to testing/tests/interrupt_handling.rs index 573819030..120f95515 100644 --- a/testing/tests/breakpoint_exception.rs +++ b/testing/tests/interrupt_handling.rs @@ -7,7 +7,10 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use lazy_static::lazy_static; use testing::{exit_qemu, serial_print, serial_println, QemuExitCode}; +use x86_64::instructions::interrupts; + static BREAKPOINT_HANDLER_CALLED: AtomicUsize = AtomicUsize::new(0); +static INTERRUPT_HANDLER_CALLED: AtomicUsize = AtomicUsize::new(0); #[no_mangle] pub extern "C" fn _start() -> ! { @@ -16,13 +19,10 @@ pub extern "C" fn _start() -> ! { init_test_idt(); // invoke a breakpoint exception - x86_64::instructions::interrupts::int3(); + interrupts::int3(); match BREAKPOINT_HANDLER_CALLED.load(Ordering::SeqCst) { - 1 => { - serial_println!("[ok]"); - exit_qemu(QemuExitCode::Success); - } + 1 => {} 0 => { serial_println!("[failed]"); serial_println!(" Breakpoint handler was not called."); @@ -35,6 +35,29 @@ pub extern "C" fn _start() -> ! { } } + serial_print!("interrupt 42... "); + unsafe { interrupts::software_interrupt::<42>() }; + serial_print!("interrupt 77... "); + unsafe { interrupts::software_interrupt::<77>() }; + serial_print!("interrupt 42... "); + unsafe { interrupts::software_interrupt::<42>() }; + + match INTERRUPT_HANDLER_CALLED.load(Ordering::SeqCst) { + 3 => {} + 0 => { + serial_println!("[failed]"); + serial_println!(" Interrupt handler was not called."); + exit_qemu(QemuExitCode::Failed); + } + other => { + serial_println!("[failed]"); + serial_println!(" Interrupt handler was called {} times", other); + exit_qemu(QemuExitCode::Failed); + } + } + + serial_println!("[ok]"); + exit_qemu(QemuExitCode::Success); loop {} } @@ -49,6 +72,8 @@ lazy_static! { static ref TEST_IDT: InterruptDescriptorTable = { let mut idt = InterruptDescriptorTable::new(); idt.breakpoint.set_handler_fn(breakpoint_handler); + idt[42].set_handler_fn(interrupt_handler); + idt[77].set_handler_fn(interrupt_handler); idt }; } @@ -60,3 +85,7 @@ pub fn init_test_idt() { extern "x86-interrupt" fn breakpoint_handler(_stack_frame: InterruptStackFrame) { BREAKPOINT_HANDLER_CALLED.fetch_add(1, Ordering::SeqCst); } + +extern "x86-interrupt" fn interrupt_handler(_stack_frame: InterruptStackFrame) { + INTERRUPT_HANDLER_CALLED.fetch_add(1, Ordering::SeqCst); +} From 69a6b1eb803531ced3486cc1d96a1ca6b455f125 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Fri, 4 Jun 2021 23:10:30 +0200 Subject: [PATCH 03/64] fix typo in docs (#265) --- src/structures/paging/mapper/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/structures/paging/mapper/mod.rs b/src/structures/paging/mapper/mod.rs index a73f2d8c4..1d83293d7 100644 --- a/src/structures/paging/mapper/mod.rs +++ b/src/structures/paging/mapper/mod.rs @@ -286,7 +286,7 @@ pub trait Mapper { /// /// This method is unsafe because changing the flags of a mapping /// might result in undefined behavior. For example, setting the - /// `GLOBAL` and `MUTABLE` flags for a page might result in the corruption + /// `GLOBAL` and `WRITABLE` flags for a page might result in the corruption /// of values stored in that page from processes running in other address /// spaces. unsafe fn update_flags( @@ -373,8 +373,8 @@ pub trait Mapper { /// This type represents a page whose mapping has changed in the page table. /// /// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs -/// to be flushed from the TLB before it's accessed. This type is returned from function that -/// change the mapping of a page to ensure that the TLB flush is not forgotten. +/// to be flushed from the TLB before it's accessed. This type is returned from a function that +/// changed the mapping of a page to ensure that the TLB flush is not forgotten. #[derive(Debug)] #[must_use = "Page Table changes must be flushed or ignored."] pub struct MapperFlush(Page); From 66952ebefe0ba67f3828fa77b7511a5479c9d97b Mon Sep 17 00:00:00 2001 From: Joseph Richey Date: Sat, 12 Jun 2021 01:01:53 -0700 Subject: [PATCH 04/64] Resubmission of #226 (#261) 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 --- src/structures/idt.rs | 136 ++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 52 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index e24aa3623..6af786c64 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -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 @@ -557,13 +559,11 @@ impl IndexMut 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 { pointer_low: u16, - gdt_selector: u16, options: EntryOptions, pointer_middle: u16, pointer_high: u32, @@ -575,7 +575,6 @@ impl fmt::Debug for Entry { 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() } @@ -584,7 +583,6 @@ impl fmt::Debug for Entry { impl PartialEq for Entry { 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 @@ -610,7 +608,6 @@ impl Entry { #[inline] pub const fn missing() -> Self { Entry { - gdt_selector: 0, pointer_low: 0, pointer_middle: 0, pointer_high: 0, @@ -620,93 +617,121 @@ impl Entry { } } - /// 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 Entry { + /// 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 } @@ -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). @@ -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. @@ -874,8 +907,7 @@ mod test { foo(Entry:: { pointer_low: 0, - gdt_selector: 0, - options: EntryOptions(0), + options: EntryOptions::minimal(), pointer_middle: 0, pointer_high: 0, reserved: 0, From 2313b3088a568fdc9aff6fdc08014feabea9aa4c Mon Sep 17 00:00:00 2001 From: Joseph Richey Date: Sat, 12 Jun 2021 01:02:33 -0700 Subject: [PATCH 05/64] Use SegmentSelector in InterruptStackFrame (#263) This makes the value easier to use and slightly improves the `Debug` implementation. Also, change the `InterruptStackFrame` to `#[repr(transparent)]`. It doesn't really matter but makes it clear that `InterruptStackFrame` and `InterruptStackFrameValue` are ABI compatible. Note that this now makes `InterruptStackFrameValue`s imposible to construct, but that seems fine. Signed-off-by: Joe Richey --- src/structures/idt.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 6af786c64..1041e2a15 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -781,10 +781,8 @@ impl EntryOptions { /// This wrapper type ensures that no accidental modification of the interrupt stack frame /// occurs, which can cause undefined behavior (see the [`as_mut`](InterruptStackFrame::as_mut) /// method for more information). -#[repr(C)] -pub struct InterruptStackFrame { - value: InterruptStackFrameValue, -} +#[repr(transparent)] +pub struct InterruptStackFrame(InterruptStackFrameValue); impl InterruptStackFrame { /// Gives mutable access to the contents of the interrupt stack frame. @@ -803,7 +801,7 @@ impl InterruptStackFrame { /// officially supported by LLVM's x86 interrupt calling convention. #[inline] pub unsafe fn as_mut(&mut self) -> Volatile<&mut InterruptStackFrameValue> { - Volatile::new(&mut self.value) + Volatile::new(&mut self.0) } } @@ -812,14 +810,14 @@ impl Deref for InterruptStackFrame { #[inline] fn deref(&self) -> &Self::Target { - &self.value + &self.0 } } impl fmt::Debug for InterruptStackFrame { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.value.fmt(f) + self.0.fmt(f) } } @@ -833,14 +831,16 @@ pub struct InterruptStackFrameValue { /// this value points to the faulting instruction, so that the instruction is restarted on /// return. See the documentation of the [`InterruptDescriptorTable`] fields for more details. pub instruction_pointer: VirtAddr, - /// The code segment selector, padded with zeros. - pub code_segment: u64, + /// The code segment selector at the time of the interrupt. + pub code_segment: SegmentSelector, + _reserved1: [u8; 6], /// The flags register before the interrupt handler was invoked. pub cpu_flags: u64, /// The stack pointer at the time of the interrupt. pub stack_pointer: VirtAddr, /// The stack segment descriptor at the time of the interrupt (often zero in 64-bit mode). - pub stack_segment: u64, + pub stack_segment: SegmentSelector, + _reserved2: [u8; 6], } impl fmt::Debug for InterruptStackFrameValue { @@ -899,6 +899,8 @@ mod test { use core::mem::size_of; assert_eq!(size_of::>(), 16); assert_eq!(size_of::(), 256 * 16); + assert_eq!(size_of::(), 40); + assert_eq!(size_of::(), 40); } #[test] From 8fe252fc40c27d5299fc83c9943a52eeb8e79e8b Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Mon, 25 Oct 2021 14:40:43 +0200 Subject: [PATCH 06/64] add `PcidTooBig` error --- src/instructions/tlb.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/instructions/tlb.rs b/src/instructions/tlb.rs index ea980863d..b01afb26a 100644 --- a/src/instructions/tlb.rs +++ b/src/instructions/tlb.rs @@ -1,5 +1,7 @@ //! Functions to flush the translation lookaside buffer (TLB). +use core::fmt; + use crate::VirtAddr; /// Invalidate the given address in the TLB using the `invlpg` instruction. @@ -55,9 +57,9 @@ pub struct Pcid(u16); impl Pcid { /// Create a new PCID. Will result in a failure if the value of /// PCID is out of expected bounds. - pub const fn new(pcid: u16) -> Result { + pub const fn new(pcid: u16) -> Result { if pcid >= 4096 { - Err("PCID should be < 4096.") + Err(PcidTooBig(pcid)) } else { Ok(Pcid(pcid)) } @@ -69,6 +71,18 @@ impl Pcid { } } +/// A passed `u16` was not a valid PCID. +/// +/// A PCID has to be <= 4096 for x86_64. +#[derive(Debug)] +pub struct PcidTooBig(u16); + +impl fmt::Display for PcidTooBig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "PCID should be < 4096, got {}", self.0) + } +} + /// Invalidate the given address in the TLB using the `invpcid` instruction. /// /// ## Safety From 9b1773cc417ecf45fb940a5d8ef599bcc87d7688 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Mon, 25 Oct 2021 14:55:49 +0200 Subject: [PATCH 07/64] add `InvalidStarSegmentSelectors` --- src/registers/model_specific.rs | 34 ++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 073a08c4f..a8856d91d 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -118,6 +118,7 @@ mod x86_64 { use crate::PrivilegeLevel; use bit_field::BitField; use core::convert::TryInto; + use core::fmt; impl Msr { /// Read 64 bits msr register. @@ -367,21 +368,21 @@ mod x86_64 { ss_sysret: SegmentSelector, cs_syscall: SegmentSelector, ss_syscall: SegmentSelector, - ) -> Result<(), &'static str> { + ) -> Result<(), InvalidStarSegmentSelectors> { if cs_sysret.0 - 16 != ss_sysret.0 - 8 { - return Err("Sysret CS and SS is not offset by 8."); + return Err(InvalidStarSegmentSelectors::SysretOffset); } if cs_syscall.0 != ss_syscall.0 - 8 { - return Err("Syscall CS and SS is not offset by 8."); + return Err(InvalidStarSegmentSelectors::SyscallOffset); } if ss_sysret.rpl() != PrivilegeLevel::Ring3 { - return Err("Sysret's segment must be a Ring3 segment."); + return Err(InvalidStarSegmentSelectors::SysretPrivilegeLevel); } if ss_syscall.rpl() != PrivilegeLevel::Ring0 { - return Err("Syscall's segment must be a Ring0 segment."); + return Err(InvalidStarSegmentSelectors::SyscallPrivilegeLevel); } unsafe { Self::write_raw(ss_sysret.0 - 8, cs_syscall.0) }; @@ -390,6 +391,29 @@ mod x86_64 { } } + #[derive(Debug)] + pub enum InvalidStarSegmentSelectors { + SysretOffset, + SyscallOffset, + SysretPrivilegeLevel, + SyscallPrivilegeLevel, + } + + impl fmt::Display for InvalidStarSegmentSelectors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::SysretOffset => write!(f, "Sysret CS and SS is not offset by 8."), + Self::SyscallOffset => write!(f, "Syscall CS and SS is not offset by 8."), + Self::SysretPrivilegeLevel => { + write!(f, "Sysret's segment must be a Ring3 segment.") + } + Self::SyscallPrivilegeLevel => { + write!(f, "Syscall's segment must be a Ring0 segment.") + } + } + } + } + impl LStar { /// Read the current LStar register. /// This holds the target RIP of a syscall. From ddf7a6882f97f2ddd08a4f86db35a0533386bd5d Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Mon, 25 Oct 2021 14:58:52 +0200 Subject: [PATCH 08/64] fix grammar --- src/registers/model_specific.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index a8856d91d..6c7a0510b 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -402,8 +402,8 @@ mod x86_64 { impl fmt::Display for InvalidStarSegmentSelectors { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::SysretOffset => write!(f, "Sysret CS and SS is not offset by 8."), - Self::SyscallOffset => write!(f, "Syscall CS and SS is not offset by 8."), + Self::SysretOffset => write!(f, "Sysret CS and SS are not offset by 8."), + Self::SyscallOffset => write!(f, "Syscall CS and SS are not offset by 8."), Self::SysretPrivilegeLevel => { write!(f, "Sysret's segment must be a Ring3 segment.") } From 829488241276e8ad985d4f1e79a8c8c076f694a5 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Mon, 8 Nov 2021 11:15:02 +0100 Subject: [PATCH 09/64] activate `feature(abi_x86_interrupt)` --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index cf146e31a..a7b0d69e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ #![cfg_attr(feature = "const_fn", feature(const_fn_fn_ptr_basics))] // IDT new() #![cfg_attr(feature = "const_fn", feature(const_fn_trait_bound))] // PageSize marker trait #![cfg_attr(feature = "inline_asm", feature(asm))] +#![cfg_attr(feature = "inline_asm", feature(asm_const))] // software_interrupt #![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))] #![cfg_attr(docsrs, feature(doc_cfg))] #![warn(missing_docs)] From 88fee50dc78a5c6ec0597af152b79ab4a51c98c6 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 21:26:25 +0100 Subject: [PATCH 10/64] change `Index` to `Index` --- src/structures/idt.rs | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index a4f74a8bb..d325fc985 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -504,15 +504,14 @@ impl InterruptDescriptorTable { } } -impl Index for InterruptDescriptorTable { +impl Index for InterruptDescriptorTable { type Output = Entry; /// Returns the IDT entry with the specified index. /// - /// Panics if index is outside the IDT (i.e. greater than 255) or if the entry is an - /// exception that pushes an error code (use the struct fields for accessing these entries). + /// Panics if the entry is an exception that pushes an error code (use the struct fields for accessing these entries). #[inline] - fn index(&self, index: usize) -> &Self::Output { + fn index(&self, index: u8) -> &Self::Output { match index { 0 => &self.divide_error, 1 => &self.debug, @@ -526,24 +525,22 @@ impl Index for InterruptDescriptorTable { 16 => &self.x87_floating_point, 19 => &self.simd_floating_point, 20 => &self.virtualization, - i @ 32..=255 => &self.interrupts[i - 32], + i @ 32..=255 => &self.interrupts[(i - 32) as usize], i @ 15 | i @ 31 | i @ 21..=29 => panic!("entry {} is reserved", i), i @ 8 | i @ 10..=14 | i @ 17 | i @ 30 => { panic!("entry {} is an exception with error code", i) } i @ 18 => panic!("entry {} is an diverging exception (must not return)", i), - i => panic!("no entry with index {}", i), } } } -impl IndexMut for InterruptDescriptorTable { +impl IndexMut for InterruptDescriptorTable { /// Returns a mutable reference to the IDT entry with the specified index. /// - /// Panics if index is outside the IDT (i.e. greater than 255) or if the entry is an - /// exception that pushes an error code (use the struct fields for accessing these entries). + /// Panics if the entry is an exception that pushes an error code (use the struct fields for accessing these entries). #[inline] - fn index_mut(&mut self, index: usize) -> &mut Self::Output { + fn index_mut(&mut self, index: u8) -> &mut Self::Output { match index { 0 => &mut self.divide_error, 1 => &mut self.debug, @@ -557,13 +554,12 @@ impl IndexMut for InterruptDescriptorTable { 16 => &mut self.x87_floating_point, 19 => &mut self.simd_floating_point, 20 => &mut self.virtualization, - i @ 32..=255 => &mut self.interrupts[i - 32], + i @ 32..=255 => &mut self.interrupts[(i - 32) as usize], i @ 15 | i @ 31 | i @ 21..=29 => panic!("entry {} is reserved", i), i @ 8 | i @ 10..=14 | i @ 17 | i @ 30 => { panic!("entry {} is an exception with error code", i) } i @ 18 => panic!("entry {} is an diverging exception (must not return)", i), - i => panic!("no entry with index {}", i), } } } From 99a334c4a4bd05babeb54aedbb7d2faf284e0004 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 21:26:55 +0100 Subject: [PATCH 11/64] use `u8` instead of `usize` for `slice` --- src/structures/idt.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index d325fc985..b4ffa568b 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -458,25 +458,21 @@ impl InterruptDescriptorTable { } } - /// Returns a normalized and ranged check slice range from a RangeBounds trait object + /// Returns a normalized and ranged check slice range from a RangeBounds trait object. /// - /// Panics if range is outside the range of user interrupts (i.e. greater than 255) or if the entry is an - /// exception - fn condition_slice_bounds(&self, bounds: impl RangeBounds) -> (usize, usize) { + /// Panics if the entry is an exception. + fn condition_slice_bounds(&self, bounds: impl RangeBounds) -> (usize, usize) { let lower_idx = match bounds.start_bound() { - Included(start) => *start, - Excluded(start) => *start + 1, + Included(start) => (*start as usize), + Excluded(start) => (*start as usize) + 1, Unbounded => 0, }; let upper_idx = match bounds.end_bound() { - Included(end) => *end + 1, - Excluded(end) => *end, + Included(end) => (*end as usize) + 1, + Excluded(end) => (*end as usize), Unbounded => 256, }; - if lower_idx > 256 || upper_idx > 256 { - panic!("Index out of range [{}..{}]", lower_idx, upper_idx); - } if lower_idx < 32 { panic!("Cannot return slice from traps, faults, and exception handlers"); } @@ -485,20 +481,18 @@ impl InterruptDescriptorTable { /// Returns slice of IDT entries with the specified range. /// - /// Panics if range is outside the range of user interrupts (i.e. greater than 255) or if the entry is an - /// exception + /// Panics if the entry is an exception. #[inline] - pub fn slice(&self, bounds: impl RangeBounds) -> &[Entry] { + pub fn slice(&self, bounds: impl RangeBounds) -> &[Entry] { let (lower_idx, upper_idx) = self.condition_slice_bounds(bounds); &self.interrupts[(lower_idx - 32)..(upper_idx - 32)] } /// Returns a mutable slice of IDT entries with the specified range. /// - /// Panics if range is outside the range of user interrupts (i.e. greater than 255) or if the entry is an - /// exception + /// Panics if the entry is an exception. #[inline] - pub fn slice_mut(&mut self, bounds: impl RangeBounds) -> &mut [Entry] { + pub fn slice_mut(&mut self, bounds: impl RangeBounds) -> &mut [Entry] { let (lower_idx, upper_idx) = self.condition_slice_bounds(bounds); &mut self.interrupts[(lower_idx - 32)..(upper_idx - 32)] } From 5be7446d46d08b0e25bf8c75d9b941125a5ae01d Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 7 Nov 2021 21:39:32 +0100 Subject: [PATCH 12/64] implement ranged index for idt --- src/structures/idt.rs | 47 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index b4ffa568b..4c2def6da 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -26,7 +26,10 @@ use bitflags::bitflags; use core::fmt; use core::marker::PhantomData; use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{Deref, Index, IndexMut, RangeBounds}; +use core::ops::{ + Bound, Deref, Index, IndexMut, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, + RangeTo, RangeToInclusive, +}; use volatile::Volatile; use super::gdt::SegmentSelector; @@ -558,6 +561,48 @@ impl IndexMut for InterruptDescriptorTable { } } +macro_rules! impl_index_for_idt { + ($ty:ty) => { + impl Index<$ty> for InterruptDescriptorTable { + type Output = [Entry]; + + /// Returns the IDT entry with the specified index. + /// + /// Panics if index is outside the IDT (i.e. greater than 255) or if the entry is an + /// exception that pushes an error code (use the struct fields for accessing these entries). + #[inline] + fn index(&self, index: $ty) -> &Self::Output { + self.slice(index) + } + } + + impl IndexMut<$ty> for InterruptDescriptorTable { + /// Returns a mutable reference to the IDT entry with the specified index. + /// + /// Panics if the entry is an exception that pushes an error code (use the struct fields for accessing these entries). + #[inline] + fn index_mut(&mut self, index: $ty) -> &mut Self::Output { + self.slice_mut(index) + } + } + }; +} + +// this list was stolen from the list of implementors in https://doc.rust-lang.org/core/ops/trait.RangeBounds.html +impl_index_for_idt!((Bound<&u8>, Bound<&u8>)); +impl_index_for_idt!((Bound, Bound)); +impl_index_for_idt!(Range<&u8>); +impl_index_for_idt!(Range); +impl_index_for_idt!(RangeFrom<&u8>); +impl_index_for_idt!(RangeFrom); +impl_index_for_idt!(RangeInclusive<&u8>); +impl_index_for_idt!(RangeInclusive); +impl_index_for_idt!(RangeTo); +impl_index_for_idt!(RangeTo<&u8>); +impl_index_for_idt!(RangeToInclusive<&u8>); +impl_index_for_idt!(RangeToInclusive); +impl_index_for_idt!(RangeFull); + /// An Interrupt Descriptor Table entry. /// /// The generic parameter is some [`InterruptFn`], depending on the interrupt vector. From 6eee5a8602a65ea06f48966801977ffd3206c635 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Mon, 8 Nov 2021 10:44:56 +0100 Subject: [PATCH 13/64] use `usize::from` instead of casting --- src/structures/idt.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 4c2def6da..27cb7f64e 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -466,13 +466,13 @@ impl InterruptDescriptorTable { /// Panics if the entry is an exception. fn condition_slice_bounds(&self, bounds: impl RangeBounds) -> (usize, usize) { let lower_idx = match bounds.start_bound() { - Included(start) => (*start as usize), - Excluded(start) => (*start as usize) + 1, + Included(start) => usize::from(*start), + Excluded(start) => usize::from(*start) + 1, Unbounded => 0, }; let upper_idx = match bounds.end_bound() { - Included(end) => (*end as usize) + 1, - Excluded(end) => (*end as usize), + Included(end) => usize::from(*end) + 1, + Excluded(end) => usize::from(*end), Unbounded => 256, }; @@ -522,7 +522,7 @@ impl Index for InterruptDescriptorTable { 16 => &self.x87_floating_point, 19 => &self.simd_floating_point, 20 => &self.virtualization, - i @ 32..=255 => &self.interrupts[(i - 32) as usize], + i @ 32..=255 => &self.interrupts[usize::from(i - 32)], i @ 15 | i @ 31 | i @ 21..=29 => panic!("entry {} is reserved", i), i @ 8 | i @ 10..=14 | i @ 17 | i @ 30 => { panic!("entry {} is an exception with error code", i) @@ -551,7 +551,7 @@ impl IndexMut for InterruptDescriptorTable { 16 => &mut self.x87_floating_point, 19 => &mut self.simd_floating_point, 20 => &mut self.virtualization, - i @ 32..=255 => &mut self.interrupts[(i - 32) as usize], + i @ 32..=255 => &mut self.interrupts[usize::from(i - 32)], i @ 15 | i @ 31 | i @ 21..=29 => panic!("entry {} is reserved", i), i @ 8 | i @ 10..=14 | i @ 17 | i @ 30 => { panic!("entry {} is an exception with error code", i) From 338d3d40958c20de0818444c4fd92cd0270436c7 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Tue, 9 Nov 2021 17:21:08 +0100 Subject: [PATCH 14/64] fix `load_tss` and `GlobalDescriptorTable` `ltr` atomically writes to memory when it changes the referenced tss entry's type to Busy 64-bit TSS. This means that the inline assembly in `load_tss` should not have the `nomem` option and that the `table` field in `GlobalDescriptorTable` must use a type that is interiorly mutable. --- src/instructions/tables.rs | 5 ++++- src/structures/gdt.rs | 44 +++++++++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/instructions/tables.rs b/src/instructions/tables.rs index 667fb9b46..4d8c7fe61 100644 --- a/src/instructions/tables.rs +++ b/src/instructions/tables.rs @@ -81,6 +81,9 @@ pub fn sidt() -> DescriptorTablePointer { /// Load the task state register using the `ltr` instruction. /// +/// Loading the task state register changes the type of the entry +/// in the GDT from `Available 64-bit TSS` to `Busy 64-bit TSS`. +/// /// ## Safety /// /// This function is unsafe because the caller must ensure that the given @@ -89,7 +92,7 @@ pub fn sidt() -> DescriptorTablePointer { #[inline] pub unsafe fn load_tss(sel: SegmentSelector) { #[cfg(feature = "inline_asm")] - asm!("ltr {0:x}", in(reg) sel.0, options(nomem, nostack, preserves_flags)); + asm!("ltr {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); #[cfg(not(feature = "inline_asm"))] crate::asm::x86_64_asm_ltr(sel.0); diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index c96d81161..17d288370 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -5,6 +5,7 @@ use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; use core::fmt; +use core::sync::atomic::{AtomicU64, Ordering}; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table @@ -89,18 +90,32 @@ impl fmt::Debug for SegmentSelector { /// // Add entry for TSS, call gdt.load() then update segment registers /// ``` -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct GlobalDescriptorTable { - table: [u64; 8], + table: [AtomicU64; 8], next_free: usize, } impl GlobalDescriptorTable { + #[inline] + const fn empty_table() -> [AtomicU64; 8] { + [ + AtomicU64::new(0), + AtomicU64::new(0), + AtomicU64::new(0), + AtomicU64::new(0), + AtomicU64::new(0), + AtomicU64::new(0), + AtomicU64::new(0), + AtomicU64::new(0), + ] + } + /// Creates an empty GDT. #[inline] pub const fn new() -> GlobalDescriptorTable { GlobalDescriptorTable { - table: [0; 8], + table: Self::empty_table(), next_free: 1, } } @@ -114,7 +129,7 @@ impl GlobalDescriptorTable { #[inline] pub const unsafe fn from_raw_slice(slice: &[u64]) -> GlobalDescriptorTable { let next_free = slice.len(); - let mut table = [0; 8]; + let mut table = Self::empty_table(); let mut idx = 0; const_assert!( @@ -123,7 +138,7 @@ impl GlobalDescriptorTable { ); while idx != next_free { - table[idx] = slice[idx]; + table[idx] = AtomicU64::new(slice[idx]); idx += 1; } @@ -132,9 +147,9 @@ impl GlobalDescriptorTable { /// Get a reference to the internal table. /// - /// The resulting slice may contain system descriptors, which span two `u64`s. + /// The resulting slice may contain system descriptors, which span two `AtomicU64`s. #[inline] - pub fn as_raw_slice(&self) -> &[u64] { + pub fn as_raw_slice(&self) -> &[AtomicU64] { &self.table[..self.next_free] } @@ -206,7 +221,7 @@ impl GlobalDescriptorTable { fn push(&mut self, value: u64) -> usize { if self.next_free < self.table.len() { let index = self.next_free; - self.table[index] = value; + self.table[index] = AtomicU64::new(value); self.next_free += 1; index } else { @@ -227,6 +242,19 @@ impl GlobalDescriptorTable { } } +impl Clone for GlobalDescriptorTable { + fn clone(&self) -> Self { + let mut table = Self::empty_table(); + for (dest, src) in table.iter_mut().zip(self.table.iter()) { + *dest = AtomicU64::new(src.load(Ordering::Relaxed)); + } + Self { + table, + next_free: self.next_free, + } + } +} + /// A 64-bit mode segment descriptor. /// /// Segmentation is no longer supported in 64-bit mode, so most of the descriptor From aefd432ddd435727e40dc7a2a7d2b05a568968f6 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 10 Nov 2021 13:31:54 +0100 Subject: [PATCH 15/64] revert to old GDT but take &mut in `load` instead --- src/structures/gdt.rs | 48 +++++++++---------------------------------- 1 file changed, 10 insertions(+), 38 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 17d288370..27bf88b02 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -5,7 +5,6 @@ use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; use core::fmt; -use core::sync::atomic::{AtomicU64, Ordering}; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table @@ -90,32 +89,18 @@ impl fmt::Debug for SegmentSelector { /// // Add entry for TSS, call gdt.load() then update segment registers /// ``` -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct GlobalDescriptorTable { - table: [AtomicU64; 8], + table: [u64; 8], next_free: usize, } impl GlobalDescriptorTable { - #[inline] - const fn empty_table() -> [AtomicU64; 8] { - [ - AtomicU64::new(0), - AtomicU64::new(0), - AtomicU64::new(0), - AtomicU64::new(0), - AtomicU64::new(0), - AtomicU64::new(0), - AtomicU64::new(0), - AtomicU64::new(0), - ] - } - /// Creates an empty GDT. #[inline] pub const fn new() -> GlobalDescriptorTable { GlobalDescriptorTable { - table: Self::empty_table(), + table: [0; 8], next_free: 1, } } @@ -129,7 +114,7 @@ impl GlobalDescriptorTable { #[inline] pub const unsafe fn from_raw_slice(slice: &[u64]) -> GlobalDescriptorTable { let next_free = slice.len(); - let mut table = Self::empty_table(); + let mut table = [0; 8]; let mut idx = 0; const_assert!( @@ -138,7 +123,7 @@ impl GlobalDescriptorTable { ); while idx != next_free { - table[idx] = AtomicU64::new(slice[idx]); + table[idx] = slice[idx]; idx += 1; } @@ -147,9 +132,9 @@ impl GlobalDescriptorTable { /// Get a reference to the internal table. /// - /// The resulting slice may contain system descriptors, which span two `AtomicU64`s. + /// The resulting slice may contain system descriptors, which span two `u64`s. #[inline] - pub fn as_raw_slice(&self) -> &[AtomicU64] { + pub fn as_raw_slice(&self) -> &[u64] { &self.table[..self.next_free] } @@ -192,7 +177,7 @@ impl GlobalDescriptorTable { /// [set_cs](crate::instructions::segmentation::set_cs). #[cfg(feature = "instructions")] #[inline] - pub fn load(&'static self) { + pub fn load(&'static mut self) { // SAFETY: static lifetime ensures no modification after loading. unsafe { self.load_unsafe() }; } @@ -211,7 +196,7 @@ impl GlobalDescriptorTable { /// #[cfg(feature = "instructions")] #[inline] - pub unsafe fn load_unsafe(&self) { + pub unsafe fn load_unsafe(&mut self) { use crate::instructions::tables::lgdt; lgdt(&self.pointer()); } @@ -221,7 +206,7 @@ impl GlobalDescriptorTable { fn push(&mut self, value: u64) -> usize { if self.next_free < self.table.len() { let index = self.next_free; - self.table[index] = AtomicU64::new(value); + self.table[index] = value; self.next_free += 1; index } else { @@ -242,19 +227,6 @@ impl GlobalDescriptorTable { } } -impl Clone for GlobalDescriptorTable { - fn clone(&self) -> Self { - let mut table = Self::empty_table(); - for (dest, src) in table.iter_mut().zip(self.table.iter()) { - *dest = AtomicU64::new(src.load(Ordering::Relaxed)); - } - Self { - table, - next_free: self.next_free, - } - } -} - /// A 64-bit mode segment descriptor. /// /// Segmentation is no longer supported in 64-bit mode, so most of the descriptor From 597ff8597dbe3a78c4aa5cf75ad3fb47719e92a6 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 10 Nov 2021 13:57:23 +0100 Subject: [PATCH 16/64] add `Singleton` --- src/lib.rs | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index a7b0d69e6..d20861bf7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,9 @@ #![warn(missing_docs)] #![deny(missing_debug_implementations)] +use core::cell::UnsafeCell; +use core::sync::atomic::{AtomicBool, Ordering}; + pub use crate::addr::{align_down, align_up, PhysAddr, VirtAddr}; /// Makes a function const only when `feature = "const_fn"` is enabled. @@ -112,3 +115,60 @@ impl PrivilegeLevel { } } } + +/// A wrapper that can be used to safely create one mutable reference `&'static mut T` from a static variable. +/// +/// `Singleton` is safe because it ensures that it only ever gives out one reference. +/// +/// ``Singleton` is a safe alternative to `static mut` or a static `UnsafeCell`. +#[derive(Debug)] +pub struct Singleton { + used: AtomicBool, + value: UnsafeCell, +} + +impl Singleton { + /// Construct a new singleton. + pub const fn new(value: T) -> Self { + Self { + used: AtomicBool::new(false), + value: UnsafeCell::new(value), + } + } + + /// Try to acquire a mutable reference to the wrapped value. + /// This will only succeed the first time the function is + /// called and fail on all following calls. + /// + /// ``` + /// use x86_64::Singleton; + /// + /// static FOO: Singleton = Singleton::new(0); + /// + /// // Call `try_get_mut` for the first time and get a reference. + /// let first: &'static mut i32 = FOO.try_get_mut().unwrap(); + /// assert_eq!(first, &0); + /// + /// // Calling `try_get_mut` again will return `None`. + /// assert_eq!(FOO.try_get_mut(), None); + /// ``` + pub fn try_get_mut(&self) -> Option<&mut T> { + match self + .used + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + { + Ok(_) => Some(unsafe { + // SAFETY: no reference has been given out yet and we won't give out another. + &mut *self.value.get() + }), + Err(_) => None, + } + } +} + +// SAFETY: Sharing a `Singleton` between threads is safe regardless of whether `T` is `Sync` +// because we only expose the inner value once to one thread. +unsafe impl Sync for Singleton {} + +// SAFETY: It's safe to send a `Singleton` to another thread if it's safe to send `T`. +unsafe impl Send for Singleton {} From 96e091704d75fbdaec27a639da9897e7da932cdf Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 10 Nov 2021 14:10:56 +0100 Subject: [PATCH 17/64] fix test --- testing/src/gdt.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index 70f4df5cf..44f1d21a3 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -1,7 +1,7 @@ use lazy_static::lazy_static; use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; use x86_64::structures::tss::TaskStateSegment; -use x86_64::VirtAddr; +use x86_64::{Singleton, VirtAddr}; pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; @@ -18,12 +18,12 @@ lazy_static! { }; tss }; - static ref GDT: (GlobalDescriptorTable, Selectors) = { + static ref GDT: (Singleton, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); ( - gdt, + Singleton::new(gdt), Selectors { code_selector, tss_selector, @@ -41,7 +41,7 @@ pub fn init() { use x86_64::instructions::segmentation::{CS, Segment}; use x86_64::instructions::tables::load_tss; - GDT.0.load(); + GDT.0.try_get_mut().unwrap().load(); unsafe { CS::set_reg(GDT.1.code_selector); load_tss(GDT.1.tss_selector); From c49a3b38e6a983c9489a249e32c2b68836432375 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 10 Nov 2021 18:39:56 +0100 Subject: [PATCH 18/64] change `cpu_flags`'s type to `RFlags` --- Cargo.toml | 2 +- src/registers/rflags.rs | 3 ++- src/structures/idt.rs | 12 +++--------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c0f64c9f1..999c11f0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ edition = "2018" [dependencies] bit_field = "0.9.0" -bitflags = "1.0.4" +bitflags = "1.3.2" volatile = "0.4.4" [build-dependencies] diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index cb133bf3b..fff539a5e 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -6,7 +6,8 @@ pub use self::x86_64::*; use bitflags::bitflags; bitflags! { - /// The RFLAGS register. + /// The RFLAGS register. All bit patterns are valid representations for this type. + #[repr(transparent)] pub struct RFlags: u64 { /// Processor feature identification flag. /// diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 27cb7f64e..e82366040 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -20,6 +20,7 @@ //! //! These types are defined for the compatibility with the Nightly Rust build. +use crate::registers::rflags::RFlags; use crate::{PrivilegeLevel, VirtAddr}; use bit_field::BitField; use bitflags::bitflags; @@ -938,7 +939,7 @@ pub struct InterruptStackFrameValue { pub code_segment: SegmentSelector, _reserved1: [u8; 6], /// The flags register before the interrupt handler was invoked. - pub cpu_flags: u64, + pub cpu_flags: RFlags, /// The stack pointer at the time of the interrupt. pub stack_pointer: VirtAddr, /// The stack segment descriptor at the time of the interrupt (often zero in 64-bit mode). @@ -948,17 +949,10 @@ pub struct InterruptStackFrameValue { impl fmt::Debug for InterruptStackFrameValue { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - struct Hex(u64); - impl fmt::Debug for Hex { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:#x}", self.0) - } - } - let mut s = f.debug_struct("InterruptStackFrame"); s.field("instruction_pointer", &self.instruction_pointer); s.field("code_segment", &self.code_segment); - s.field("cpu_flags", &Hex(self.cpu_flags)); + s.field("cpu_flags", &self.cpu_flags); s.field("stack_pointer", &self.stack_pointer); s.field("stack_segment", &self.stack_segment); s.finish() From f677c78ed37b0bfb5401724d0020111a1ab5aaf8 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Nov 2021 18:02:21 +0100 Subject: [PATCH 19/64] use swap and fix ordering Co-authored-by: Philipp Oppermann --- src/lib.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d20861bf7..c115e9e60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -153,15 +153,14 @@ impl Singleton { /// assert_eq!(FOO.try_get_mut(), None); /// ``` pub fn try_get_mut(&self) -> Option<&mut T> { - match self - .used - .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) - { - Ok(_) => Some(unsafe { + let already_used = self.used.swap(true, Ordering::AcqRel); + if already_used { + None + } else { + Some(unsafe { // SAFETY: no reference has been given out yet and we won't give out another. &mut *self.value.get() - }), - Err(_) => None, + }) } } } From df498d8c2876ee4954d1f48b832d57d19d2e7379 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Nov 2021 18:07:18 +0100 Subject: [PATCH 20/64] change Singleton's name to SingleUseCell --- src/lib.rs | 22 +++++++++++----------- testing/src/gdt.rs | 8 ++++---- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c115e9e60..9129624f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,17 +118,17 @@ impl PrivilegeLevel { /// A wrapper that can be used to safely create one mutable reference `&'static mut T` from a static variable. /// -/// `Singleton` is safe because it ensures that it only ever gives out one reference. +/// `SingleUseCell` is safe because it ensures that it only ever gives out one reference. /// -/// ``Singleton` is a safe alternative to `static mut` or a static `UnsafeCell`. +/// ``SingleUseCell` is a safe alternative to `static mut` or a static `UnsafeCell`. #[derive(Debug)] -pub struct Singleton { +pub struct SingleUseCell { used: AtomicBool, value: UnsafeCell, } -impl Singleton { - /// Construct a new singleton. +impl SingleUseCell { + /// Construct a new SingleUseCell. pub const fn new(value: T) -> Self { Self { used: AtomicBool::new(false), @@ -141,9 +141,9 @@ impl Singleton { /// called and fail on all following calls. /// /// ``` - /// use x86_64::Singleton; + /// use x86_64::SingleUseCell; /// - /// static FOO: Singleton = Singleton::new(0); + /// static FOO: SingleUseCell = SingleUseCell::new(0); /// /// // Call `try_get_mut` for the first time and get a reference. /// let first: &'static mut i32 = FOO.try_get_mut().unwrap(); @@ -165,9 +165,9 @@ impl Singleton { } } -// SAFETY: Sharing a `Singleton` between threads is safe regardless of whether `T` is `Sync` +// SAFETY: Sharing a `SingleUseCell` between threads is safe regardless of whether `T` is `Sync` // because we only expose the inner value once to one thread. -unsafe impl Sync for Singleton {} +unsafe impl Sync for SingleUseCell {} -// SAFETY: It's safe to send a `Singleton` to another thread if it's safe to send `T`. -unsafe impl Send for Singleton {} +// SAFETY: It's safe to send a `SingleUseCell` to another thread if it's safe to send `T`. +unsafe impl Send for SingleUseCell {} diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index 44f1d21a3..d273631c1 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -1,7 +1,7 @@ use lazy_static::lazy_static; use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; use x86_64::structures::tss::TaskStateSegment; -use x86_64::{Singleton, VirtAddr}; +use x86_64::{SingleUseCell, VirtAddr}; pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; @@ -18,12 +18,12 @@ lazy_static! { }; tss }; - static ref GDT: (Singleton, Selectors) = { + static ref GDT: (SingleUseCell, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); ( - Singleton::new(gdt), + SingleUseCell::new(gdt), Selectors { code_selector, tss_selector, @@ -38,7 +38,7 @@ struct Selectors { } pub fn init() { - use x86_64::instructions::segmentation::{CS, Segment}; + use x86_64::instructions::segmentation::{Segment, CS}; use x86_64::instructions::tables::load_tss; GDT.0.try_get_mut().unwrap().load(); From 23cf4c5b3920e41d6decedfb722ab35f3c78060f Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Nov 2021 18:32:56 +0100 Subject: [PATCH 21/64] add `Send` bound for `SingleUseCell`'s `Sync` impl --- src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9129624f2..752ca3e55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,8 +166,9 @@ impl SingleUseCell { } // SAFETY: Sharing a `SingleUseCell` between threads is safe regardless of whether `T` is `Sync` -// because we only expose the inner value once to one thread. -unsafe impl Sync for SingleUseCell {} +// because we only expose the inner value once to one thread. The `T: Send` bound makes sure that +// sending a unique reference to another thread is safe. +unsafe impl Sync for SingleUseCell {} // SAFETY: It's safe to send a `SingleUseCell` to another thread if it's safe to send `T`. unsafe impl Send for SingleUseCell {} From 0aa7b932cea1053ffd43694812cffc09ca1e43a7 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Thu, 2 Dec 2021 22:02:16 +0100 Subject: [PATCH 22/64] add an immutable getter for the level 4 page table --- src/structures/paging/mapper/mapped_page_table.rs | 9 +++++++-- src/structures/paging/mapper/offset_page_table.rs | 9 +++++++-- src/structures/paging/mapper/recursive_page_table.rs | 9 +++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/structures/paging/mapper/mapped_page_table.rs b/src/structures/paging/mapper/mapped_page_table.rs index 7ae9e2c83..83878d5b8 100644 --- a/src/structures/paging/mapper/mapped_page_table.rs +++ b/src/structures/paging/mapper/mapped_page_table.rs @@ -37,9 +37,14 @@ impl<'a, P: PageTableFrameMapping> MappedPageTable<'a, P> { } } + /// Returns an immutable reference to the wrapped level 4 `PageTable` instance. + pub fn level_4_table(&self) -> &PageTable { + self.level_4_table + } + /// Returns a mutable reference to the wrapped level 4 `PageTable` instance. - pub fn level_4_table(&mut self) -> &mut PageTable { - &mut self.level_4_table + pub fn level_4_table_mut(&mut self) -> &mut PageTable { + self.level_4_table } /// Helper function for implementing Mapper. Safe to limit the scope of unsafe, see diff --git a/src/structures/paging/mapper/offset_page_table.rs b/src/structures/paging/mapper/offset_page_table.rs index 6cfaf42f6..a0b1d5eab 100644 --- a/src/structures/paging/mapper/offset_page_table.rs +++ b/src/structures/paging/mapper/offset_page_table.rs @@ -37,10 +37,15 @@ impl<'a> OffsetPageTable<'a> { } } - /// Returns a mutable reference to the wrapped level 4 `PageTable` instance. - pub fn level_4_table(&mut self) -> &mut PageTable { + /// Returns an immutable reference to the wrapped level 4 `PageTable` instance. + pub fn level_4_table(&self) -> &PageTable { self.inner.level_4_table() } + + /// Returns a mutable reference to the wrapped level 4 `PageTable` instance. + pub fn level_4_table_mut(&mut self) -> &mut PageTable { + self.inner.level_4_table_mut() + } } #[derive(Debug)] diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index b66aec176..bb6df2cfe 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -83,9 +83,14 @@ impl<'a> RecursivePageTable<'a> { } } + /// Returns an immutable reference to the wrapped level 4 `PageTable` instance. + pub fn level_4_table(&self) -> &PageTable { + self.p4 + } + /// Returns a mutable reference to the wrapped level 4 `PageTable` instance. - pub fn level_4_table(&mut self) -> &mut PageTable { - &mut self.p4 + pub fn level_4_table_mut(&mut self) -> &mut PageTable { + self.p4 } /// Internal helper function to create the page table of the next level if needed. From ea00bfd39f9df0307da4de18fe3428d9090c1e28 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 3 Feb 2022 12:14:33 +0100 Subject: [PATCH 23/64] Unsafe blocks are now required in `unsafe fn`s too (since #328) --- src/instructions/interrupts.rs | 4 +++- src/structures/idt.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index b47788dac..527f20a74 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -164,5 +164,7 @@ pub fn int3() { doc(cfg(any(feature = "nightly", feature = "inline_asm"))) )] pub unsafe fn software_interrupt() { - asm!("int {num}", num = const NUM, options(nomem, nostack)); + unsafe { + asm!("int {num}", num = const NUM, options(nomem, nostack)); + } } diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 10af345d4..531d36276 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -762,7 +762,7 @@ impl Entry { self.options = EntryOptions::minimal(); // SAFETY: The current CS is a valid, long-mode code segment. - self.options.set_code_selector(CS::get_reg()); + unsafe { self.options.set_code_selector(CS::get_reg()) }; self.options.set_present(true); &mut self.options } From 5efea6a6afac8dea1c8dfbc1efe7326142c15bcf Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 3 Feb 2022 12:15:33 +0100 Subject: [PATCH 24/64] Page table are now indexed with `u8` instead of `usize` Since #319 --- src/structures/idt.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 531d36276..d43f0090a 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1394,7 +1394,7 @@ macro_rules! set_general_handler_entry { extern "x86-interrupt" fn handler(frame: $crate::structures::idt::InterruptStackFrame) { $handler(frame, $idx.into(), None); } - $idt[$idx as usize].set_handler_fn(handler); + $idt[$idx].set_handler_fn(handler); }}; } @@ -1402,7 +1402,7 @@ macro_rules! set_general_handler_entry { mod test { use super::*; - fn entry_present(idt: &InterruptDescriptorTable, index: usize) -> bool { + fn entry_present(idt: &InterruptDescriptorTable, index: u8) -> bool { let options = match index { 8 => &idt.double_fault.options, 10 => &idt.invalid_tss.options, @@ -1413,13 +1413,13 @@ mod test { 15 => &idt.reserved_1.options, 17 => &idt.alignment_check.options, 18 => &idt.machine_check.options, - i @ 21..=28 => &idt.reserved_2[i - 21].options, + i @ 21..=28 => &idt.reserved_2[usize::from(i) - 21].options, 29 => &idt.vmm_communication_exception.options, 30 => &idt.security_exception.options, 31 => &idt.reserved_3.options, other => &idt[other].options, }; - options.0.get_bit(15) + options.bits.get_bit(15) } #[test] @@ -1446,7 +1446,7 @@ mod test { let mut idt = InterruptDescriptorTable::new(); set_general_handler!(&mut idt, general_handler, 0); - for i in 0..256 { + for i in 0..=255 { if i == 0 { assert!(entry_present(&idt, i)); } else { @@ -1454,7 +1454,7 @@ mod test { } } set_general_handler!(&mut idt, general_handler, 14); - for i in 0..256 { + for i in 0..=255 { if i == 0 || i == 14 { assert!(entry_present(&idt, i)); } else { @@ -1462,7 +1462,7 @@ mod test { } } set_general_handler!(&mut idt, general_handler, 32..64); - for i in 1..256 { + for i in 1..=255 { if i == 0 || i == 14 || (32..64).contains(&i) { assert!(entry_present(&idt, i), "{}", i); } else { @@ -1470,7 +1470,7 @@ mod test { } } set_general_handler!(&mut idt, general_handler); - for i in 0..256 { + for i in 0..=255 { if i == 15 || i == 31 || (21..=28).contains(&i) { // reserved entries should not be set assert!(!entry_present(&idt, i)); From d9eb7f6926c3a42bf4f5daeffac1870c32d668b5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 3 Feb 2022 12:16:37 +0100 Subject: [PATCH 25/64] Update test for new `InterruptStackFrame` format There were multiple changes to use proper field types. --- src/structures/idt.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index d43f0090a..be82359d3 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1496,15 +1496,15 @@ mod test { #[test] fn isr_frame_manipulation() { - let mut frame = InterruptStackFrame { - value: InterruptStackFrameValue { - instruction_pointer: VirtAddr::new(0x1000), - code_segment: 0, - cpu_flags: 0, - stack_pointer: VirtAddr::new(0x2000), - stack_segment: 0, - }, - }; + let mut frame = InterruptStackFrame(InterruptStackFrameValue { + instruction_pointer: VirtAddr::new(0x1000), + code_segment: SegmentSelector(0), + cpu_flags: RFlags::empty(), + stack_pointer: VirtAddr::new(0x2000), + stack_segment: SegmentSelector(0), + _reserved1: Default::default(), + _reserved2: Default::default(), + }); unsafe { frame From b820a2e98f797978569e77dced07e8827873f669 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Thu, 3 Feb 2022 12:17:27 +0100 Subject: [PATCH 26/64] The `level_4_table` function only returns an immutable reference since #327 --- src/structures/paging/mapper/recursive_page_table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/structures/paging/mapper/recursive_page_table.rs b/src/structures/paging/mapper/recursive_page_table.rs index b09d797e2..af8af5af2 100644 --- a/src/structures/paging/mapper/recursive_page_table.rs +++ b/src/structures/paging/mapper/recursive_page_table.rs @@ -913,7 +913,7 @@ impl<'a> CleanUp for RecursivePageTable<'a> { clean_up( self.recursive_index, - self.level_4_table(), + self.level_4_table_mut(), PageTableLevel::Four, range, frame_deallocator, From 11f64f6e5cd02a16a838e56c639419a90240f098 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 16 Jan 2022 15:04:23 +0100 Subject: [PATCH 27/64] make `Cr2::read` return a result --- src/registers/control.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/registers/control.rs b/src/registers/control.rs index bd2b06316..0393a3b16 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -160,7 +160,10 @@ bitflags! { #[cfg(feature = "instructions")] mod x86_64 { use super::*; - use crate::{instructions::tlb::Pcid, structures::paging::PhysFrame, PhysAddr, VirtAddr}; + use crate::{ + addr::VirtAddrNotValid, instructions::tlb::Pcid, structures::paging::PhysFrame, PhysAddr, + VirtAddr, + }; #[cfg(feature = "inline_asm")] use core::arch::asm; @@ -252,8 +255,8 @@ mod x86_64 { impl Cr2 { /// Read the current page fault linear address from the CR2 register. #[inline] - pub fn read() -> VirtAddr { - VirtAddr::new(Self::read_raw()) + pub fn read() -> Result { + VirtAddr::try_new(Self::read_raw()) } /// Read the current page fault linear address from the CR2 register as a raw `u64`. From 3de203e6307149246e3103957fb7224ddcb5e7d9 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sat, 5 Feb 2022 12:00:30 +0100 Subject: [PATCH 28/64] add errors section in doc comments Co-authored-by: Hiroki Tokunaga --- src/registers/control.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/registers/control.rs b/src/registers/control.rs index 0393a3b16..859230f2e 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -254,6 +254,10 @@ mod x86_64 { impl Cr2 { /// Read the current page fault linear address from the CR2 register. + /// + /// # Errors + /// + /// This method returns a [`VirtAddrNotValid`] error if the CR2 register contains a non-canonical address. Call [`Cr2::read_raw`] to handle such cases. #[inline] pub fn read() -> Result { VirtAddr::try_new(Self::read_raw()) From 5475d89b7a296effa1338c5c9748408f5172519a Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 6 Feb 2022 16:05:11 +0100 Subject: [PATCH 29/64] wrap comment --- src/registers/control.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/registers/control.rs b/src/registers/control.rs index 859230f2e..315612c81 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -257,7 +257,8 @@ mod x86_64 { /// /// # Errors /// - /// This method returns a [`VirtAddrNotValid`] error if the CR2 register contains a non-canonical address. Call [`Cr2::read_raw`] to handle such cases. + /// This method returns a [`VirtAddrNotValid`] error if the CR2 register contains a + /// non-canonical address. Call [`Cr2::read_raw`] to handle such cases. #[inline] pub fn read() -> Result { VirtAddr::try_new(Self::read_raw()) From 33194b6d7a7661a1d3f95714755f681705b52edd Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Fri, 25 Feb 2022 13:43:39 +0100 Subject: [PATCH 30/64] Merge remote-tracking branch 'origin/master' into next --- Cargo.toml | 14 +- Changelog.md | 5 + build.rs | 54 ----- src/addr.rs | 4 +- src/asm/asm.s | 352 ------------------------------- src/asm/mod.rs | 314 --------------------------- src/instructions/interrupts.rs | 33 ++- src/instructions/mod.rs | 18 -- src/instructions/port.rs | 58 +---- src/instructions/segmentation.rs | 35 --- src/instructions/tables.rs | 27 --- src/instructions/tlb.rs | 11 - src/lib.rs | 6 +- src/registers/control.rs | 39 ---- src/registers/mod.rs | 2 +- src/registers/model_specific.rs | 31 +-- src/registers/mxcsr.rs | 19 +- src/registers/rflags.rs | 12 -- src/registers/xcontrol.rs | 13 -- 19 files changed, 50 insertions(+), 997 deletions(-) delete mode 100644 build.rs delete mode 100644 src/asm/asm.s delete mode 100644 src/asm/mod.rs diff --git a/Cargo.toml b/Cargo.toml index e22d70a77..732cfac53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ license = "MIT/Apache-2.0" name = "x86_64" readme = "README.md" repository = "https://github.com/rust-osdev/x86_64" -version = "0.14.7" +version = "0.14.8" edition = "2018" [dependencies] @@ -30,19 +30,19 @@ bit_field = "0.10.1" bitflags = "1.3.2" volatile = "0.4.4" -[build-dependencies] -cc = { version = "1.0.37", optional = true } - [features] default = [ "nightly", "instructions" ] instructions = [] -external_asm = [ "cc" ] -nightly = [ "inline_asm", "const_fn", "abi_x86_interrupt", "doc_cfg" ] -inline_asm = [] +nightly = [ "const_fn", "abi_x86_interrupt", "asm_const", "doc_cfg" ] abi_x86_interrupt = [] const_fn = [] +asm_const = [] doc_cfg = [] +# These features are no longer used and only there for backwards compatibility. +external_asm = [] +inline_asm = [] + [package.metadata.release] no-dev-version = true pre-release-replacements = [ diff --git a/Changelog.md b/Changelog.md index 8f57b26b2..a64977e74 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,10 @@ # Unreleased +# 0.14.8 – 2022-02-03 + +- Add `Cr2::read_raw` ([#334](https://github.com/rust-osdev/x86_64/pull/334)) +- Add support for `MXCSR` register ([#336](https://github.com/rust-osdev/x86_64/pull/336)) + # 0.14.7 – 2021-12-18 - fix: build error on the latest nightly ([#329](https://github.com/rust-osdev/x86_64/pull/329)) diff --git a/build.rs b/build.rs deleted file mode 100644 index 21c783bad..000000000 --- a/build.rs +++ /dev/null @@ -1,54 +0,0 @@ -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - #[cfg(all(feature = "external_asm", windows))] - compile_error!("\"external_asm\" feature is not available on windows toolchain!"); - - #[cfg(feature = "instructions")] - if std::env::var("CARGO_CFG_TARGET_ARCH").unwrap() != "x86_64" { - panic!("\"instructions\" feature is only available for x86_64 targets!"); - } - - #[cfg(all( - feature = "instructions", - not(feature = "inline_asm"), - not(feature = "external_asm") - ))] - compile_error!("\"instructions\" feature is enabled, but neither feature \"external_asm\" nor \"inline_asm\" was set!"); - - #[cfg(all(feature = "inline_asm", feature = "external_asm"))] - compile_error!( - "\"inline_asm\" and \"external_asm\" features can not be enabled at the same time!" - ); - - #[cfg(all(feature = "instructions", feature = "external_asm"))] - { - use std::ffi::OsString; - use std::fs; - - let entries = fs::read_dir("src/asm") - .unwrap() - .filter_map(|f| { - f.ok().and_then(|e| { - let path = e.path(); - match path.extension() { - Some(ext) if ext.eq(&OsString::from("s")) => Some(path), - _ => None, - } - }) - }) - .collect::>(); - - cc::Build::new() - .no_default_flags(true) - .files(&entries) - .pic(true) - .static_flag(true) - .shared_flag(false) - .compile("x86_64_asm"); - - for e in entries { - println!("cargo:rerun-if-changed={}", e.to_str().unwrap()); - } - } -} diff --git a/src/addr.rs b/src/addr.rs index b71d7b63d..74f677453 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -41,7 +41,7 @@ pub struct PhysAddr(u64); /// overwritten possibly meaningful bits. This likely indicates a bug, for example an invalid /// address calculation. #[derive(Debug)] -pub struct VirtAddrNotValid(u64); +pub struct VirtAddrNotValid(pub u64); impl VirtAddr { /// Creates a new canonical virtual address. @@ -326,7 +326,7 @@ impl Sub for VirtAddr { /// /// This means that bits 52 to 64 were not all null. #[derive(Debug)] -pub struct PhysAddrNotValid(u64); +pub struct PhysAddrNotValid(pub u64); impl PhysAddr { /// Creates a new physical address. diff --git a/src/asm/asm.s b/src/asm/asm.s deleted file mode 100644 index 831828a82..000000000 --- a/src/asm/asm.s +++ /dev/null @@ -1,352 +0,0 @@ -.text -.code64 - -# REMEMBER: This code uses the AMD64 calling convention: -# Arguments: RDI, RSI, RDX, RCX -# Return: RAX - -.global _x86_64_asm_interrupt_enable -.p2align 4 -_x86_64_asm_interrupt_enable: - sti - retq - -.global _x86_64_asm_interrupt_disable -.p2align 4 -_x86_64_asm_interrupt_disable: - cli - retq - -.global _x86_64_asm_interrupt_enable_and_hlt -.p2align 4 -_x86_64_asm_interrupt_enable_and_hlt: - sti - hlt - retq - -.global _x86_64_asm_int3 -.p2align 4 -_x86_64_asm_int3: - int3 - retq - -.global _x86_64_asm_read_from_port_u8 -.p2align 4 -_x86_64_asm_read_from_port_u8: - mov %edi, %edx - inb (%dx), %al - retq - -.global _x86_64_asm_read_from_port_u16 -.p2align 4 -_x86_64_asm_read_from_port_u16: - mov %edi, %edx - inw (%dx), %ax - retq - -.global _x86_64_asm_read_from_port_u32 -.p2align 4 -_x86_64_asm_read_from_port_u32: - mov %edi, %edx - inl (%dx), %eax - retq - - -.global _x86_64_asm_write_to_port_u8 -.p2align 4 -_x86_64_asm_write_to_port_u8: - mov %edi, %edx - mov %si, %ax - outb %al, (%dx) - retq - -.global _x86_64_asm_write_to_port_u16 -.p2align 4 -_x86_64_asm_write_to_port_u16: - mov %edi, %edx - mov %si, %ax - outw %ax, (%dx) - retq - -.global _x86_64_asm_write_to_port_u32 -.p2align 4 -_x86_64_asm_write_to_port_u32: - mov %edi, %edx - mov %esi, %eax - outl %eax, (%dx) - retq - -.global _x86_64_asm_set_cs -.p2align 4 -_x86_64_asm_set_cs: - pushq %rdi - leaq 1f(%rip), %rax - pushq %rax - lretq -1: - retq - -.global _x86_64_asm_get_cs -.p2align 4 -_x86_64_asm_get_cs: - mov %cs, %ax - retq - -.global _x86_64_asm_invlpg -.p2align 4 -_x86_64_asm_invlpg: - invlpg (%rdi) - retq - -.global _x86_64_asm_invpcid -.p2align 4 -_x86_64_asm_invpcid: - invpcid (%rsi), %rdi - retq - -.global _x86_64_asm_ltr -.p2align 4 -_x86_64_asm_ltr: - mov %edi, %edx - ltr %dx - retq - -.global _x86_64_asm_lgdt -.p2align 4 -_x86_64_asm_lgdt: - lgdt (%rdi) - retq - -.global _x86_64_asm_lidt -.p2align 4 -_x86_64_asm_lidt: - lidt (%rdi) - retq - -.global _x86_64_asm_sgdt -.p2align 4 -_x86_64_asm_sgdt: - sgdt (%rdi) - retq - -.global _x86_64_asm_sidt -.p2align 4 -_x86_64_asm_sidt: - sidt (%rdi) - retq - -.global _x86_64_asm_write_rflags -.p2align 4 -_x86_64_asm_write_rflags: - pushq %rdi - popfq - retq - -.global _x86_64_asm_read_rflags -.p2align 4 -_x86_64_asm_read_rflags: - pushfq - popq %rax - retq - -.global _x86_64_asm_load_ss -.p2align 4 -_x86_64_asm_load_ss: - mov %di, %ss - retq - -.global _x86_64_asm_load_ds -.p2align 4 -_x86_64_asm_load_ds: - mov %di, %ds - retq - -.global _x86_64_asm_load_es -.p2align 4 -_x86_64_asm_load_es: - mov %di, %es - retq - -.global _x86_64_asm_load_fs -.p2align 4 -_x86_64_asm_load_fs: - mov %di, %fs - retq - -.global _x86_64_asm_load_gs -.p2align 4 -_x86_64_asm_load_gs: - mov %di, %gs - retq - -.global _x86_64_asm_get_ss -.p2align 4 -_x86_64_asm_get_ss: - mov %ss, %ax - retq - -.global _x86_64_asm_get_ds -.p2align 4 -_x86_64_asm_get_ds: - mov %ds, %ax - retq - -.global _x86_64_asm_get_es -.p2align 4 -_x86_64_asm_get_es: - mov %es, %ax - retq - -.global _x86_64_asm_get_fs -.p2align 4 -_x86_64_asm_get_fs: - mov %fs, %ax - retq - -.global _x86_64_asm_get_gs -.p2align 4 -_x86_64_asm_get_gs: - mov %gs, %ax - retq - -.global _x86_64_asm_swapgs -.p2align 4 -_x86_64_asm_swapgs: - swapgs - retq - -.global _x86_64_asm_read_cr0 -.p2align 4 -_x86_64_asm_read_cr0: - movq %cr0, %rax - retq - -.global _x86_64_asm_read_cr2 -.p2align 4 -_x86_64_asm_read_cr2: - movq %cr2, %rax - retq - -.global _x86_64_asm_read_cr3 -.p2align 4 -_x86_64_asm_read_cr3: - movq %cr3, %rax - retq - -.global _x86_64_asm_read_cr4 -.p2align 4 -_x86_64_asm_read_cr4: - movq %cr4, %rax - retq - -.global _x86_64_asm_write_cr0 -.p2align 4 -_x86_64_asm_write_cr0: - movq %rdi, %cr0 - retq - -.global _x86_64_asm_write_cr3 -.p2align 4 -_x86_64_asm_write_cr3: - movq %rdi, %cr3 - retq - -.global _x86_64_asm_write_cr4 -.p2align 4 -_x86_64_asm_write_cr4: - movq %rdi, %cr4 - retq - -.global _x86_64_asm_rdmsr -.p2align 4 -_x86_64_asm_rdmsr: - mov %edi, %ecx # First param is the MSR number - rdmsr - shl $32, %rdx # shift edx to upper 32bit - mov %eax, %eax # clear upper 32bit of rax - or %rdx, %rax # or with rdx - retq - -.global _x86_64_asm_wrmsr -.p2align 4 -_x86_64_asm_wrmsr: - movl %edi, %ecx # First param is the MSR number - movl %esi, %eax # Second param is the low 32-bits - wrmsr # Third param (high 32-bits) is already in %edx - retq - -.global _x86_64_asm_hlt -.p2align 4 -_x86_64_asm_hlt: - hlt - retq - -.global _x86_64_asm_nop -.p2align 4 -_x86_64_asm_nop: - nop - retq - -.global _x86_64_asm_bochs -.p2align 4 -_x86_64_asm_bochs: - xchgw %bx, %bx - retq - -.global _x86_64_asm_rdfsbase -.p2align 4 -_x86_64_asm_rdfsbase: - rdfsbase %rax - retq - -.global _x86_64_asm_wrfsbase -.p2align 4 -_x86_64_asm_wrfsbase: - wrfsbase %rdi - retq - -.global _x86_64_asm_rdgsbase -.p2align 4 -_x86_64_asm_rdgsbase: - rdgsbase %rax - retq - -.global _x86_64_asm_wrgsbase -.p2align 4 -_x86_64_asm_wrgsbase: - wrgsbase %rdi - retq - -.global _x86_64_asm_xgetbv -.p2align 4 -_x86_64_asm_xgetbv: - mov %edi, %ecx # First param is the XCR number - xgetbv - shl $32, %rdx # shift edx to upper 32bit - mov %eax, %eax # clear upper 32bit of rax - or %rdx, %rax # or with rdx - retq - -.global _x86_64_asm_xsetbv -.p2align 4 -_x86_64_asm_xsetbv: - movl %edi, %ecx # First param is the XCR number - movl %esi, %eax # Second param is the low 32-bits - xsetbv # Third param (high 32-bits) is already in %edx - retq - -.global _x86_64_asm_write_mxcsr -.p2align 4 -_x86_64_asm_write_mxcsr: - pushq %rdi - ldmxcsr (%rsp) - popq %rdi - retq - -.global _x86_64_asm_read_mxcsr -.p2align 4 -_x86_64_asm_read_mxcsr: - pushq $0 - stmxcsr (%rsp) - popq %rax - retq diff --git a/src/asm/mod.rs b/src/asm/mod.rs deleted file mode 100644 index 6d481dad9..000000000 --- a/src/asm/mod.rs +++ /dev/null @@ -1,314 +0,0 @@ -#[link(name = "x86_64_asm", kind = "static")] -extern "sysv64" { - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_interrupt_enable" - )] - pub(crate) fn x86_64_asm_interrupt_enable(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_interrupt_disable" - )] - pub(crate) fn x86_64_asm_interrupt_disable(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_interrupt_enable_and_hlt" - )] - pub(crate) fn x86_64_asm_interrupt_enable_and_hlt(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_int3" - )] - pub(crate) fn x86_64_asm_int3(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_hlt" - )] - pub(crate) fn x86_64_asm_hlt(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_nop" - )] - pub(crate) fn x86_64_asm_nop(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_bochs" - )] - pub(crate) fn x86_64_asm_bochs(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_from_port_u8" - )] - pub(crate) fn x86_64_asm_read_from_port_u8(port: u16) -> u8; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_from_port_u16" - )] - pub(crate) fn x86_64_asm_read_from_port_u16(port: u16) -> u16; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_from_port_u32" - )] - pub(crate) fn x86_64_asm_read_from_port_u32(port: u16) -> u32; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_to_port_u8" - )] - pub(crate) fn x86_64_asm_write_to_port_u8(port: u16, value: u8); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_to_port_u16" - )] - pub(crate) fn x86_64_asm_write_to_port_u16(port: u16, value: u16); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_to_port_u32" - )] - pub(crate) fn x86_64_asm_write_to_port_u32(port: u16, value: u32); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_set_cs" - )] - pub(crate) fn x86_64_asm_set_cs(sel: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_load_ss" - )] - pub(crate) fn x86_64_asm_load_ss(sel: u16); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_load_ds" - )] - pub(crate) fn x86_64_asm_load_ds(sel: u16); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_load_es" - )] - pub(crate) fn x86_64_asm_load_es(sel: u16); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_load_fs" - )] - pub(crate) fn x86_64_asm_load_fs(sel: u16); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_load_gs" - )] - pub(crate) fn x86_64_asm_load_gs(sel: u16); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_get_ss" - )] - pub(crate) fn x86_64_asm_get_ss() -> u16; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_get_ds" - )] - pub(crate) fn x86_64_asm_get_ds() -> u16; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_get_es" - )] - pub(crate) fn x86_64_asm_get_es() -> u16; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_get_fs" - )] - pub(crate) fn x86_64_asm_get_fs() -> u16; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_get_gs" - )] - pub(crate) fn x86_64_asm_get_gs() -> u16; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_swapgs" - )] - pub(crate) fn x86_64_asm_swapgs(); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_get_cs" - )] - pub(crate) fn x86_64_asm_get_cs() -> u16; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_lgdt" - )] - pub(crate) fn x86_64_asm_lgdt(gdt: *const crate::instructions::tables::DescriptorTablePointer); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_lidt" - )] - pub(crate) fn x86_64_asm_lidt(idt: *const crate::instructions::tables::DescriptorTablePointer); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_sgdt" - )] - pub(crate) fn x86_64_asm_sgdt(gdt: *mut crate::instructions::tables::DescriptorTablePointer); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_sidt" - )] - pub(crate) fn x86_64_asm_sidt(idt: *mut crate::instructions::tables::DescriptorTablePointer); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_ltr" - )] - pub(crate) fn x86_64_asm_ltr(sel: u16); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_invlpg" - )] - pub(crate) fn x86_64_asm_invlpg(addr: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_invpcid" - )] - pub(crate) fn x86_64_asm_invpcid(kind: u64, desc: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_cr0" - )] - pub(crate) fn x86_64_asm_read_cr0() -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_cr0" - )] - pub(crate) fn x86_64_asm_write_cr0(value: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_cr2" - )] - pub(crate) fn x86_64_asm_read_cr2() -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_cr3" - )] - pub(crate) fn x86_64_asm_read_cr3() -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_cr3" - )] - pub(crate) fn x86_64_asm_write_cr3(value: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_cr4" - )] - pub(crate) fn x86_64_asm_read_cr4() -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_cr4" - )] - pub(crate) fn x86_64_asm_write_cr4(value: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_rdmsr" - )] - pub(crate) fn x86_64_asm_rdmsr(msr: u32) -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_wrmsr" - )] - pub(crate) fn x86_64_asm_wrmsr(msr: u32, low: u32, high: u32); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_rflags" - )] - pub(crate) fn x86_64_asm_read_rflags() -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_rflags" - )] - pub(crate) fn x86_64_asm_write_rflags(val: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_rdfsbase" - )] - pub(crate) fn x86_64_asm_rdfsbase() -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_wrfsbase" - )] - pub(crate) fn x86_64_asm_wrfsbase(val: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_rdgsbase" - )] - pub(crate) fn x86_64_asm_rdgsbase() -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_wrgsbase" - )] - pub(crate) fn x86_64_asm_wrgsbase(val: u64); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_xgetbv" - )] - pub(crate) fn x86_64_asm_xgetbv(xcr: u32) -> u64; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_xsetbv" - )] - pub(crate) fn x86_64_asm_xsetbv(xcr: u32, low: u32, high: u32); - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_read_mxcsr" - )] - pub(crate) fn x86_64_asm_read_mxcsr() -> u32; - - #[cfg_attr( - any(target_env = "gnu", target_env = "musl"), - link_name = "_x86_64_asm_write_mxcsr" - )] - pub(crate) fn x86_64_asm_write_mxcsr(val: u32); -} diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index 527f20a74..a1e526d7c 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -1,6 +1,5 @@ //! Enabling and disabling interrupts -#[cfg(feature = "inline_asm")] use core::arch::asm; /// Returns whether interrupts are enabled. @@ -17,11 +16,7 @@ pub fn are_enabled() -> bool { #[inline] pub fn enable() { unsafe { - #[cfg(feature = "inline_asm")] asm!("sti", options(nomem, nostack)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_interrupt_enable(); } } @@ -31,11 +26,7 @@ pub fn enable() { #[inline] pub fn disable() { unsafe { - #[cfg(feature = "inline_asm")] asm!("cli", options(nomem, nostack)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_interrupt_disable(); } } @@ -130,11 +121,7 @@ where #[inline] pub fn enable_and_hlt() { unsafe { - #[cfg(feature = "inline_asm")] asm!("sti; hlt", options(nomem, nostack)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_interrupt_enable_and_hlt(); } } @@ -142,14 +129,22 @@ pub fn enable_and_hlt() { #[inline] pub fn int3() { unsafe { - #[cfg(feature = "inline_asm")] asm!("int3", options(nomem, nostack)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_int3(); } } +/// Generate a software interrupt by invoking the `int` instruction. +/// +/// This currently needs to be a macro because the `int` argument needs to be an +/// immediate. This macro will be replaced by a generic function when support for +/// const generics is implemented in Rust. +#[macro_export] +macro_rules! software_interrupt { + ($x:expr) => {{ + asm!("int {id}", id = const $x, options(nomem, nostack)); + }}; +} + /// Generate a software interrupt by invoking the `int` instruction. /// /// ## Safety @@ -158,10 +153,10 @@ pub fn int3() { /// crash if you invoke a double-fault (#8) or machine-check (#18) exception. /// It can also cause memory/register corruption depending on the interrupt /// implementation (if it expects values/pointers to be passed in registers). -#[cfg(feature = "inline_asm")] +#[cfg(feature = "asm_const")] #[cfg_attr( feature = "doc_cfg", - doc(cfg(any(feature = "nightly", feature = "inline_asm"))) + doc(cfg(any(feature = "nightly", feature = "asm_const"))) )] pub unsafe fn software_interrupt() { unsafe { diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index bd35f2a82..984c13284 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -9,18 +9,13 @@ pub mod segmentation; pub mod tables; pub mod tlb; -#[cfg(feature = "inline_asm")] use core::arch::asm; /// Halts the CPU until the next interrupt arrives. #[inline] pub fn hlt() { unsafe { - #[cfg(feature = "inline_asm")] asm!("hlt", options(nomem, nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_hlt(); } } @@ -33,11 +28,7 @@ pub fn hlt() { #[inline] pub fn nop() { unsafe { - #[cfg(feature = "inline_asm")] asm!("nop", options(nomem, nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_nop(); } } @@ -46,21 +37,12 @@ pub fn nop() { #[inline] pub fn bochs_breakpoint() { unsafe { - #[cfg(feature = "inline_asm")] asm!("xchg bx, bx", options(nomem, nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_bochs(); } } /// Gets the current instruction pointer. Note that this is only approximate as it requires a few /// instructions to execute. -#[cfg(feature = "inline_asm")] -#[cfg_attr( - feature = "doc_cfg", - doc(cfg(any(feature = "nightly", feature = "inline_asm"))) -)] #[inline(always)] pub fn read_rip() -> crate::VirtAddr { let rip: u64; diff --git a/src/instructions/port.rs b/src/instructions/port.rs index b23be4401..0263d0e0e 100644 --- a/src/instructions/port.rs +++ b/src/instructions/port.rs @@ -1,6 +1,5 @@ //! Access to I/O ports -#[cfg(feature = "inline_asm")] use core::arch::asm; use core::fmt; use core::marker::PhantomData; @@ -10,99 +9,60 @@ pub use crate::structures::port::{PortRead, PortWrite}; impl PortRead for u8 { #[inline] unsafe fn read_from_port(port: u16) -> u8 { - #[cfg(feature = "inline_asm")] - { - let value: u8; - unsafe { - asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags)); - } - value - } - #[cfg(not(feature = "inline_asm"))] + let value: u8; unsafe { - crate::asm::x86_64_asm_read_from_port_u8(port) + asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags)); } + value } } impl PortRead for u16 { #[inline] unsafe fn read_from_port(port: u16) -> u16 { - #[cfg(feature = "inline_asm")] - { - let value: u16; - unsafe { - asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack, preserves_flags)); - } - value - } - #[cfg(not(feature = "inline_asm"))] + let value: u16; unsafe { - crate::asm::x86_64_asm_read_from_port_u16(port) + asm!("in ax, dx", out("ax") value, in("dx") port, options(nomem, nostack, preserves_flags)); } + value } } impl PortRead for u32 { #[inline] unsafe fn read_from_port(port: u16) -> u32 { - #[cfg(feature = "inline_asm")] - { - let value: u32; - unsafe { - asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack, preserves_flags)); - } - value - } - #[cfg(not(feature = "inline_asm"))] + let value: u32; unsafe { - crate::asm::x86_64_asm_read_from_port_u32(port) + asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack, preserves_flags)); } + value } } impl PortWrite for u8 { #[inline] unsafe fn write_to_port(port: u16, value: u8) { - #[cfg(feature = "inline_asm")] unsafe { asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_to_port_u8(port, value); - } } } impl PortWrite for u16 { #[inline] unsafe fn write_to_port(port: u16, value: u16) { - #[cfg(feature = "inline_asm")] unsafe { asm!("out dx, ax", in("dx") port, in("ax") value, options(nomem, nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_to_port_u16(port, value); - } } } impl PortWrite for u32 { #[inline] unsafe fn write_to_port(port: u16, value: u32) { - #[cfg(feature = "inline_asm")] unsafe { asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_to_port_u32(port, value); - } } } diff --git a/src/instructions/segmentation.rs b/src/instructions/segmentation.rs index fda49eec7..334c42237 100644 --- a/src/instructions/segmentation.rs +++ b/src/instructions/segmentation.rs @@ -6,21 +6,15 @@ use crate::{ structures::gdt::SegmentSelector, VirtAddr, }; -#[cfg(feature = "inline_asm")] use core::arch::asm; macro_rules! get_reg_impl { ($name:literal, $asm_get:ident) => { fn get_reg() -> SegmentSelector { let segment: u16; - #[cfg(feature = "inline_asm")] unsafe { asm!(concat!("mov {0:x}, ", $name), out(reg) segment, options(nomem, nostack, preserves_flags)); } - #[cfg(not(feature = "inline_asm"))] - unsafe { - segment = crate::asm::$asm_get(); - } SegmentSelector(segment) } }; @@ -32,15 +26,9 @@ macro_rules! segment_impl { get_reg_impl!($name, $asm_get); unsafe fn set_reg(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] unsafe { asm!(concat!("mov ", $name, ", {0:x}"), in(reg) sel.0, options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe{ - crate::asm::$asm_load(sel.0); - } } } }; @@ -51,28 +39,17 @@ macro_rules! segment64_impl { impl Segment64 for $type { const BASE: Msr = <$base>::MSR; fn read_base() -> VirtAddr { - #[cfg(feature = "inline_asm")] unsafe { let val: u64; asm!(concat!("rd", $name, "base {}"), out(reg) val, options(nomem, nostack, preserves_flags)); VirtAddr::new_unsafe(val) } - #[cfg(not(feature = "inline_asm"))] - unsafe { - VirtAddr::new_unsafe(crate::asm::$asm_rd()) - } } unsafe fn write_base(base: VirtAddr) { - #[cfg(feature = "inline_asm")] unsafe{ asm!(concat!("wr", $name, "base {}"), in(reg) base.as_u64(), options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe{ - crate::asm::$asm_wr(base.as_u64()); - } } } }; @@ -90,7 +67,6 @@ impl Segment for CS { /// would only be able to jump to 32-bit instruction pointers. Only Intel implements support /// for 64-bit far calls/jumps in long-mode, AMD does not. unsafe fn set_reg(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] unsafe { asm!( "push {sel}", @@ -103,11 +79,6 @@ impl Segment for CS { options(preserves_flags), ); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_set_cs(u64::from(sel.0)); - } } } @@ -127,15 +98,9 @@ impl GS { /// This function is unsafe because the caller must ensure that the /// swap operation cannot lead to undefined behavior. pub unsafe fn swap() { - #[cfg(feature = "inline_asm")] unsafe { asm!("swapgs", options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_swapgs(); - } } } diff --git a/src/instructions/tables.rs b/src/instructions/tables.rs index 1b89d22f8..4eb3b1af5 100644 --- a/src/instructions/tables.rs +++ b/src/instructions/tables.rs @@ -2,7 +2,6 @@ use crate::structures::gdt::SegmentSelector; use crate::VirtAddr; -#[cfg(feature = "inline_asm")] use core::arch::asm; pub use crate::structures::DescriptorTablePointer; @@ -20,15 +19,9 @@ pub use crate::structures::DescriptorTablePointer; /// GDT is safe. #[inline] pub unsafe fn lgdt(gdt: &DescriptorTablePointer) { - #[cfg(feature = "inline_asm")] unsafe { asm!("lgdt [{}]", in(reg) gdt, options(readonly, nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_lgdt(gdt as *const _); - } } /// Load an IDT. @@ -44,15 +37,9 @@ pub unsafe fn lgdt(gdt: &DescriptorTablePointer) { /// IDT is safe. #[inline] pub unsafe fn lidt(idt: &DescriptorTablePointer) { - #[cfg(feature = "inline_asm")] unsafe { asm!("lidt [{}]", in(reg) idt, options(readonly, nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_lidt(idt as *const _); - } } /// Get the address of the current GDT. @@ -63,11 +50,7 @@ pub fn sgdt() -> DescriptorTablePointer { base: VirtAddr::new(0), }; unsafe { - #[cfg(feature = "inline_asm")] asm!("sgdt [{}]", in(reg) &mut gdt, options(nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_sgdt(&mut gdt as *mut _); } gdt } @@ -80,11 +63,7 @@ pub fn sidt() -> DescriptorTablePointer { base: VirtAddr::new(0), }; unsafe { - #[cfg(feature = "inline_asm")] asm!("sidt [{}]", in(reg) &mut idt, options(nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_sidt(&mut idt as *mut _); } idt } @@ -101,13 +80,7 @@ pub fn sidt() -> DescriptorTablePointer { /// this TSS is safe. #[inline] pub unsafe fn load_tss(sel: SegmentSelector) { - #[cfg(feature = "inline_asm")] unsafe { asm!("ltr {0:x}", in(reg) sel.0, options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_ltr(sel.0); - } } diff --git a/src/instructions/tlb.rs b/src/instructions/tlb.rs index 27814072b..9ee79e18a 100644 --- a/src/instructions/tlb.rs +++ b/src/instructions/tlb.rs @@ -3,18 +3,13 @@ use core::fmt; use crate::VirtAddr; -#[cfg(feature = "inline_asm")] use core::arch::asm; /// Invalidate the given address in the TLB using the `invlpg` instruction. #[inline] pub fn flush(addr: VirtAddr) { unsafe { - #[cfg(feature = "inline_asm")] asm!("invlpg [{}]", in(reg) addr.as_u64(), options(nostack, preserves_flags)); - - #[cfg(not(feature = "inline_asm"))] - crate::asm::x86_64_asm_invlpg(addr.as_u64()); } } @@ -112,13 +107,7 @@ pub unsafe fn flush_pcid(command: InvPicdCommand) { InvPicdCommand::AllExceptGlobal => kind = 3, } - #[cfg(feature = "inline_asm")] unsafe { asm!("invpcid {0}, [{1}]", in(reg) kind, in(reg) &desc, options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_invpcid(kind, &desc as *const _ as u64); - } } diff --git a/src/lib.rs b/src/lib.rs index 0ee7b0b80..2f14df068 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,8 +5,7 @@ #![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT add_entry() #![cfg_attr(feature = "const_fn", feature(const_fn_fn_ptr_basics))] // IDT new() #![cfg_attr(feature = "const_fn", feature(const_fn_trait_bound))] // PageSize marker trait -#![cfg_attr(feature = "inline_asm", feature(asm))] -#![cfg_attr(feature = "inline_asm", feature(asm_const))] // software_interrupt +#![cfg_attr(feature = "asm_const", feature(asm_const))] #![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))] #![cfg_attr(feature = "doc_cfg", feature(doc_cfg))] #![warn(missing_docs)] @@ -49,9 +48,6 @@ macro_rules! const_fn { }; } -#[cfg(all(feature = "instructions", feature = "external_asm"))] -pub(crate) mod asm; - pub mod addr; pub mod instructions; pub mod registers; diff --git a/src/registers/control.rs b/src/registers/control.rs index 315612c81..791671129 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -164,7 +164,6 @@ mod x86_64 { addr::VirtAddrNotValid, instructions::tlb::Pcid, structures::paging::PhysFrame, PhysAddr, VirtAddr, }; - #[cfg(feature = "inline_asm")] use core::arch::asm; impl Cr0 { @@ -179,14 +178,9 @@ mod x86_64 { pub fn read_raw() -> u64 { let value: u64; - #[cfg(feature = "inline_asm")] unsafe { asm!("mov {}, cr0", out(reg) value, options(nomem, nostack, preserves_flags)); } - #[cfg(not(feature = "inline_asm"))] - unsafe { - value = crate::asm::x86_64_asm_read_cr0(); - } value } @@ -220,15 +214,9 @@ mod x86_64 { /// safety through it, e.g. by disabling paging. #[inline] pub unsafe fn write_raw(value: u64) { - #[cfg(feature = "inline_asm")] unsafe { asm!("mov cr0, {}", in(reg) value, options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_cr0(value); - } } /// Updates CR0 flags. @@ -269,14 +257,9 @@ mod x86_64 { pub fn read_raw() -> u64 { let value: u64; - #[cfg(feature = "inline_asm")] unsafe { asm!("mov {}, cr2", out(reg) value, options(nomem, nostack, preserves_flags)); } - #[cfg(not(feature = "inline_asm"))] - unsafe { - value = crate::asm::x86_64_asm_read_cr2(); - } value } @@ -296,14 +279,9 @@ mod x86_64 { pub fn read_raw() -> (PhysFrame, u16) { let value: u64; - #[cfg(feature = "inline_asm")] unsafe { asm!("mov {}, cr3", out(reg) value, options(nomem, nostack, preserves_flags)); } - #[cfg(not(feature = "inline_asm"))] - unsafe { - value = crate::asm::x86_64_asm_read_cr3(); - } let addr = PhysAddr::new(value & 0x_000f_ffff_ffff_f000); let frame = PhysFrame::containing_address(addr); @@ -357,15 +335,9 @@ mod x86_64 { let addr = frame.start_address(); let value = addr.as_u64() | val as u64; - #[cfg(feature = "inline_asm")] unsafe { asm!("mov cr3, {}", in(reg) value, options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_cr3(value) - } } } @@ -381,14 +353,9 @@ mod x86_64 { pub fn read_raw() -> u64 { let value: u64; - #[cfg(feature = "inline_asm")] unsafe { asm!("mov {}, cr4", out(reg) value, options(nomem, nostack, preserves_flags)); } - #[cfg(not(feature = "inline_asm"))] - unsafe { - value = crate::asm::x86_64_asm_read_cr4(); - } value } @@ -424,15 +391,9 @@ mod x86_64 { /// flag. #[inline] pub unsafe fn write_raw(value: u64) { - #[cfg(feature = "inline_asm")] unsafe { asm!("mov cr4, {}", in(reg) value, options(nostack, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_cr4(value); - } } /// Updates CR4 flags. diff --git a/src/registers/mod.rs b/src/registers/mod.rs index 68ab57081..dfde2439a 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -11,5 +11,5 @@ pub mod xcontrol; #[allow(deprecated)] pub use crate::instructions::segmentation::{rdfsbase, rdgsbase, wrfsbase, wrgsbase}; -#[cfg(all(feature = "instructions", feature = "inline_asm"))] +#[cfg(feature = "instructions")] pub use crate::instructions::read_rip; diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 099d2f471..503bd57f0 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -128,7 +128,6 @@ mod x86_64 { control::Cr4Flags, segmentation::{Segment, Segment64, CS, SS}, }; - #[cfg(feature = "inline_asm")] use core::arch::asm; impl Msr { @@ -140,24 +139,16 @@ mod x86_64 { /// effects. #[inline] pub unsafe fn read(&self) -> u64 { - #[cfg(feature = "inline_asm")] - { - let (high, low): (u32, u32); - unsafe { - asm!( - "rdmsr", - in("ecx") self.0, - out("eax") low, out("edx") high, - options(nomem, nostack, preserves_flags), - ); - } - ((high as u64) << 32) | (low as u64) - } - - #[cfg(not(feature = "inline_asm"))] + let (high, low): (u32, u32); unsafe { - crate::asm::x86_64_asm_rdmsr(self.0) + asm!( + "rdmsr", + in("ecx") self.0, + out("eax") low, out("edx") high, + options(nomem, nostack, preserves_flags), + ); } + ((high as u64) << 32) | (low as u64) } /// Write 64 bits to msr register. @@ -171,7 +162,6 @@ mod x86_64 { let low = value as u32; let high = (value >> 32) as u32; - #[cfg(feature = "inline_asm")] unsafe { asm!( "wrmsr", @@ -180,11 +170,6 @@ mod x86_64 { options(nostack, preserves_flags), ); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_wrmsr(self.0, low, high); - } } } diff --git a/src/registers/mxcsr.rs b/src/registers/mxcsr.rs index f10812b31..ecb13a933 100644 --- a/src/registers/mxcsr.rs +++ b/src/registers/mxcsr.rs @@ -62,37 +62,24 @@ impl Default for MxCsr { #[cfg(feature = "instructions")] mod x86_64 { use super::*; - #[cfg(feature = "inline_asm")] use core::arch::asm; /// Read the value of MXCSR. #[inline] pub fn read() -> MxCsr { - #[cfg(feature = "inline_asm")] - { - let mut mxcsr: u32 = 0; - unsafe { - asm!("stmxcsr [{}]", in(reg) &mut mxcsr, options(nostack, preserves_flags)); - } - MxCsr::from_bits_truncate(mxcsr) - } - #[cfg(not(feature = "inline_asm"))] + let mut mxcsr: u32 = 0; unsafe { - MxCsr::from_bits_truncate(crate::asm::x86_64_asm_read_mxcsr()) + asm!("stmxcsr [{}]", in(reg) &mut mxcsr, options(nostack, preserves_flags)); } + MxCsr::from_bits_truncate(mxcsr) } /// Write MXCSR. #[inline] pub fn write(mxcsr: MxCsr) { - #[cfg(feature = "inline_asm")] unsafe { asm!("ldmxcsr [{}]", in(reg) &mxcsr, options(nostack, readonly)); } - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_mxcsr(mxcsr.bits()); - } } #[cfg(test)] diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index dad0b51ce..0f93384b5 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -66,7 +66,6 @@ bitflags! { #[cfg(feature = "instructions")] mod x86_64 { use super::*; - #[cfg(feature = "inline_asm")] use core::arch::asm; /// Returns the current value of the RFLAGS register. @@ -82,14 +81,9 @@ mod x86_64 { pub fn read_raw() -> u64 { let r: u64; - #[cfg(feature = "inline_asm")] unsafe { asm!("pushfq; pop {}", out(reg) r, options(nomem, preserves_flags)); } - #[cfg(not(feature = "inline_asm"))] - unsafe { - r = crate::asm::x86_64_asm_read_rflags(); - } r } @@ -126,15 +120,9 @@ mod x86_64 { pub unsafe fn write_raw(val: u64) { // HACK: we mark this function as preserves_flags to prevent Rust from restoring // saved flags after the "popf" below. See above note on safety. - #[cfg(feature = "inline_asm")] unsafe { asm!("push {}; popfq", in(reg) val, options(nomem, preserves_flags)); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_write_rflags(val); - } } #[cfg(test)] diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 3f85001c3..655e4ea9d 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -54,7 +54,6 @@ bitflags! { #[cfg(feature = "instructions")] mod x86_64 { use super::*; - #[cfg(feature = "inline_asm")] use core::arch::asm; impl XCr0 { @@ -67,7 +66,6 @@ mod x86_64 { /// Read the current raw XCR0 value. #[inline] pub fn read_raw() -> u64 { - #[cfg(feature = "inline_asm")] unsafe { let (low, high): (u32, u32); asm!( @@ -78,11 +76,6 @@ mod x86_64 { ); (high as u64) << 32 | (low as u64) } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_xgetbv(0) - } } /// Write XCR0 flags. @@ -144,7 +137,6 @@ mod x86_64 { let low = value as u32; let high = (value >> 32) as u32; - #[cfg(feature = "inline_asm")] unsafe { asm!( "xsetbv", @@ -153,11 +145,6 @@ mod x86_64 { options(nomem, nostack, preserves_flags), ); } - - #[cfg(not(feature = "inline_asm"))] - unsafe { - crate::asm::x86_64_asm_xsetbv(0, low, high); - } } } } From f7b2e9043eb06f00aa1ffce4ca4e6e39ddba576b Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Fri, 25 Feb 2022 13:53:40 +0100 Subject: [PATCH 31/64] remove `external_asm` and `inline_asm` features --- .github/workflows/build.yml | 10 +++++----- Cargo.toml | 4 ---- README.md | 1 - 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2c75adf9a..d65f86867 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -64,7 +64,7 @@ jobs: uses: actions-rs/cargo@v1 with: command: doc - args: --no-default-features --features external_asm,instructions + args: --no-default-features --features instructions if: runner.os != 'Windows' - name: "Run cargo doc without default features" @@ -83,14 +83,14 @@ jobs: uses: actions-rs/cargo@v1 with: command: build - args: --no-default-features --features external_asm,instructions + args: --no-default-features --features instructions if: runner.os != 'Windows' - name: "Run cargo build for stable on musl" uses: actions-rs/cargo@v1 with: command: build - args: --target x86_64-unknown-linux-musl --no-default-features --features external_asm,instructions + args: --target x86_64-unknown-linux-musl --no-default-features --features instructions if: runner.os == 'Linux' - name: "Run cargo test" @@ -102,14 +102,14 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --no-default-features --features external_asm,instructions + args: --no-default-features --features instructions if: runner.os != 'Windows' - name: "Run cargo test for stable on musl" uses: actions-rs/cargo@v1 with: command: test - args: --target x86_64-unknown-linux-musl --no-default-features --features external_asm,instructions + args: --target x86_64-unknown-linux-musl --no-default-features --features instructions if: runner.os == 'Linux' - name: "Install Rustup Targets" diff --git a/Cargo.toml b/Cargo.toml index 732cfac53..891acf4dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,10 +39,6 @@ const_fn = [] asm_const = [] doc_cfg = [] -# These features are no longer used and only there for backwards compatibility. -external_asm = [] -inline_asm = [] - [package.metadata.release] no-dev-version = true pre-release-replacements = [ diff --git a/README.md b/README.md index 26eadd3bd..987e31943 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ Support for x86_64 specific instructions (e.g. TLB flush), registers (e.g. contr * `nightly`: Enables features only available on nightly Rust; enabled by default. * `instructions`: Enabled by default, turns on x86\_64 specific instructions, and dependent features. Only available for x86\_64 targets. -* `external_asm`: Use this to build with non-nightly rust. Needs `default-features = false, features = ["instructions"]`. Is unsupported on Windows. ## Building with stable rust From 518e43ca66ddb08ba4caf437c41d0d60331d6dfd Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sat, 26 Mar 2022 14:59:50 -0700 Subject: [PATCH 32/64] Update "next" MSRV for stable features to 1.59 Signed-off-by: Joe Richey --- .github/workflows/build.yml | 4 ---- Cargo.toml | 2 +- README.md | 10 ++++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7959f9662..7ad75185f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,6 @@ jobs: rust: - nightly - 1.59 - - 1.57 runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -28,7 +27,6 @@ jobs: toolchain: ${{ matrix.rust }} override: true - name: Run cargo build for stable - if: matrix.rust != 1.57 uses: actions-rs/cargo@v1 with: command: build @@ -39,7 +37,6 @@ jobs: command: build args: --no-default-features - name: Run cargo doc for stable - if: matrix.rust != 1.57 uses: actions-rs/cargo@v1 with: command: doc @@ -50,7 +47,6 @@ jobs: command: doc args: --no-default-features - name: Run cargo test for stable - if: matrix.rust != 1.57 uses: actions-rs/cargo@v1 with: command: test diff --git a/Cargo.toml b/Cargo.toml index a38eb3ad7..072512ed9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ readme = "README.md" repository = "https://github.com/rust-osdev/x86_64" version = "0.14.8" edition = "2018" -rust-version = "1.57" # Needed to support panic! in const fns +rust-version = "1.59" # Needed to support inline asm and default const generics [dependencies] bit_field = "0.10.1" diff --git a/README.md b/README.md index 5670179a3..38eac9f7a 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,10 @@ Support for x86_64 specific instructions (e.g. TLB flush), registers (e.g. contr ## Minimum Supported Rust Version (MSRV) -If no features are enabled (`--no-default-features`), Rust 1.57.0 is required. +If no nightly features are enabled, Rust 1.59.0 is required. +This can be done by either: + - `--no-default-features --features instructions` + - `--no-default-features` -If only the `instructions` feature is enabled (`--no-default-features --features instructions`), Rust 1.59.0 is required. - -If the `nightly` feature or any of its sub-features is enabled, a recent nightly is required. +If the `nightly` feature or any of its sub-features is enabled (which is the +default), a recent nightly is required. From 8c3f1237f777fdfe21c62c0cebd1f2c5531b4613 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 24 Mar 2022 01:45:39 -0700 Subject: [PATCH 33/64] Allow the GDT to be of any length Signed-off-by: Joe Richey --- src/structures/gdt.rs | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index c78e4a03c..4f2cce010 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -14,10 +14,13 @@ use crate::registers::segmentation::{Segment, CS, SS}; /// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for /// switching between user and kernel mode or for loading a TSS. /// -/// The GDT has a fixed size of 8 entries, trying to add more entries will panic. +/// The GDT has a fixed maximum size given by the `MAX` const generic parameter. +/// Trying to add more entries than this maximum via [`GlobalDescriptorTable::add_entry`] +/// will panic. /// /// You do **not** need to add a null segment descriptor yourself - this is already done -/// internally. +/// internally. This means you can add up to `MAX - 1` additional [`Descriptor`]s to +/// this table. /// /// Data segment registers in ring 0 can be loaded with the null segment selector. When running in /// ring 3, the `ss` register must point to a valid data segment which can be obtained through the @@ -45,17 +48,24 @@ use crate::registers::segmentation::{Segment, CS, SS}; /// ``` #[derive(Debug, Clone)] -pub struct GlobalDescriptorTable { - table: [u64; 8], +pub struct GlobalDescriptorTable { + table: [u64; MAX], next_free: usize, } impl GlobalDescriptorTable { - /// Creates an empty GDT. + /// Creates an empty GDT with the default length of 8. + pub const fn new() -> Self { + Self::empty() + } +} + +impl GlobalDescriptorTable { + /// Creates an empty GDT which can hold `MAX` number of [`Descriptor`]s. #[inline] - pub const fn new() -> GlobalDescriptorTable { - GlobalDescriptorTable { - table: [0; 8], + pub const fn empty() -> Self { + Self { + table: [0; MAX], next_free: 1, } } @@ -67,13 +77,13 @@ impl GlobalDescriptorTable { /// * The user must make sure that the entries are well formed /// * The provided slice **must not be larger than 8 items** (only up to the first 8 will be observed.) #[inline] - pub const unsafe fn from_raw_slice(slice: &[u64]) -> GlobalDescriptorTable { + pub const unsafe fn from_raw_slice(slice: &[u64]) -> Self { let next_free = slice.len(); - let mut table = [0; 8]; + let mut table = [0; MAX]; let mut idx = 0; assert!( - next_free <= 8, + next_free <= MAX, "initializing a GDT from a slice requires it to be **at most** 8 elements." ); @@ -82,7 +92,7 @@ impl GlobalDescriptorTable { idx += 1; } - GlobalDescriptorTable { table, next_free } + Self { table, next_free } } /// Get a reference to the internal table. From f4114969c0146041b2e5ef7612d470a10ce1d8e4 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sun, 27 Mar 2022 13:07:12 -0700 Subject: [PATCH 34/64] gdt: Update comments and assert message Signed-off-by: Joe Richey --- src/structures/gdt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 4f2cce010..c30c52adc 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -75,7 +75,7 @@ impl GlobalDescriptorTable { /// # Safety /// /// * The user must make sure that the entries are well formed - /// * The provided slice **must not be larger than 8 items** (only up to the first 8 will be observed.) + /// * Panics if the provided slice has more than `MAX` entries #[inline] pub const unsafe fn from_raw_slice(slice: &[u64]) -> Self { let next_free = slice.len(); @@ -84,7 +84,7 @@ impl GlobalDescriptorTable { assert!( next_free <= MAX, - "initializing a GDT from a slice requires it to be **at most** 8 elements." + "cannot initialize GDT with slice exceeding the maximum length" ); while idx != next_free { From cfd24c7a50ed2f5233c89e7df908368f792aefef Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 28 Mar 2022 00:43:18 -0700 Subject: [PATCH 35/64] Remove software_interrupt! macro Given that const generics are stable but const values in asm! are not, having a macro in addtion to a generic function isn't really useful. Signed-off-by: Joe Richey --- src/instructions/interrupts.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index a1e526d7c..2176d42b1 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -133,18 +133,6 @@ pub fn int3() { } } -/// Generate a software interrupt by invoking the `int` instruction. -/// -/// This currently needs to be a macro because the `int` argument needs to be an -/// immediate. This macro will be replaced by a generic function when support for -/// const generics is implemented in Rust. -#[macro_export] -macro_rules! software_interrupt { - ($x:expr) => {{ - asm!("int {id}", id = const $x, options(nomem, nostack)); - }}; -} - /// Generate a software interrupt by invoking the `int` instruction. /// /// ## Safety From 5bbc76ea438a25d19a52463bea2968373ff7766f Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 28 Mar 2022 00:50:37 -0700 Subject: [PATCH 36/64] Remove usize trait impls This removes `Add`/`AddAssign`/`Sub`/`SubAssign` with `usize` from `VirtAddr` and `PhysAddr` (which are always 64-bit, even on 32-bit platforms). This makes it possible to write code like: ```rust let addr1 = PhysAddr::new(0x52); let addr2 = addr1 + 3; ``` without getting a "multiple `impl`s" compiler error. This is essentially the breaking change mentioned in https://github.com/rust-osdev/x86_64/issues/293#issuecomment-901804330 Signed-off-by: Joe Richey --- src/addr.rs | 68 ----------------------------------------------------- 1 file changed, 68 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index 09ec65328..46852c081 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -281,23 +281,6 @@ impl AddAssign for VirtAddr { } } -#[cfg(target_pointer_width = "64")] -impl Add for VirtAddr { - type Output = Self; - #[inline] - fn add(self, rhs: usize) -> Self::Output { - self + rhs as u64 - } -} - -#[cfg(target_pointer_width = "64")] -impl AddAssign for VirtAddr { - #[inline] - fn add_assign(&mut self, rhs: usize) { - self.add_assign(rhs as u64) - } -} - impl Sub for VirtAddr { type Output = Self; #[inline] @@ -313,23 +296,6 @@ impl SubAssign for VirtAddr { } } -#[cfg(target_pointer_width = "64")] -impl Sub for VirtAddr { - type Output = Self; - #[inline] - fn sub(self, rhs: usize) -> Self::Output { - self - rhs as u64 - } -} - -#[cfg(target_pointer_width = "64")] -impl SubAssign for VirtAddr { - #[inline] - fn sub_assign(&mut self, rhs: usize) { - self.sub_assign(rhs as u64) - } -} - impl Sub for VirtAddr { type Output = u64; #[inline] @@ -564,23 +530,6 @@ impl AddAssign for PhysAddr { } } -#[cfg(target_pointer_width = "64")] -impl Add for PhysAddr { - type Output = Self; - #[inline] - fn add(self, rhs: usize) -> Self::Output { - self + rhs as u64 - } -} - -#[cfg(target_pointer_width = "64")] -impl AddAssign for PhysAddr { - #[inline] - fn add_assign(&mut self, rhs: usize) { - self.add_assign(rhs as u64) - } -} - impl Sub for PhysAddr { type Output = Self; #[inline] @@ -596,23 +545,6 @@ impl SubAssign for PhysAddr { } } -#[cfg(target_pointer_width = "64")] -impl Sub for PhysAddr { - type Output = Self; - #[inline] - fn sub(self, rhs: usize) -> Self::Output { - self - rhs as u64 - } -} - -#[cfg(target_pointer_width = "64")] -impl SubAssign for PhysAddr { - #[inline] - fn sub_assign(&mut self, rhs: usize) { - self.sub_assign(rhs as u64) - } -} - impl Sub for PhysAddr { type Output = u64; #[inline] From bce9ff530bf43c82730f3efe2c61d93a3a07f6fa Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 28 Mar 2022 11:21:41 -0700 Subject: [PATCH 37/64] Update bootloader tests --- testing/src/gdt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index d273631c1..14fc74023 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -13,7 +13,7 @@ lazy_static! { static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE]; let stack_start = VirtAddr::from_ptr(unsafe { &STACK }); - let stack_end = stack_start + STACK_SIZE; + let stack_end = stack_start + STACK_SIZE as u64; stack_end }; tss From 941c50d5ec332eab09de8ffe1344cc76ecddb521 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 28 Mar 2022 18:07:21 -0700 Subject: [PATCH 38/64] gdt: Check that MAX is in range The GDT can have a maxium length of 2^16 bytes, and must contain at least one null descriptor. As `MAX` counts the number of `u64` entries, we must have `0 < MAX <= 2^13`. Unfortunely, we cannot do this check with a `where` clause, as `feature(generic_const_expers)` is not yet stable. However, we can do this check with an `assert!` in `GlobalDescriptorTable::empty()`, which is a `const fn`. Pointed out by @Freax13 in https://github.com/rust-osdev/x86_64/pull/360#discussion_r836788703 Signed-off-by: Joe Richey --- src/structures/gdt.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index c30c52adc..8c4ddf7e1 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -64,6 +64,9 @@ impl GlobalDescriptorTable { /// Creates an empty GDT which can hold `MAX` number of [`Descriptor`]s. #[inline] pub const fn empty() -> Self { + // TODO: Replace with compiler error when feature(generic_const_exprs) is stable. + assert!(MAX > 0, "A GDT cannot have 0 entries"); + assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries"); Self { table: [0; MAX], next_free: 1, @@ -184,6 +187,8 @@ impl GlobalDescriptorTable { use core::mem::size_of; super::DescriptorTablePointer { base: crate::VirtAddr::new(self.table.as_ptr() as u64), + // 0 < self.next_free <= MAX <= 2^13, so the limit calculation + // will not underflow or overflow. limit: (self.next_free * size_of::() - 1) as u16, } } From b070bb69678e8110b350b7e45b5979dfd2260edd Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Mon, 28 Mar 2022 19:22:01 -0700 Subject: [PATCH 39/64] Remove deprecated functions/flags Signed-off-by: Joe Richey --- src/instructions/segmentation.rs | 89 -------------------------------- src/registers/control.rs | 3 -- src/registers/mod.rs | 4 -- src/registers/xcontrol.rs | 3 -- 4 files changed, 99 deletions(-) diff --git a/src/instructions/segmentation.rs b/src/instructions/segmentation.rs index 334c42237..96b625323 100644 --- a/src/instructions/segmentation.rs +++ b/src/instructions/segmentation.rs @@ -103,92 +103,3 @@ impl GS { } } } - -/// Alias for [`CS::set_reg()`] -#[deprecated(since = "0.14.4", note = "use `CS::set_reg()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn set_cs(sel: SegmentSelector) { - unsafe { CS::set_reg(sel) } -} -/// Alias for [`SS::set_reg()`] -#[deprecated(since = "0.14.4", note = "use `SS::set_reg()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn load_ss(sel: SegmentSelector) { - unsafe { SS::set_reg(sel) } -} -/// Alias for [`DS::set_reg()`] -#[deprecated(since = "0.14.4", note = "use `DS::set_reg()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn load_ds(sel: SegmentSelector) { - unsafe { DS::set_reg(sel) } -} -/// Alias for [`ES::set_reg()`] -#[deprecated(since = "0.14.4", note = "use `ES::set_reg()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn load_es(sel: SegmentSelector) { - unsafe { ES::set_reg(sel) } -} -/// Alias for [`FS::set_reg()`] -#[deprecated(since = "0.14.4", note = "use `FS::set_reg()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn load_fs(sel: SegmentSelector) { - unsafe { FS::set_reg(sel) } -} -/// Alias for [`GS::set_reg()`] -#[deprecated(since = "0.14.4", note = "use `GS::set_reg()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn load_gs(sel: SegmentSelector) { - unsafe { GS::set_reg(sel) } -} -/// Alias for [`GS::swap()`] -#[deprecated(since = "0.14.4", note = "use `GS::swap()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn swap_gs() { - unsafe { GS::swap() } -} -/// Alias for [`CS::get_reg()`] -#[deprecated(since = "0.14.4", note = "use `CS::get_reg()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub fn cs() -> SegmentSelector { - CS::get_reg() -} -/// Alias for [`FS::write_base()`]. -/// -/// Panics if the provided address is non-canonical. -#[deprecated(since = "0.14.4", note = "use `FS::write_base()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn wrfsbase(val: u64) { - unsafe { FS::write_base(VirtAddr::new(val)) } -} -/// Alias for [`FS::read_base()`] -#[deprecated(since = "0.14.4", note = "use `FS::read_base()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn rdfsbase() -> u64 { - FS::read_base().as_u64() -} -/// Alias for [`GS::write_base()`]. -/// -/// Panics if the provided address is non-canonical. -#[deprecated(since = "0.14.4", note = "use `GS::write_base()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn wrgsbase(val: u64) { - unsafe { GS::write_base(VirtAddr::new(val)) } -} -/// Alias for [`GS::read_base()`] -#[deprecated(since = "0.14.4", note = "use `GS::read_base()` instead")] -#[allow(clippy::missing_safety_doc)] -#[inline] -pub unsafe fn rdgsbase() -> u64 { - GS::read_base().as_u64() -} diff --git a/src/registers/control.rs b/src/registers/control.rs index 791671129..1f270f779 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -141,9 +141,6 @@ bitflags! { /// Also enables access to the PKRU register (via the `RDPKRU`/`WRPKRU` /// instructions) to set user-mode protection key access controls. const PROTECTION_KEY_USER = 1 << 22; - /// Alias for [`PROTECTION_KEY_USER`](Cr4Flags::PROTECTION_KEY_USER) - #[deprecated(since = "0.14.5", note = "use `PROTECTION_KEY_USER` instead")] - const PROTECTION_KEY = 1 << 22; /// Enables Control-flow Enforcement Technology (CET) /// /// This enables the shadow stack feature, ensuring return addresses read diff --git a/src/registers/mod.rs b/src/registers/mod.rs index dfde2439a..4357e750b 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -7,9 +7,5 @@ pub mod rflags; pub mod segmentation; pub mod xcontrol; -#[cfg(feature = "instructions")] -#[allow(deprecated)] -pub use crate::instructions::segmentation::{rdfsbase, rdgsbase, wrfsbase, wrgsbase}; - #[cfg(feature = "instructions")] pub use crate::instructions::read_rip; diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index 655e4ea9d..95699ddef 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -24,9 +24,6 @@ bitflags! { /// Enables AVX instructions and using the upper halves of the AVX registers /// with `XSAVE`/`XRSTOR`. const AVX = 1 << 2; - /// Alias for [`AVX`](XCr0Flags::AVX) - #[deprecated(since = "0.14.5", note = "use `AVX` instead")] - const YMM = 1<<2; /// Enables MPX instructions and using the BND0-BND3 bound registers /// with `XSAVE`/`XRSTOR` (Intel Only). const BNDREG = 1 << 3; From 3c48283d816926375412a7b2c52973e46e6f99c6 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 31 Mar 2022 00:06:16 -0700 Subject: [PATCH 40/64] VirtAddr: Remove 32-bit conversion Signed-off-by: Joe Richey --- src/addr.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index 46852c081..744a8acd6 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -125,11 +125,7 @@ impl VirtAddr { } /// Creates a virtual address from the given pointer - // cfg(target_pointer_width = "32") is only here for backwards - // compatibility: Earlier versions of this crate did not have any `cfg()` - // on this function. At least for 32- and 64-bit we know the `as u64` cast - // doesn't truncate. - #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] + #[cfg(target_pointer_width = "64")] #[inline] pub fn from_ptr(ptr: *const T) -> Self { Self::new(ptr as u64) From b0d47b34541d086daede63f1b4a02be879b1f630 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 31 Mar 2022 00:11:54 -0700 Subject: [PATCH 41/64] VirtAddr: don't succeed on non-canonical addresses Signed-off-by: Joe Richey --- src/addr.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index 744a8acd6..bacf2066d 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -66,7 +66,8 @@ impl VirtAddr { /// /// ## Panics /// - /// This function panics if the bits in the range 48 to 64 contain data (i.e. are not null and no sign extension). + /// This function panics if the bits in the range 48 to 64 are invalid + /// (i.e. are not a proper sign extension of bit 47). #[inline] pub fn new(addr: u64) -> VirtAddr { Self::try_new(addr).expect( @@ -77,16 +78,16 @@ impl VirtAddr { /// Tries to create a new canonical virtual address. /// - /// This function tries to performs sign - /// extension of bit 47 to make the address canonical. It succeeds if bits 48 to 64 are - /// either a correct sign extension (i.e. copies of bit 47) or all null. Else, an error - /// is returned. + /// This function tries to performs sign extension of bit 47 to make the + /// address canonical. It succeeds if bits 48 to 64 are a correct sign + /// extension (i.e. copies of bit 47). Else, an error is returned. #[inline] pub fn try_new(addr: u64) -> Result { - match addr.get_bits(47..64) { - 0 | 0x1ffff => Ok(VirtAddr(addr)), // address is canonical - 1 => Ok(VirtAddr::new_truncate(addr)), // address needs sign extension - _ => Err(VirtAddrNotValid(addr)), + let v = Self::new_truncate(addr); + if v.0 == addr { + Ok(v) + } else { + Err(VirtAddrNotValid(addr)) } } From 49b41c8332f36177baaa17d0ca51729fbe0a4ca2 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 31 Mar 2022 00:15:41 -0700 Subject: [PATCH 42/64] VirtAddr: make new() and try_new() const Signed-off-by: Joe Richey --- src/addr.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index bacf2066d..03ed5125a 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -69,11 +69,12 @@ impl VirtAddr { /// This function panics if the bits in the range 48 to 64 are invalid /// (i.e. are not a proper sign extension of bit 47). #[inline] - pub fn new(addr: u64) -> VirtAddr { - Self::try_new(addr).expect( - "address passed to VirtAddr::new must not contain any data \ - in bits 48 to 64", - ) + pub const fn new(addr: u64) -> VirtAddr { + // TODO: Replace with .ok().expect(msg) when that works on stable. + match Self::try_new(addr) { + Ok(v) => v, + Err(_) => panic!("virtual address must be sign extended in bits 48 to 64"), + } } /// Tries to create a new canonical virtual address. @@ -82,7 +83,7 @@ impl VirtAddr { /// address canonical. It succeeds if bits 48 to 64 are a correct sign /// extension (i.e. copies of bit 47). Else, an error is returned. #[inline] - pub fn try_new(addr: u64) -> Result { + pub const fn try_new(addr: u64) -> Result { let v = Self::new_truncate(addr); if v.0 == addr { Ok(v) From 872edf7589de425a24c9aa482ded711cf38847c1 Mon Sep 17 00:00:00 2001 From: Joseph Richey Date: Thu, 31 Mar 2022 10:09:51 -0700 Subject: [PATCH 43/64] Update VirtAddr documentation and fix typos Co-authored-by: Philipp Oppermann Signed-off-by: Joe Richey --- src/addr.rs | 18 ++++++++++-------- src/instructions/interrupts.rs | 4 ++-- src/structures/idt.rs | 6 +++--- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/addr.rs b/src/addr.rs index 03ed5125a..84373160a 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -22,7 +22,7 @@ const ADDRESS_SPACE_SIZE: u64 = 0x1_0000_0000_0000; /// between `u64` and `usize`. /// /// On `x86_64`, only the 48 lower bits of a virtual address can be used. The top 16 bits need -/// to be copies of bit 47, i.e. the most significant bit. Addresses that fulfil this criterium +/// to be copies of bit 47, i.e. the most significant bit. Addresses that fulfil this criterion /// are called “canonical”. This type guarantees that it always represents a canonical address. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] @@ -62,7 +62,8 @@ impl core::fmt::Debug for VirtAddrNotValid { impl VirtAddr { /// Creates a new canonical virtual address. /// - /// This function performs sign extension of bit 47 to make the address canonical. + /// The provided address should already be canonical. If you want to check + /// whether an address is canonical, use [`try_new`](Self::try_new). /// /// ## Panics /// @@ -79,9 +80,10 @@ impl VirtAddr { /// Tries to create a new canonical virtual address. /// - /// This function tries to performs sign extension of bit 47 to make the - /// address canonical. It succeeds if bits 48 to 64 are a correct sign - /// extension (i.e. copies of bit 47). Else, an error is returned. + /// This function checks wether the given address is canonical + /// and returns an error otherwise. An address is canonical + /// if bits 48 to 64 are a correct sign + /// extension (i.e. copies of bit 47). #[inline] pub const fn try_new(addr: u64) -> Result { let v = Self::new_truncate(addr); @@ -94,9 +96,9 @@ impl VirtAddr { /// Creates a new canonical virtual address, throwing out bits 48..64. /// - /// This function performs sign extension of bit 47 to make the address canonical, so - /// bits 48 to 64 are overwritten. If you want to check that these bits contain no data, - /// use `new` or `try_new`. + /// This function performs sign extension of bit 47 to make the address + /// canonical, overwriting bits 48 to 64. If you want to check whether an + /// address is canonical, use [`new`](Self::new) or [`try_new`](Self::try_new). #[inline] pub const fn new_truncate(addr: u64) -> VirtAddr { // By doing the right shift as a signed operation (on a i64), it will diff --git a/src/instructions/interrupts.rs b/src/instructions/interrupts.rs index 2176d42b1..7c5f4900e 100644 --- a/src/instructions/interrupts.rs +++ b/src/instructions/interrupts.rs @@ -109,10 +109,10 @@ where /// On some processors, the interrupt shadow of `sti` does not apply to /// non-maskable interrupts (NMIs). This means that an NMI can occur between /// the `sti` and `hlt` instruction, with the result that the CPU is put to -/// sleep even though a new interrupt occured. +/// sleep even though a new interrupt occurred. /// /// To work around this, it is recommended to check in the NMI handler if -/// the interrupt occured between `sti` and `hlt` instructions. If this is the +/// the interrupt occurred between `sti` and `hlt` instructions. If this is the /// case, the handler should increase the instruction pointer stored in the /// interrupt stack frame so that the `hlt` instruction is skipped. /// diff --git a/src/structures/idt.rs b/src/structures/idt.rs index e8cab39e0..5ad61e507 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -42,7 +42,7 @@ use super::gdt::SegmentSelector; /// first entry, the entry for the `divide_error` exception. Note that the index access is /// not possible for entries for which an error code is pushed. /// -/// The remaining entries are used for interrupts. They can be accesed through index +/// The remaining entries are used for interrupts. They can be accessed through index /// operations on the idt, e.g. `idt[32]` returns the first interrupt entry, which is the 32nd IDT /// entry). /// @@ -1263,7 +1263,7 @@ macro_rules! set_general_handler { #[macro_export] #[doc(hidden)] /// We can't loop in macros, but we can use recursion. -/// This macro recursivly adds one more bit to it's arguments until we have 8 bits so that we can call set_general_handler_entry. +/// This macro recursively adds one more bit to it's arguments until we have 8 bits so that we can call set_general_handler_entry. macro_rules! set_general_handler_recursive_bits { // if we have 8 all bits, construct the index from the bits, check if the entry is in range and invoke the macro that sets the handler ($idt:expr, $handler:ident, $range:expr, $bit7:tt, $bit6:tt, $bit5:tt, $bit4:tt, $bit3:tt, $bit2:tt, $bit1:tt, $bit0:tt) => {{ @@ -1274,7 +1274,7 @@ macro_rules! set_general_handler_recursive_bits { $crate::set_general_handler_entry!($idt, $handler, IDX, $bit7, $bit6, $bit5, $bit4, $bit3, $bit2, $bit1, $bit0); } }}; - // otherwise recursivly invoke the macro adding one more bit + // otherwise recursively invoke the macro adding one more bit ($idt:expr, $handler:ident, $range:expr $(, $bits:tt)*) => { $crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 0); $crate::set_general_handler_recursive_bits!($idt, $handler, $range $(, $bits)*, 1); From 272ff0404b39c639ddfb0e5478a2bc39e4c9c1b6 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 5 Apr 2022 03:32:03 -0700 Subject: [PATCH 44/64] Conditionally import bit_field Signed-off-by: Joe Richey --- src/addr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/addr.rs b/src/addr.rs index fac4d5ef0..3931deaba 100644 --- a/src/addr.rs +++ b/src/addr.rs @@ -9,6 +9,7 @@ use core::ops::{Add, AddAssign, Sub, SubAssign}; use crate::structures::paging::page_table::PageTableLevel; use crate::structures::paging::{PageOffset, PageTableIndex}; +#[cfg(feature = "step_trait")] use bit_field::BitField; #[cfg(feature = "step_trait")] From 48dbe8f254dd59f603af8382e0e09f971514d106 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 22:37:01 -0700 Subject: [PATCH 45/64] Add structures::gdt::Entry type The current documentation for the GDT often confuses entries and `Descriptor`s. For example, adding a new `Descriptor` uses a method confusingly named `add_entry`. An entry is a raw 64-bit value that is indexed by a segment selector. The `MAX` length of the GDT is a number of _entries_, not `Descriptor`s. To fix this confusion, this PR makes the following changes: - Adds a transparent `u64` newtype called `Entry`. - Updates the `GlobalDescriptorTable` documentation to correctly use `Entry` or `Descriptor` where appropriate. - Renames the `add_entry` to `append`. - This better expresses that this method might add multiple entries. - Renames `from_raw_slice` to `from_raw_entries` - Renames `as_raw_slice` to `entries` - This method now returns a slice of `Entry`s instead of `u64`s This also fixes an issue where our `assert!`s in `empty()` wouldn't trigger if the GDT was constructed from raw values. Signed-off-by: Joe Richey --- src/lib.rs | 2 +- src/structures/gdt.rs | 98 +++++++++++++++++++++++++++++-------------- testing/src/gdt.rs | 4 +- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6c32eebd6..2319dacfa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ //! and access to various system registers. #![cfg_attr(not(test), no_std)] -#![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT add_entry() +#![cfg_attr(feature = "const_fn", feature(const_mut_refs))] // GDT::append() #![cfg_attr(feature = "asm_const", feature(asm_const))] #![cfg_attr(feature = "abi_x86_interrupt", feature(abi_x86_interrupt))] #![cfg_attr(feature = "step_trait", feature(step_trait))] diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index bcf65d388..3225e273b 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -9,18 +9,42 @@ use bitflags::bitflags; #[cfg(doc)] use crate::registers::segmentation::{Segment, CS, SS}; +/// 8-byte entry in a descriptor table. +/// +/// A [`GlobalDescriptorTable`] (or LDT) is an array of these entries, and +/// [`SegmentSelector`]s index into this array. Each [`Descriptor`] in the table +/// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or +/// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This +/// type exists to give users access to the raw entry bits in a GDT. +#[derive(Clone, Debug)] +#[repr(transparent)] +pub struct Entry(u64); + +impl Entry { + // Create a new Entry from a raw value. + const fn new(raw: u64) -> Self { + Self(raw) + } + + /// The raw bits for this entry. Depending on the [`Descriptor`] type, these + /// bits may correspond to those in [`DescriptorFlags`]. + pub fn raw(&self) -> u64 { + self.0 + } +} + /// A 64-bit mode global descriptor table (GDT). /// /// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for /// switching between user and kernel mode or for loading a TSS. /// /// The GDT has a fixed maximum size given by the `MAX` const generic parameter. -/// Trying to add more entries than this maximum via [`GlobalDescriptorTable::add_entry`] -/// will panic. +/// Overflowing this limit by adding too many [`Descriptor`]s via +/// [`GlobalDescriptorTable::append`] will panic. /// /// You do **not** need to add a null segment descriptor yourself - this is already done -/// internally. This means you can add up to `MAX - 1` additional [`Descriptor`]s to -/// this table. +/// internally. This means you can add up to `MAX - 1` additional [`Entry`]s to +/// this table. Note that some [`Descriptor`]s may take up 2 [`Entry`]s. /// /// Data segment registers in ring 0 can be loaded with the null segment selector. When running in /// ring 3, the `ss` register must point to a valid data segment which can be obtained through the @@ -40,16 +64,16 @@ use crate::registers::segmentation::{Segment, CS, SS}; /// use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor}; /// /// let mut gdt = GlobalDescriptorTable::new(); -/// gdt.add_entry(Descriptor::kernel_code_segment()); -/// gdt.add_entry(Descriptor::user_code_segment()); -/// gdt.add_entry(Descriptor::user_data_segment()); +/// gdt.append(Descriptor::kernel_code_segment()); +/// gdt.append(Descriptor::user_code_segment()); +/// gdt.append(Descriptor::user_data_segment()); /// /// // Add entry for TSS, call gdt.load() then update segment registers /// ``` #[derive(Debug, Clone)] pub struct GlobalDescriptorTable { - table: [u64; MAX], + table: [Entry; MAX], len: usize, } @@ -61,28 +85,29 @@ impl GlobalDescriptorTable { } impl GlobalDescriptorTable { - /// Creates an empty GDT which can hold `MAX` number of [`Descriptor`]s. + /// Creates an empty GDT which can hold `MAX` number of [`Entry`]s. #[inline] pub const fn empty() -> Self { // TODO: Replace with compiler error when feature(generic_const_exprs) is stable. assert!(MAX > 0, "A GDT cannot have 0 entries"); assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries"); + const NULL: Entry = Entry::new(0); Self { - table: [0; MAX], + table: [NULL; MAX], len: 1, } } - /// Forms a GDT from a slice of `u64`. + /// Forms a GDT from a slice of raw [`Entry`] values. /// /// # Safety /// /// * The user must make sure that the entries are well formed /// * Panics if the provided slice has more than `MAX` entries #[inline] - pub const unsafe fn from_raw_slice(slice: &[u64]) -> Self { + pub const unsafe fn from_raw_entries(slice: &[u64]) -> Self { let len = slice.len(); - let mut table = [0; MAX]; + let mut table = Self::empty().table; let mut idx = 0; assert!( @@ -91,27 +116,30 @@ impl GlobalDescriptorTable { ); while idx < len { - table[idx] = slice[idx]; + table[idx] = Entry::new(slice[idx]); idx += 1; } Self { table, len } } - /// Get a reference to the internal table. + /// Get a reference to the internal [`Entry`] table. /// - /// The resulting slice may contain system descriptors, which span two `u64`s. + /// The resulting slice may contain system descriptors, which span two [`Entry`]s. #[inline] - pub fn as_raw_slice(&self) -> &[u64] { + pub fn entries(&self) -> &[Entry] { &self.table[..self.len] } /// Adds the given segment descriptor to the GDT, returning the segment selector. /// - /// Panics if the GDT doesn't have enough free entries to hold the Descriptor. + /// Note that depending on the type of the [`Descriptor`] this may add either + /// one or two new entries. + /// + /// Panics if the GDT doesn't have enough free entries. #[inline] #[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))] - pub fn add_entry(&mut self, entry: Descriptor) -> SegmentSelector { + pub fn append(&mut self, entry: Descriptor) -> SegmentSelector { let index = match entry { Descriptor::UserSegment(value) => { if self.len > self.table.len().saturating_sub(1) { @@ -179,7 +207,7 @@ impl GlobalDescriptorTable { #[cfg_attr(feature = "const_fn", rustversion::attr(all(), const))] fn push(&mut self, value: u64) -> usize { let index = self.len; - self.table[index] = value; + self.table[index] = Entry::new(value); self.len += 1; index } @@ -378,11 +406,11 @@ mod tests { // Makes a GDT that has two free slots fn make_six_entry_gdt() -> GlobalDescriptorTable { let mut gdt = GlobalDescriptorTable::new(); - gdt.add_entry(Descriptor::kernel_code_segment()); - gdt.add_entry(Descriptor::kernel_data_segment()); - gdt.add_entry(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits())); - gdt.add_entry(Descriptor::user_data_segment()); - gdt.add_entry(Descriptor::user_code_segment()); + gdt.append(Descriptor::kernel_code_segment()); + gdt.append(Descriptor::kernel_data_segment()); + gdt.append(Descriptor::UserSegment(DescriptorFlags::USER_CODE32.bits())); + gdt.append(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_code_segment()); assert_eq!(gdt.len, 6); gdt } @@ -391,7 +419,7 @@ mod tests { fn make_full_gdt() -> GlobalDescriptorTable { let mut gdt = make_six_entry_gdt(); - gdt.add_entry(Descriptor::tss_segment(&TSS)); + gdt.append(Descriptor::tss_segment(&TSS)); assert_eq!(gdt.len, 8); gdt } @@ -400,9 +428,9 @@ mod tests { pub fn push_max_segments() { // Make sure we don't panic with user segments let mut gdt = make_six_entry_gdt(); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); assert_eq!(gdt.len, 7); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); assert_eq!(gdt.len, 8); // Make sure we don't panic with system segments let _ = make_full_gdt(); @@ -412,15 +440,23 @@ mod tests { #[should_panic] pub fn panic_user_segment() { let mut gdt = make_full_gdt(); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); } #[test] #[should_panic] pub fn panic_system_segment() { let mut gdt = make_six_entry_gdt(); - gdt.add_entry(Descriptor::user_data_segment()); + gdt.append(Descriptor::user_data_segment()); // We have one free slot, but the GDT requires two - gdt.add_entry(Descriptor::tss_segment(&TSS)); + gdt.append(Descriptor::tss_segment(&TSS)); + } + + #[test] + pub fn from_entries() { + let raw = [0, Flags::KERNEL_CODE64.bits(), Flags::KERNEL_DATA.bits()]; + let gdt = unsafe { GlobalDescriptorTable::<3>::from_raw_entries(&raw) }; + assert_eq!(gdt.table.len(), 3); + assert_eq!(gdt.entries().len(), 3); } } diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index 14fc74023..f4d2643c7 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -20,8 +20,8 @@ lazy_static! { }; static ref GDT: (SingleUseCell, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); - let code_selector = gdt.add_entry(Descriptor::kernel_code_segment()); - let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS)); + let code_selector = gdt.append(Descriptor::kernel_code_segment()); + let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); ( SingleUseCell::new(gdt), Selectors { From 255124a08352bde1433b59e97ad6c96df74d918f Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 02:33:53 -0700 Subject: [PATCH 46/64] Add PartialEq, Eq, and Debug impls Signed-off-by: Joe Richey --- src/structures/gdt.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 3225e273b..6ffacc84e 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -5,6 +5,7 @@ use crate::structures::tss::TaskStateSegment; use crate::PrivilegeLevel; use bit_field::BitField; use bitflags::bitflags; +use core::fmt; // imports for intra-doc links #[cfg(doc)] use crate::registers::segmentation::{Segment, CS, SS}; @@ -16,7 +17,7 @@ use crate::registers::segmentation::{Segment, CS, SS}; /// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or /// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This /// type exists to give users access to the raw entry bits in a GDT. -#[derive(Clone, Debug)] +#[derive(Clone, PartialEq, Eq)] #[repr(transparent)] pub struct Entry(u64); @@ -33,6 +34,13 @@ impl Entry { } } +impl fmt::Debug for Entry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Display inner value as hex + write!(f, "Entry({:#018x})", self.raw()) + } +} + /// A 64-bit mode global descriptor table (GDT). /// /// In 64-bit mode, segmentation is not supported. The GDT is used nonetheless, for example for From 07f7ebf44fea0b27b1d9a9b29272fbc188fa5cbf Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 02:49:38 -0700 Subject: [PATCH 47/64] Make from_raw_entries safe Also update the documentation Signed-off-by: Joe Richey --- src/structures/gdt.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 6ffacc84e..d3ef086e0 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -106,18 +106,26 @@ impl GlobalDescriptorTable { } } - /// Forms a GDT from a slice of raw [`Entry`] values. + /// Forms a GDT from a slice of `u64`. /// - /// # Safety + /// This method allows for creation of a GDT with malformed or invalid + /// entries. However, it is safe because loading a GDT with invalid + /// entires doesn't do anything until those entries are used. For example, + /// [`CS::set_reg`] and [`load_tss`](crate::instructions::tables::load_tss) + /// are both unsafe for this reason. /// - /// * The user must make sure that the entries are well formed - /// * Panics if the provided slice has more than `MAX` entries + /// Panics if: + /// * the provided slice has more than `MAX` entries + /// * the provided slice is empty + /// * the first entry is not zero #[inline] - pub const unsafe fn from_raw_entries(slice: &[u64]) -> Self { + pub const fn from_raw_entries(slice: &[u64]) -> Self { let len = slice.len(); let mut table = Self::empty().table; let mut idx = 0; + assert!(len > 0, "cannot initialize GDT with empty slice"); + assert!(slice[0] == 0, "first GDT entry must be zero"); assert!( len <= MAX, "cannot initialize GDT with slice exceeding the maximum length" @@ -463,7 +471,7 @@ mod tests { #[test] pub fn from_entries() { let raw = [0, Flags::KERNEL_CODE64.bits(), Flags::KERNEL_DATA.bits()]; - let gdt = unsafe { GlobalDescriptorTable::<3>::from_raw_entries(&raw) }; + let gdt = GlobalDescriptorTable::<3>::from_raw_entries(&raw); assert_eq!(gdt.table.len(), 3); assert_eq!(gdt.entries().len(), 3); } From 49b5295aacca3e9d039107ba0d76816f5cc0ce60 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 11:27:52 -0700 Subject: [PATCH 48/64] Fix nits/warnings in docs Signed-off-by: Joe Richey --- src/structures/gdt.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index d3ef086e0..c81d58137 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -118,6 +118,7 @@ impl GlobalDescriptorTable { /// * the provided slice has more than `MAX` entries /// * the provided slice is empty /// * the first entry is not zero + #[cfg_attr(not(feature = "instructions"), allow(rustdoc::broken_intra_doc_links))] #[inline] pub const fn from_raw_entries(slice: &[u64]) -> Self { let len = slice.len(); @@ -147,10 +148,10 @@ impl GlobalDescriptorTable { &self.table[..self.len] } - /// Adds the given segment descriptor to the GDT, returning the segment selector. + /// Appends the given segment descriptor to the GDT, returning the segment selector. /// - /// Note that depending on the type of the [`Descriptor`] this may add either - /// one or two new entries. + /// Note that depending on the type of the [`Descriptor`] this may append + /// either one or two new [`Entry`]s to the table. /// /// Panics if the GDT doesn't have enough free entries. #[inline] From b61f341d92e5cee6faa6cc27df45df8cf1a70fdb Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 23:43:22 -0700 Subject: [PATCH 49/64] Use AtomicU64 for Entry type This gives our GDT the appropriate atomic interior mutablity if we could potentially call `lgdt` and `ltr`. Signed-off-by: Joe Richey --- src/structures/gdt.rs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index c81d58137..d3cb597da 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -10,6 +10,11 @@ use core::fmt; #[cfg(doc)] use crate::registers::segmentation::{Segment, CS, SS}; +#[cfg(feature = "instructions")] +use core::sync::atomic::{AtomicU64 as EntryValue, Ordering}; +#[cfg(not(feature = "instructions"))] +use u64 as EntryValue; + /// 8-byte entry in a descriptor table. /// /// A [`GlobalDescriptorTable`] (or LDT) is an array of these entries, and @@ -17,23 +22,43 @@ use crate::registers::segmentation::{Segment, CS, SS}; /// uses either 1 Entry (if it is a [`UserSegment`](Descriptor::UserSegment)) or /// 2 Entries (if it is a [`SystemSegment`](Descriptor::SystemSegment)). This /// type exists to give users access to the raw entry bits in a GDT. -#[derive(Clone, PartialEq, Eq)] #[repr(transparent)] -pub struct Entry(u64); +pub struct Entry(EntryValue); impl Entry { // Create a new Entry from a raw value. const fn new(raw: u64) -> Self { + #[cfg(feature = "instructions")] + let raw = EntryValue::new(raw); Self(raw) } /// The raw bits for this entry. Depending on the [`Descriptor`] type, these /// bits may correspond to those in [`DescriptorFlags`]. pub fn raw(&self) -> u64 { - self.0 + // TODO: Make this const fn when AtomicU64::load is const. + #[cfg(feature = "instructions")] + let raw = self.0.load(Ordering::SeqCst); + #[cfg(not(feature = "instructions"))] + let raw = self.0; + raw + } +} + +impl Clone for Entry { + fn clone(&self) -> Self { + Self::new(self.raw()) } } +impl PartialEq for Entry { + fn eq(&self, other: &Self) -> bool { + self.raw() == other.raw() + } +} + +impl Eq for Entry {} + impl fmt::Debug for Entry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Display inner value as hex From 8792dc9d464a630f410060854401213d4883c531 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 00:51:52 -0700 Subject: [PATCH 50/64] Fix Clippy warning about const item used to construct array Signed-off-by: Joe Richey --- src/structures/gdt.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index d3cb597da..a61252f23 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -124,6 +124,9 @@ impl GlobalDescriptorTable { // TODO: Replace with compiler error when feature(generic_const_exprs) is stable. assert!(MAX > 0, "A GDT cannot have 0 entries"); assert!(MAX <= (1 << 13), "A GDT can only have at most 2^13 entries"); + + // TODO: Replace with inline_const when it's stable. + #[allow(clippy::declare_interior_mutable_const)] const NULL: Entry = Entry::new(0); Self { table: [NULL; MAX], From 055d014edf3cd1446bfeb7abffb57fc6d8934c2b Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 23:48:23 -0700 Subject: [PATCH 51/64] Allow GDT to be loaded with shared reference Now that the `Entry` type has the appropriate interior mutability, we can safely load a GDT using `&'static self`. Signed-off-by: Joe Richey --- src/structures/gdt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index a61252f23..cc937a78e 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -223,7 +223,7 @@ impl GlobalDescriptorTable { /// [`SS::set_reg()`] and [`CS::set_reg()`]. #[cfg(feature = "instructions")] #[inline] - pub fn load(&'static mut self) { + pub fn load(&'static self) { // SAFETY: static lifetime ensures no modification after loading. unsafe { self.load_unsafe() }; } @@ -241,7 +241,7 @@ impl GlobalDescriptorTable { /// #[cfg(feature = "instructions")] #[inline] - pub unsafe fn load_unsafe(&mut self) { + pub unsafe fn load_unsafe(&self) { use crate::instructions::tables::lgdt; unsafe { lgdt(&self.pointer()); From b2fadb8430f27fabd496d40a25b3c9597ec826dd Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Thu, 14 Apr 2022 23:49:46 -0700 Subject: [PATCH 52/64] Remove SignleUseCell It's no longer used, so we don't need it anymore. Signed-off-by: Joe Richey --- src/lib.rs | 60 ---------------------------------------------- testing/src/gdt.rs | 8 +++---- 2 files changed, 4 insertions(+), 64 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2319dacfa..1c0f74d76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,9 +11,6 @@ #![deny(missing_debug_implementations)] #![deny(unsafe_op_in_unsafe_fn)] -use core::cell::UnsafeCell; -use core::sync::atomic::{AtomicBool, Ordering}; - pub use crate::addr::{align_down, align_up, PhysAddr, VirtAddr}; pub mod addr; @@ -66,60 +63,3 @@ impl PrivilegeLevel { } } } - -/// A wrapper that can be used to safely create one mutable reference `&'static mut T` from a static variable. -/// -/// `SingleUseCell` is safe because it ensures that it only ever gives out one reference. -/// -/// ``SingleUseCell` is a safe alternative to `static mut` or a static `UnsafeCell`. -#[derive(Debug)] -pub struct SingleUseCell { - used: AtomicBool, - value: UnsafeCell, -} - -impl SingleUseCell { - /// Construct a new SingleUseCell. - pub const fn new(value: T) -> Self { - Self { - used: AtomicBool::new(false), - value: UnsafeCell::new(value), - } - } - - /// Try to acquire a mutable reference to the wrapped value. - /// This will only succeed the first time the function is - /// called and fail on all following calls. - /// - /// ``` - /// use x86_64::SingleUseCell; - /// - /// static FOO: SingleUseCell = SingleUseCell::new(0); - /// - /// // Call `try_get_mut` for the first time and get a reference. - /// let first: &'static mut i32 = FOO.try_get_mut().unwrap(); - /// assert_eq!(first, &0); - /// - /// // Calling `try_get_mut` again will return `None`. - /// assert_eq!(FOO.try_get_mut(), None); - /// ``` - pub fn try_get_mut(&self) -> Option<&mut T> { - let already_used = self.used.swap(true, Ordering::AcqRel); - if already_used { - None - } else { - Some(unsafe { - // SAFETY: no reference has been given out yet and we won't give out another. - &mut *self.value.get() - }) - } - } -} - -// SAFETY: Sharing a `SingleUseCell` between threads is safe regardless of whether `T` is `Sync` -// because we only expose the inner value once to one thread. The `T: Send` bound makes sure that -// sending a unique reference to another thread is safe. -unsafe impl Sync for SingleUseCell {} - -// SAFETY: It's safe to send a `SingleUseCell` to another thread if it's safe to send `T`. -unsafe impl Send for SingleUseCell {} diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index f4d2643c7..2e8440642 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -1,7 +1,7 @@ use lazy_static::lazy_static; use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector}; use x86_64::structures::tss::TaskStateSegment; -use x86_64::{SingleUseCell, VirtAddr}; +use x86_64::VirtAddr; pub const DOUBLE_FAULT_IST_INDEX: u16 = 0; @@ -18,12 +18,12 @@ lazy_static! { }; tss }; - static ref GDT: (SingleUseCell, Selectors) = { + static ref GDT: (GlobalDescriptorTable, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); let code_selector = gdt.append(Descriptor::kernel_code_segment()); let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); ( - SingleUseCell::new(gdt), + gdt, Selectors { code_selector, tss_selector, @@ -41,7 +41,7 @@ pub fn init() { use x86_64::instructions::segmentation::{Segment, CS}; use x86_64::instructions::tables::load_tss; - GDT.0.try_get_mut().unwrap().load(); + GDT.0.load(); unsafe { CS::set_reg(GDT.1.code_selector); load_tss(GDT.1.tss_selector); From 8bb01e87887476f59b652f6d03b19f2002fcb341 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 15 Apr 2022 00:53:09 -0700 Subject: [PATCH 53/64] Add additional tests for TSS entry Signed-off-by: Joe Richey --- testing/src/gdt.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/testing/src/gdt.rs b/testing/src/gdt.rs index 2e8440642..28024da9e 100644 --- a/testing/src/gdt.rs +++ b/testing/src/gdt.rs @@ -20,6 +20,8 @@ lazy_static! { }; static ref GDT: (GlobalDescriptorTable, Selectors) = { let mut gdt = GlobalDescriptorTable::new(); + // Add an unused segment so we get a different value for CS + gdt.append(Descriptor::kernel_data_segment()); let code_selector = gdt.append(Descriptor::kernel_code_segment()); let tss_selector = gdt.append(Descriptor::tss_segment(&TSS)); ( @@ -41,9 +43,16 @@ pub fn init() { use x86_64::instructions::segmentation::{Segment, CS}; use x86_64::instructions::tables::load_tss; + // Make sure loading CS actually changes the value GDT.0.load(); - unsafe { - CS::set_reg(GDT.1.code_selector); - load_tss(GDT.1.tss_selector); - } + assert_ne!(CS::get_reg(), GDT.1.code_selector); + unsafe { CS::set_reg(GDT.1.code_selector) }; + assert_eq!(CS::get_reg(), GDT.1.code_selector); + + // Loading the TSS should mark the GDT entry as busy + let tss_idx: usize = GDT.1.tss_selector.index().into(); + let old_tss_entry = GDT.0.entries()[tss_idx].clone(); + unsafe { load_tss(GDT.1.tss_selector) }; + let new_tss_entry = GDT.0.entries()[tss_idx].clone(); + assert_ne!(old_tss_entry, new_tss_entry); } From 62a4ce04e36f03080aa238ac58002d32337f5d3d Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 25 Dec 2022 14:36:17 +0100 Subject: [PATCH 54/64] fix `Page::from_page_table_indices` When we construct the address from the page table indices, we don't make the address canonical. This was fine in the previous versions where we automatically corrected this in `VirtAddr::new`, but that's no longer the case. Instead we can use `VirtAddr::new_truncate`. --- src/structures/paging/page.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/structures/paging/page.rs b/src/structures/paging/page.rs index 3e2878bfe..13db89f82 100644 --- a/src/structures/paging/page.rs +++ b/src/structures/paging/page.rs @@ -171,7 +171,7 @@ impl Page { let mut addr = 0; addr.set_bits(39..48, u64::from(p4_index)); addr.set_bits(30..39, u64::from(p3_index)); - Page::containing_address(VirtAddr::new(addr)) + Page::containing_address(VirtAddr::new_truncate(addr)) } } @@ -189,7 +189,7 @@ impl Page { addr.set_bits(39..48, u64::from(p4_index)); addr.set_bits(30..39, u64::from(p3_index)); addr.set_bits(21..30, u64::from(p2_index)); - Page::containing_address(VirtAddr::new(addr)) + Page::containing_address(VirtAddr::new_truncate(addr)) } } @@ -209,7 +209,7 @@ impl Page { addr.set_bits(30..39, u64::from(p3_index)); addr.set_bits(21..30, u64::from(p2_index)); addr.set_bits(12..21, u64::from(p1_index)); - Page::containing_address(VirtAddr::new(addr)) + Page::containing_address(VirtAddr::new_truncate(addr)) } /// Returns the level 1 page table index of this page. From d4a780e2d49a8878225b23a826edda61d96d6a64 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 8 Mar 2023 12:52:34 +0100 Subject: [PATCH 55/64] create unified `Sealed` trait The traits we currently want to seal off are all marker traits and they all share some kind of debug representation. --- src/instructions/port.rs | 31 ++++++++++++++++--------------- src/lib.rs | 7 +++++++ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/instructions/port.rs b/src/instructions/port.rs index 0263d0e0e..9353634e9 100644 --- a/src/instructions/port.rs +++ b/src/instructions/port.rs @@ -4,6 +4,7 @@ use core::arch::asm; use core::fmt; use core::marker::PhantomData; +use crate::sealed::Sealed; pub use crate::structures::port::{PortRead, PortWrite}; impl PortRead for u8 { @@ -66,43 +67,43 @@ impl PortWrite for u32 { } } -mod sealed { - pub trait Access { - const DEBUG_NAME: &'static str; - } -} +/// A marker trait for access types which allow accessing port values. +pub trait PortAccess: Sealed {} /// A marker trait for access types which allow reading port values. -pub trait PortReadAccess: sealed::Access {} +pub trait PortReadAccess: PortAccess {} /// A marker trait for access types which allow writing port values. -pub trait PortWriteAccess: sealed::Access {} +pub trait PortWriteAccess: PortAccess {} /// An access marker type indicating that a port is only allowed to read values. #[derive(Debug)] pub struct ReadOnlyAccess(()); -impl sealed::Access for ReadOnlyAccess { - const DEBUG_NAME: &'static str = "ReadOnly"; +impl Sealed for ReadOnlyAccess { + const DEBUG_STR: &'static str = "ReadOnly"; } +impl PortAccess for ReadOnlyAccess {} impl PortReadAccess for ReadOnlyAccess {} /// An access marker type indicating that a port is only allowed to write values. #[derive(Debug)] pub struct WriteOnlyAccess(()); -impl sealed::Access for WriteOnlyAccess { - const DEBUG_NAME: &'static str = "WriteOnly"; +impl Sealed for WriteOnlyAccess { + const DEBUG_STR: &'static str = "WriteOnly"; } +impl PortAccess for WriteOnlyAccess {} impl PortWriteAccess for WriteOnlyAccess {} /// An access marker type indicating that a port is allowed to read or write values. #[derive(Debug)] pub struct ReadWriteAccess(()); -impl sealed::Access for ReadWriteAccess { - const DEBUG_NAME: &'static str = "ReadWrite"; +impl Sealed for ReadWriteAccess { + const DEBUG_STR: &'static str = "ReadWrite"; } +impl PortAccess for ReadWriteAccess {} impl PortReadAccess for ReadWriteAccess {} impl PortWriteAccess for ReadWriteAccess {} @@ -165,12 +166,12 @@ impl PortGeneric { } } -impl fmt::Debug for PortGeneric { +impl fmt::Debug for PortGeneric { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("PortGeneric") .field("port", &self.port) .field("size", &core::mem::size_of::()) - .field("access", &format_args!("{}", A::DEBUG_NAME)) + .field("access", &format_args!("{}", A::DEBUG_STR)) .finish() } } diff --git a/src/lib.rs b/src/lib.rs index 1c0f74d76..c8384a7b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -63,3 +63,10 @@ impl PrivilegeLevel { } } } + +pub(crate) mod sealed { + pub trait Sealed { + /// A string representation for debug output. + const DEBUG_STR: &'static str; + } +} From b568699456350e0fb97bef4c717d718380c3a762 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Wed, 8 Mar 2023 12:54:52 +0100 Subject: [PATCH 56/64] seal off the `PageSize` trait Users should never implement this trait themselves. This also allows us to add more supertraits to `PageSize` and `NotGiantPageSize` without introducing a major breaking change. --- src/structures/paging/frame.rs | 2 +- src/structures/paging/page.rs | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/structures/paging/frame.rs b/src/structures/paging/frame.rs index 64935caee..6cae8faba 100644 --- a/src/structures/paging/frame.rs +++ b/src/structures/paging/frame.rs @@ -86,7 +86,7 @@ impl fmt::Debug for PhysFrame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_fmt(format_args!( "PhysFrame[{}]({:#x})", - S::SIZE_AS_DEBUG_STR, + S::DEBUG_STR, self.start_address().as_u64() )) } diff --git a/src/structures/paging/page.rs b/src/structures/paging/page.rs index 13db89f82..933b8c3e6 100644 --- a/src/structures/paging/page.rs +++ b/src/structures/paging/page.rs @@ -1,5 +1,6 @@ //! Abstractions for default-sized and huge virtual memory pages. +use crate::sealed::Sealed; use crate::structures::paging::page_table::PageTableLevel; use crate::structures::paging::PageTableIndex; use crate::VirtAddr; @@ -10,12 +11,9 @@ use core::marker::PhantomData; use core::ops::{Add, AddAssign, Sub, SubAssign}; /// Trait for abstracting over the three possible page sizes on x86_64, 4KiB, 2MiB, 1GiB. -pub trait PageSize: Copy + Eq + PartialOrd + Ord { +pub trait PageSize: Copy + Eq + PartialOrd + Ord + Sealed { /// The page size in bytes. const SIZE: u64; - - /// A string representation of the page size for debug output. - const SIZE_AS_DEBUG_STR: &'static str; } /// This trait is implemented for 4KiB and 2MiB pages, but not for 1GiB pages. @@ -37,21 +35,30 @@ pub enum Size1GiB {} impl PageSize for Size4KiB { const SIZE: u64 = 4096; - const SIZE_AS_DEBUG_STR: &'static str = "4KiB"; } impl NotGiantPageSize for Size4KiB {} +impl Sealed for super::Size4KiB { + const DEBUG_STR: &'static str = "4KiB"; +} + impl PageSize for Size2MiB { const SIZE: u64 = Size4KiB::SIZE * 512; - const SIZE_AS_DEBUG_STR: &'static str = "2MiB"; } impl NotGiantPageSize for Size2MiB {} +impl Sealed for super::Size2MiB { + const DEBUG_STR: &'static str = "2MiB"; +} + impl PageSize for Size1GiB { const SIZE: u64 = Size2MiB::SIZE * 512; - const SIZE_AS_DEBUG_STR: &'static str = "1GiB"; +} + +impl Sealed for super::Size1GiB { + const DEBUG_STR: &'static str = "1GiB"; } /// A virtual memory page. @@ -223,7 +230,7 @@ impl fmt::Debug for Page { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_fmt(format_args!( "Page[{}]({:#x})", - S::SIZE_AS_DEBUG_STR, + S::DEBUG_STR, self.start_address().as_u64() )) } From ee78cc02afa9b63b0d6699491a7666cf547c5924 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Jan 2024 14:25:16 +0000 Subject: [PATCH 57/64] fix date for 0.14.11 release --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 947e1d09b..aa25c900f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,7 @@ - [Add `HandlerFuncType` trait](https://github.com/rust-osdev/x86_64/pull/439) -# 0.14.11 – 2022-09-15 +# 0.14.11 – 2023-09-15 ## New Features From 5133c370f75ac023e7af2736f5a1f82a33014496 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Jan 2024 14:47:39 +0000 Subject: [PATCH 58/64] update changelog for 0.15.0 release --- Changelog.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Changelog.md b/Changelog.md index aa25c900f..4d339e42d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,9 +1,43 @@ # Unreleased +# 0.15.0 – 2024-01-14 + +## Breaking changes + +- [replace software_interrupt! macro with generic function](https://github.com/rust-osdev/x86_64/pull/259) +- [Use SegmentSelector in InterruptStackFrame](https://github.com/rust-osdev/x86_64/pull/263) +- [add `InvalidStarSegmentSelectors` error](https://github.com/rust-osdev/x86_64/pull/317) +- [add `PcidTooBig` error](https://github.com/rust-osdev/x86_64/pull/316) +- [implement `Index` for IDT instead of `Index`](https://github.com/rust-osdev/x86_64/pull/319) +- [change `cpu_flags`'s type to `RFlags`](https://github.com/rust-osdev/x86_64/pull/324) +- [fix `load_tss` and `GlobalDescriptorTable`](https://github.com/rust-osdev/x86_64/pull/323) +- [add an immutable getter for the level 4 page table](https://github.com/rust-osdev/x86_64/pull/327) +- [make `Cr2::read` return a result](https://github.com/rust-osdev/x86_64/pull/335) +- [remove `external_asm` and `inline_asm` features](https://github.com/rust-osdev/x86_64/pull/345) +- [Allow the GDT to be of any length](https://github.com/rust-osdev/x86_64/pull/360) +- [Remove software_interrupt! macro](https://github.com/rust-osdev/x86_64/pull/363) +- [Remove usize trait impls](https://github.com/rust-osdev/x86_64/pull/364) +- [Remove deprecated functions/flags](https://github.com/rust-osdev/x86_64/pull/368) +- [VirtAddr improvements](https://github.com/rust-osdev/x86_64/pull/370) +- [Add structures::gdt::Entry type](https://github.com/rust-osdev/x86_64/pull/380) +- [Allow GDT to be loaded with shared reference](https://github.com/rust-osdev/x86_64/pull/381) +- [seal off the `PageSize` trait](https://github.com/rust-osdev/x86_64/pull/404) + ## New Features - [Add `HandlerFuncType` trait](https://github.com/rust-osdev/x86_64/pull/439) +## Fixes + +- [fix typo in docs](https://github.com/rust-osdev/x86_64/pull/265) +- [activate `feature(asm_const)`](https://github.com/rust-osdev/x86_64/pull/320) +- [gdt: Check that MAX is in range](https://github.com/rust-osdev/x86_64/pull/365) +- [fix `Page::from_page_table_indices`](https://github.com/rust-osdev/x86_64/pull/398) + +## Other improvements + +- [idt: Fixup Options structure and cleanup set_handler_fn](https://github.com/rust-osdev/x86_64/pull/226) + # 0.14.11 – 2023-09-15 ## New Features From c34c84a5c98ae37f7337b21dc656462fbca65367 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Jan 2024 14:51:54 +0000 Subject: [PATCH 59/64] bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index df8e5c075..756ddbe66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT/Apache-2.0" name = "x86_64" readme = "README.md" repository = "https://github.com/rust-osdev/x86_64" -version = "0.14.11" +version = "0.15.0" edition = "2018" rust-version = "1.59" # Needed to support inline asm and default const generics From e24d8111febac1a375dcb82491a5c3efcf8b5612 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Jan 2024 16:04:46 +0000 Subject: [PATCH 60/64] remove deprecated from_bits_unchecked functions --- src/registers/control.rs | 24 ------------------------ src/registers/debug.rs | 12 ------------ src/registers/model_specific.rs | 16 ---------------- src/registers/mxcsr.rs | 8 -------- src/registers/rflags.rs | 8 -------- src/registers/xcontrol.rs | 8 -------- src/structures/gdt.rs | 6 ------ src/structures/idt.rs | 8 -------- src/structures/paging/page_table.rs | 8 -------- 9 files changed, 98 deletions(-) diff --git a/src/registers/control.rs b/src/registers/control.rs index eec587b3e..4056bfea8 100644 --- a/src/registers/control.rs +++ b/src/registers/control.rs @@ -51,14 +51,6 @@ bitflags! { } } -impl Cr0Flags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - /// Contains the Page Fault Linear Address (PFLA). /// /// When a page fault occurs, the CPU sets this register to the faulting virtual address. @@ -82,14 +74,6 @@ bitflags! { } } -impl Cr3Flags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - /// Contains various control flags that enable architectural extensions, and /// indicate support for specific processor capabilities. #[derive(Debug)] @@ -175,14 +159,6 @@ bitflags! { } } -impl Cr4Flags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - #[cfg(feature = "instructions")] mod x86_64 { use super::*; diff --git a/src/registers/debug.rs b/src/registers/debug.rs index f70119746..068b2ee52 100644 --- a/src/registers/debug.rs +++ b/src/registers/debug.rs @@ -160,12 +160,6 @@ impl Dr6Flags { DebugAddressRegisterNumber::Dr3 => Self::TRAP3, } } - - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } } bitflags! { @@ -239,12 +233,6 @@ impl Dr7Flags { DebugAddressRegisterNumber::Dr3 => Self::GLOBAL_BREAKPOINT_3_ENABLE, } } - - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } } /// The condition for a hardware breakpoint. diff --git a/src/registers/model_specific.rs b/src/registers/model_specific.rs index 0a60b847f..18d98bcda 100644 --- a/src/registers/model_specific.rs +++ b/src/registers/model_specific.rs @@ -132,14 +132,6 @@ bitflags! { } } -impl EferFlags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - bitflags! { /// Flags stored in IA32_U_CET and IA32_S_CET (Table-2-2 in Intel SDM Volume /// 4). The Intel SDM-equivalent names are described in parentheses. @@ -165,14 +157,6 @@ bitflags! { } } -impl CetFlags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - #[cfg(feature = "instructions")] mod x86_64 { use super::*; diff --git a/src/registers/mxcsr.rs b/src/registers/mxcsr.rs index 55e05cc56..19db2741d 100644 --- a/src/registers/mxcsr.rs +++ b/src/registers/mxcsr.rs @@ -60,14 +60,6 @@ impl Default for MxCsr { } } -impl MxCsr { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u32) -> Self { - Self::from_bits_retain(bits) - } -} - #[cfg(feature = "instructions")] mod x86_64 { use super::*; diff --git a/src/registers/rflags.rs b/src/registers/rflags.rs index c0f9510ac..921bb8f2c 100644 --- a/src/registers/rflags.rs +++ b/src/registers/rflags.rs @@ -64,14 +64,6 @@ bitflags! { } } -impl RFlags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - #[cfg(feature = "instructions")] mod x86_64 { use super::*; diff --git a/src/registers/xcontrol.rs b/src/registers/xcontrol.rs index ebb1d617a..09087a430 100644 --- a/src/registers/xcontrol.rs +++ b/src/registers/xcontrol.rs @@ -50,14 +50,6 @@ bitflags! { } } -impl XCr0Flags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - #[cfg(feature = "instructions")] mod x86_64 { use super::*; diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index d56e5793d..701c7a1d4 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -357,12 +357,6 @@ impl DescriptorFlags { /// A 64-bit user code segment pub const USER_CODE64: Self = Self::from_bits_truncate(Self::KERNEL_CODE64.bits() | Self::DPL_RING_3.bits()); - - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } } impl Descriptor { diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 5ec0d84d3..5505afe3d 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -1115,14 +1115,6 @@ bitflags! { } } -impl PageFaultErrorCode { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - /// Describes an error code referencing a segment selector. #[derive(Clone, Copy, PartialEq, Eq, Hash)] #[repr(transparent)] diff --git a/src/structures/paging/page_table.rs b/src/structures/paging/page_table.rs index 0af5350e1..03203ed52 100644 --- a/src/structures/paging/page_table.rs +++ b/src/structures/paging/page_table.rs @@ -169,14 +169,6 @@ bitflags! { } } -impl PageTableFlags { - #[deprecated = "use the safe `from_bits_retain` method instead"] - /// Convert from underlying bit representation, preserving all bits (even those not corresponding to a defined flag). - pub const unsafe fn from_bits_unchecked(bits: u64) -> Self { - Self::from_bits_retain(bits) - } -} - /// The number of entries in a page table. const ENTRY_COUNT: usize = 512; From e7f53ac46c2ff9a8f1bb2c6bacaeb3ceeb628d48 Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Sun, 14 Jan 2024 16:41:20 +0000 Subject: [PATCH 61/64] make `HandlerFuncType` unsafe --- src/structures/idt.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index 5ec0d84d3..023edf90a 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -831,7 +831,11 @@ impl Entry { } /// A common trait for all handler functions usable in [`Entry`]. -pub trait HandlerFuncType { +/// +/// # Safety +/// +/// Implementors have to ensure that `to_virt_addr` returns a valid address. +pub unsafe trait HandlerFuncType { /// Get the virtual address of the handler function. fn to_virt_addr(self) -> VirtAddr; } @@ -839,7 +843,7 @@ pub trait HandlerFuncType { macro_rules! impl_handler_func_type { ($f:ty) => { #[cfg(feature = "abi_x86_interrupt")] - impl HandlerFuncType for $f { + unsafe impl HandlerFuncType for $f { #[inline] fn to_virt_addr(self) -> VirtAddr { VirtAddr::new(self as u64) From 5384bd14c7dd51b9eea032c9b5c66aa19beccab5 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 14 Jan 2024 18:51:56 +0100 Subject: [PATCH 62/64] Update docs to clarify new defaults for `set_handler_fn` --- src/structures/idt.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/structures/idt.rs b/src/structures/idt.rs index c20892683..3952ae96f 100644 --- a/src/structures/idt.rs +++ b/src/structures/idt.rs @@ -771,10 +771,12 @@ impl Entry { } } - /// 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. + /// Sets the handler address 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. @@ -814,10 +816,12 @@ impl Entry { #[cfg(feature = "instructions")] impl Entry { - /// 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. + /// 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. From 0c368ad2b846ee56b7953cf9a9bcbc0870217e88 Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Sun, 14 Jan 2024 18:53:08 +0100 Subject: [PATCH 63/64] List new IDT defaults as breaking change in changelog --- Changelog.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 4d339e42d..bb4928eb2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ - [Add structures::gdt::Entry type](https://github.com/rust-osdev/x86_64/pull/380) - [Allow GDT to be loaded with shared reference](https://github.com/rust-osdev/x86_64/pull/381) - [seal off the `PageSize` trait](https://github.com/rust-osdev/x86_64/pull/404) +- [idt: Fixup Options structure and cleanup set_handler_fn](https://github.com/rust-osdev/x86_64/pull/226) ## New Features @@ -34,10 +35,6 @@ - [gdt: Check that MAX is in range](https://github.com/rust-osdev/x86_64/pull/365) - [fix `Page::from_page_table_indices`](https://github.com/rust-osdev/x86_64/pull/398) -## Other improvements - -- [idt: Fixup Options structure and cleanup set_handler_fn](https://github.com/rust-osdev/x86_64/pull/226) - # 0.14.11 – 2023-09-15 ## New Features From 8e13191086dd83e688850cc72ab90c97fa95cb5e Mon Sep 17 00:00:00 2001 From: Tom Dohrmann Date: Thu, 8 Feb 2024 17:44:12 +0100 Subject: [PATCH 64/64] mark as beta-release --- Cargo.toml | 2 +- Changelog.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 756ddbe66..b04202dd5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ license = "MIT/Apache-2.0" name = "x86_64" readme = "README.md" repository = "https://github.com/rust-osdev/x86_64" -version = "0.15.0" +version = "0.15.0-beta" edition = "2018" rust-version = "1.59" # Needed to support inline asm and default const generics diff --git a/Changelog.md b/Changelog.md index bb4928eb2..b52196e6d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,6 @@ # Unreleased -# 0.15.0 – 2024-01-14 +# 0.15.0-beta – 2024-02-08 ## Breaking changes