diff --git a/Changelog.md b/Changelog.md index 7b61d208e..567d3fc2b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ # Unreleased +- Add additional `DescriptorFlags` and aliases compatible with `syscall`/`sysenter` ([#181](https://github.com/rust-osdev/x86_64/pull/181)) +- Fix (another) build error on latest nightly ([#186](https://github.com/rust-osdev/x86_64/pull/186)) + # 0.12.1 – 2020-09-24 - Fix build error on latest nightly ([#182](https://github.com/rust-osdev/x86_64/pull/182)) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 68a07d2cb..54e0a4c9c 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -184,55 +184,114 @@ pub enum Descriptor { bitflags! { /// Flags for a GDT descriptor. Not all flags are valid for all descriptor types. pub struct DescriptorFlags: u64 { - /// For data segments, this flag sets the segment as writable. Ignored for code segments. + /// Set by the processor if this segment has been accessed. Only cleared by software. + /// _Setting_ this bit in software prevents GDT writes on first use. + const ACCESSED = 1 << 40; + /// For 32-bit data segments, sets the segment as writable. For 32-bit code segments, + /// sets the segment as _readable_. In 64-bit mode, ignored for all segments. const WRITABLE = 1 << 41; - /// Marks a code segment as “conforming”. This influences the privilege checks that - /// occur on control transfers. + /// For code segments, sets the segment as “conforming”, influencing the + /// privilege checks that occur on control transfers. For 32-bit data segments, + /// sets the segment as "expand down". In 64-bit mode, ignored for data segments. const CONFORMING = 1 << 42; - /// This flag must be set for code segments. + /// This flag must be set for code segments and unset for data segments. const EXECUTABLE = 1 << 43; /// This flag must be set for user segments (in contrast to system segments). const USER_SEGMENT = 1 << 44; + /// The DPL for this descriptor is Ring 3. In 64-bit mode, ignored for data segments. + const DPL_RING_3 = 3 << 45; /// Must be set for any segment, causes a segment not present exception if not set. const PRESENT = 1 << 47; - /// Must be set for long mode code segments. + /// Available for use by the Operating System + const AVAILABLE = 1 << 52; + /// Must be set for 64-bit code segments, unset otherwise. const LONG_MODE = 1 << 53; + /// Use 32-bit (as opposed to 16-bit) operands. If [`LONG_MODE`][Self::LONG_MODE] is set, + /// this must be unset. In 64-bit mode, ignored for data segments. + const DEFAULT_SIZE = 1 << 54; + /// Limit field is scaled by 4096 bytes. In 64-bit mode, ignored for all segments. + const GRANULARITY = 1 << 55; - /// The DPL for this descriptor is Ring 3 - const DPL_RING_3 = 3 << 45; + /// Bits `0..=15` of the limit field (ignored in 64-bit mode) + const LIMIT_0_15 = 0xFFFF; + /// Bits `16..=19` of the limit field (ignored in 64-bit mode) + const LIMIT_16_19 = 0xF << 48; + /// Bits `0..=23` of the base field (ignored in 64-bit mode, except for fs and gs) + const BASE_0_23 = 0xFF_FFFF << 16; + /// Bits `24..=31` of the base field (ignored in 64-bit mode, except for fs and gs) + const BASE_24_31 = 0xFF << 56; } } +/// The following constants define default values for common GDT entries. They +/// are all "flat" segments, meaning they can access the entire address space. +/// These values all set [`WRITABLE`][DescriptorFlags::WRITABLE] and +/// [`ACCESSED`][DescriptorFlags::ACCESSED]. They also match the values loaded +/// by the `syscall`/`sysret` and `sysenter`/`sysexit` instructions. +/// +/// In short, these values disable segmentation, permission checks, and access +/// tracking at the GDT level. Kernels using these values should use paging to +/// implement this functionality. +impl DescriptorFlags { + // Flags that we set for all our default segments + const COMMON: Self = Self::from_bits_truncate( + Self::USER_SEGMENT.bits() + | Self::PRESENT.bits() + | Self::WRITABLE.bits() + | Self::ACCESSED.bits() + | Self::LIMIT_0_15.bits() + | Self::LIMIT_16_19.bits() + | Self::GRANULARITY.bits(), + ); + /// A kernel data segment (64-bit or flat 32-bit) + pub const KERNEL_DATA: Self = + Self::from_bits_truncate(Self::COMMON.bits() | Self::DEFAULT_SIZE.bits()); + /// A flat 32-bit kernel code segment + pub const KERNEL_CODE32: Self = Self::from_bits_truncate( + Self::COMMON.bits() | Self::EXECUTABLE.bits() | Self::DEFAULT_SIZE.bits(), + ); + /// A 64-bit kernel code segment + pub const KERNEL_CODE64: Self = Self::from_bits_truncate( + Self::COMMON.bits() | Self::EXECUTABLE.bits() | Self::LONG_MODE.bits(), + ); + /// A user data segment (64-bit or flat 32-bit) + pub const USER_DATA: Self = + Self::from_bits_truncate(Self::KERNEL_DATA.bits() | Self::DPL_RING_3.bits()); + /// A flat 32-bit user code segment + pub const USER_CODE32: Self = + Self::from_bits_truncate(Self::KERNEL_CODE32.bits() | Self::DPL_RING_3.bits()); + /// A 64-bit user code segment + pub const USER_CODE64: Self = + Self::from_bits_truncate(Self::KERNEL_CODE64.bits() | Self::DPL_RING_3.bits()); +} + impl Descriptor { - /// Creates a segment descriptor for a long mode kernel code segment. + /// Creates a segment descriptor for a 64-bit kernel code segment. Suitable + /// for use with `syscall` or 64-bit `sysenter`. #[inline] - pub fn kernel_code_segment() -> Descriptor { - use self::DescriptorFlags as Flags; - - let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::LONG_MODE; - Descriptor::UserSegment(flags.bits()) + pub const fn kernel_code_segment() -> Descriptor { + Descriptor::UserSegment(DescriptorFlags::KERNEL_CODE64.bits()) } - /// Creates a segment descriptor for a long mode ring 3 data segment. + /// Creates a segment descriptor for a kernel data segment (32-bit or + /// 64-bit). Suitable for use with `syscall` or `sysenter`. #[inline] - pub fn user_data_segment() -> Descriptor { - use self::DescriptorFlags as Flags; - - let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::WRITABLE | Flags::DPL_RING_3; - Descriptor::UserSegment(flags.bits()) + pub const fn kernel_data_segment() -> Descriptor { + Descriptor::UserSegment(DescriptorFlags::KERNEL_DATA.bits()) } - /// Creates a segment descriptor for a long mode ring 3 code segment. + /// Creates a segment descriptor for a ring 3 data segment (32-bit or + /// 64-bit). Suitable for use with `sysret` or `sysexit`. #[inline] - pub fn user_code_segment() -> Descriptor { - use self::DescriptorFlags as Flags; + pub const fn user_data_segment() -> Descriptor { + Descriptor::UserSegment(DescriptorFlags::USER_DATA.bits()) + } - let flags = Flags::USER_SEGMENT - | Flags::PRESENT - | Flags::EXECUTABLE - | Flags::LONG_MODE - | Flags::DPL_RING_3; - Descriptor::UserSegment(flags.bits()) + /// Creates a segment descriptor for a 64-bit ring 3 code segment. Suitable + /// for use with `sysret` or `sysexit`. + #[inline] + pub const fn user_code_segment() -> Descriptor { + Descriptor::UserSegment(DescriptorFlags::USER_CODE64.bits()) } /// Creates a TSS system descriptor for the given TSS. @@ -258,3 +317,21 @@ impl Descriptor { Descriptor::SystemSegment(low, high) } } + +#[cfg(test)] +mod tests { + use super::DescriptorFlags as Flags; + + #[test] + #[rustfmt::skip] + pub fn linux_kernel_defaults() { + // Make sure our defaults match the ones used by the Linux kernel. + // Constants pulled from an old version of arch/x86/kernel/cpu/common.c + assert_eq!(Flags::KERNEL_CODE64.bits(), 0x00af9b000000ffff); + assert_eq!(Flags::KERNEL_CODE32.bits(), 0x00cf9b000000ffff); + assert_eq!(Flags::KERNEL_DATA.bits(), 0x00cf93000000ffff); + assert_eq!(Flags::USER_CODE64.bits(), 0x00affb000000ffff); + assert_eq!(Flags::USER_CODE32.bits(), 0x00cffb000000ffff); + assert_eq!(Flags::USER_DATA.bits(), 0x00cff3000000ffff); + } +}