aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Gerd Zellweger <mail@gerdzellweger.com> 2018-04-26 14:06:06 -0700
committerGravatar Gerd Zellweger <mail@gerdzellweger.com> 2018-04-26 14:06:06 -0700
commitca17286ab682cc0c89f1d7f335f90168ce0bfdf9 (patch)
tree09bfb9cedafa856eff44a4d89d883cc10b8739aa
parentee389098aaff520aa36096734ac8d974d58e7181 (diff)
downloadrust-x86-ca17286ab682cc0c89f1d7f335f90168ce0bfdf9.tar.gz
rust-x86-ca17286ab682cc0c89f1d7f335f90168ce0bfdf9.tar.zst
rust-x86-ca17286ab682cc0c89f1d7f335f90168ce0bfdf9.zip
Rewrite all segmentation code and some minor refactoring.
Signed-off-by: Gerd Zellweger <mail@gerdzellweger.com>
-rw-r--r--.travis.yml1
-rw-r--r--src/bits16/mod.rs1
-rw-r--r--src/bits16/segmentation.rs25
-rw-r--r--src/bits32/dtables.rs19
-rw-r--r--src/bits32/irq.rs58
-rw-r--r--src/bits32/mod.rs4
-rw-r--r--src/bits32/segmentation.rs201
-rw-r--r--src/bits64/dtables.rs18
-rw-r--r--src/bits64/irq.rs86
-rw-r--r--src/bits64/mod.rs19
-rw-r--r--src/bits64/segmentation.rs120
-rw-r--r--src/dtables.rs19
-rw-r--r--src/lib.rs23
-rw-r--r--src/segmentation.rs345
14 files changed, 517 insertions, 422 deletions
diff --git a/.travis.yml b/.travis.yml
index 2128c58..5dd2f72 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,6 +14,7 @@ rust:
env:
global:
- CRATE_NAME=x86
+ - RUSTFLAGS='-D warnings'
matrix:
# TODO These are all the build jobs. Adjust as necessary. Comment out what you
diff --git a/src/bits16/mod.rs b/src/bits16/mod.rs
new file mode 100644
index 0000000..458319a
--- /dev/null
+++ b/src/bits16/mod.rs
@@ -0,0 +1 @@
+pub mod segmentation;
diff --git a/src/bits16/segmentation.rs b/src/bits16/segmentation.rs
new file mode 100644
index 0000000..029d2e1
--- /dev/null
+++ b/src/bits16/segmentation.rs
@@ -0,0 +1,25 @@
+use segmentation::{SegmentSelector, DescriptorBuilder, GateDescriptorBuilder, DescriptorType, SystemDescriptorTypes32};
+
+impl GateDescriptorBuilder<u16> for DescriptorBuilder {
+
+ fn tss_descriptor(selector: SegmentSelector, offset: u16, available: bool) -> DescriptorBuilder {
+ let typ = match available {
+ true => DescriptorType::System32(SystemDescriptorTypes32::TssAvailable32),
+ false => DescriptorType::System32(SystemDescriptorTypes32::TssBusy32),
+ };
+
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(typ)
+ }
+
+ fn call_gate_descriptor(selector: SegmentSelector, offset: u16) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(DescriptorType::System32(SystemDescriptorTypes32::CallGate16))
+ }
+
+ fn interrupt_descriptor(selector: SegmentSelector, offset: u16) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(DescriptorType::System32(SystemDescriptorTypes32::InterruptGate16))
+ }
+
+ fn trap_gate_descriptor(selector: SegmentSelector, offset: u16) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(DescriptorType::System32(SystemDescriptorTypes32::TrapGate16))
+ }
+} \ No newline at end of file
diff --git a/src/bits32/dtables.rs b/src/bits32/dtables.rs
new file mode 100644
index 0000000..2be3be3
--- /dev/null
+++ b/src/bits32/dtables.rs
@@ -0,0 +1,19 @@
+//! Functions to load descriptor tables.
+use dtables::DescriptorTablePointer;
+use bits32::segmentation::Descriptor32;
+
+/// Load GDT table with 32bit descriptors
+pub unsafe fn lgdt(gdt: &DescriptorTablePointer<Descriptor32>) {
+ asm!("lgdt ($0)" :: "r" (gdt) : "memory");
+}
+
+/// Load LDT table with 32bit descriptors.
+pub unsafe fn lldt(ldt: &DescriptorTablePointer<Descriptor32>) {
+ asm!("lldt ($0)" :: "r" (ldt) : "memory");
+}
+
+/// Load IDT table with 32bit descriptors.
+pub unsafe fn lidt(idt: &DescriptorTablePointer<Descriptor32>) {
+ asm!("lidt ($0)" :: "r" (idt) : "memory");
+}
+
diff --git a/src/bits32/irq.rs b/src/bits32/irq.rs
deleted file mode 100644
index bc385ee..0000000
--- a/src/bits32/irq.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-//! Interrupt description and set-up code.
-/*
-use ::descriptor::*;
-use ::paging::VAddr;
-use ::Ring;
-
-/// An interrupt gate or trap gate descriptor.
-///
-/// See Intel manual 3a for details, specifically section 6.11.
-#[derive(Debug, Copy, Clone)]
-#[repr(C, packed)]
-pub struct IdtEntry {
- /// Lower 16 bits of ISR.
- pub offset_lo: u16,
- /// Segment selector.
- pub selector: u16,
- /// This must always be zero.
- pub reserved: u8,
- /// flags.
- pub struct: Flags,
- /// The upper 16 bits of ISR.
- pub offset_hi: u16
-}
-
-impl IdtEntry {
- pub const MISSING: IdtEntry = IdtEntry {
- offset_lo: 0,
- selector: 0,
- reserved: 0,
- flags: Flags::BLANK,
- offset_hi: 0
- };
-
- /// Create a new IdtEntry pointing at `handler`, which must be a function
- /// with interrupt calling conventions. (This must be currently defined in
- /// assembly language.) The `gdt_code_selector` value must be the offset of
- /// code segment entry in the GDT.
- ///
- /// The "Present" flag set, which is the most common case. If you need
- /// something else, you can construct it manually.
- pub const fn new(handler: VAddr, gdt_code_selector: u16,
- dpl: Ring, block: bool) -> IdtEntry {
- IdtEntry {
- offset_lo: ((handler.as_usize() as u32) & 0xFFFF) as u16,
- offset_hi: ((handler.as_usize() as u32 & 0xFFFF0000) >> 16) as u16,
- selector: gdt_code_selector,
- reserved: 0,
- // Nice bitflags operations don't work in const fn, hence these
- // ad-hoc methods.
- flags: Flags::from_priv(dpl)
- .const_or(FLAGS_TYPE_SYS_NATIVE_INTERRUPT_GATE
- .const_mux(FLAGS_TYPE_SYS_NATIVE_TRAP_GATE,
- block))
- .const_or(FLAGS_PRESENT),
- }
- }
-}
-*/ \ No newline at end of file
diff --git a/src/bits32/mod.rs b/src/bits32/mod.rs
index d4f76f6..87a9d9c 100644
--- a/src/bits32/mod.rs
+++ b/src/bits32/mod.rs
@@ -1,4 +1,4 @@
-pub mod irq;
+pub mod dtables;
pub mod eflags;
pub mod segmentation;
pub mod task;
@@ -7,4 +7,4 @@ pub mod task;
pub unsafe fn stack_jmp(stack: *mut (), ip: *const ()) -> ! {
asm!("mov esp, $0; jmp $1" :: "rg"(stack), "r"(ip) :: "volatile", "intel");
loop { }
-}
+} \ No newline at end of file
diff --git a/src/bits32/segmentation.rs b/src/bits32/segmentation.rs
index 5327d5e..d75f4d8 100644
--- a/src/bits32/segmentation.rs
+++ b/src/bits32/segmentation.rs
@@ -1,5 +1,202 @@
#[allow(unused_imports)]
-use segmentation::SegmentSelector;
+use segmentation::{SegmentSelector};
+use segmentation::{DescriptorBuilder, DescriptorType, GateDescriptorBuilder, TaskGateDescriptorBuilder, SegmentDescriptorBuilder, LdtDescriptorBuilder, BuildDescriptor, SystemDescriptorTypes32, CodeSegmentType, DataSegmentType};
+use ::Ring;
+
+/// Entry for IDT, GDT or LDT. Provides size and location of a segment.
+///
+/// See Intel 3a, Section 3.4.5 "Segment Descriptors", and Section 3.5.2
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C, packed)]
+pub struct Descriptor32 {
+ pub lower: u32,
+ pub upper: u32,
+}
+
+impl Descriptor32 {
+
+ pub(crate) fn apply_builder_settings(&mut self, builder: &DescriptorBuilder) {
+ builder.dpl.map(|ring| self.set_dpl(ring));
+ builder.base_limit.map(|(base, limit)| self.set_base_limit(base as u32, limit as u32));
+ builder.selector_offset.map(|(selector, offset)| self.set_selector_offset(selector, offset as u32));
+
+ if builder.present {
+ self.set_p();
+ }
+ if builder.avl {
+ self.set_avl();
+ }
+ if builder.db {
+ self.set_db();
+ }
+ if builder.limit_granularity_4k {
+ self.set_g();
+ }
+ }
+
+ /// Create a new segment, TSS or LDT descriptor
+ /// by setting the three base and two limit fields.
+ pub fn set_base_limit(&mut self, base: u32, limit: u32) {
+ // Clear the base and limit fields in Descriptor
+ self.lower = 0;
+ self.upper = self.upper & 0x00F0FF00;
+
+ // Set the new base
+ self.lower |= base << 16;
+ self.upper |= (base >> 16) & 0xff;
+ self.upper |= (base >> 24) << 24;
+
+ // Set the new limit
+ self.lower |= limit & 0xffff;
+ let limit_last_four_bits = (limit >> 16) & 0x0f;
+ self.upper |= limit_last_four_bits << 16;
+ }
+
+ /// Creates a new descriptor with selector and offset (for IDT Gate descriptors,
+ /// e.g. Trap, Interrupts and Task gates)
+ pub fn set_selector_offset(&mut self, selector: SegmentSelector, offset: u32) {
+ // Clear the selector and offset
+ self.lower = 0;
+ self.upper = self.upper & 0x0000ffff;
+
+ // Set selector
+ self.lower |= (selector.bits() as u32) << 16;
+
+ // Set offset
+ self.lower |= offset & 0x0000ffff;
+ self.upper |= offset & 0xffff0000;
+ }
+
+ /// Set the type of the descriptor (bits 8-11).
+ /// Indicates the segment or gate type and specifies the kinds of access that can be made to the
+ /// segment and the direction of growth. The interpretation of this field depends on whether the descriptor
+ /// type flag specifies an application (code or data) descriptor or a system descriptor.
+ pub fn set_type(&mut self, typ: u8) {
+ self.upper &= !(0x0f << 8); // clear
+ self.upper |= (typ as u32 & 0x0f) << 8;
+ }
+
+ /// Specifies whether the segment descriptor is for a system segment (S flag is clear) or a code or data segment (S flag is set).
+ pub fn set_s(&mut self) {
+ self.upper |= bit!(12);
+ }
+
+ /// Specifies the privilege level of the segment. The DPL is used to control access to the segment.
+ pub fn set_dpl(&mut self, ring: Ring) {
+ assert!(ring as u32 <= 0b11);
+ self.upper &= !(0b11 << 13);
+ self.upper |= (ring as u32) << 13;
+ }
+
+ /// Set Present bit.
+ /// Indicates whether the segment is present in memory (set) or not present (clear).
+ /// If this flag is clear, the processor generates a segment-not-present exception (#NP) when a segment selector
+ /// that points to the segment descriptor is loaded into a segment register.
+ pub fn set_p(&mut self) {
+ self.upper |= bit!(15);
+ }
+
+ /// Set AVL bit. System software can use this bit to store information.
+ pub fn set_avl(&mut self) {
+ self.upper |= bit!(20);
+ }
+
+ /// Set L
+ /// In IA-32e mode, bit 21 of the second doubleword of the segment descriptor indicates whether a
+ /// code segment contains native 64-bit code. A value of 1 indicates instructions in this code
+ /// segment are executed in 64-bit mode. A value of 0 indicates the instructions in this code segment
+ /// are executed in compatibility mode. If L-bit is set, then D-bit must be cleared.
+ pub fn set_l(&mut self) {
+ self.upper |= bit!(21);
+ }
+
+ /// Set D/B.
+ /// Performs different functions depending on whether the segment descriptor is an executable code segment,
+ /// an expand-down data segment, or a stack segment.
+ pub fn set_db(&mut self) {
+ self.upper |= bit!(22);
+ }
+
+ /// Set G bit
+ /// Determines the scaling of the segment limit field.
+ /// When the granularity flag is clear, the segment limit is interpreted in byte units;
+ /// when flag is set, the segment limit is interpreted in 4-KByte units.
+ pub fn set_g(&mut self) {
+ self.upper |= bit!(23);
+ }
+}
+
+impl GateDescriptorBuilder<u32> for DescriptorBuilder {
+
+ fn tss_descriptor(selector: SegmentSelector, offset: u32, available: bool) -> DescriptorBuilder {
+ let typ = match available {
+ true => DescriptorType::System32(SystemDescriptorTypes32::TssAvailable32),
+ false => DescriptorType::System32(SystemDescriptorTypes32::TssBusy32),
+ };
+
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(typ)
+ }
+
+ fn call_gate_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(DescriptorType::System32(SystemDescriptorTypes32::CallGate32))
+ }
+
+ fn interrupt_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(DescriptorType::System32(SystemDescriptorTypes32::InterruptGate32))
+ }
+
+ fn trap_gate_descriptor(selector: SegmentSelector, offset: u32) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(DescriptorType::System32(SystemDescriptorTypes32::TrapGate32))
+ }
+}
+
+impl TaskGateDescriptorBuilder for DescriptorBuilder {
+ fn task_gate_descriptor(selector: SegmentSelector) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, 0).set_type(DescriptorType::System32(SystemDescriptorTypes32::TaskGate))
+ }
+}
+
+impl SegmentDescriptorBuilder<u32> for DescriptorBuilder {
+ fn code_descriptor(base: u32, limit: u32, cst: CodeSegmentType) -> DescriptorBuilder {
+ DescriptorBuilder::with_base_limit(base.into(), limit.into()).set_type(DescriptorType::Code(cst)).db()
+ }
+
+ fn data_descriptor(base: u32, limit: u32, dst: DataSegmentType) -> DescriptorBuilder {
+ DescriptorBuilder::with_base_limit(base.into(), limit.into()).set_type(DescriptorType::Data(dst)).db()
+ }
+}
+
+impl LdtDescriptorBuilder<u32> for DescriptorBuilder {
+ fn ldt_descriptor(base: u32, limit: u32) -> DescriptorBuilder {
+ DescriptorBuilder::with_base_limit(base.into(), limit.into()).set_type(DescriptorType::System32(SystemDescriptorTypes32::LDT))
+ }
+}
+
+impl BuildDescriptor<Descriptor32> for DescriptorBuilder {
+ fn finish(&self) -> Descriptor32 {
+ let mut desc: Descriptor32 = Default::default();
+ desc.apply_builder_settings(self);
+
+ let typ = match self.typ {
+ Some(DescriptorType::System64(_)) => panic!("You shall not use 64-bit types on 32-bit descriptor."),
+ Some(DescriptorType::System32(typ)) => {
+ typ as u8
+ },
+ Some(DescriptorType::Data(typ)) => {
+ desc.set_s();
+ typ as u8
+ },
+ Some(DescriptorType::Code(typ)) => {
+ desc.set_s();
+ typ as u8
+ },
+ None => unreachable!("Type not set, this is a library bug in x86."),
+ };
+ desc.set_type(typ);
+
+ desc
+ }
+}
/// Reload code segment register.
/// Note this is special since we can not directly move
@@ -12,4 +209,4 @@ pub unsafe fn set_cs(sel: SegmentSelector) {
pushl $$1f; \
lretl; \
1:" :: "ri" (sel.bits() as u32) : "memory");
-}
+} \ No newline at end of file
diff --git a/src/bits64/dtables.rs b/src/bits64/dtables.rs
new file mode 100644
index 0000000..2bc06c8
--- /dev/null
+++ b/src/bits64/dtables.rs
@@ -0,0 +1,18 @@
+//! Functions to load descriptor tables.
+use dtables::DescriptorTablePointer;
+use bits64::segmentation::Descriptor64;
+
+/// Load GDT table with 64-bits descriptors.
+pub unsafe fn lgdt(gdt: &DescriptorTablePointer<Descriptor64>) {
+ asm!("lgdt ($0)" :: "r" (gdt) : "memory");
+}
+
+/// Load LDT table with 64-bits descriptors.
+pub unsafe fn lldt(ldt: &DescriptorTablePointer<Descriptor64>) {
+ asm!("lldt ($0)" :: "r" (ldt) : "memory");
+}
+
+/// Load IDT table with 64-bits descriptors.
+pub unsafe fn lidt(idt: &DescriptorTablePointer<Descriptor64>) {
+ asm!("lidt ($0)" :: "r" (idt) : "memory");
+}
diff --git a/src/bits64/irq.rs b/src/bits64/irq.rs
deleted file mode 100644
index da2e3c4..0000000
--- a/src/bits64/irq.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-//! Interrupt description and set-up code.
-/*
-use ::segmentation::SegmentSelector;
-use Ring;
-use descriptor::*;
-use paging::VAddr;
-
-pub use ::irq::*;
-
-/// An interrupt gate descriptor.
-///
-/// See Intel manual 3a for details, specifically section "6.14.1 64-Bit Mode
-/// IDT" and "Figure 6-7. 64-Bit IDT Gate Descriptors".
-#[derive(Debug, Clone, Copy)]
-#[repr(C, packed)]
-pub struct IdtEntry {
- /// Lower 16 bits of ISR.
- pub base_lo: u16,
- /// Segment selector.
- pub selector: SegmentSelector,
- /// This must always be zero.
- pub ist_index: u8,
- /// Flags.
- pub struct: Flags,
- /// The upper 48 bits of ISR (the last 16 bits must be zero).
- pub base_hi: u64,
- /// Must be zero.
- pub reserved1: u16,
-}
-
-pub enum Type {
- InterruptGate,
- TrapGate,
-}
-
-impl Type {
- pub fn pack(self) -> Flags {
- match self {
- Type::InterruptGate => FLAGS_TYPE_SYS_NATIVE_INTERRUPT_GATE,
- Type::TrapGate => FLAGS_TYPE_SYS_NATIVE_TRAP_GATE,
- }
- }
-}
-
-impl IdtEntry {
- /// A "missing" IdtEntry.
- ///
- /// If the CPU tries to invoke a missing interrupt, it will instead
- /// send a General Protection fault (13), with the interrupt number and
- /// some other data stored in the error code.
- pub const MISSING: IdtEntry = IdtEntry {
- base_lo: 0,
- selector: SegmentSelector::from_raw(0),
- ist_index: 0,
- flags: Flags::BLANK,
- base_hi: 0,
- reserved1: 0,
- };
-
- /// Create a new IdtEntry pointing at `handler`, which must be a function
- /// with interrupt calling conventions. (This must be currently defined in
- /// assembly language.) The `gdt_code_selector` value must be the offset of
- /// code segment entry in the GDT.
- ///
- /// The "Present" flag set, which is the most common case. If you need
- /// something else, you can construct it manually.
- pub fn new(
- handler: VAddr,
- gdt_code_selector: SegmentSelector,
- dpl: Ring,
- ty: Type,
- ist_index: u8,
- ) -> IdtEntry {
- assert!(ist_index < 0b1000);
- IdtEntry {
- base_lo: ((handler.as_usize() as u64) & 0xFFFF) as u16,
- base_hi: handler.as_usize() as u64 >> 16,
- selector: gdt_code_selector,
- ist_index: ist_index,
- flags: Flags::from_priv(dpl) | ty.pack() | FLAGS_PRESENT,
- reserved1: 0,
- }
- }
-}
-
-*/ \ No newline at end of file
diff --git a/src/bits64/mod.rs b/src/bits64/mod.rs
index 2c4a776..7f8cdc3 100644
--- a/src/bits64/mod.rs
+++ b/src/bits64/mod.rs
@@ -1,19 +1,12 @@
//! Data structures and functions used by IA-32e but not Protected Mode.
-macro_rules! check_flag {
- ($doc:meta, $fun:ident, $flag:expr) => (
- #[$doc]
- pub fn $fun(&self) -> bool {
- self.contains($flag)
- }
- )
-}
-
-pub mod irq;
-pub mod rflags;
+pub mod dtables;
pub mod paging;
+pub mod rflags;
pub mod segmentation;
-pub mod task;
-pub mod syscall;
#[cfg(target_arch="x86-64")]
pub mod sgx;
+pub mod syscall;
+pub mod task;
+
+
diff --git a/src/bits64/segmentation.rs b/src/bits64/segmentation.rs
index f43828a..8bcd8cf 100644
--- a/src/bits64/segmentation.rs
+++ b/src/bits64/segmentation.rs
@@ -1,5 +1,121 @@
#[allow(unused_imports)]
-use segmentation::SegmentSelector;
+use segmentation::{SegmentSelector};
+use segmentation::{DescriptorBuilder, BuildDescriptor, DescriptorType, GateDescriptorBuilder, SegmentDescriptorBuilder, LdtDescriptorBuilder, CodeSegmentType, DataSegmentType, SystemDescriptorTypes64};
+use bits32::segmentation::{Descriptor32};
+
+/// Entry for IDT, GDT or LDT.
+///
+/// See Intel 3a, Section 3.4.5 "Segment Descriptors", and Section 3.5.2
+/// "Segment Descriptor Tables in IA-32e Mode", especially Figure 3-8.
+#[derive(Copy, Clone, Debug, Default)]
+#[repr(C, packed)]
+pub struct Descriptor64 {
+ desc32: Descriptor32,
+ lower: u32,
+ upper: u32
+}
+
+impl Descriptor64 {
+
+ pub(crate) fn apply_builder_settings(&mut self, builder: &DescriptorBuilder) {
+ self.desc32.apply_builder_settings(builder);
+ builder.base_limit.map(|(base, limit)| self.set_base_limit(base, limit));
+ builder.selector_offset.map(|(selector, offset)| self.set_selector_offset(selector, offset));
+ }
+
+ /// Create a new segment, TSS or LDT descriptor
+ /// by setting the three base and two limit fields.
+ pub fn set_base_limit(&mut self, base: u64, limit: u64) {
+ self.desc32.set_base_limit(base as u32, limit as u32);
+ self.lower = (base >> 32) as u32;
+ }
+
+ /// Creates a new descriptor with selector and offset (for IDT Gate descriptors,
+ /// e.g. Trap, Interrupts and Task gates)
+ pub fn set_selector_offset(&mut self, selector: SegmentSelector, offset: u64) {
+ self.desc32.set_selector_offset(selector, offset as u32);
+ self.lower = (offset >> 32) as u32;
+ }
+
+ /// Sets the interrupt stack table index.
+ /// The 3-bit IST index field that provides an offset into the IST section of the TSS.
+ /// Using the IST mechanism, the processor loads the value pointed by an IST pointer into the RSP.
+ pub fn set_ist(&mut self, index: u8) {
+ assert!(index <= 0b111);
+ self.desc32.upper |= index as u32;
+ }
+
+}
+
+impl GateDescriptorBuilder<u64> for DescriptorBuilder {
+
+ fn tss_descriptor(selector: SegmentSelector, offset: u64, available: bool) -> DescriptorBuilder {
+ let typ = match available {
+ true => DescriptorType::System64(SystemDescriptorTypes64::TssAvailable),
+ false => DescriptorType::System64(SystemDescriptorTypes64::TssBusy),
+ };
+
+ DescriptorBuilder::with_selector_offset(selector, offset).set_type(typ)
+ }
+
+ fn call_gate_descriptor(selector: SegmentSelector, offset: u64) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset).set_type(DescriptorType::System64(SystemDescriptorTypes64::CallGate))
+ }
+
+ fn interrupt_descriptor(selector: SegmentSelector, offset: u64) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset).set_type(DescriptorType::System64(SystemDescriptorTypes64::InterruptGate))
+ }
+
+ fn trap_gate_descriptor(selector: SegmentSelector, offset: u64) -> DescriptorBuilder {
+ DescriptorBuilder::with_selector_offset(selector, offset).set_type(DescriptorType::System64(SystemDescriptorTypes64::TrapGate))
+ }
+}
+
+impl SegmentDescriptorBuilder<u64> for DescriptorBuilder {
+ fn code_descriptor(base: u64, limit: u64, cst: CodeSegmentType) -> DescriptorBuilder {
+ DescriptorBuilder::with_base_limit(base, limit).set_type(DescriptorType::Code(cst)).db()
+ }
+
+ fn data_descriptor(base: u64, limit: u64, dst: DataSegmentType) -> DescriptorBuilder {
+ DescriptorBuilder::with_base_limit(base, limit).set_type(DescriptorType::Data(dst)).db()
+ }
+}
+
+impl LdtDescriptorBuilder<u64> for DescriptorBuilder {
+ fn ldt_descriptor(base: u64, limit: u64) -> DescriptorBuilder {
+ DescriptorBuilder::with_base_limit(base, limit).set_type(DescriptorType::System64(SystemDescriptorTypes64::LDT))
+ }
+}
+
+impl BuildDescriptor<Descriptor64> for DescriptorBuilder {
+ fn finish(&self) -> Descriptor64 {
+ let mut desc: Descriptor64 = Default::default();
+ desc.apply_builder_settings(self);
+ desc.desc32.set_l(); // 64-bit descriptor
+
+ let typ = match self.typ {
+ Some(DescriptorType::System64(typ)) => {
+ if typ == SystemDescriptorTypes64::LDT || typ == SystemDescriptorTypes64::TssAvailable || typ == SystemDescriptorTypes64::TssBusy {
+ assert!(!self.db);
+ assert!(!self.db);
+ }
+ typ as u8
+ },
+ Some(DescriptorType::System32(_typ)) => panic!("You shall not use 32-bit types on 64-bit descriptors."),
+ Some(DescriptorType::Data(typ)) => {
+ desc.desc32.set_s();
+ typ as u8
+ },
+ Some(DescriptorType::Code(typ)) => {
+ desc.desc32.set_s();
+ typ as u8
+ },
+ None => unreachable!("Type not set, this is a library bug in x86."),
+ };
+ desc.desc32.set_type(typ);
+ desc
+ }
+}
/// Reload code segment register.
/// Note this is special since we can not directly move
@@ -13,4 +129,4 @@ pub unsafe fn set_cs(sel: SegmentSelector) {
pushq %rax; \
lretq; \
1:" :: "ri" (sel.bits() as usize) : "rax" "memory");
-}
+} \ No newline at end of file
diff --git a/src/dtables.rs b/src/dtables.rs
index 97395d6..19baa9a 100644
--- a/src/dtables.rs
+++ b/src/dtables.rs
@@ -1,10 +1,7 @@
//! Functions and data-structures to load descriptor tables.
-
use core::fmt;
use core::mem::size_of;
-use segmentation::SegmentDescriptor;
-
/// A struct describing a pointer to a descriptor table (GDT / IDT).
/// This is in a format suitable for giving to 'lgdt' or 'lidt'.
#[repr(C, packed)]
@@ -34,19 +31,3 @@ impl<T> fmt::Debug for DescriptorTablePointer<T> {
unsafe { write!(f, "DescriptorTablePointer ({} {:?})", self.limit, self.base) }
}
}
-
-/// Load GDT table.
-pub unsafe fn lgdt(gdt: &DescriptorTablePointer<SegmentDescriptor>) {
- asm!("lgdt ($0)" :: "r" (gdt) : "memory");
-}
-
-/// Load LDT table.
-pub unsafe fn lldt(ldt: &DescriptorTablePointer<SegmentDescriptor>) {
- asm!("lldt ($0)" :: "r" (ldt) : "memory");
-}
-/*
-/// Load IDT table.
-pub unsafe fn lidt(idt: &DescriptorTablePointer<IdtEntry>) {
- asm!("lidt ($0)" :: "r" (idt) : "memory");
-}
-*/ \ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index 1d93883..9458f2d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -12,12 +12,22 @@ extern crate raw_cpuid;
#[macro_use]
extern crate phf;
+macro_rules! check_flag {
+ ($doc:meta, $fun:ident, $flag:expr) => (
+ #[$doc]
+ pub fn $fun(&self) -> bool {
+ self.contains($flag)
+ }
+ )
+}
+
macro_rules! bit {
( $x:expr ) => {
1 << $x
};
}
+pub mod bits16;
pub mod bits32;
pub mod bits64;
@@ -52,13 +62,16 @@ mod std {
pub use core::option;
}
-#[derive(Copy, Clone, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
+/// x86 Protection levels
+/// Note: This should not contain values larger than 2 bits, otherwise
+/// segment descriptor code needs to be adjusted accordingly.
pub enum Ring {
- Ring0 = 0,
- Ring1 = 1,
- Ring2 = 2,
- Ring3 = 3,
+ Ring0 = 0b00,
+ Ring1 = 0b01,
+ Ring2 = 0b10,
+ Ring3 = 0b11,
}
#[inline(always)]
diff --git a/src/segmentation.rs b/src/segmentation.rs
index e919a8b..cf8843e 100644
--- a/src/segmentation.rs
+++ b/src/segmentation.rs
@@ -76,21 +76,54 @@ impl fmt::Display for SegmentSelector {
}
}
-/// Entry for GDT or LDT. Provides size and location of a segment.
-///
-/// See Intel 3a, Section 3.4.5 "Segment Descriptors", and Section 3.5.2
-/// "Segment Descriptor Tables in IA-32e Mode", especially Figure 3-8.
-#[derive(Copy, Clone, Debug)]
-#[repr(C, packed)]
-pub struct SegmentDescriptor {
- limit1: u16,
- base1: u16,
- base2: u8,
- type_access: u8,
- limit2_flags: u8,
- base3: u8,
+/// System-Segment and Gate-Descriptor Types 64-bit mode
+/// See also Intel 3a, Table 3-2 System Segment and Gate-Descriptor Types.
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum SystemDescriptorTypes64 {
+ //Reserved0 = 0b0000,
+ //Reserved1 = 0b0001,
+ LDT = 0b0010,
+ //Reserved = 0b0011,
+ //Reserved = 0b0100,
+ //Reserved = 0b0101,
+ //Reserved = 0b0110,
+ //Reserved = 0b0111,
+ //Reserved = 0b1000,
+ TssAvailable = 0b1001,
+ //Reserved = 0b1010,
+ TssBusy = 0b1011,
+ CallGate = 0b1100,
+ //Reserved = 0b1101,
+ InterruptGate = 0b1110,
+ TrapGate = 0b1111,
+}
+
+/// System-Segment and Gate-Descriptor Types 32-bit mode.
+/// See also Intel 3a, Table 3-2 System Segment and Gate-Descriptor Types.
+#[repr(u8)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum SystemDescriptorTypes32 {
+ //Reserved0 = 0b0000,
+ TSSAvailable16 = 0b0001,
+ LDT = 0b0010,
+ TSSBusy16 = 0b0011,
+ CallGate16 = 0b0100,
+ TaskGate = 0b0101,
+ InterruptGate16 = 0b0110,
+ TrapGate16 = 0b0111,
+ //Reserved1 = 0b1000,
+ TssAvailable32 = 0b1001,
+ //Reserved2 = 0b1010,
+ TssBusy32 = 0b1011,
+ CallGate32 = 0b1100,
+ //Reserved3 = 0b1101,
+ InterruptGate32 = 0b1110,
+ TrapGate32 = 0b1111,
}
+/// Data Segment types for descriptors.
+/// See also Intel 3a, Table 3-1 Code- and Data-Segment Types.
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum DataSegmentType {
@@ -112,6 +145,8 @@ pub enum DataSegmentType {
ReadWriteExpandAccessed = 0b0111,
}
+/// Code Segment types for descriptors.
+/// See also Intel 3a, Table 3-1 Code- and Data-Segment Types.
#[repr(u8)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CodeSegmentType {
@@ -133,115 +168,82 @@ pub enum CodeSegmentType {
ExecuteReadConformingAccessed = 0b1111,
}
-/// System-Segment and Gate-Descriptor Types 32-bit mode
-#[repr(u8)]
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-enum SystemDescriptor32 {
- //Reserved0 = 0b0000,
- TSSAvailable16 = 0b0001,
- LDT = 0b0010,
- TSSBusy16 = 0b0011,
- CallGate16 = 0b0100,
- TaskGate = 0b0101,
- InterruptGate16 = 0b0110,
- TrapGate16 = 0b0111,
- //Reserved1 = 0b1000,
- TssAvailable32 = 0b1001,
- //Reserved2 = 0b1010,
- TssBusy32 = 0b1011,
- CallGate32 = 0b1100,
- //Reserved3 = 0b1101,
- InterruptGate32 = 0b1110,
- TrapGate32 = 0b1111,
+/// Helper enum type to differentiate between the different descriptor types that all end up written in the same field.
+#[derive(Debug, Eq, PartialEq)]
+pub(crate) enum DescriptorType {
+ System64(SystemDescriptorTypes64),
+ System32(SystemDescriptorTypes32),
+ Data(DataSegmentType),
+ Code(CodeSegmentType)
}
-/// System-Segment and Gate-Descriptor Types 64-bit mode
-#[repr(u8)]
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-enum SystemDescriptor64 {
- //Reserved0 = 0b0000,
- //Reserved1 = 0b0001,
- LDT = 0b0010,
- //Reserved = 0b0011,
- //Reserved = 0b0100,
- //Reserved = 0b0101,
- //Reserved = 0b0110,
- //Reserved = 0b0111,
- //Reserved = 0b1000,
- TssAvailable64 = 0b1001,
- //Reserved = 0b1010,
- TssBusy64 = 0b1011,
- CallGate64 = 0b1100,
- //Reserved = 0b1101,
- InterruptGate64 = 0b1110,
- TrapGate64 = 0b1111,
+/// Trait that defines the architecture specific functions for building various system segment descriptors
+/// which are available on all 16, 32, and 64 bits.
+pub(crate) trait GateDescriptorBuilder<Size> {
+ fn tss_descriptor(selector: SegmentSelector, offset: Size, available: bool) -> Self;
+ fn call_gate_descriptor(selector: SegmentSelector, offset: Size) -> Self;
+ fn interrupt_descriptor(selector: SegmentSelector, offset: Size) -> Self;
+ fn trap_gate_descriptor(selector: SegmentSelector, offset: Size) -> Self;
}
-#[derive(Debug, Eq, PartialEq)]
-pub enum SystemMode {
- Mode16,
- Mode32,
- Mode64,
+/// Trait to implement for building a task-gate (this descriptor is not implemented for 64-bit systems since
+/// Hardware task switches are not supported in IA-32e mode.).
+pub(crate) trait TaskGateDescriptorBuilder {
+ fn task_gate_descriptor(selector: SegmentSelector) -> Self;
+}
+
+/// Trait to define functions that build architecture specific code and data descriptors.
+pub(crate) trait SegmentDescriptorBuilder<Size> {
+ fn code_descriptor(base: Size, limit: Size, cst: CodeSegmentType) -> Self;
+ fn data_descriptor(base: Size, limit: Size, dst: DataSegmentType) -> Self;
+}
+
+/// Trait to define functions that build an architecture specific ldt descriptor.
+/// There is no corresponding ldt descriptor type for 16 bit.
+pub(crate) trait LdtDescriptorBuilder<Size> {
+ fn ldt_descriptor(base: Size, limit: Size) -> Self;
+}
+
+pub(crate) trait BuildDescriptor<Descriptor> {
+ fn finish(&self) -> Descriptor;
}
/// Makes building descriptors easier (hopefully).
+#[derive(Debug)]
pub struct DescriptorBuilder {
- /// What privilege level the descriptor is for.
- mode: SystemMode,
-
- /// Defines the location of byte 0 of the segment within the 4-GByte linear address space.
- base: u32,
- /// The size of the range covered by the segment. Really a 20bit value.
- limit: u32,
-
- /// The type of the segment if we have a data segment.
- dst: Option<DataSegmentType>,
- /// The type of the segment if we have a code segment.
- cst: Option<CodeSegmentType>,
- /// The type of the segment if we have a system segment in 32bit mode.
- system_type32: Option<SystemDescriptor32>,
- /// The type of the segment if we have a system segment in 64bit mode.
- system_type64: Option<SystemDescriptor64>,
-
+ /// The base defines the location of byte 0 of the segment within the 4-GByte linear address space.
+ /// The limit is the size of the range covered by the segment. Really a 20bit value.
+ pub(crate) base_limit: Option<(u64, u64)>,
+ /// Alternative to base_limit we use a selector that points to a segment and an an offset for certain descriptors.
+ pub(crate) selector_offset: Option<(SegmentSelector, u64)>,
+ /// Descriptor type
+ pub(crate) typ: Option<DescriptorType>,
/// Specifies the privilege level of the segment. The privilege level can range from 0 to 3, with 0 being the most privileged level.
- dpl: Option<Ring>,
+ pub(crate) dpl: Option<Ring>,
/// Indicates whether the segment is present in memory (set) or not present (clear).
- present: bool,
+ pub(crate) present: bool,
/// Available for use by system software
- avl: bool,
- /// Performs different functions depending on whether the segment descriptor is an executable code segment, an expand-down data segment, or a stack segment.
- db: bool,
+ pub(crate) avl: bool,
+ /// Default operation size
+ pub(crate) db: bool,
/// Determines the scaling of the segment limit field. When the granularity flag is clear, the segment limit is interpreted in byte units; when flag is set, the segment limit is interpreted in 4-KByte units.
- limit_granularity_4k: bool,
+ pub(crate) limit_granularity_4k: bool,
}
impl DescriptorBuilder {
- pub fn new(mode: SystemMode) -> DescriptorBuilder {
- DescriptorBuilder {
- base: 0,
- limit: 0,
- mode: mode,
- dst: None,
- cst: None,
- system_type32: None,
- system_type64: None,
- dpl: None,
- present: false,
- db: false,
- avl: false,
- limit_granularity_4k: false,
- }
- }
- /// Set a base for the descriptor.
- pub fn base(mut self, base: u32) -> DescriptorBuilder {
- self.base = base;
- self
+ /// Start building a new descriptor with a base and limit.
+ pub(crate) fn with_base_limit(base: u64, limit: u64) -> DescriptorBuilder {
+ DescriptorBuilder { base_limit: Some((base, limit)), selector_offset: None, typ: None, dpl: None, present: false, avl: false, db: false, limit_granularity_4k: false }
}
- /// Set the limit for the descriptor.
- pub fn limit(mut self, limit: u32) -> DescriptorBuilder {
- self.limit = limit;
+ /// Start building a new descriptor with a segment selector and offset.
+ pub(crate) fn with_selector_offset(selector: SegmentSelector, offset: u64) -> DescriptorBuilder {
+ DescriptorBuilder { base_limit: None, selector_offset: Some((selector, offset)), typ: None, dpl: None, present: false, avl: false, db: false, limit_granularity_4k: false }
+ }
+
+ pub(crate) fn set_type(mut self, typ: DescriptorType) -> DescriptorBuilder {
+ self.typ = Some(typ);
self
}
@@ -269,141 +271,14 @@ impl DescriptorBuilder {
self
}
- /// Make a ldt descriptor.
- pub fn ldt_descriptor(mut self) -> DescriptorBuilder {
- match self.mode {
- SystemMode::Mode16 => self.system_type32 = Some(SystemDescriptor32::LDT),
- SystemMode::Mode32 => self.system_type32 = Some(SystemDescriptor32::LDT),
- SystemMode::Mode64 => self.system_type64 = Some(SystemDescriptor64::LDT),
- }
- self
- }
-
- /// Make a tss descriptor.
- pub fn tss_descriptor(mut self, available: bool) -> DescriptorBuilder {
- match (available, &self.mode) {
- (true, SystemMode::Mode16) => self.system_type32 = Some(SystemDescriptor32::TSSAvailable16),
- (true, SystemMode::Mode32) => self.system_type32 = Some(SystemDescriptor32::TssAvailable32),
- (true, SystemMode::Mode64) => self.system_type64 = Some(SystemDescriptor64::TssAvailable64),
- (false, SystemMode::Mode16) => self.system_type32 = Some(SystemDescriptor32::TSSBusy16),
- (false, SystemMode::Mode32) => self.system_type32 = Some(SystemDescriptor32::TssBusy32),
- (false, SystemMode::Mode64) => self.system_type64 = Some(SystemDescriptor64::TssBusy64),
- }
- self
- }
-
- /// Make a call gate descriptor.
- pub fn call_gate_descriptor(mut self) -> DescriptorBuilder {
- match self.mode {
- SystemMode::Mode16 => self.system_type32 = Some(SystemDescriptor32::CallGate16),
- SystemMode::Mode32 => self.system_type32 = Some(SystemDescriptor32::CallGate32),
- SystemMode::Mode64 => self.system_type64 = Some(SystemDescriptor64::CallGate64),
- }
- self
- }
-
- /// Make an interrupt descriptor.
- pub fn interrupt_descriptor(mut self) -> DescriptorBuilder {
- match self.mode {
- SystemMode::Mode16 => self.system_type32 = Some(SystemDescriptor32::InterruptGate16),
- SystemMode::Mode32 => self.system_type32 = Some(SystemDescriptor32::InterruptGate32),
- SystemMode::Mode64 => self.system_type64 = Some(SystemDescriptor64::InterruptGate64),
- }
+ /// Set default operation size (false for 16bit segment, true for 32bit segments).
+ pub fn db(mut self) -> DescriptorBuilder {
+ self.db = true;
self
}
-
- /// Make a trap gate descriptor
- pub fn trap_gate_descriptor(mut self) -> DescriptorBuilder {
- match self.mode {
- SystemMode::Mode16 => self.system_type32 = Some(SystemDescriptor32::TrapGate16),
- SystemMode::Mode32 => self.system_type32 = Some(SystemDescriptor32::TrapGate32),
- SystemMode::Mode64 => self.system_type64 = Some(SystemDescriptor64::TrapGate64),
- }
- self
- }
-
- /// Make a task gate descriptor. Note: This call will panic if mode is not 32bit!
- pub fn task_gate_descriptor(mut self) -> DescriptorBuilder {
- match self.mode {
- SystemMode::Mode32 => self.system_type32 = Some(SystemDescriptor32::TaskGate),
- _ => panic!("Can't build a taskgate for {:?}", self.mode)
- }
- self
- }
-
- // Make a code segment descriptor.
- pub fn new_code_descriptor(mut self, cst: CodeSegmentType) -> DescriptorBuilder {
- self.cst = Some(cst);
- if self.mode == SystemMode::Mode32 {
- // Not sure it's always ok to do this here but the manual says:
- // This flag should always be set to 1 for 32-bit code and data segments and to 0 for 16-bit code and data segments.
- self.db = true;
- }
- self
- }
-
- // Make a data segment descriptor.
- pub fn new_data_descriptor(mut self, dst: DataSegmentType) -> DescriptorBuilder {
- self.dst = Some(dst);
- if self.mode == SystemMode::Mode32 {
- // Not sure it's always ok to do this here but the manual says:
- // This flag should always be set to 1 for 32-bit code and data segments and to 0 for 16-bit code and data segments.
- self.db = true;
- }
- self
- }
-
- // Build the final segment descriptor.
- pub fn finish(&self) -> SegmentDescriptor {
- let mut sd = SegmentDescriptor {
- limit1: 0,
- base1: 0,
- base2: 0,
- type_access: 0,
- limit2_flags: 0,
- base3: 0,
- };
-
- // Set base
- sd.base1 = self.base as u16;
- sd.base2 = (self.base >> 16) as u8;
- sd.base3 = (self.base >> 24) as u8;
-
- // Set limit
- sd.limit1 = self.limit as u16;
- sd.limit2_flags = (sd.limit2_flags & 0xf0) | (((self.limit >> 16) as u8) & 0x0f);
-
- // Set Type and S
- // s_bit specifies whether the segment descriptor is for a system segment (S flag is clear) or a code or data segment (S flag is set).
- let s_bit = 1 << 4;
- match (self.dst, self.cst, self.system_type32, self.system_type64) {
- (Some(typ), None, None, None) => sd.type_access = (sd.type_access & 0xf0) | s_bit | (typ as u8 & 0x0f),
- (None, Some(typ), None, None) => sd.type_access = (sd.type_access & 0xf0) | s_bit | (typ as u8 & 0x0f),
- (None, None, Some(typ), None) => sd.type_access = (sd.type_access & 0xf0) | (typ as u8 & 0x0f),
- (None, None, None, Some(typ)) => sd.type_access = (sd.type_access & 0xf0) | (typ as u8 & 0x0f),
- (None, None, None, None) => {/* do nothing */},
- _ => panic!("Trying to build a segment descriptor that is multiple types is not possible."),
- }
-
- // Set DPL
- self.dpl.map(|ring| {
- sd.type_access |= (ring as u8) << 5;
- });
- // Set P
- sd.type_access |= (self.present as u8) << 7;
- // Set AVL
- sd.limit2_flags |= (self.avl as u8) << 4;
- // Set L
- sd.limit2_flags |= ((self.mode == SystemMode::Mode64) as u8) << 5;
- // Set D/B
- sd.limit2_flags |= (self.db as u8) << 6;
- // Set G
- sd.limit2_flags |= (self.limit_granularity_4k as u8) << 7;
-
- sd
- }
}
+
/// Reload stack segment register.
pub unsafe fn load_ss(sel: SegmentSelector) {
asm!("movw $0, %ss " :: "r" (sel.bits()) : "memory");