diff options
author | 2018-04-26 14:06:06 -0700 | |
---|---|---|
committer | 2018-04-26 14:06:06 -0700 | |
commit | ca17286ab682cc0c89f1d7f335f90168ce0bfdf9 (patch) | |
tree | 09bfb9cedafa856eff44a4d89d883cc10b8739aa | |
parent | ee389098aaff520aa36096734ac8d974d58e7181 (diff) | |
download | rust-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.yml | 1 | ||||
-rw-r--r-- | src/bits16/mod.rs | 1 | ||||
-rw-r--r-- | src/bits16/segmentation.rs | 25 | ||||
-rw-r--r-- | src/bits32/dtables.rs | 19 | ||||
-rw-r--r-- | src/bits32/irq.rs | 58 | ||||
-rw-r--r-- | src/bits32/mod.rs | 4 | ||||
-rw-r--r-- | src/bits32/segmentation.rs | 201 | ||||
-rw-r--r-- | src/bits64/dtables.rs | 18 | ||||
-rw-r--r-- | src/bits64/irq.rs | 86 | ||||
-rw-r--r-- | src/bits64/mod.rs | 19 | ||||
-rw-r--r-- | src/bits64/segmentation.rs | 120 | ||||
-rw-r--r-- | src/dtables.rs | 19 | ||||
-rw-r--r-- | src/lib.rs | 23 | ||||
-rw-r--r-- | src/segmentation.rs | 345 |
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 @@ -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"); |