From 4e092262b7221c965028b174729c00e734c71a96 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 22 Sep 2020 20:57:45 -0700 Subject: [PATCH 1/4] gdt: Add remaining GDT flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the remaining GDT flags. This now allows users to create 32-bit and 64-bit GDTs. We also add flag aliases for common cases. We then verify that these cases match what the Linux kernel uses by default. This also allows us to make additional methods `const fn`. I used [AMD64 Architecture Programmer’s Manual, Volume 2: System Programming](https://www.amd.com/system/files/TechDocs/24593.pdf) as the main reference for this PR. Two remaining open questions: - Should we set `WRITABLE` (aka Readable for code-segments) by default? - This bit is ignored in Long Mode (for code and data) - Linux always sets it by default - This is probably what users want for legacy mode segments. - Should we set `ACCESSED` by default? - Linux [sets it by default](https://github.com/torvalds/linux/blob/00e4db51259a5f936fec1424b884f029479d3981/arch/x86/kernel/cpu/common.c#L124-L129) - Except [sometimes it doesn't](https://github.com/torvalds/linux/blob/ab851d49f6bfc781edd8bd44c72ec1e49211670b/arch/x86/platform/pvh/head.S#L160-L166) - AMD claims this is ignored, but Intel doesn't make the same claim Signed-off-by: Joe Richey --- src/structures/gdt.rs | 101 ++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 29 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 68a07d2cb..633aaa10d 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -184,55 +184,80 @@ 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. + 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`] 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) + const BASE_0_23 = 0xFF_FFFF << 16; + /// Bits 24..=31 of the base field (ignored in 64-bit mode) + const BASE_24_31 = 0xFF << 56; + + /// Flags that should be set for all flat user segments + const FLAT_COMMON = Self::LIMIT_0_15.bits | Self::LIMIT_16_19.bits + | Self::GRANULARITY.bits | Self::ACCESSED.bits | Self::WRITABLE.bits + | Self::USER_SEGMENT.bits | Self::PRESENT.bits; + + /// Flags for a flat 32-bit kernel code segment + const KERNEL_CODE32 = Self::FLAT_COMMON.bits | Self::EXECUTABLE.bits | Self::DEFAULT_SIZE.bits; + /// Flags for a 64-bit kernel code segment + const KERNEL_CODE64 = Self::FLAT_COMMON.bits | Self::EXECUTABLE.bits | Self::LONG_MODE.bits; + /// Flags for a kernel data segment (64-bit or flat 32-bit) + const KERNEL_DATA = Self::FLAT_COMMON.bits | Self::DEFAULT_SIZE.bits; + + /// Flags for a flat 32-bit user code segment + const USER_CODE32 = Self::KERNEL_CODE32.bits | Self::DPL_RING_3.bits; + /// Flags for a 64-bit user code segment + const USER_CODE64 = Self::KERNEL_CODE64.bits | Self::DPL_RING_3.bits; + /// Flags for a user data segment (64-bit or flat 32-bit) + const USER_DATA = Self::KERNEL_DATA.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. #[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 ring 3 data segment (64-bit or 32-bit). #[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 user_data_segment() -> Descriptor { + Descriptor::UserSegment(DescriptorFlags::USER_DATA.bits()) } - /// Creates a segment descriptor for a long mode ring 3 code segment. + /// Creates a segment descriptor for a 64-bit ring 3 code segment. #[inline] - pub fn user_code_segment() -> Descriptor { - use self::DescriptorFlags as Flags; - - let flags = Flags::USER_SEGMENT - | Flags::PRESENT - | Flags::EXECUTABLE - | Flags::LONG_MODE - | Flags::DPL_RING_3; - Descriptor::UserSegment(flags.bits()) + pub const fn user_code_segment() -> Descriptor { + Descriptor::UserSegment(DescriptorFlags::USER_CODE64.bits()) } /// Creates a TSS system descriptor for the given TSS. @@ -258,3 +283,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_CODE32.bits(), 0x00cf9b000000ffff); + assert_eq!(Flags::KERNEL_CODE64.bits(), 0x00af9b000000ffff); + assert_eq!(Flags::KERNEL_DATA.bits(), 0x00cf93000000ffff); + assert_eq!(Flags::USER_CODE32.bits(), 0x00cffb000000ffff); + assert_eq!(Flags::USER_DATA.bits(), 0x00cff3000000ffff); + assert_eq!(Flags::USER_CODE64.bits(), 0x00affb000000ffff); + } +} From e265b0e794671e36d497c7d734a18520acd1efc8 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 23 Sep 2020 02:54:59 -0700 Subject: [PATCH 2/4] gdt: Clarify documentation and move flags Signed-off-by: Joe Richey --- src/structures/gdt.rs | 83 +++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index 633aaa10d..edac050de 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -185,6 +185,7 @@ bitflags! { /// Flags for a GDT descriptor. Not all flags are valid for all descriptor types. pub struct DescriptorFlags: u64 { /// 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. @@ -205,42 +206,68 @@ bitflags! { 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`] is set, + /// 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; - /// Bits 0..=15 of the limit field (ignored in 64-bit mode) + /// 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) + /// 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) + /// Bits `0..=23` of the base field (ignored in 64-bit mode) const BASE_0_23 = 0xFF_FFFF << 16; - /// Bits 24..=31 of the base field (ignored in 64-bit mode) + /// Bits `24..=31` of the base field (ignored in 64-bit mode) const BASE_24_31 = 0xFF << 56; - - /// Flags that should be set for all flat user segments - const FLAT_COMMON = Self::LIMIT_0_15.bits | Self::LIMIT_16_19.bits - | Self::GRANULARITY.bits | Self::ACCESSED.bits | Self::WRITABLE.bits - | Self::USER_SEGMENT.bits | Self::PRESENT.bits; - - /// Flags for a flat 32-bit kernel code segment - const KERNEL_CODE32 = Self::FLAT_COMMON.bits | Self::EXECUTABLE.bits | Self::DEFAULT_SIZE.bits; - /// Flags for a 64-bit kernel code segment - const KERNEL_CODE64 = Self::FLAT_COMMON.bits | Self::EXECUTABLE.bits | Self::LONG_MODE.bits; - /// Flags for a kernel data segment (64-bit or flat 32-bit) - const KERNEL_DATA = Self::FLAT_COMMON.bits | Self::DEFAULT_SIZE.bits; - - /// Flags for a flat 32-bit user code segment - const USER_CODE32 = Self::KERNEL_CODE32.bits | Self::DPL_RING_3.bits; - /// Flags for a 64-bit user code segment - const USER_CODE64 = Self::KERNEL_CODE64.bits | Self::DPL_RING_3.bits; - /// Flags for a user data segment (64-bit or flat 32-bit) - const USER_DATA = Self::KERNEL_DATA.bits | Self::DPL_RING_3.bits; } } +/// The following constants define default values for common GDT use-cases. They +/// are all "flat" segments meaning that they can access the entire address +/// space. These values all set [`WRITABLE`][DescriptorFlags::WRITABLE] and +/// [`ACCESSED`][DescriptorFlags::ACCESSED]. +/// +/// 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 user segments + const COMMON: Self = Self::from_bits_truncate( + Self::USER_SEGMENT.bits() + | Self::PRESENT.bits() + | Self::WRITABLE.bits() + | Self::ACCESSED.bits(), + ); + // Flags that need to be set for all flat 32-bit segments + const COMMON_FLAT: Self = Self::from_bits_truncate( + Self::COMMON.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_FLAT.bits() | Self::DEFAULT_SIZE.bits()); + /// A flat 32-bit kernel code segment + pub const KERNEL_CODE32: Self = Self::from_bits_truncate( + Self::COMMON_FLAT.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 64-bit kernel code segment. #[inline] @@ -291,13 +318,15 @@ mod tests { #[test] #[rustfmt::skip] pub fn linux_kernel_defaults() { - // Make sure our defaults match the ones used by the Linux kernel. + // Make sure our 32-bit 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_CODE32.bits(), 0x00cf9b000000ffff); - assert_eq!(Flags::KERNEL_CODE64.bits(), 0x00af9b000000ffff); assert_eq!(Flags::KERNEL_DATA.bits(), 0x00cf93000000ffff); assert_eq!(Flags::USER_CODE32.bits(), 0x00cffb000000ffff); assert_eq!(Flags::USER_DATA.bits(), 0x00cff3000000ffff); - assert_eq!(Flags::USER_CODE64.bits(), 0x00affb000000ffff); + + // Our 64-bit code segments only use a subset of the kernel's flags + assert!(Flags::from_bits(0x00af9b000000ffff).unwrap().contains(Flags::KERNEL_CODE64)); + assert!(Flags::from_bits(0x00affb000000ffff).unwrap().contains(Flags::KERNEL_CODE64)); } } From 5c11d0d751b838572691870e6ddbd60c828568df Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Sun, 27 Sep 2020 00:58:59 -0700 Subject: [PATCH 3/4] gdt: Set unused bits for syscall/sysret compatiblity Also update the documentation to note that fs/gs don't ignored the BASE value. Signed-off-by: Joe Richey --- src/structures/gdt.rs | 49 ++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/structures/gdt.rs b/src/structures/gdt.rs index edac050de..54e0a4c9c 100644 --- a/src/structures/gdt.rs +++ b/src/structures/gdt.rs @@ -216,42 +216,39 @@ bitflags! { 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) + /// 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) + /// 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 use-cases. They -/// are all "flat" segments meaning that they can access the entire address -/// space. These values all set [`WRITABLE`][DescriptorFlags::WRITABLE] and -/// [`ACCESSED`][DescriptorFlags::ACCESSED]. +/// 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 user segments + // 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(), - ); - // Flags that need to be set for all flat 32-bit segments - const COMMON_FLAT: Self = Self::from_bits_truncate( - Self::COMMON.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_FLAT.bits() | Self::DEFAULT_SIZE.bits()); + 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_FLAT.bits() | Self::EXECUTABLE.bits() | Self::DEFAULT_SIZE.bits(), + 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( @@ -269,19 +266,29 @@ impl DescriptorFlags { } impl Descriptor { - /// Creates a segment descriptor for a 64-bit 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 const fn kernel_code_segment() -> Descriptor { Descriptor::UserSegment(DescriptorFlags::KERNEL_CODE64.bits()) } - /// Creates a segment descriptor for a ring 3 data segment (64-bit or 32-bit). + /// Creates a segment descriptor for a kernel data segment (32-bit or + /// 64-bit). Suitable for use with `syscall` or `sysenter`. + #[inline] + pub const fn kernel_data_segment() -> Descriptor { + Descriptor::UserSegment(DescriptorFlags::KERNEL_DATA.bits()) + } + + /// Creates a segment descriptor for a ring 3 data segment (32-bit or + /// 64-bit). Suitable for use with `sysret` or `sysexit`. #[inline] pub const fn user_data_segment() -> Descriptor { Descriptor::UserSegment(DescriptorFlags::USER_DATA.bits()) } - /// Creates a segment descriptor for a 64-bit ring 3 code segment. + /// 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()) @@ -318,15 +325,13 @@ mod tests { #[test] #[rustfmt::skip] pub fn linux_kernel_defaults() { - // Make sure our 32-bit defaults match the ones used by the Linux kernel. + // 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); - - // Our 64-bit code segments only use a subset of the kernel's flags - assert!(Flags::from_bits(0x00af9b000000ffff).unwrap().contains(Flags::KERNEL_CODE64)); - assert!(Flags::from_bits(0x00affb000000ffff).unwrap().contains(Flags::KERNEL_CODE64)); } } From d6a45d72f923e9a0efd3d93002850609ec84edc6 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Tue, 29 Sep 2020 00:10:54 -0700 Subject: [PATCH 4/4] Update Changelog Signed-off-by: Joe Richey --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) 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))