diff options
-rw-r--r-- | src/bits32/mod.rs | 79 | ||||
-rw-r--r-- | src/bits64/mod.rs | 1 | ||||
-rw-r--r-- | src/bits64/segmentation.rs | 92 | ||||
-rw-r--r-- | src/bits64/task.rs | 2 | ||||
-rw-r--r-- | src/shared/descriptor.rs | 85 | ||||
-rw-r--r-- | src/shared/dtables.rs | 44 | ||||
-rw-r--r-- | src/shared/segmentation.rs | 127 |
7 files changed, 253 insertions, 177 deletions
diff --git a/src/bits32/mod.rs b/src/bits32/mod.rs index 13aa20e..ac872d7 100644 --- a/src/bits32/mod.rs +++ b/src/bits32/mod.rs @@ -3,64 +3,7 @@ pub mod irq; pub mod task; -pub use shared::{ - Flags, - PrivilegeLevel, -}; -pub use self::irq::IdtEntry; - -use core::mem::size_of; - -bitflags! { - pub flags GdtAccess: u8 { - const Accessed = 1 << 0, - const Writable = 1 << 1, - const Direction = 1 << 2, - const Executable = 1 << 3, - const NotTss = 1 << 4, - } -} - -#[derive(Copy, Clone, Debug)] -#[repr(C, packed)] -pub struct GdtEntry { - limit: u16, - base1: u16, - base2: u8, - access: u8, - flags: u8, - base3: u8, -} - -impl GdtEntry { - pub const NULL: GdtEntry = GdtEntry { - base1: 0, - base2: 0, - base3: 0, - access: 0, - limit: 0, - flags: 0 - }; - - pub fn new(base: *const (), limit: usize, access: GdtAccess, dpl: PrivilegeLevel) -> GdtEntry { - let (limit, flags) = if limit < 0x100000 { - ((limit & 0xFFFF) as u16, ((limit & 0xF0000) >> 16) as u8 | 0x40u8) - } else { - if ((limit - 0xFFF) & 0xFFF) > 0 { - panic!("bad segment limit for GDT entry"); - } - (((limit & 0xFFFF000) >> 12) as u16, ((limit & 0xF0000000) >> 28) as u8 | 0xC0u8) - }; - GdtEntry { - base1: base as u16, - base2: ((base as usize & 0xFF0000) >> 16) as u8, - base3: ((base as usize & 0xFF000000) >> 24) as u8, - access: access.bits() | ((dpl as u8) << 5) | 0x80, - limit: limit, - flags: flags - } - } -} +pub use shared::Flags; #[inline(always)] pub fn get_flags() -> Flags { @@ -77,26 +20,6 @@ pub unsafe fn set_flags(val: Flags) { } #[inline(always)] -pub unsafe fn set_gdt(gdt: &[GdtEntry]) { - #[repr(C, packed)] - struct GDTR { - limit: u16, - ptr: *const GdtEntry, - } - asm!("lgdtl $0" :: "*m"(&GDTR { ptr: gdt.as_ptr(), limit: (gdt.len()*size_of::<GdtEntry>() - 1) as u16 }) :: "volatile"); -} - -#[inline(always)] -pub unsafe fn set_idt(idt: &[IdtEntry]) { - #[repr(C, packed)] - struct IDTR { - limit: u16, - ptr: *const IdtEntry, - } - asm!("lidtl $0" :: "*m"(&IDTR { ptr: idt.as_ptr(), limit: idt.len() as u16 * 8 }) :: "volatile"); -} - -#[inline(always)] pub unsafe fn stack_jmp(stack: *mut (), ip: *const ()) -> ! { asm!("mov esp, $0; jmp $1" :: "rg"(stack), "r"(ip) :: "volatile", "intel"); loop { } diff --git a/src/bits64/mod.rs b/src/bits64/mod.rs index 9e5994a..413d3cf 100644 --- a/src/bits64/mod.rs +++ b/src/bits64/mod.rs @@ -35,7 +35,6 @@ pub mod time; pub mod irq; pub mod rflags; pub mod paging; -pub mod segmentation; pub mod task; pub mod syscall; pub mod sgx; diff --git a/src/bits64/segmentation.rs b/src/bits64/segmentation.rs deleted file mode 100644 index 4f12358..0000000 --- a/src/bits64/segmentation.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! Program x86 segmentation hardware. - -/// Entry for GDT or LDT. Provides size and location of a segment. -bitflags! { - pub flags SegmentDescriptor: u64 { - /// Descriptor type (0 = system; 1 = code or data). - const DESC_S = 1 << (32+12), - /// Descriptor privilege level 0. - const DESC_DPL0 = 0b00 << (32+13), - /// Descriptor privilege level 1. - const DESC_DPL1 = 0b01 << (32+13), - /// Descriptor privilege level 2. - const DESC_DPL2 = 0b10 << (32+13), - /// Descriptor privilege level 3. - const DESC_DPL3 = 0b11 << (32+13), - /// Descriptor is Present. - const DESC_P = 1 << (32+15), - /// Available for use by system software. - const DESC_AVL = 1 << (32+20), - /// 64-bit code segment (IA-32e mode only). - const DESC_L = 1 << (32+21), - /// Default operation size (0 = 16-bit segment, 1 = 32-bit segment) - const DESC_DB = 1 << (32+22), - /// Granularity. - const DESC_G = 1 << (32+23), - - // System-Segment and Gate-Descriptor Types for IA32e mode. - // When the S (descriptor type) flag in a segment descriptor is clear, - // the descriptor type is a system descriptor. - - const TYPE_SYS_LDT = 0b0010 << (32+8), - const TYPE_SYS_TSS_AVAILABLE = 0b1001 << (32+8), - const TYPE_SYS_TSS_BUSY = 0b1011 << (32+8), - const TYPE_SYS_CALL_GATE = 0b1100 << (32+8), - const TYPE_SYS_INTERRUPT_GATE = 0b1110 << (32+8), - const TYPE_SYS_TRAP_GATE = 0b1111 << (32+8), - - // Code- and Data-Segment Descriptor Types. - // When the S (descriptor type) flag in a segment descriptor is set, - // the descriptor is for either a code or a data segment. - - /// Data Read-Only - const TYPE_D_RO = 0b0000 << (32+8), - /// Data Read-Only, accessed - const TYPE_D_ROA = 0b0001 << (32+8), - /// Data Read/Write - const TYPE_D_RW = 0b0010 << (32+8), - /// Data Read/Write, accessed - const TYPE_D_RWA = 0b0011 << (32+8), - /// Data Read-Only, expand-down - const TYPE_D_ROEXD = 0b0100 << (32+8), - /// Data Read-Only, expand-down, accessed - const TYPE_D_ROEXDA = 0b0101 << (32+8), - /// Data Read/Write, expand-down - const TYPE_D_RWEXD = 0b0110 << (32+8), - /// Data Read/Write, expand-down, accessed - const TYPE_D_RWEXDA = 0b0111 << (32+8), - - /// Code Execute-Only - const TYPE_C_EO = 0b1000 << (32+8), - /// Code Execute-Only, accessed - const TYPE_C_EOA = 0b1001 << (32+8), - /// Code Execute/Read - const TYPE_C_ER = 0b1010 << (32+8), - /// Code Execute/Read, accessed - const TYPE_C_ERA = 0b1011 << (32+8), - /// Code Execute-Only, conforming - const TYPE_C_EOC = 0b1100 << (32+8), - /// Code Execute-Only, conforming, accessed - const TYPE_C_EOCA = 0b1101 << (32+8), - /// Code Execute/Read, conforming - const TYPE_C_ERC = 0b1110 << (32+8), - /// Code Execute/Read, conforming, accessed - const TYPE_C_ERCA = 0b1111 << (32+8), - } -} - -/// This is data-structure is a ugly mess thing so we provide some -/// convenience function to program it. -impl SegmentDescriptor { - pub fn new(base: u32, limit: u32) -> SegmentDescriptor { - let base_low: u64 = base as u64 & 0xffffff; - let base_high: u64 = (base as u64 >> 24) & 0xff; - - let limit_low: u64 = limit as u64 & 0xffff; - let limit_high: u64 = (limit as u64 & (0b1111 << 16)) >> 16; - - SegmentDescriptor { - bits: limit_low | base_low << 16 | limit_high << (32 + 16) | base_high << (32 + 24), - } - } -} diff --git a/src/bits64/task.rs b/src/bits64/task.rs index da3093c..1d98bad 100644 --- a/src/bits64/task.rs +++ b/src/bits64/task.rs @@ -1,7 +1,7 @@ //! Helpers to program the task state segment. //! See Intel 3a, Chapter 7, Section 7 -use super::segmentation; +use shared::segmentation; pub type TaskStateDescriptorLow = segmentation::SegmentDescriptor; pub type TaskStateDescriptorHigh = u64; diff --git a/src/shared/descriptor.rs b/src/shared/descriptor.rs index 201d8cc..32abb79 100644 --- a/src/shared/descriptor.rs +++ b/src/shared/descriptor.rs @@ -1,6 +1,7 @@ //! Fields which are common to all segment-section and gate descriptors use shared::PrivilegeLevel; +use shared::segmentation; /// System-Segment and Gate-Descriptor Types for IA32e mode. When the `S` /// (descriptor type) flag in a segment descriptor is clear, the descriptor type @@ -24,6 +25,7 @@ pub enum SystemType { /// A high-level representation of a descriptor type. One can convert to and /// from the `Flags` bitfield to encode/decode an actual descriptor. +#[repr(u8)] pub enum Type { SystemDescriptor { /// false/0: 16-bit @@ -31,6 +33,10 @@ pub enum Type { size: bool, ty: SystemType }, + SegmentDescriptor { + ty: segmentation::Type, + accessed: bool + } } impl Type { @@ -38,6 +44,8 @@ impl Type { match self { Type::SystemDescriptor { size, ty } => (size as u8) << 3 | (ty as u8) | FLAGS_TYPE_SYS.bits, + Type::SegmentDescriptor { ty, accessed } => + (accessed as u8) | ty.pack() | FLAGS_TYPE_SYS.bits, } } } @@ -59,6 +67,7 @@ bitflags!{ // Is system descriptor const FLAGS_TYPE_SYS = 0 << 4, + const FLAGS_TYPE_SEG = 1 << 4, // System-Segment and Gate-Descriptor Types. // When the S (descriptor type) flag in a segment descriptor is clear, @@ -81,6 +90,82 @@ bitflags!{ const FLAGS_TYPE_SYS_NATIVE_CALL_GATE = 0b0_1100, const FLAGS_TYPE_SYS_NATIVE_INTERRUPT_GATE = 0b0_1110, const FLAGS_TYPE_SYS_NATIVE_TRAP_GATE = 0b0_1111, + + // Code- and Data-Segment Descriptor Types. + // When the S (descriptor type) flag in a segment descriptor is set, + // the descriptor is for either a code or a data segment. + + /// Data or code, accessed + const FLAGS_TYPE_SEG_ACCESSED = 0b1_0001, + + const FLAGS_TYPE_DATA = 0b1_0000, + const FLAGS_TYPE_CODE = 0b1_1000, + + // Data => permissions + const FLAGS_TYPE_SEG_D_WRITE = 0b1_0010, + const FLAGS_TYPE_SEG_D_EXPAND_DOWN = 0b1_0100, + + // Code => permissions + const FLAGS_TYPE_SEG_C_READ = 0b1_0010, + const FLAGS_TYPE_SEG_D_CONFORMING = 0b1_0100, + + /// Data Read-Only + const FLAGS_TYPE_SEG_D_RO = FLAGS_TYPE_DATA.bits, + /// Data Read-Only, accessed + const FLAGS_TYPE_SEG_D_ROA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, + /// Data Read/Write + const FLAGS_TYPE_SEG_D_RW = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_WRITE.bits, + /// Data Read/Write, accessed + const FLAGS_TYPE_SEG_D_RWA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_WRITE.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, + /// Data Read-Only, expand-down + const FLAGS_TYPE_SEG_D_ROEXD = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_EXPAND_DOWN.bits, + /// Data Read-Only, expand-down, accessed + const FLAGS_TYPE_SEG_D_ROEXDA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_EXPAND_DOWN.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, + /// Data Read/Write, expand-down + const FLAGS_TYPE_SEG_D_RWEXD = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_WRITE.bits + | FLAGS_TYPE_SEG_D_EXPAND_DOWN.bits, + /// Data Read/Write, expand-down, accessed + const FLAGS_TYPE_SEG_D_RWEXDA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_WRITE.bits + | FLAGS_TYPE_SEG_D_EXPAND_DOWN.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, + + /// Code Execute-Only + const FLAGS_TYPE_SEG_C_EO = FLAGS_TYPE_DATA.bits, + /// Code Execute-Only, accessed + const FLAGS_TYPE_SEG_C_EOA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, + /// Code Execute/Read + const FLAGS_TYPE_SEG_C_ER = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_C_READ.bits, + /// Code Execute/Read, accessed + const FLAGS_TYPE_SEG_C_ERA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_C_READ.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, + /// Code Execute-Only, conforming + const FLAGS_TYPE_SEG_C_EOC = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_CONFORMING.bits, + /// Code Execute-Only, conforming, accessed + const FLAGS_TYPE_SEG_C_EOCA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_D_CONFORMING.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, + /// Code Execute/Read, conforming + const FLAGS_TYPE_SEG_C_ERC = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_C_READ.bits + | FLAGS_TYPE_SEG_D_CONFORMING.bits, + /// Code Execute/Read, conforming, accessed + const FLAGS_TYPE_SEG_C_ERCA = FLAGS_TYPE_DATA.bits + | FLAGS_TYPE_SEG_C_READ.bits + | FLAGS_TYPE_SEG_D_CONFORMING.bits + | FLAGS_TYPE_SEG_ACCESSED.bits, } } diff --git a/src/shared/dtables.rs b/src/shared/dtables.rs index d2e3413..c5337fb 100644 --- a/src/shared/dtables.rs +++ b/src/shared/dtables.rs @@ -1,27 +1,61 @@ //! Functions and data-structures to load descriptor tables. +use core::mem::size_of; + +use current::irq::IdtEntry; +use shared::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'. #[derive(Debug)] #[repr(C, packed)] -pub struct DescriptorTablePointer { +pub struct DescriptorTablePointer<Entry> { /// Size of the DT. pub limit: u16, /// Pointer to the memory region containing the DT. - pub base: u64, + pub base: *const Entry, } +impl<T> DescriptorTablePointer<T> { + fn new(slice: &[T]) -> Self { + let len = slice.len() * size_of::<T>(); + assert!(len < 0x10000); + DescriptorTablePointer { + base: slice.as_ptr(), + limit: len as u16, + } + } +} + +impl DescriptorTablePointer<SegmentDescriptor> { + pub fn new_gdtp(gdt: &[SegmentDescriptor]) -> Self { + let mut p = Self::new(gdt); + p.limit -= 1; + p + } + pub fn new_ldtp(ldt: &[SegmentDescriptor]) -> Self { + Self::new(ldt) + } +} + +impl DescriptorTablePointer<IdtEntry> { + pub fn new_idtp(idt: &[IdtEntry]) -> Self { + Self::new(idt) + } +} + + /// Load GDT table. -pub unsafe fn lgdt(gdt: &DescriptorTablePointer) { +pub unsafe fn lgdt(gdt: &DescriptorTablePointer<SegmentDescriptor>) { asm!("lgdt ($0)" :: "r" (gdt) : "memory"); } /// Load LDT table. -pub unsafe fn lldt(ldt: &DescriptorTablePointer) { +pub unsafe fn lldt(ldt: &DescriptorTablePointer<SegmentDescriptor>) { asm!("lldt ($0)" :: "r" (ldt) : "memory"); } /// Load IDT table. -pub unsafe fn lidt(idt: &DescriptorTablePointer) { +pub unsafe fn lidt(idt: &DescriptorTablePointer<IdtEntry>) { asm!("lidt ($0)" :: "r" (idt) : "memory"); } diff --git a/src/shared/segmentation.rs b/src/shared/segmentation.rs index f4dc194..7f895ae 100644 --- a/src/shared/segmentation.rs +++ b/src/shared/segmentation.rs @@ -1,10 +1,13 @@ use core::fmt; +use shared::descriptor; use shared::PrivilegeLevel; /// Specifies which element to load into a segment from /// descriptor tables (i.e., is a index to LDT or GDT table /// with some additional flags). +/// +/// See Intel 3a, Section 3.4.2 "Segment Selectors" bitflags! { #[repr(C, packed)] pub flags SegmentSelector: u16 { @@ -133,3 +136,127 @@ pub fn cs() -> SegmentSelector { unsafe { asm!("mov %cs, $0" : "=r" (segment) ) }; SegmentSelector::from_raw(segment) } + + +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, + } +} + +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, + } +} + +/// Umbrella Segment Type. +/// +/// See Table 3-1, "Code- and Data-Segment Types" +#[repr(u8)] +pub enum Type { + Data(DataAccess), + Code(CodeAccess), +} + +impl Type { + pub fn pack(self) -> u8 { + match self { + Type::Data(d) => d.bits | 0b0_000, + Type::Code(c) => c.bits | 0b1_000, + } + } +} + + +/// 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, +} + +/// This is data-structure is a ugly mess thing so we provide some +/// convenience function to program it. +impl SegmentDescriptor { + pub const NULL: SegmentDescriptor = SegmentDescriptor { + base1: 0, + base2: 0, + base3: 0, + access: descriptor::Flags::BLANK, + limit1: 0, + limit2_flags: Flags::BLANK, + }; + + pub fn new(base: u32, limit: u32, + ty: Type, accessed: bool, dpl: PrivilegeLevel) -> 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) + }; + let ty1 = descriptor::Type::SegmentDescriptor { + ty: ty, + accessed: accessed + }; + SegmentDescriptor { + base1: base as u16, + base2: ((base as usize & 0xFF0000) >> 16) as u8, + base3: ((base as usize & 0xFF000000) >> 24) as u8, + access: descriptor::Flags::from_type(ty1) + | descriptor::Flags::from_priv(dpl), + limit1: limit1, + limit2_flags: FLAGS_DB + | if fine_grained { FLAGS_G } else { Flags::empty() } + | Flags::from_limit2(limit2), + } + } +} + +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 = limt in 4 KiB Pages). + const FLAGS_G = 1 << 7, + + } +} + +impl Flags { + pub const BLANK: Flags = Flags { bits: 0 }; + + pub fn from_limit2(limit2: u8) -> Flags { + assert_eq!(limit2 & !0b111, 0); + Flags { bits: limit2 } + } +} |