aboutsummaryrefslogtreecommitdiff
path: root/src/segmentation.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/segmentation.rs')
-rw-r--r--src/segmentation.rs243
1 files changed, 243 insertions, 0 deletions
diff --git a/src/segmentation.rs b/src/segmentation.rs
new file mode 100644
index 0000000..7672d4a
--- /dev/null
+++ b/src/segmentation.rs
@@ -0,0 +1,243 @@
+use core::fmt;
+
+use ::descriptor;
+use ::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 {
+ /// Requestor Privilege Level
+ 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,
+ /// Table Indicator (TI) 1 means LDT is used.
+ const TI_LDT = 1 << 2,
+ }
+}
+
+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) }
+ }
+
+ pub const fn from_raw(bits: u16) -> SegmentSelector {
+ SegmentSelector { bits: bits }
+ }
+}
+
+impl fmt::Display for SegmentSelector {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let r0 = match self.contains(RPL_0) {
+ false => "",
+ true => "Ring 0 segment selector.",
+ };
+ let r1 = match self.contains(RPL_1) {
+ false => "",
+ true => "Ring 1 segment selector.",
+ };
+ let r2 = match self.contains(RPL_2) {
+ false => "",
+ true => "Ring 2 segment selector.",
+ };
+ let r3 = match self.contains(RPL_3) {
+ false => "",
+ true => "Ring 3 segment selector.",
+ };
+ let tbl = match self.contains(TI_LDT) {
+ false => "GDT Table",
+ true => "LDT Table",
+ };
+
+ write!(f,
+ "Index {} in {}, {}{}{}{}",
+ self.bits >> 3,
+ tbl,
+ r0,
+ r1,
+ r2,
+ r3)
+ // write!(f, "Index")
+ }
+}
+
+
+/// 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");
+}
+
+/// Reload es segment register.
+pub unsafe fn load_es(sel: SegmentSelector) {
+ asm!("movw $0, %es " :: "r" (sel.bits()) : "memory");
+}
+
+/// 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)
+}
+
+
+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 | 0b10_000,
+ Type::Code(c) => c.bits | 0b11_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 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,
+ }
+ }
+
+ /// 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,
+ base2: 0,
+ access: descriptor::Flags::BLANK,
+ limit2_flags: Flags::BLANK,
+ base3: 0,
+ }
+ }
+}
+
+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,
+
+ }
+}
+
+impl Flags {
+ pub const BLANK: Flags = Flags { bits: 0 };
+
+ pub fn from_limit2(limit2: u8) -> Flags {
+ assert_eq!(limit2 & !0b1111, 0);
+ Flags { bits: limit2 }
+ }
+}