diff options
author | 2018-04-26 19:20:36 -0700 | |
---|---|---|
committer | 2018-04-26 19:20:36 -0700 | |
commit | 57a2bfac28ce1e62ce1051b9de731d81e1c25a8b (patch) | |
tree | 3d0f93200a5ad3c11cb685a4da9682b9c226c6a5 | |
parent | 848ec0fae090dbc1657d02b436b8b887df519e90 (diff) | |
download | rust-x86-57a2bfac28ce1e62ce1051b9de731d81e1c25a8b.tar.gz rust-x86-57a2bfac28ce1e62ce1051b9de731d81e1c25a8b.tar.zst rust-x86-57a2bfac28ce1e62ce1051b9de731d81e1c25a8b.zip |
More segmentation refactoring.
Signed-off-by: Gerd Zellweger <mail@gerdzellweger.com>
-rw-r--r-- | src/bits16/segmentation.rs | 4 | ||||
-rw-r--r-- | src/bits32/dtables.rs | 19 | ||||
-rw-r--r-- | src/bits32/mod.rs | 1 | ||||
-rw-r--r-- | src/bits32/segmentation.rs | 199 | ||||
-rw-r--r-- | src/bits64/dtables.rs | 18 | ||||
-rw-r--r-- | src/bits64/mod.rs | 1 | ||||
-rw-r--r-- | src/bits64/segmentation.rs | 46 | ||||
-rw-r--r-- | src/dtables.rs | 15 | ||||
-rw-r--r-- | src/segmentation.rs | 226 |
9 files changed, 251 insertions, 278 deletions
diff --git a/src/bits16/segmentation.rs b/src/bits16/segmentation.rs index 4e95a1d..516433d 100644 --- a/src/bits16/segmentation.rs +++ b/src/bits16/segmentation.rs @@ -2,13 +2,13 @@ use segmentation::{SegmentSelector, DescriptorBuilder, GateDescriptorBuilder, De impl GateDescriptorBuilder<u16> for DescriptorBuilder { - fn tss_descriptor(selector: SegmentSelector, offset: u16, available: bool) -> DescriptorBuilder { + fn tss_descriptor(base: u64, limit: u64, available: bool) -> DescriptorBuilder { let typ = match available { true => DescriptorType::System32(SystemDescriptorTypes32::TSSAvailable16), false => DescriptorType::System32(SystemDescriptorTypes32::TSSBusy16), }; - DescriptorBuilder::with_selector_offset(selector, offset.into()).set_type(typ) + DescriptorBuilder::with_base_limit(base, limit).set_type(typ) } fn call_gate_descriptor(selector: SegmentSelector, offset: u16) -> DescriptorBuilder { diff --git a/src/bits32/dtables.rs b/src/bits32/dtables.rs deleted file mode 100644 index 51596f8..0000000 --- a/src/bits32/dtables.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Functions to load descriptor tables. -use dtables::DescriptorTablePointer; -use bits32::segmentation::Descriptor; - -/// Load GDT table with 32bit descriptors -pub unsafe fn lgdt(gdt: &DescriptorTablePointer<Descriptor>) { - asm!("lgdt ($0)" :: "r" (gdt) : "memory"); -} - -/// Load LDT table with 32bit descriptors. -pub unsafe fn lldt(ldt: &DescriptorTablePointer<Descriptor>) { - asm!("lldt ($0)" :: "r" (ldt) : "memory"); -} - -/// Load IDT table with 32bit descriptors. -pub unsafe fn lidt(idt: &DescriptorTablePointer<Descriptor>) { - asm!("lidt ($0)" :: "r" (idt) : "memory"); -} - diff --git a/src/bits32/mod.rs b/src/bits32/mod.rs index 87a9d9c..e79b1e6 100644 --- a/src/bits32/mod.rs +++ b/src/bits32/mod.rs @@ -1,4 +1,3 @@ -pub mod dtables; pub mod eflags; pub mod segmentation; pub mod task; diff --git a/src/bits32/segmentation.rs b/src/bits32/segmentation.rs index dc6da52..5114394 100644 --- a/src/bits32/segmentation.rs +++ b/src/bits32/segmentation.rs @@ -1,202 +1,5 @@ #[allow(unused_imports)] 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 Descriptor { - pub lower: u32, - pub upper: u32, -} - -impl Descriptor { - - 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<Descriptor> for DescriptorBuilder { - fn finish(&self) -> Descriptor { - let mut desc: Descriptor = 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 @@ -204,7 +7,7 @@ impl BuildDescriptor<Descriptor> for DescriptorBuilder { /// and return value on the stack and use lretl /// to reload cs and continue at 1:. #[cfg(target_arch="x86")] -pub unsafe fn set_cs(sel: SegmentSelector) { +pub unsafe fn load_cs(sel: SegmentSelector) { asm!("pushl $0; \ pushl $$1f; \ lretl; \ diff --git a/src/bits64/dtables.rs b/src/bits64/dtables.rs deleted file mode 100644 index 00fad0f..0000000 --- a/src/bits64/dtables.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Functions to load descriptor tables. -use dtables::DescriptorTablePointer; -use bits64::segmentation::Descriptor; - -/// Load GDT table with 64-bits descriptors. -pub unsafe fn lgdt(gdt: &DescriptorTablePointer<Descriptor>) { - asm!("lgdt ($0)" :: "r" (gdt) : "memory"); -} - -/// Load LDT table with 64-bits descriptors. -pub unsafe fn lldt(ldt: &DescriptorTablePointer<Descriptor>) { - asm!("lldt ($0)" :: "r" (ldt) : "memory"); -} - -/// Load IDT table with 64-bits descriptors. -pub unsafe fn lidt(idt: &DescriptorTablePointer<Descriptor>) { - asm!("lidt ($0)" :: "r" (idt) : "memory"); -} diff --git a/src/bits64/mod.rs b/src/bits64/mod.rs index 7f8cdc3..72ea049 100644 --- a/src/bits64/mod.rs +++ b/src/bits64/mod.rs @@ -1,6 +1,5 @@ //! Data structures and functions used by IA-32e but not Protected Mode. -pub mod dtables; pub mod paging; pub mod rflags; pub mod segmentation; diff --git a/src/bits64/segmentation.rs b/src/bits64/segmentation.rs index 058bead..0c03780 100644 --- a/src/bits64/segmentation.rs +++ b/src/bits64/segmentation.rs @@ -1,7 +1,6 @@ #[allow(unused_imports)] use segmentation::{SegmentSelector}; -use segmentation::{DescriptorBuilder, BuildDescriptor, DescriptorType, GateDescriptorBuilder, SegmentDescriptorBuilder, LdtDescriptorBuilder, CodeSegmentType, DataSegmentType, SystemDescriptorTypes64}; -use bits32::segmentation::Descriptor as Descriptor32; +use segmentation::{DescriptorBuilder, BuildDescriptor, Descriptor, DescriptorType, GateDescriptorBuilder, LdtDescriptorBuilder, SystemDescriptorTypes64}; /// Entry for IDT, GDT or LDT. /// @@ -9,13 +8,13 @@ use bits32::segmentation::Descriptor as Descriptor32; /// "Segment Descriptor Tables in IA-32e Mode", especially Figure 3-8. #[derive(Copy, Clone, Debug, Default)] #[repr(C, packed)] -pub struct Descriptor { - desc32: Descriptor32, +pub struct Descriptor64 { + desc32: Descriptor, lower: u32, upper: u32 } -impl Descriptor { +impl Descriptor64 { pub(crate) fn apply_builder_settings(&mut self, builder: &DescriptorBuilder) { self.desc32.apply_builder_settings(builder); @@ -49,13 +48,13 @@ impl Descriptor { impl GateDescriptorBuilder<u64> for DescriptorBuilder { - fn tss_descriptor(selector: SegmentSelector, offset: u64, available: bool) -> DescriptorBuilder { + fn tss_descriptor(base: u64, limit: 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) + DescriptorBuilder::with_base_limit(base, limit).set_type(typ) } fn call_gate_descriptor(selector: SegmentSelector, offset: u64) -> DescriptorBuilder { @@ -71,46 +70,31 @@ impl GateDescriptorBuilder<u64> for DescriptorBuilder { } } -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<Descriptor> for DescriptorBuilder { - fn finish(&self) -> Descriptor { - let mut desc: Descriptor = Default::default(); +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)) => { + assert!(!self.l); if typ == SystemDescriptorTypes64::LDT || typ == SystemDescriptorTypes64::TssAvailable || typ == SystemDescriptorTypes64::TssBusy { 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 - }, + Some(DescriptorType::System32(_typ)) => panic!("Can't build a 64-bit version of this type."), + Some(DescriptorType::Data(_typ)) => panic!("Can't build a 64-bit version of this type."), + Some(DescriptorType::Code(_typ)) => panic!("Can't build a 64-bit version of this type."), None => unreachable!("Type not set, this is a library bug in x86."), }; + desc.desc32.set_type(typ); desc } @@ -122,7 +106,7 @@ impl BuildDescriptor<Descriptor> for DescriptorBuilder { /// and return value on the stack and use lretq /// to reload cs and continue at 1:. #[cfg(target_arch="x86_64")] -pub unsafe fn set_cs(sel: SegmentSelector) { +pub unsafe fn load_cs(sel: SegmentSelector) { asm!("pushq $0; \ leaq 1f(%rip), %rax; \ pushq %rax; \ diff --git a/src/dtables.rs b/src/dtables.rs index 19baa9a..43eb13b 100644 --- a/src/dtables.rs +++ b/src/dtables.rs @@ -31,3 +31,18 @@ impl<T> fmt::Debug for DescriptorTablePointer<T> { unsafe { write!(f, "DescriptorTablePointer ({} {:?})", self.limit, self.base) } } } + +/// Load GDT table with 32bit descriptors +pub unsafe fn lgdt<T>(gdt: &DescriptorTablePointer<T>) { + asm!("lgdt ($0)" :: "r" (gdt) : "memory"); +} + +/// Load LDT table with 32bit descriptors. +pub unsafe fn lldt<T>(ldt: &DescriptorTablePointer<T>) { + asm!("lldt ($0)" :: "r" (ldt) : "memory"); +} + +/// Load IDT table with 32bit descriptors. +pub unsafe fn lidt<T>(idt: &DescriptorTablePointer<T>) { + asm!("lidt ($0)" :: "r" (idt) : "memory"); +} diff --git a/src/segmentation.rs b/src/segmentation.rs index cf8843e..6f17c75 100644 --- a/src/segmentation.rs +++ b/src/segmentation.rs @@ -179,8 +179,8 @@ pub(crate) enum DescriptorType { /// 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; +pub trait GateDescriptorBuilder<Size> { + fn tss_descriptor(base: u64, limit: u64, 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; @@ -188,23 +188,23 @@ pub(crate) trait GateDescriptorBuilder<Size> { /// 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 { +pub 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> { +pub 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> { +pub trait LdtDescriptorBuilder<Size> { fn ldt_descriptor(base: Size, limit: Size) -> Self; } -pub(crate) trait BuildDescriptor<Descriptor> { +pub trait BuildDescriptor<Descriptor> { fn finish(&self) -> Descriptor; } @@ -228,18 +228,21 @@ pub struct DescriptorBuilder { 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. pub(crate) limit_granularity_4k: bool, + /// 64-bit code segment (IA-32e mode only) + pub(crate) l: bool + } impl DescriptorBuilder { /// 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 } + DescriptorBuilder { base_limit: Some((base, limit)), selector_offset: None, typ: None, dpl: None, present: false, avl: false, db: false, limit_granularity_4k: false, l: false } } /// 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 } + DescriptorBuilder { base_limit: None, selector_offset: Some((selector, offset)), typ: None, dpl: None, present: false, avl: false, db: false, limit_granularity_4k: false, l: false } } pub(crate) fn set_type(mut self, typ: DescriptorType) -> DescriptorBuilder { @@ -276,6 +279,213 @@ impl DescriptorBuilder { self.db = true; self } + + /// Set L bit if this descriptor is a 64-bit code segment. + /// 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. + pub fn l(mut self) -> DescriptorBuilder { + self.l = true; + self + } +} + +impl GateDescriptorBuilder<u32> for DescriptorBuilder { + + fn tss_descriptor(base: u64, limit: u64, available: bool) -> DescriptorBuilder { + let typ = match available { + true => DescriptorType::System32(SystemDescriptorTypes32::TssAvailable32), + false => DescriptorType::System32(SystemDescriptorTypes32::TssBusy32), + }; + + DescriptorBuilder::with_base_limit(base.into(), limit.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<Descriptor> for DescriptorBuilder { + fn finish(&self) -> Descriptor { + let mut desc: Descriptor = 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 + } +} + + +/// 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 Descriptor { + pub lower: u32, + pub upper: u32, +} + +impl Descriptor { + + 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(); + } + if builder.l { + self.set_l(); + } + } + + /// 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); + } } |