Skip to content

Commit d2f3145

Browse files
authored
Merge pull request #181 from josephlr/gdt
gdt: Add remaining GDT flags
2 parents 885a5c0 + d6a45d7 commit d2f3145

File tree

2 files changed

+108
-28
lines changed

2 files changed

+108
-28
lines changed

Changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# Unreleased
22

3+
- Add additional `DescriptorFlags` and aliases compatible with `syscall`/`sysenter` ([#181](https://github.com/rust-osdev/x86_64/pull/181))
4+
- Fix (another) build error on latest nightly ([#186](https://github.com/rust-osdev/x86_64/pull/186))
5+
36
# 0.12.1 – 2020-09-24
47

58
- Fix build error on latest nightly ([#182](https://github.com/rust-osdev/x86_64/pull/182))

src/structures/gdt.rs

Lines changed: 105 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -184,55 +184,114 @@ pub enum Descriptor {
184184
bitflags! {
185185
/// Flags for a GDT descriptor. Not all flags are valid for all descriptor types.
186186
pub struct DescriptorFlags: u64 {
187-
/// For data segments, this flag sets the segment as writable. Ignored for code segments.
187+
/// Set by the processor if this segment has been accessed. Only cleared by software.
188+
/// _Setting_ this bit in software prevents GDT writes on first use.
189+
const ACCESSED = 1 << 40;
190+
/// For 32-bit data segments, sets the segment as writable. For 32-bit code segments,
191+
/// sets the segment as _readable_. In 64-bit mode, ignored for all segments.
188192
const WRITABLE = 1 << 41;
189-
/// Marks a code segment as “conforming”. This influences the privilege checks that
190-
/// occur on control transfers.
193+
/// For code segments, sets the segment as “conforming”, influencing the
194+
/// privilege checks that occur on control transfers. For 32-bit data segments,
195+
/// sets the segment as "expand down". In 64-bit mode, ignored for data segments.
191196
const CONFORMING = 1 << 42;
192-
/// This flag must be set for code segments.
197+
/// This flag must be set for code segments and unset for data segments.
193198
const EXECUTABLE = 1 << 43;
194199
/// This flag must be set for user segments (in contrast to system segments).
195200
const USER_SEGMENT = 1 << 44;
201+
/// The DPL for this descriptor is Ring 3. In 64-bit mode, ignored for data segments.
202+
const DPL_RING_3 = 3 << 45;
196203
/// Must be set for any segment, causes a segment not present exception if not set.
197204
const PRESENT = 1 << 47;
198-
/// Must be set for long mode code segments.
205+
/// Available for use by the Operating System
206+
const AVAILABLE = 1 << 52;
207+
/// Must be set for 64-bit code segments, unset otherwise.
199208
const LONG_MODE = 1 << 53;
209+
/// Use 32-bit (as opposed to 16-bit) operands. If [`LONG_MODE`][Self::LONG_MODE] is set,
210+
/// this must be unset. In 64-bit mode, ignored for data segments.
211+
const DEFAULT_SIZE = 1 << 54;
212+
/// Limit field is scaled by 4096 bytes. In 64-bit mode, ignored for all segments.
213+
const GRANULARITY = 1 << 55;
200214

201-
/// The DPL for this descriptor is Ring 3
202-
const DPL_RING_3 = 3 << 45;
215+
/// Bits `0..=15` of the limit field (ignored in 64-bit mode)
216+
const LIMIT_0_15 = 0xFFFF;
217+
/// Bits `16..=19` of the limit field (ignored in 64-bit mode)
218+
const LIMIT_16_19 = 0xF << 48;
219+
/// Bits `0..=23` of the base field (ignored in 64-bit mode, except for fs and gs)
220+
const BASE_0_23 = 0xFF_FFFF << 16;
221+
/// Bits `24..=31` of the base field (ignored in 64-bit mode, except for fs and gs)
222+
const BASE_24_31 = 0xFF << 56;
203223
}
204224
}
205225

226+
/// The following constants define default values for common GDT entries. They
227+
/// are all "flat" segments, meaning they can access the entire address space.
228+
/// These values all set [`WRITABLE`][DescriptorFlags::WRITABLE] and
229+
/// [`ACCESSED`][DescriptorFlags::ACCESSED]. They also match the values loaded
230+
/// by the `syscall`/`sysret` and `sysenter`/`sysexit` instructions.
231+
///
232+
/// In short, these values disable segmentation, permission checks, and access
233+
/// tracking at the GDT level. Kernels using these values should use paging to
234+
/// implement this functionality.
235+
impl DescriptorFlags {
236+
// Flags that we set for all our default segments
237+
const COMMON: Self = Self::from_bits_truncate(
238+
Self::USER_SEGMENT.bits()
239+
| Self::PRESENT.bits()
240+
| Self::WRITABLE.bits()
241+
| Self::ACCESSED.bits()
242+
| Self::LIMIT_0_15.bits()
243+
| Self::LIMIT_16_19.bits()
244+
| Self::GRANULARITY.bits(),
245+
);
246+
/// A kernel data segment (64-bit or flat 32-bit)
247+
pub const KERNEL_DATA: Self =
248+
Self::from_bits_truncate(Self::COMMON.bits() | Self::DEFAULT_SIZE.bits());
249+
/// A flat 32-bit kernel code segment
250+
pub const KERNEL_CODE32: Self = Self::from_bits_truncate(
251+
Self::COMMON.bits() | Self::EXECUTABLE.bits() | Self::DEFAULT_SIZE.bits(),
252+
);
253+
/// A 64-bit kernel code segment
254+
pub const KERNEL_CODE64: Self = Self::from_bits_truncate(
255+
Self::COMMON.bits() | Self::EXECUTABLE.bits() | Self::LONG_MODE.bits(),
256+
);
257+
/// A user data segment (64-bit or flat 32-bit)
258+
pub const USER_DATA: Self =
259+
Self::from_bits_truncate(Self::KERNEL_DATA.bits() | Self::DPL_RING_3.bits());
260+
/// A flat 32-bit user code segment
261+
pub const USER_CODE32: Self =
262+
Self::from_bits_truncate(Self::KERNEL_CODE32.bits() | Self::DPL_RING_3.bits());
263+
/// A 64-bit user code segment
264+
pub const USER_CODE64: Self =
265+
Self::from_bits_truncate(Self::KERNEL_CODE64.bits() | Self::DPL_RING_3.bits());
266+
}
267+
206268
impl Descriptor {
207-
/// Creates a segment descriptor for a long mode kernel code segment.
269+
/// Creates a segment descriptor for a 64-bit kernel code segment. Suitable
270+
/// for use with `syscall` or 64-bit `sysenter`.
208271
#[inline]
209-
pub fn kernel_code_segment() -> Descriptor {
210-
use self::DescriptorFlags as Flags;
211-
212-
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::EXECUTABLE | Flags::LONG_MODE;
213-
Descriptor::UserSegment(flags.bits())
272+
pub const fn kernel_code_segment() -> Descriptor {
273+
Descriptor::UserSegment(DescriptorFlags::KERNEL_CODE64.bits())
214274
}
215275

216-
/// Creates a segment descriptor for a long mode ring 3 data segment.
276+
/// Creates a segment descriptor for a kernel data segment (32-bit or
277+
/// 64-bit). Suitable for use with `syscall` or `sysenter`.
217278
#[inline]
218-
pub fn user_data_segment() -> Descriptor {
219-
use self::DescriptorFlags as Flags;
220-
221-
let flags = Flags::USER_SEGMENT | Flags::PRESENT | Flags::WRITABLE | Flags::DPL_RING_3;
222-
Descriptor::UserSegment(flags.bits())
279+
pub const fn kernel_data_segment() -> Descriptor {
280+
Descriptor::UserSegment(DescriptorFlags::KERNEL_DATA.bits())
223281
}
224282

225-
/// Creates a segment descriptor for a long mode ring 3 code segment.
283+
/// Creates a segment descriptor for a ring 3 data segment (32-bit or
284+
/// 64-bit). Suitable for use with `sysret` or `sysexit`.
226285
#[inline]
227-
pub fn user_code_segment() -> Descriptor {
228-
use self::DescriptorFlags as Flags;
286+
pub const fn user_data_segment() -> Descriptor {
287+
Descriptor::UserSegment(DescriptorFlags::USER_DATA.bits())
288+
}
229289

230-
let flags = Flags::USER_SEGMENT
231-
| Flags::PRESENT
232-
| Flags::EXECUTABLE
233-
| Flags::LONG_MODE
234-
| Flags::DPL_RING_3;
235-
Descriptor::UserSegment(flags.bits())
290+
/// Creates a segment descriptor for a 64-bit ring 3 code segment. Suitable
291+
/// for use with `sysret` or `sysexit`.
292+
#[inline]
293+
pub const fn user_code_segment() -> Descriptor {
294+
Descriptor::UserSegment(DescriptorFlags::USER_CODE64.bits())
236295
}
237296

238297
/// Creates a TSS system descriptor for the given TSS.
@@ -258,3 +317,21 @@ impl Descriptor {
258317
Descriptor::SystemSegment(low, high)
259318
}
260319
}
320+
321+
#[cfg(test)]
322+
mod tests {
323+
use super::DescriptorFlags as Flags;
324+
325+
#[test]
326+
#[rustfmt::skip]
327+
pub fn linux_kernel_defaults() {
328+
// Make sure our defaults match the ones used by the Linux kernel.
329+
// Constants pulled from an old version of arch/x86/kernel/cpu/common.c
330+
assert_eq!(Flags::KERNEL_CODE64.bits(), 0x00af9b000000ffff);
331+
assert_eq!(Flags::KERNEL_CODE32.bits(), 0x00cf9b000000ffff);
332+
assert_eq!(Flags::KERNEL_DATA.bits(), 0x00cf93000000ffff);
333+
assert_eq!(Flags::USER_CODE64.bits(), 0x00affb000000ffff);
334+
assert_eq!(Flags::USER_CODE32.bits(), 0x00cffb000000ffff);
335+
assert_eq!(Flags::USER_DATA.bits(), 0x00cff3000000ffff);
336+
}
337+
}

0 commit comments

Comments
 (0)