diff options
Diffstat (limited to 'src/segmentation.rs')
-rw-r--r-- | src/segmentation.rs | 506 |
1 files changed, 350 insertions, 156 deletions
diff --git a/src/segmentation.rs b/src/segmentation.rs index 7672d4a..e919a8b 100644 --- a/src/segmentation.rs +++ b/src/segmentation.rs @@ -1,7 +1,6 @@ use core::fmt; -use ::descriptor; -use ::PrivilegeLevel; +use Ring; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table @@ -9,18 +8,17 @@ use ::PrivilegeLevel; /// /// See Intel 3a, Section 3.4.2 "Segment Selectors" bitflags! { - #[repr(C, packed)] - pub flags SegmentSelector: u16 { + pub struct SegmentSelector: u16 { /// Requestor Privilege Level - const RPL_0 = 0b00, - const RPL_1 = 0b01, - const RPL_2 = 0b10, - const RPL_3 = 0b11, + const RPL_0 = 0b00; + const RPL_1 = 0b01; + const RPL_2 = 0b10; + const RPL_3 = 0b11; /// Table Indicator (TI) 0 means GDT is used. - const TI_GDT = 0 << 2, + const TI_GDT = 0 << 2; /// Table Indicator (TI) 1 means LDT is used. - const TI_LDT = 1 << 2, + const TI_LDT = 1 << 2; } } @@ -28,12 +26,15 @@ impl SegmentSelector { /// Create a new SegmentSelector /// /// # Arguments - /// * `index` index in GDT or LDT array. - /// - pub const fn new(index: u16, rpl: PrivilegeLevel) -> SegmentSelector { - SegmentSelector { bits: index << 3 | (rpl as u16) } + /// * `index` - index in GDT or LDT array. + /// * `rpl` - Requested privilege level of the selector + pub const fn new(index: u16, rpl: Ring) -> SegmentSelector { + SegmentSelector { + bits: index << 3 | (rpl as u16), + } } + /// Make a new segment selector from a untyped u16 value. pub const fn from_raw(bits: u16) -> SegmentSelector { SegmentSelector { bits: bits } } @@ -41,203 +42,396 @@ impl SegmentSelector { impl fmt::Display for SegmentSelector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let r0 = match self.contains(RPL_0) { + let r0 = match self.contains(SegmentSelector::RPL_0) { false => "", true => "Ring 0 segment selector.", }; - let r1 = match self.contains(RPL_1) { + let r1 = match self.contains(SegmentSelector::RPL_1) { false => "", true => "Ring 1 segment selector.", }; - let r2 = match self.contains(RPL_2) { + let r2 = match self.contains(SegmentSelector::RPL_2) { false => "", true => "Ring 2 segment selector.", }; - let r3 = match self.contains(RPL_3) { + let r3 = match self.contains(SegmentSelector::RPL_3) { false => "", true => "Ring 3 segment selector.", }; - let tbl = match self.contains(TI_LDT) { + let tbl = match self.contains(SegmentSelector::TI_LDT) { false => "GDT Table", true => "LDT Table", }; - write!(f, - "Index {} in {}, {}{}{}{}", - self.bits >> 3, - tbl, - r0, - r1, - r2, - r3) - // write!(f, "Index") + write!( + f, + "Index {} in {}, {}{}{}{}", + self.bits >> 3, + tbl, + r0, + r1, + r2, + r3 + ) } } +/// 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, +} -/// Reload stack segment register. -pub unsafe fn load_ss(sel: SegmentSelector) { - asm!("movw $0, %ss " :: "r" (sel.bits()) : "memory"); +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum DataSegmentType { + /// Data Read-Only + ReadOnly = 0b0000, + /// Data Read-Only, accessed + ReadOnlyAccessed = 0b0001, + /// Data Read/Write + ReadWrite = 0b0010, + /// Data Read/Write, accessed + ReadWriteAccessed = 0b0011, + /// Data Read-Only, expand-down + ReadExpand = 0b0100, + /// Data Read-Only, expand-down, accessed + ReadExpandAccessed = 0b0101, + /// Data Read/Write, expand-down + ReadWriteExpand = 0b0110, + /// Data Read/Write, expand-down, accessed + ReadWriteExpandAccessed = 0b0111, } -/// Reload data segment register. -pub unsafe fn load_ds(sel: SegmentSelector) { - asm!("movw $0, %ds " :: "r" (sel.bits()) : "memory"); +#[repr(u8)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum CodeSegmentType { + /// Code Execute-Only + Execute = 0b1000, + /// Code Execute-Only, accessed + ExecuteAccessed = 0b1001, + /// Code Execute/Read + ExecuteRead = 0b1010, + /// Code Execute/Read, accessed + ExecuteReadAccessed = 0b1011, + /// Code Execute-Only, conforming + ExecuteConforming = 0b1100, + /// Code Execute-Only, conforming, accessed + ExecuteConformingAccessed = 0b1101, + /// Code Execute/Read, conforming + ExecuteReadConforming = 0b1110, + /// Code Execute/Read, conforming, accessed + ExecuteReadConformingAccessed = 0b1111, } -/// Reload es segment register. -pub unsafe fn load_es(sel: SegmentSelector) { - asm!("movw $0, %es " :: "r" (sel.bits()) : "memory"); +/// 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, } -/// Reload fs segment register. -pub unsafe fn load_fs(sel: SegmentSelector) { - asm!("movw $0, %fs " :: "r" (sel.bits()) : "memory"); +/// 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, } -/// Reload gs segment register. -pub unsafe fn load_gs(sel: SegmentSelector) { - asm!("movw $0, %gs " :: "r" (sel.bits()) : "memory"); +#[derive(Debug, Eq, PartialEq)] +pub enum SystemMode { + Mode16, + Mode32, + Mode64, } -/// Returns the current value of the code segment register. -pub fn cs() -> SegmentSelector { - let segment: u16; - unsafe { asm!("mov %cs, $0" : "=r" (segment) ) }; - SegmentSelector::from_raw(segment) +/// Makes building descriptors easier (hopefully). +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>, + + /// 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>, + /// Indicates whether the segment is present in memory (set) or not present (clear). + 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, + /// 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, } +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, + } + } -bitflags! { - /// Data segment types. All are readable. - /// - /// See Table 3-1, "Code- and Data-Segment Types" - pub flags DataAccess: u8 { - /// Segment is writable - const DATA_WRITE = 1 << 1, - /// Segment grows down, for stack - const DATA_EXPAND_DOWN = 1 << 2, + /// Set a base for the descriptor. + pub fn base(mut self, base: u32) -> DescriptorBuilder { + self.base = base; + self } -} -bitflags! { - /// Code segment types. All are executable. - /// - /// See Table 3-1, "Code- and Data-Segment Types" - pub flags CodeAccess: u8 { - /// Segment is readable - const CODE_READ = 1 << 1, - /// Segment is callable from segment with fewer privileges. - const CODE_CONFORMING = 1 << 2, + /// Set the limit for the descriptor. + pub fn limit(mut self, limit: u32) -> DescriptorBuilder { + self.limit = limit; + self } -} -/// Umbrella Segment Type. -/// -/// See Table 3-1, "Code- and Data-Segment Types" -#[repr(u8)] -pub enum Type { - Data(DataAccess), - Code(CodeAccess), -} + /// The segment limit is interpreted in 4-KByte units if this is set. + pub fn limit_granularity_4kb(mut self) -> DescriptorBuilder { + self.limit_granularity_4k = true; + self + } -impl Type { - pub fn pack(self) -> u8 { - match self { - Type::Data(d) => d.bits | 0b10_000, - Type::Code(c) => c.bits | 0b11_000, + /// Indicates whether the segment is present in memory (set) or not present (clear). + pub fn present(mut self) -> DescriptorBuilder { + self.present = true; + self + } + + /// Specifies the privilege level of the segment. + pub fn dpl(mut self, dpl: Ring) -> DescriptorBuilder { + self.dpl = Some(dpl); + self + } + + /// Toggle the AVL bit. + pub fn avl(mut self) -> DescriptorBuilder { + self.avl = true; + 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 + } -/// 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, - access: descriptor::Flags, - limit2_flags: Flags, - base3: u8, -} + /// 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), + } + 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 + } -/// This data-structure is an ugly mess thing so we provide some -/// convenience function to program it. -impl SegmentDescriptor { - pub const NULL: SegmentDescriptor = SegmentDescriptor { - limit1: 0, - base1: 0, - base2: 0, - access: descriptor::Flags::BLANK, - limit2_flags: Flags::BLANK, - base3: 0, - }; - - /// Outputs a memory or TSS descriptor. - /// For a TSS descriptor on x86-64, you also need a high descriptor as second entry (see below). - pub(crate) fn memory_or_tss(base: u32, limit: u32, ty: descriptor::Type, dpl: PrivilegeLevel, flags: Flags) -> SegmentDescriptor { - let fine_grained = limit < 0x100000; - let (limit1, limit2) = if fine_grained { - ((limit & 0xFFFF) as u16, ((limit & 0xF0000) >> 16) as u8) - } else { - if ((limit - 0xFFF) & 0xFFF) > 0 { - panic!("bad segment limit for GDT entry"); - } - (((limit & 0xFFFF000) >> 12) as u16, ((limit & 0xF0000000) >> 28) as u8) - }; - SegmentDescriptor { - limit1: limit1, - base1: base as u16, - base2: ((base as usize & 0xFF0000) >> 16) as u8, - access: descriptor::Flags::from_type(ty) - | descriptor::Flags::from_priv(dpl) - | descriptor::FLAGS_PRESENT, - limit2_flags: if fine_grained { Flags::empty() } else { FLAGS_G } - | flags - | Flags::from_limit2(limit2), - base3: ((base as usize & 0xFF000000) >> 24) as u8, + // 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 } - /// Outputs a descriptor containing the high 32 bits of a memory address. - /// Serves as the second entry for descriptors that consume 2 table entries in x86-64. - pub(crate) const fn high(address: u64) -> SegmentDescriptor { - SegmentDescriptor { - limit1: (address >> 32) as u16, - base1: (address >> 48) as u16, + // 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, - access: descriptor::Flags::BLANK, - limit2_flags: Flags::BLANK, + 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 } } -bitflags! { - pub flags Flags: u8 { - /// Available for use by system software. - const FLAGS_AVL = 1 << 4, - /// 64-bit code segment (IA-32e mode only). - const FLAGS_L = 1 << 5, - /// Default operation size (0 = 16-bit segment, 1 = 32-bit segment). - const FLAGS_DB = 1 << 6, - /// Granularity (0 = limit in bytes, 1 = limit in 4 KiB Pages). - const FLAGS_G = 1 << 7, +/// Reload stack segment register. +pub unsafe fn load_ss(sel: SegmentSelector) { + asm!("movw $0, %ss " :: "r" (sel.bits()) : "memory"); +} - } +/// Reload data segment register. +pub unsafe fn load_ds(sel: SegmentSelector) { + asm!("movw $0, %ds " :: "r" (sel.bits()) : "memory"); } -impl Flags { - pub const BLANK: Flags = Flags { bits: 0 }; +/// Reload es segment register. +pub unsafe fn load_es(sel: SegmentSelector) { + asm!("movw $0, %es " :: "r" (sel.bits()) : "memory"); +} - pub fn from_limit2(limit2: u8) -> Flags { - assert_eq!(limit2 & !0b1111, 0); - Flags { bits: limit2 } - } +/// Reload fs segment register. +pub unsafe fn load_fs(sel: SegmentSelector) { + asm!("movw $0, %fs " :: "r" (sel.bits()) : "memory"); +} + +/// Reload gs segment register. +pub unsafe fn load_gs(sel: SegmentSelector) { + asm!("movw $0, %gs " :: "r" (sel.bits()) : "memory"); +} + +/// Returns the current value of the code segment register. +pub fn cs() -> SegmentSelector { + let segment: u16; + unsafe { asm!("mov %cs, $0" : "=r" (segment) ) }; + SegmentSelector::from_raw(segment) } |