diff options
author | 2016-07-04 14:08:17 +0200 | |
---|---|---|
committer | 2016-07-04 14:08:17 +0200 | |
commit | c12e050a69dd1a9b04f07ab78a166c6371d35a6f (patch) | |
tree | fea35bcf5b507efdd4c1c0f5dab146360c70478a /src | |
parent | 32257991aaa3700620f1d6c180cfec3e2d65a360 (diff) | |
parent | bd2950de1a48d72cbb718cc9a367142e0eb97b72 (diff) | |
download | rust-x86-c12e050a69dd1a9b04f07ab78a166c6371d35a6f.tar.gz rust-x86-c12e050a69dd1a9b04f07ab78a166c6371d35a6f.tar.zst rust-x86-c12e050a69dd1a9b04f07ab78a166c6371d35a6f.zip |
Merge pull request #16 from QuiltOS/master
Fix #15: Combine with https://github.com/Tobba/libcpu
Diffstat (limited to 'src')
-rw-r--r-- | src/bitflags.rs | 163 | ||||
-rw-r--r-- | src/bits32/irq.rs | 57 | ||||
-rw-r--r-- | src/bits32/mod.rs | 8 | ||||
-rw-r--r-- | src/bits32/task.rs | 93 | ||||
-rw-r--r-- | src/bits64/irq.rs | 143 | ||||
-rw-r--r-- | src/bits64/mod.rs | 44 | ||||
-rw-r--r-- | src/bits64/paging.rs (renamed from src/paging.rs) | 60 | ||||
-rw-r--r-- | src/bits64/perfcnt/intel/counters.rs (renamed from src/perfcnt/intel/counters.rs) | 0 | ||||
-rw-r--r-- | src/bits64/perfcnt/intel/description.rs (renamed from src/perfcnt/intel/description.rs) | 0 | ||||
-rw-r--r-- | src/bits64/perfcnt/intel/mod.rs (renamed from src/perfcnt/intel/mod.rs) | 0 | ||||
-rw-r--r-- | src/bits64/perfcnt/mod.rs (renamed from src/perfcnt/mod.rs) | 0 | ||||
-rw-r--r-- | src/bits64/sgx.rs (renamed from src/sgx.rs) | 10 | ||||
-rw-r--r-- | src/bits64/syscall.rs (renamed from src/syscall.rs) | 0 | ||||
-rw-r--r-- | src/bits64/task.rs (renamed from src/task.rs) | 14 | ||||
-rw-r--r-- | src/bits64/time.rs (renamed from src/time.rs) | 0 | ||||
-rw-r--r-- | src/bits64/tlb.rs (renamed from src/tlb.rs) | 2 | ||||
-rw-r--r-- | src/controlregs.rs | 43 | ||||
-rw-r--r-- | src/dtables.rs | 27 | ||||
-rw-r--r-- | src/io.rs | 37 | ||||
-rw-r--r-- | src/irq.rs | 340 | ||||
-rw-r--r-- | src/lib.rs | 70 | ||||
-rw-r--r-- | src/rflags.rs | 56 | ||||
-rw-r--r-- | src/segmentation.rs | 214 | ||||
-rw-r--r-- | src/shared/control_regs.rs | 84 | ||||
-rw-r--r-- | src/shared/descriptor.rs | 194 | ||||
-rw-r--r-- | src/shared/dtables.rs | 61 | ||||
-rw-r--r-- | src/shared/flags.rs | 102 | ||||
-rw-r--r-- | src/shared/io.rs | 80 | ||||
-rw-r--r-- | src/shared/irq.rs | 207 | ||||
-rw-r--r-- | src/shared/mod.rs | 24 | ||||
-rw-r--r-- | src/shared/msr.rs (renamed from src/msr.rs) | 4 | ||||
-rw-r--r-- | src/shared/paging.rs | 48 | ||||
-rw-r--r-- | src/shared/segmentation.rs | 262 | ||||
-rw-r--r-- | src/shared/task.rs | 9 |
34 files changed, 1452 insertions, 1004 deletions
diff --git a/src/bitflags.rs b/src/bitflags.rs deleted file mode 100644 index 8d16e41..0000000 --- a/src/bitflags.rs +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2014 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -macro_rules! bitflags { - ($(#[$attr:meta])* flags $BitFlags:ident: $T:ty { - $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+ - }) => { - #[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] - $(#[$attr])* - pub struct $BitFlags { - bits: $T, - } - - $($(#[$Flag_attr])* pub const $Flag: $BitFlags = $BitFlags { bits: $value };)+ - - impl $BitFlags { - /// Returns an empty set of flags. - #[inline] - pub fn empty() -> $BitFlags { - $BitFlags { bits: 0 } - } - - /// Returns the set containing all flags. - #[inline] - pub fn all() -> $BitFlags { - $BitFlags { bits: $($value)|+ } - } - - /// Returns the raw value of the flags currently stored. - #[inline] - pub fn bits(&self) -> $T { - self.bits - } - - /// Convert from underlying bit representation, unless that - /// representation contains bits that do not correspond to a flag. - #[inline] - pub fn from_bits(bits: $T) -> ::std::option::Option<$BitFlags> { - if (bits & !$BitFlags::all().bits()) != 0 { - ::std::option::Option::None - } else { - ::std::option::Option::Some($BitFlags { bits: bits }) - } - } - - /// Convert from underlying bit representation, dropping any bits - /// that do not correspond to flags. - #[inline] - pub fn from_bits_truncate(bits: $T) -> $BitFlags { - $BitFlags { bits: bits } & $BitFlags::all() - } - - /// Returns `true` if no flags are currently stored. - #[inline] - pub fn is_empty(&self) -> bool { - *self == $BitFlags::empty() - } - - /// Returns `true` if all flags are currently set. - #[inline] - pub fn is_all(&self) -> bool { - *self == $BitFlags::all() - } - - /// Returns `true` if there are flags common to both `self` and `other`. - #[inline] - pub fn intersects(&self, other: $BitFlags) -> bool { - !(*self & other).is_empty() - } - - /// Returns `true` all of the flags in `other` are contained within `self`. - #[inline] - pub fn contains(&self, other: $BitFlags) -> bool { - (*self & other) == other - } - - /// Inserts the specified flags in-place. - #[inline] - pub fn insert(&mut self, other: $BitFlags) { - self.bits |= other.bits; - } - - /// Removes the specified flags in-place. - #[inline] - pub fn remove(&mut self, other: $BitFlags) { - self.bits &= !other.bits; - } - - /// Toggles the specified flags in-place. - #[inline] - pub fn toggle(&mut self, other: $BitFlags) { - self.bits ^= other.bits; - } - } - - impl ::std::ops::BitOr for $BitFlags { - type Output = $BitFlags; - - /// Returns the union of the two sets of flags. - #[inline] - fn bitor(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits | other.bits } - } - } - - impl ::std::ops::BitXor for $BitFlags { - type Output = $BitFlags; - - /// Returns the left flags, but with all the right flags toggled. - #[inline] - fn bitxor(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits ^ other.bits } - } - } - - impl ::std::ops::BitAnd for $BitFlags { - type Output = $BitFlags; - - /// Returns the intersection between the two sets of flags. - #[inline] - fn bitand(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits & other.bits } - } - } - - impl ::std::ops::Sub for $BitFlags { - type Output = $BitFlags; - - /// Returns the set difference of the two sets of flags. - #[inline] - fn sub(self, other: $BitFlags) -> $BitFlags { - $BitFlags { bits: self.bits & !other.bits } - } - } - - impl ::std::ops::Not for $BitFlags { - type Output = $BitFlags; - - /// Returns the complement of this set of flags. - #[inline] - fn not(self) -> $BitFlags { - $BitFlags { bits: !self.bits } & $BitFlags::all() - } - } - }; - ($(#[$attr:meta])* flags $BitFlags:ident: $T:ty { - $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+, - }) => { - bitflags! { - $(#[$attr])* - flags $BitFlags: $T { - $($(#[$Flag_attr])* const $Flag = $value),+ - } - } - }; -} diff --git a/src/bits32/irq.rs b/src/bits32/irq.rs new file mode 100644 index 0000000..5d6eb0d --- /dev/null +++ b/src/bits32/irq.rs @@ -0,0 +1,57 @@ +//! Interrupt description and set-up code. + +use shared::descriptor::*; +use shared::paging::VAddr; +use shared::PrivilegeLevel; + +/// 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 flags: 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: PrivilegeLevel, 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), + } + } +} diff --git a/src/bits32/mod.rs b/src/bits32/mod.rs new file mode 100644 index 0000000..e98b862 --- /dev/null +++ b/src/bits32/mod.rs @@ -0,0 +1,8 @@ +pub mod irq; +pub mod task; + +#[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/bits32/task.rs b/src/bits32/task.rs new file mode 100644 index 0000000..17bdcd4 --- /dev/null +++ b/src/bits32/task.rs @@ -0,0 +1,93 @@ +//! Helpers to program the task state segment. +//! See Intel 3a, Chapter 7 + +use core::mem::size_of; + +#[derive(Copy, Clone, Debug)] +#[repr(C, packed)] +pub struct TaskStateSegment { + pub link: u16, + reserved0: u16, + pub esp0: u32, + pub ss0: u16, + reserved1: u16, + pub esp1: u32, + pub ss1: u16, + reserved2: u16, + pub esp2: u32, + pub ss2: u16, + reserved3: u16, + + pub cr3: u32, + pub eip: u32, + pub eflags: u32, + + pub eax: u32, + pub ecx: u32, + pub edx: u32, + pub ebx: u32, + pub esp: u32, + pub ebp: u32, + pub esi: u32, + pub edi: u32, + + pub es: u16, + reserved4: u16, + pub cs: u16, + reserved5: u16, + pub ss: u16, + reserved6: u16, + pub ds: u16, + reserved7: u16, + pub fs: u16, + reserved8: u16, + pub gs: u16, + reserved9: u16, + pub ldtr: u16, + reserved10: u32, + pub iobp_offset: u16 +} + +impl TaskStateSegment { + pub fn new() -> TaskStateSegment { + TaskStateSegment { + link: 0, + reserved0: 0, + esp0: 0, + ss0: 0, + reserved1: 0, + esp1: 0, + ss1: 0, + reserved2: 0, + esp2: 0, + ss2: 0, + reserved3: 0, + cr3: 0, + eip: 0, + eflags: 0, + eax: 0, + ecx: 0, + edx: 0, + ebx: 0, + esp: 0, + ebp: 0, + esi: 0, + edi: 0, + es: 0, + reserved4: 0, + cs: 0, + reserved5: 0, + ss: 0, + reserved6: 0, + ds: 0, + reserved7: 0, + fs: 0, + reserved8: 0, + gs: 0, + reserved9: 0, + ldtr: 0, + reserved10: 0, + iobp_offset: size_of::<TaskStateSegment>() as u16 + } + } +} diff --git a/src/bits64/irq.rs b/src/bits64/irq.rs new file mode 100644 index 0000000..84ee0e6 --- /dev/null +++ b/src/bits64/irq.rs @@ -0,0 +1,143 @@ +//! Interrupt description and set-up code. + +use core::fmt; + +use shared::descriptor::*; +use shared::paging::VAddr; +use shared::PrivilegeLevel; + +/// An interrupt gate descriptor. +/// +/// See Intel manual 3a for details, specifically section "6.14.1 64-Bit Mode +/// IDT" and "Table 3-2. System-Segment and Gate-Descriptor Types". +#[derive(Debug, Clone, Copy)] +#[repr(C, packed)] +pub struct IdtEntry { + /// Lower 16 bits of ISR. + pub base_lo: u16, + /// Segment selector. + pub selector: u16, + /// This must always be zero. + pub reserved0: u8, + /// Flags. + pub flags: Flags, + /// The upper 48 bits of ISR (the last 16 bits must be zero). + pub base_hi: u64, + /// Must be zero. + pub reserved1: u16, +} + +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: 0, + reserved0: 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 const fn new(handler: VAddr, gdt_code_selector: u16, + dpl: PrivilegeLevel, block: bool) -> IdtEntry { + IdtEntry { + base_lo: ((handler.as_usize() as u64) & 0xFFFF) as u16, + base_hi: handler.as_usize() as u64 >> 16, + selector: gdt_code_selector, + reserved0: 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), + reserved1: 0, + } + } +} + +bitflags!{ + // Taken from Intel Manual Section 4.7 Page-Fault Exceptions. + pub flags PageFaultError: u32 { + /// 0: The fault was caused by a non-present page. + /// 1: The fault was caused by a page-level protection violation + const PFAULT_ERROR_P = bit!(0), + + /// 0: The access causing the fault was a read. + /// 1: The access causing the fault was a write. + const PFAULT_ERROR_WR = bit!(1), + + /// 0: The access causing the fault originated when the processor + /// was executing in supervisor mode. + /// 1: The access causing the fault originated when the processor + /// was executing in user mode. + const PFAULT_ERROR_US = bit!(2), + + /// 0: The fault was not caused by reserved bit violation. + /// 1: The fault was caused by reserved bits set to 1 in a page directory. + const PFAULT_ERROR_RSVD = bit!(3), + + /// 0: The fault was not caused by an instruction fetch. + /// 1: The fault was caused by an instruction fetch. + const PFAULT_ERROR_ID = bit!(4), + + /// 0: The fault was not by protection keys. + /// 1: There was a protection key violation. + const PFAULT_ERROR_PK = bit!(5), + } +} + +impl fmt::Display for PageFaultError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let p = match self.contains(PFAULT_ERROR_P) { + false => "The fault was caused by a non-present page.", + true => "The fault was caused by a page-level protection violation.", + }; + let wr = match self.contains(PFAULT_ERROR_WR) { + false => "The access causing the fault was a read.", + true => "The access causing the fault was a write.", + }; + let us = match self.contains(PFAULT_ERROR_US) { + false => { + "The access causing the fault originated when the processor was executing in \ + supervisor mode." + } + true => { + "The access causing the fault originated when the processor was executing in user \ + mode." + } + }; + let rsvd = match self.contains(PFAULT_ERROR_RSVD) { + false => "The fault was not caused by reserved bit violation.", + true => "The fault was caused by reserved bits set to 1 in a page directory.", + }; + let id = match self.contains(PFAULT_ERROR_ID) { + false => "The fault was not caused by an instruction fetch.", + true => "The fault was caused by an instruction fetch.", + }; + + write!(f, "{}\n{}\n{}\n{}\n{}", p, wr, us, rsvd, id) + } +} + +#[test] +fn bit_macro() { + assert!(PFAULT_ERROR_PK.bits() == 0b100000); + assert!(PFAULT_ERROR_ID.bits() == 0b10000); + assert!(PFAULT_ERROR_RSVD.bits() == 0b1000); + assert!(PFAULT_ERROR_US.bits() == 0b100); + assert!(PFAULT_ERROR_WR.bits() == 0b10); + assert!(PFAULT_ERROR_P.bits() == 0b1); +} diff --git a/src/bits64/mod.rs b/src/bits64/mod.rs new file mode 100644 index 0000000..4b83c7c --- /dev/null +++ b/src/bits64/mod.rs @@ -0,0 +1,44 @@ +//! Data structures and functions used by IA-32e but not Protected Mode. + +macro_rules! bit { + ( $x:expr ) => { + 1 << $x + }; +} + +macro_rules! check_flag { + ($doc:meta, $fun:ident, $flag:ident) => ( + #[$doc] + pub fn $fun(&self) -> bool { + self.contains($flag) + } + ) +} + +macro_rules! is_bit_set { + ($field:expr, $bit:expr) => ( + $field & (1 << $bit) > 0 + ) +} + +macro_rules! check_bit_fn { + ($doc:meta, $fun:ident, $field:ident, $bit:expr) => ( + #[$doc] + pub fn $fun(&self) -> bool { + is_bit_set!(self.$field, $bit) + } + ) +} + +pub mod time; +pub mod irq; +pub mod paging; +pub mod task; +pub mod syscall; +pub mod sgx; +#[cfg(feature = "performance-counter")] +pub mod perfcnt; +pub mod cpuid { + pub use raw_cpuid::*; +} +pub mod tlb; diff --git a/src/paging.rs b/src/bits64/paging.rs index 22d9526..47ac8ed 100644 --- a/src/paging.rs +++ b/src/bits64/paging.rs @@ -1,14 +1,13 @@ //! Description of the data-structures for IA-32e paging mode. + use core::fmt; +use shared::paging::*; + /// Represents a physical memory address #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PAddr(u64); -/// Represent a virtual (linear) memory address -#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct VAddr(usize); - impl PAddr { /// Convert to `u64` pub const fn as_u64(&self) -> u64 { @@ -20,17 +19,6 @@ impl PAddr { } } -impl VAddr { - /// Convert to `usize` - pub const fn as_usize(&self) -> usize { - self.0 - } - /// Convert from `usize` - pub const fn from_usize(v: usize) -> Self { - VAddr(v) - } -} - impl fmt::Binary for PAddr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.fmt(f) @@ -61,36 +49,6 @@ impl fmt::UpperHex for PAddr { } } -impl fmt::Binary for VAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::Display for VAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::LowerHex for VAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::Octal for VAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::UpperHex for VAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - pub const BASE_PAGE_SIZE: u64 = 4096; // 4 KiB pub const LARGE_PAGE_SIZE: u64 = 1024 * 1024 * 2; // 2 MiB pub const HUGE_PAGE_SIZE: u64 = 1024 * 1024 * 1024; // 1 GiB @@ -140,8 +98,7 @@ pub fn pt_index(addr: VAddr) -> usize { /// PML4 Entry bits description. bitflags! { - #[derive(Debug)] - flags PML4Entry: u64 { + pub flags PML4Entry: u64 { /// Present; must be 1 to reference a page-directory-pointer table const PML4_P = bit!(0), /// Read/write; if 0, writes may not be allowed to the 512-GByte region @@ -200,8 +157,7 @@ impl PML4Entry { /// PDPT Entry bits description. bitflags! { - #[derive(Debug)] - flags PDPTEntry: u64 { + pub flags PDPTEntry: u64 { /// Present; must be 1 to map a 1-GByte page or reference a page directory. const PDPT_P = bit!(0), /// Read/write; if 0, writes may not be allowed to the 1-GByte region controlled by this entry @@ -269,8 +225,7 @@ impl PDPTEntry { /// PD Entry bits description. bitflags! { - #[derive(Debug)] - flags PDEntry: u64 { + pub flags PDEntry: u64 { /// Present; must be 1 to map a 2-MByte page or reference a page table. const PD_P = bit!(0), /// Read/write; if 0, writes may not be allowed to the 2-MByte region controlled by this entry @@ -345,8 +300,7 @@ impl PDEntry { /// PT Entry bits description. bitflags! { - #[derive(Debug)] - flags PTEntry: u64 { + pub flags PTEntry: u64 { /// Present; must be 1 to map a 4-KByte page. const PT_P = bit!(0), /// Read/write; if 0, writes may not be allowed to the 4-KByte region controlled by this entry diff --git a/src/perfcnt/intel/counters.rs b/src/bits64/perfcnt/intel/counters.rs index e2987cc..e2987cc 100644 --- a/src/perfcnt/intel/counters.rs +++ b/src/bits64/perfcnt/intel/counters.rs diff --git a/src/perfcnt/intel/description.rs b/src/bits64/perfcnt/intel/description.rs index f64426d..f64426d 100644 --- a/src/perfcnt/intel/description.rs +++ b/src/bits64/perfcnt/intel/description.rs diff --git a/src/perfcnt/intel/mod.rs b/src/bits64/perfcnt/intel/mod.rs index 960e0c6..960e0c6 100644 --- a/src/perfcnt/intel/mod.rs +++ b/src/bits64/perfcnt/intel/mod.rs diff --git a/src/perfcnt/mod.rs b/src/bits64/perfcnt/mod.rs index 74b80a2..74b80a2 100644 --- a/src/perfcnt/mod.rs +++ b/src/bits64/perfcnt/mod.rs diff --git a/src/sgx.rs b/src/bits64/sgx.rs index e611620..c705e47 100644 --- a/src/sgx.rs +++ b/src/bits64/sgx.rs @@ -6,13 +6,13 @@ /// * Function needs to be executed in ring 0. macro_rules! encls { ($rax:expr, $rbx:expr) - => ( $crate::sgx::encls2($rax as u64, $rbx as u64) ); + => ( $crate::bits64::sgx::encls2($rax as u64, $rbx as u64) ); ($rax:expr, $rbx:expr, $rcx:expr) - => ( $crate::sgx::encls3($rax as u64, $rbx as u64, $rcx as u64) ); + => ( $crate::bits64::sgx::encls3($rax as u64, $rbx as u64, $rcx as u64) ); ($rax:expr, $rbx:expr, $rcx:expr, $rdx:expr) - => ( $crate::sgx::encls4($rax as u64, $rbx as u64, $rcx as u64, $rdx as u64) ); + => ( $crate::bits64::sgx::encls4($rax as u64, $rbx as u64, $rcx as u64, $rdx as u64) ); } /// encls with two arguments -- consider calling the encls! macro instead! @@ -226,10 +226,10 @@ pub unsafe fn encls_ewb(pageinfo: u64, epc_page: u64, va_slot: u64) -> u32 { /// * Function needs to be executed in ring 3. macro_rules! enclu { ($rax:expr, $rbx:expr, $rcx:expr) - => ( $crate::sgx::enclu3($rax as u64, $rbx as u64, $rcx as u64) ); + => ( $crate::bits64::sgx::enclu3($rax as u64, $rbx as u64, $rcx as u64) ); ($rax:expr, $rbx:expr, $rcx:expr, $rdx:expr) - => ( $crate::sgx::enclu4($rax as u64, $rbx as u64, $rcx as u64, $rdx as u64) ); + => ( $crate::bits64::sgx::enclu4($rax as u64, $rbx as u64, $rcx as u64, $rdx as u64) ); } /// enclu with three arguments -- consider calling the enclu! macro instead! diff --git a/src/syscall.rs b/src/bits64/syscall.rs index c554177..c554177 100644 --- a/src/syscall.rs +++ b/src/bits64/syscall.rs diff --git a/src/task.rs b/src/bits64/task.rs index f37bd8d..1d98bad 100644 --- a/src/task.rs +++ b/src/bits64/task.rs @@ -1,6 +1,7 @@ //! Helpers to program the task state segment. +//! See Intel 3a, Chapter 7, Section 7 -use segmentation; +use shared::segmentation; pub type TaskStateDescriptorLow = segmentation::SegmentDescriptor; pub type TaskStateDescriptorHigh = u64; @@ -25,20 +26,15 @@ pub struct TaskStateSegment { } impl TaskStateSegment { - pub fn new() -> TaskStateSegment { + pub const fn new() -> TaskStateSegment { TaskStateSegment { reserved: 0, - rsp: [0, 0, 0], + rsp: [0; 3], reserved2: 0, - ist: [0, 0, 0, 0, 0, 0, 0], + ist: [0; 7], reserved3: 0, reserved4: 0, iomap_base: 0, } } } - -/// Load the task state register. -pub unsafe fn load_ltr(sel: segmentation::SegmentSelector) { - asm!("ltr $0" :: "r" (sel)); -} diff --git a/src/time.rs b/src/bits64/time.rs index eff567d..eff567d 100644 --- a/src/time.rs +++ b/src/bits64/time.rs diff --git a/src/tlb.rs b/src/bits64/tlb.rs index ec39e3e..f99604f 100644 --- a/src/tlb.rs +++ b/src/bits64/tlb.rs @@ -15,6 +15,6 @@ pub unsafe fn flush(addr: usize) { /// This function is unsafe as it causes a general protection fault (GP) if the current privilege /// level is not 0. pub unsafe fn flush_all() { - use controlregs::{cr3, cr3_write}; + use shared::control_regs::{cr3, cr3_write}; cr3_write(cr3()) } diff --git a/src/controlregs.rs b/src/controlregs.rs deleted file mode 100644 index c243caf..0000000 --- a/src/controlregs.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! Functions to read and write control registers. - -pub unsafe fn cr0() -> u64 { - let ret: u64; - asm!("mov %cr0, $0" : "=r" (ret)); - ret -} - -/// Write cr0. -pub unsafe fn cr0_write(val: u64) { - asm!("mov $0, %cr0" :: "r" (val) : "memory"); -} - -/// Contains page-fault linear address. -pub unsafe fn cr2() -> u64 { - let ret: u64; - asm!("mov %cr2, $0" : "=r" (ret)); - ret -} - -/// Contains page-table root pointer. -pub unsafe fn cr3() -> u64 { - let ret: u64; - asm!("mov %cr3, $0" : "=r" (ret)); - ret -} - -/// Switch page-table PML4 pointer. -pub unsafe fn cr3_write(val: u64) { - asm!("mov $0, %cr3" :: "r" (val) : "memory"); -} - -/// Contains various flags to control operations in protected mode. -pub unsafe fn cr4() -> u64 { - let ret: u64; - asm!("mov %cr4, $0" : "=r" (ret)); - ret -} - -/// Write cr4. -pub unsafe fn cr4_write(val: u64) { - asm!("mov $0, %cr4" :: "r" (val) : "memory"); -} diff --git a/src/dtables.rs b/src/dtables.rs deleted file mode 100644 index d2e3413..0000000 --- a/src/dtables.rs +++ /dev/null @@ -1,27 +0,0 @@ -//! Functions and data-structures to load descriptor tables. - -/// 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 { - /// Size of the DT. - pub limit: u16, - /// Pointer to the memory region containing the DT. - pub base: u64, -} - -/// Load GDT table. -pub unsafe fn lgdt(gdt: &DescriptorTablePointer) { - asm!("lgdt ($0)" :: "r" (gdt) : "memory"); -} - -/// Load LDT table. -pub unsafe fn lldt(ldt: &DescriptorTablePointer) { - asm!("lldt ($0)" :: "r" (ldt) : "memory"); -} - -/// Load IDT table. -pub unsafe fn lidt(idt: &DescriptorTablePointer) { - asm!("lidt ($0)" :: "r" (idt) : "memory"); -} diff --git a/src/io.rs b/src/io.rs deleted file mode 100644 index bb7cfb0..0000000 --- a/src/io.rs +++ /dev/null @@ -1,37 +0,0 @@ -//! I/O port functionality. - -/// Write 8 bits to port -pub unsafe fn outb(port: u16, val: u8) { - asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(val)); -} - -/// Read 8 bits from port -pub unsafe fn inb(port: u16) -> u8 { - let ret: u8; - asm!("inb %dx, %al" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); - return ret; -} - -/// Write 16 bits to port -pub unsafe fn outw(port: u16, val: u16) { - asm!("outw %ax, %dx" :: "{dx}"(port), "{al}"(val)); -} - -/// Read 16 bits from port -pub unsafe fn inw(port: u16) -> u16 { - let ret: u16; - asm!("inw %dx, %ax" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); - return ret; -} - -/// Write 32 bits to port -pub unsafe fn outl(port: u16, val: u32) { - asm!("outl %eax, %dx" :: "{dx}"(port), "{al}"(val)); -} - -/// Read 32 bits from port -pub unsafe fn inl(port: u16) -> u32 { - let ret: u32; - asm!("inl %dx, %eax" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); - return ret; -} diff --git a/src/irq.rs b/src/irq.rs deleted file mode 100644 index 10be7fa..0000000 --- a/src/irq.rs +++ /dev/null @@ -1,340 +0,0 @@ -//! Interrupt description and set-up code. - -use core::fmt; -use paging::VAddr; - -/// x86 Exception description (see also Intel Vol. 3a Chapter 6). -#[derive(Debug)] -pub struct InterruptDescription { - pub vector: u8, - pub mnemonic: &'static str, - pub description: &'static str, - pub irqtype: &'static str, - pub source: &'static str, -} - -impl fmt::Display for InterruptDescription { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, - "{} ({}, vec={}) {}", - self.mnemonic, - self.irqtype, - self.vector, - self.description) - } -} - - -/// x86 External Interrupts (1-16). -pub static EXCEPTIONS: [InterruptDescription; 21] = [InterruptDescription { - vector: 0, - mnemonic: "#DE", - description: "Divide Error", - irqtype: "Fault", - source: "DIV and IDIV instructions.", - }, - InterruptDescription { - vector: 1, - mnemonic: "#DB", - description: "Debug", - irqtype: "Fault/ Trap", - source: "Debug condition", - }, - InterruptDescription { - vector: 2, - mnemonic: "NMI", - description: "Nonmaskable Interrupt", - irqtype: "Interrupt", - source: "Nonmaskable external interrupt.", - }, - InterruptDescription { - vector: 3, - mnemonic: "#BP", - description: "Breakpoint", - irqtype: "Trap", - source: "INT 3 instruction.", - }, - InterruptDescription { - vector: 4, - mnemonic: "#OF", - description: "Overflow", - irqtype: "Trap", - source: "INTO instruction.", - }, - InterruptDescription { - vector: 5, - mnemonic: "#BR", - description: "BOUND Range Exceeded", - irqtype: "Fault", - source: "BOUND instruction.", - }, - InterruptDescription { - vector: 6, - mnemonic: "#UD", - description: "Invalid Opcode (Undefined \ - Opcode)", - irqtype: "Fault", - source: "UD2 instruction or reserved \ - opcode.", - }, - InterruptDescription { - vector: 7, - mnemonic: "#NM", - description: "Device Not Available (No \ - Math Coprocessor)", - irqtype: "Fault", - source: "Floating-point or WAIT/FWAIT \ - instruction.", - }, - InterruptDescription { - vector: 8, - mnemonic: "#DF", - description: "Double Fault", - irqtype: "Abort", - source: "Any instruction that can \ - generate an exception, an NMI, \ - or an INTR.", - }, - InterruptDescription { - vector: 9, - mnemonic: "", - description: "Coprocessor Segment Overrun", - irqtype: "Fault", - source: "Floating-point instruction.", - }, - InterruptDescription { - vector: 10, - mnemonic: "#TS", - description: "Invalid TSS", - irqtype: "Fault", - source: "Task switch or TSS access.", - }, - InterruptDescription { - vector: 11, - mnemonic: "#NP", - description: "Segment Not Present", - irqtype: "Fault", - source: "Loading segment registers or \ - accessing system segments.", - }, - InterruptDescription { - vector: 12, - mnemonic: "#SS", - description: "Stack-Segment Fault", - irqtype: "Fault", - source: "Stack operations and SS register \ - loads.", - }, - InterruptDescription { - vector: 13, - mnemonic: "#GP", - description: "General Protection", - irqtype: "Fault", - source: "Any memory reference and other \ - protection checks.", - }, - InterruptDescription { - vector: 14, - mnemonic: "#PF", - description: "Page Fault", - irqtype: "Fault", - source: "Any memory reference.", - }, - InterruptDescription { - vector: 15, - mnemonic: "", - description: "RESERVED", - irqtype: "", - source: "None.", - }, - InterruptDescription { - vector: 16, - mnemonic: "#MF", - description: "x87 FPU Floating-Point", - irqtype: "Fault", - source: "x87 FPU instructions.", - }, - InterruptDescription { - vector: 17, - mnemonic: "#AC", - description: "Alignment Check", - irqtype: "Fault", - source: "Unaligned memory access.", - }, - InterruptDescription { - vector: 18, - mnemonic: "#MC", - description: "Machine Check", - irqtype: "Abort", - source: "Internal machine error.", - }, - InterruptDescription { - vector: 19, - mnemonic: "#XM", - description: "SIMD Floating-Point", - irqtype: "Fault", - source: "SSE SIMD instructions.", - }, - InterruptDescription { - vector: 20, - mnemonic: "#VE", - description: "Virtualization", - irqtype: "Fault", - source: "EPT violation.", - }]; - - -/// Enable Interrupts. -pub unsafe fn enable() { - asm!("sti"); -} - -/// Disable Interrupts. -pub unsafe fn disable() { - asm!("cli"); -} - -/// Generate a software interrupt. -/// This is a macro argument needs to be an immediate. -#[macro_export] -macro_rules! int { - ( $x:expr ) => { - { - asm!("int $0" :: "N" ($x)); - } - }; -} - -/// A struct describing an interrupt gate. See the Intel manual mentioned -/// above for details, specifically, the section "6.14.1 64-Bit Mode IDT" -/// and "Table 3-2. System-Segment and Gate-Descriptor Types". -#[derive(Debug, Clone, Copy)] -#[repr(C, packed)] -pub struct IdtEntry { - /// Lower 16 bits of ISR. - pub base_lo: u16, - /// Segment selector. - pub sel: u16, - /// This must always be zero. - pub res0: u8, - /// Flags. - pub flags: u8, - /// The upper 48 bits of ISR (the last 16 bits must be zero). - pub base_hi: u64, - /// Must be zero. - pub res1: u16, -} - -impl IdtEntry { - /// Create a "missing" IdtEntry. This is a `const` function, so we can - /// call it at compile time to initialize static variables. - /// - /// 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 fn missing() -> IdtEntry { - IdtEntry { - base_lo: 0, - sel: 0, - res0: 0, - flags: 0, - base_hi: 0, - res1: 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. - /// - /// Create an interrupt gate with the "Present" flag set, which is the - /// most common case. If you need something else, you can construct it - /// manually. - pub const fn interrupt_gate(gdt_code_selector: u16, handler: VAddr) -> IdtEntry { - IdtEntry { - base_lo: ((handler.as_usize() as u64) & 0xFFFF) as u16, - sel: gdt_code_selector, - res0: 0, - // Bit 7: "Present" flag set. - // Bits 0-4: This is an interrupt gate. - flags: 0b1000_1110, - base_hi: handler.as_usize() as u64 >> 16, - res1: 0, - } - } -} - -bitflags!{ - // Taken from Intel Manual Section 4.7 Page-Fault Exceptions. - flags PageFaultError: u32 { - /// 0: The fault was caused by a non-present page. - /// 1: The fault was caused by a page-level protection violation - const PFAULT_ERROR_P = bit!(0), - - /// 0: The access causing the fault was a read. - /// 1: The access causing the fault was a write. - const PFAULT_ERROR_WR = bit!(1), - - /// 0: The access causing the fault originated when the processor - /// was executing in supervisor mode. - /// 1: The access causing the fault originated when the processor - /// was executing in user mode. - const PFAULT_ERROR_US = bit!(2), - - /// 0: The fault was not caused by reserved bit violation. - /// 1: The fault was caused by reserved bits set to 1 in a page directory. - const PFAULT_ERROR_RSVD = bit!(3), - - /// 0: The fault was not caused by an instruction fetch. - /// 1: The fault was caused by an instruction fetch. - const PFAULT_ERROR_ID = bit!(4), - - /// 0: The fault was not by protection keys. - /// 1: There was a protection key violation. - const PFAULT_ERROR_PK = bit!(5), - } -} - -impl fmt::Debug for PageFaultError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let p = match self.contains(PFAULT_ERROR_P) { - false => "The fault was caused by a non-present page.", - true => "The fault was caused by a page-level protection violation.", - }; - let wr = match self.contains(PFAULT_ERROR_WR) { - false => "The access causing the fault was a read.", - true => "The access causing the fault was a write.", - }; - let us = match self.contains(PFAULT_ERROR_US) { - false => { - "The access causing the fault originated when the processor was executing in \ - supervisor mode." - } - true => { - "The access causing the fault originated when the processor was executing in user \ - mode." - } - }; - let rsvd = match self.contains(PFAULT_ERROR_RSVD) { - false => "The fault was not caused by reserved bit violation.", - true => "The fault was caused by reserved bits set to 1 in a page directory.", - }; - let id = match self.contains(PFAULT_ERROR_ID) { - false => "The fault was not caused by an instruction fetch.", - true => "The fault was caused by an instruction fetch.", - }; - - write!(f, "{}\n{}\n{}\n{}\n{}", p, wr, us, rsvd, id) - } -} - -#[test] -fn bit_macro() { - assert!(PFAULT_ERROR_PK.bits() == 0b100000); - assert!(PFAULT_ERROR_ID.bits() == 0b10000); - assert!(PFAULT_ERROR_RSVD.bits() == 0b1000); - assert!(PFAULT_ERROR_US.bits() == 0b100); - assert!(PFAULT_ERROR_WR.bits() == 0b10); - assert!(PFAULT_ERROR_P.bits() == 0b1); -} @@ -1,13 +1,13 @@ +#![cfg(any(target_arch="x86", target_arch="x86_64"))] + #![feature(const_fn)] #![feature(asm)] +#![feature(associated_consts)] #![no_std] #![cfg_attr(test, allow(unused_features))] -#![crate_name = "x86"] -#![crate_type = "lib"] - #[macro_use] -mod bitflags; +extern crate bitflags; #[macro_use] extern crate raw_cpuid; @@ -16,57 +16,21 @@ extern crate raw_cpuid; #[macro_use] extern crate phf; +#[cfg(target_arch="x86")] +pub mod bits32; +#[cfg(target_arch="x86_64")] +pub mod bits64; +pub mod shared; + +pub mod current { + #[cfg(target_arch="x86")] + pub use bits32::*; + #[cfg(target_arch="x86_64")] + pub use bits64::*; +} + mod std { pub use core::fmt; pub use core::ops; pub use core::option; } - -macro_rules! bit { - ( $x:expr ) => { - 1 << $x - }; -} - -macro_rules! check_flag { - ($doc:meta, $fun:ident, $flag:ident) => ( - #[$doc] - pub fn $fun(&self) -> bool { - self.contains($flag) - } - ) -} - -macro_rules! is_bit_set { - ($field:expr, $bit:expr) => ( - $field & (1 << $bit) > 0 - ) -} - -macro_rules! check_bit_fn { - ($doc:meta, $fun:ident, $field:ident, $bit:expr) => ( - #[$doc] - pub fn $fun(&self) -> bool { - is_bit_set!(self.$field, $bit) - } - ) -} - -pub mod io; -pub mod controlregs; -pub mod msr; -pub mod time; -pub mod irq; -pub mod rflags; -pub mod paging; -pub mod segmentation; -pub mod task; -pub mod dtables; -pub mod syscall; -pub mod sgx; -#[cfg(feature = "performance-counter")] -pub mod perfcnt; -pub mod cpuid { - pub use raw_cpuid::*; -} -pub mod tlb; diff --git a/src/rflags.rs b/src/rflags.rs deleted file mode 100644 index 6af25b1..0000000 --- a/src/rflags.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Description of RFlag values that store the results of operations and the state of the processor. - -/// RFLAGS description. -bitflags! { - flags RFlags: u64 { - /// ID Flag (ID) - const RFLAGS_ID = 1 << 21, - /// Virtual Interrupt Pending (VIP) - const RFLAGS_VIP = 1 << 20, - /// Virtual Interrupt Flag (VIF) - const RFLAGS_VIF = 1 << 19, - /// Alignment Check (AC) - const RFLAGS_AC = 1 << 18, - /// Virtual-8086 Mode (VM) - const RFLAGS_VM = 1 << 17, - /// Resume Flag (RF) - const RFLAGS_RF = 1 << 16, - /// Nested Task (NT) - const RFLAGS_NT = 1 << 14, - /// I/O Privilege Level (IOPL) 0 - const RFLAGS_IOPL0 = 0 << 12, - /// I/O Privilege Level (IOPL) 1 - const RFLAGS_IOPL1 = 1 << 12, - /// I/O Privilege Level (IOPL) 2 - const RFLAGS_IOPL2 = 2 << 12, - /// I/O Privilege Level (IOPL) 3 - const RFLAGS_IOPL3 = 3 << 12, - /// Overflow Flag (OF) - const RFLAGS_OF = 1 << 11, - /// Direction Flag (DF) - const RFLAGS_DF = 1 << 10, - /// Interrupt Enable Flag (IF) - const RFLAGS_IF = 1 << 9, - /// Trap Flag (TF) - const RFLAGS_TF = 1 << 8, - /// Sign Flag (SF) - const RFLAGS_SF = 1 << 7, - /// Zero Flag (ZF) - const RFLAGS_ZF = 1 << 6, - /// Auxiliary Carry Flag (AF) - const RFLAGS_AF = 1 << 4, - /// Parity Flag (PF) - const RFLAGS_PF = 1 << 2, - /// Bit 1 is always 1. - const RFLAGS_A1 = 1 << 1, - /// Carry Flag (CF) - const RFLAGS_CF = 1 << 0, - } -} - -impl RFlags { - /// Creates a new RFlags entry. Ensures bit 1 is set. - pub fn new() -> RFlags { - RFLAGS_A1 - } -} diff --git a/src/segmentation.rs b/src/segmentation.rs deleted file mode 100644 index e3c259e..0000000 --- a/src/segmentation.rs +++ /dev/null @@ -1,214 +0,0 @@ -//! Program x86 segmentation hardware. - -use core::fmt; - -/// 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). -bitflags! { - 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 << 3, - /// Table Indicator (TI) 1 means LDT is used. - const TI_LDT = 1 << 3, - } -} - -impl SegmentSelector { - /// Create a new SegmentSelector - /// - /// # Arguments - /// * `index` index in GDT or LDT array. - /// - pub fn new(index: u16) -> SegmentSelector { - SegmentSelector { bits: index << 3 } - } - - pub fn from_raw(bits: u16) -> SegmentSelector { - SegmentSelector { bits: bits } - } -} - -impl fmt::Debug 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") - } -} - - -/// Entry for GDT or LDT. Provides size and location of a segment. -bitflags! { - 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), - } - } -} - -impl fmt::Debug for SegmentDescriptor { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "SD: 0x{:x}", self.bits) - } -} - -/// Reload stack segment register. -pub unsafe fn load_ss(sel: SegmentSelector) { - asm!("movw $0, %ss " :: "r" (sel) : "memory"); -} - -/// Reload data segment register. -pub unsafe fn load_ds(sel: SegmentSelector) { - asm!("movw $0, %ds " :: "r" (sel) : "memory"); -} - -/// Reload es segment register. -pub unsafe fn load_es(sel: SegmentSelector) { - asm!("movw $0, %es " :: "r" (sel) : "memory"); -} - -/// Reload fs segment register. -pub unsafe fn load_fs(sel: SegmentSelector) { - asm!("movw $0, %fs " :: "r" (sel) : "memory"); -} - -/// Reload gs segment register. -pub unsafe fn load_gs(sel: SegmentSelector) { - asm!("movw $0, %gs " :: "r" (sel) : "memory"); -} - -/// Reload code segment register. -/// Note this is special since we can not directly move -/// to %cs. Instead we push the new segment selector -/// and return value on the stack and use lretq -/// to reload cs and continue at 1:. -pub unsafe fn load_cs(sel: SegmentSelector) { - asm!("pushq $0 - lea 1f(%rip), %rax - pushq %rax - lretq - 1:" :: "r" (sel.bits() as u64) : "{rax}" "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) -} diff --git a/src/shared/control_regs.rs b/src/shared/control_regs.rs new file mode 100644 index 0000000..ab03eba --- /dev/null +++ b/src/shared/control_regs.rs @@ -0,0 +1,84 @@ +//! Functions to read and write control registers. +//! See Intel Vol. 3a Section 2.5, especially Figure 2.6. + +bitflags! { + pub flags Cr0: usize { + const CR0_ENABLE_PAGING = 1 << 31, + const CR0_CACHE_DISABLE = 1 << 30, + const CR0_NOT_WRITE_THROUGH = 1 << 29, + const CR0_ALIGNMENT_MASK = 1 << 18, + const CR0_WRITE_PROTECT = 1 << 16, + const CR0_NUMERIC_ERROR = 1 << 5, + const CR0_EXTENSION_TYPE = 1 << 4, + const CR0_TASK_SWITCHED = 1 << 3, + const CR0_EMULATE_COPROCESSOR = 1 << 2, + const CR0_MONITOR_COPROCESSOR = 1 << 1, + const CR0_PROTECTED_MODE = 1 << 0, + } +} + +bitflags! { + pub flags Cr4: usize { + const CR4_ENABLE_SMAP = 1 << 21, + const CR4_ENABLE_SMEP = 1 << 20, + const CR4_ENABLE_OS_XSAVE = 1 << 18, + const CR4_ENABLE_PCID = 1 << 17, + const CR4_ENABLE_SMX = 1 << 14, + const CR4_ENABLE_VMX = 1 << 13, + const CR4_UNMASKED_SSE = 1 << 10, + const CR4_ENABLE_SSE = 1 << 9, + const CR4_ENABLE_PPMC = 1 << 8, + const CR4_ENABLE_GLOBAL_PAGES = 1 << 7, + const CR4_ENABLE_MACHINE_CHECK = 1 << 6, + const CR4_ENABLE_PAE = 1 << 5, + const CR4_ENABLE_PSE = 1 << 4, + const CR4_DEBUGGING_EXTENSIONS = 1 << 3, + const CR4_TIME_STAMP_DISABLE = 1 << 2, + const CR4_VIRTUAL_INTERRUPTS = 1 << 1, + const CR4_ENABLE_VME = 1 << 0, + } +} + + +/// Read cr0 +pub unsafe fn cr0() -> Cr0 { + let ret: usize; + asm!("mov %cr0, $0" : "=r" (ret)); + Cr0::from_bits_truncate(ret) +} + +/// Write cr0. +pub unsafe fn cr0_write(val: Cr0) { + asm!("mov $0, %cr0" :: "r" (val.bits) : "memory"); +} + +/// Contains page-fault linear address. +pub unsafe fn cr2() -> usize { + let ret: usize; + asm!("mov %cr2, $0" : "=r" (ret)); + ret +} + +/// Contains page-table root pointer. +pub unsafe fn cr3() -> usize { + let ret: usize; + asm!("mov %cr3, $0" : "=r" (ret)); + ret +} + +/// Switch page-table PML4 pointer. +pub unsafe fn cr3_write(val: usize) { + asm!("mov $0, %cr3" :: "r" (val) : "memory"); +} + +/// Contains various flags to control operations in protected mode. +pub unsafe fn cr4() -> Cr4 { + let ret: usize; + asm!("mov %cr4, $0" : "=r" (ret)); + Cr4::from_bits_truncate(ret) +} + +/// Write cr4. +pub unsafe fn cr4_write(val: Cr4) { + asm!("mov $0, %cr4" :: "r" (val.bits) : "memory"); +} diff --git a/src/shared/descriptor.rs b/src/shared/descriptor.rs new file mode 100644 index 0000000..32abb79 --- /dev/null +++ b/src/shared/descriptor.rs @@ -0,0 +1,194 @@ +//! 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 +/// is a system descriptor. +/// +/// See Intel manual 3a, 3.5 "System Descriptor Types", and Table 3-2, +/// System-Segment and Gate-Descriptor Types". +#[repr(u8)] +pub enum SystemType { + // Reserved = 0 + TssAvailable = 1, + /// Only for 16-bit + LocalDescriptorTable = 2, + TssBusy = 3, + CallGate = 4, + /// Only for 16-bit + TaskGate = 5, + InterruptGate = 6, + TrapGate = 7, +} + +/// 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 + /// true/1: native (32- or 64-bit) + size: bool, + ty: SystemType + }, + SegmentDescriptor { + ty: segmentation::Type, + accessed: bool + } +} + +impl Type { + pub fn pack(self) -> u8 { + 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, + } + } +} + + +bitflags!{ + /// Actual encoding of the flags in byte 6 common to all descriptors. + /// + /// See Intel manual 3a, figures 3-8, 6-2, and 6-7. + pub flags Flags: u8 { + /// Descriptor is Present. + const FLAGS_PRESENT = 1 << 7, + + // Descriptor privilege level + const FLAGS_DPL_RING_0 = 0b00 << 5, + const FLAGS_DPL_RING_1 = 0b01 << 5, + const FLAGS_DPL_RING_2 = 0b10 << 5, + const FLAGS_DPL_RING_3 = 0b11 << 5, + + // 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, + // the descriptor type is a system descriptor + + // All modes (supporting segments) + const TYPE_SYS_LDT = 0b0_0010, + + // Protected Mode and older + const FLAGS_TYPE_SYS_16BIT_TSS_AVAILABLE = 0b0_0001, + const FLAGS_TYPE_SYS_16BIT_TSS_BUSY = 0b0_0011, + const FLAGS_TYPE_SYS_16BIT_CALL_GATE = 0b0_0100, + const FLAGS_TYPE_SYS_16BIT_TASK_GATE = 0b0_0101, + const FLAGS_TYPE_SYS_16BIT_INTERRUPT_GATE = 0b0_0110, + const FLAGS_TYPE_SYS_16BIT_TRAP_GATE = 0b0_0111, + + // 64-bit in IA-32e Mode (either submode), 32-bit in Protected Mode + const FLAGS_TYPE_SYS_NATIVE_TSS_AVAILABLE = 0b0_1001, + const FLAGS_TYPE_SYS_NATIVE_TSS_BUSY = 0b0_1011, + 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, + } +} + +impl Flags { + pub const BLANK: Flags = Flags { bits: 0 }; + + pub const fn from_priv(dpl: PrivilegeLevel) -> Flags { + Flags { bits: (dpl as u8) << 5 } + } + + pub fn from_type(ty: Type) -> Flags { + Flags { bits: ty.pack() } + } + + pub const fn const_or(self, other: Self) -> Flags { + Flags { bits: self.bits | other.bits } + } + + pub const fn cond(self, cond: bool) -> Flags { + Flags { bits: (-(cond as i8) as u8) & self.bits } + } + + pub const fn const_mux(self, other: Self, cond: bool) -> Flags { + self.cond(cond).const_or(other.cond(!cond)) + } +} diff --git a/src/shared/dtables.rs b/src/shared/dtables.rs new file mode 100644 index 0000000..c5337fb --- /dev/null +++ b/src/shared/dtables.rs @@ -0,0 +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<Entry> { + /// Size of the DT. + pub limit: u16, + /// Pointer to the memory region containing the DT. + 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<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"); +} diff --git a/src/shared/flags.rs b/src/shared/flags.rs new file mode 100644 index 0000000..55dad6d --- /dev/null +++ b/src/shared/flags.rs @@ -0,0 +1,102 @@ +//! Processor state stored in the FLAGS, EFLAGS, or RFLAGS register. + +use shared::PrivilegeLevel; + +/// The RFLAGS register. All variants are backwards compatable so only one +/// bitflags struct needed. +bitflags! { + pub flags Flags: usize { + /// ID Flag (ID) + const FLAGS_ID = 1 << 21, + /// Virtual Interrupt Pending (VIP) + const FLAGS_VIP = 1 << 20, + /// Virtual Interrupt Flag (VIF) + const FLAGS_VIF = 1 << 19, + /// Alignment Check (AC) + const FLAGS_AC = 1 << 18, + /// Virtual-8086 Mode (VM) + const FLAGS_VM = 1 << 17, + /// Resume Flag (RF) + const FLAGS_RF = 1 << 16, + /// Nested Task (NT) + const FLAGS_NT = 1 << 14, + /// I/O Privilege Level (IOPL) 0 + const FLAGS_IOPL0 = 0 << 12, + /// I/O Privilege Level (IOPL) 1 + const FLAGS_IOPL1 = 1 << 12, + /// I/O Privilege Level (IOPL) 2 + const FLAGS_IOPL2 = 2 << 12, + /// I/O Privilege Level (IOPL) 3 + const FLAGS_IOPL3 = 3 << 12, + /// Overflow Flag (OF) + const FLAGS_OF = 1 << 11, + /// Direction Flag (DF) + const FLAGS_DF = 1 << 10, + /// Interrupt Enable Flag (IF) + const FLAGS_IF = 1 << 9, + /// Trap Flag (TF) + const FLAGS_TF = 1 << 8, + /// Sign Flag (SF) + const FLAGS_SF = 1 << 7, + /// Zero Flag (ZF) + const FLAGS_ZF = 1 << 6, + /// Auxiliary Carry Flag (AF) + const FLAGS_AF = 1 << 4, + /// Parity Flag (PF) + const FLAGS_PF = 1 << 2, + /// Bit 1 is always 1. + const FLAGS_A1 = 1 << 1, + /// Carry Flag (CF) + const FLAGS_CF = 1 << 0, + } +} + +impl Flags { + /// Creates a new Flags entry. Ensures bit 1 is set. + pub const fn new() -> Flags { + FLAGS_A1 + } + + /// Creates a new Flags with the given I/O privilege level. + pub const fn from_priv(iopl: PrivilegeLevel) -> Flags { + Flags { bits: (iopl as usize) << 12 } + } +} + +pub fn flags() -> Flags { + + #[cfg(target_arch="x86")] + #[inline(always)] + unsafe fn inner() -> Flags { + let r: usize; + asm!("pushfl; popl $0" : "=r"(r) :: "memory"); + Flags::from_bits_truncate(r) + } + + #[cfg(target_arch="x86_64")] + #[inline(always)] + unsafe fn inner() -> Flags { + let r: usize; + asm!("pushfq; popq $0" : "=r"(r) :: "memory"); + Flags::from_bits_truncate(r) + } + + unsafe { inner() } +} + +pub fn set(val: Flags) { + + #[cfg(target_arch="x86")] + #[inline(always)] + unsafe fn inner(val: Flags) { + asm!("pushl $0; popfl" :: "r"(val.bits()) : "memory" "flags"); + } + + #[cfg(target_arch="x86_64")] + #[inline(always)] + unsafe fn inner(val: Flags) { + asm!("pushq $0; popfq" :: "r"(val.bits()) : "memory" "flags"); + } + + unsafe { inner(val) } +} diff --git a/src/shared/io.rs b/src/shared/io.rs new file mode 100644 index 0000000..16f641c --- /dev/null +++ b/src/shared/io.rs @@ -0,0 +1,80 @@ +//! I/O port functionality. + +/// Write 8 bits to port +pub unsafe fn outb(port: u16, val: u8) { + asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(val)); +} + +/// Read 8 bits from port +pub unsafe fn inb(port: u16) -> u8 { + let ret: u8; + asm!("inb %dx, %al" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); + ret +} + +/// Write 16 bits to port +pub unsafe fn outw(port: u16, val: u16) { + asm!("outw %ax, %dx" :: "{dx}"(port), "{al}"(val)); +} + +/// Read 16 bits from port +pub unsafe fn inw(port: u16) -> u16 { + let ret: u16; + asm!("inw %dx, %ax" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); + ret +} + +/// Write 32 bits to port +pub unsafe fn outl(port: u16, val: u32) { + asm!("outl %eax, %dx" :: "{dx}"(port), "{al}"(val)); +} + +/// Read 32 bits from port +pub unsafe fn inl(port: u16) -> u32 { + let ret: u32; + asm!("inl %dx, %eax" : "={ax}"(ret) : "{dx}"(port) :: "volatile"); + ret +} + + +// Write 8-bit array to port +pub unsafe fn outsb(port: u16, buf: &[u8]) { + asm!("rep outsb (%esi), %dx" + :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) + : "ecx", "edi"); +} + +// Read 8-bit array from port +pub unsafe fn insb(port: u16, buf: &mut [u8]) { + asm!("rep insb %dx, (%edi)" + :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) + : "ecx", "edi" : "volatile"); +} + +// Write 16-bit array to port +pub unsafe fn outsw(port: u16, buf: &[u16]) { + asm!("rep outsw (%esi), %dx" + :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) + : "ecx", "edi"); +} + +// Read 16-bit array from port +pub unsafe fn insw(port: u16, buf: &mut [u16]) { + asm!("rep insw %dx, (%edi)" + :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) + : "ecx", "edi" : "volatile"); +} + +// Write 32-bit array to port +pub unsafe fn outsl(port: u16, buf: &[u32]) { + asm!("rep outsl (%esi), %dx" + :: "{ecx}"(buf.len()), "{dx}"(port), "{esi}"(buf.as_ptr()) + : "ecx", "edi"); +} + +// Read 32-bit array from port +pub unsafe fn insl(port: u16, buf: &mut [u32]) { + asm!("rep insl %dx, (%edi)" + :: "{ecx}"(buf.len()), "{dx}"(port), "{edi}"(buf.as_ptr()) + : "ecx", "edi" : "volatile"); +} diff --git a/src/shared/irq.rs b/src/shared/irq.rs new file mode 100644 index 0000000..4654ad3 --- /dev/null +++ b/src/shared/irq.rs @@ -0,0 +1,207 @@ +//! Shared interrupt description and set-up code. +//! See the `bits*::irq` modules for arch-specific portions. + +use core::fmt; + +/// x86 Exception description (see also Intel Vol. 3a Chapter 6). +#[derive(Debug)] +pub struct InterruptDescription { + pub vector: u8, + pub mnemonic: &'static str, + pub description: &'static str, + pub irqtype: &'static str, + pub source: &'static str, +} + +impl fmt::Display for InterruptDescription { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, + "{} ({}, vec={}) {}", + self.mnemonic, + self.irqtype, + self.vector, + self.description) + } +} + + +/// x86 External Interrupts (1-16). +pub static EXCEPTIONS: [InterruptDescription; 21] = [ + InterruptDescription { + vector: 0, + mnemonic: "#DE", + description: "Divide Error", + irqtype: "Fault", + source: "DIV and IDIV instructions.", + }, + InterruptDescription { + vector: 1, + mnemonic: "#DB", + description: "Debug", + irqtype: "Fault/ Trap", + source: "Debug condition", + }, + InterruptDescription { + vector: 2, + mnemonic: "NMI", + description: "Nonmaskable Interrupt", + irqtype: "Interrupt", + source: "Nonmaskable external interrupt.", + }, + InterruptDescription { + vector: 3, + mnemonic: "#BP", + description: "Breakpoint", + irqtype: "Trap", + source: "INT 3 instruction.", + }, + InterruptDescription { + vector: 4, + mnemonic: "#OF", + description: "Overflow", + irqtype: "Trap", + source: "INTO instruction.", + }, + InterruptDescription { + vector: 5, + mnemonic: "#BR", + description: "BOUND Range Exceeded", + irqtype: "Fault", + source: "BOUND instruction.", + }, + InterruptDescription { + vector: 6, + mnemonic: "#UD", + description: "Invalid Opcode (Undefined \ + Opcode)", + irqtype: "Fault", + source: "UD2 instruction or reserved \ + opcode.", + }, + InterruptDescription { + vector: 7, + mnemonic: "#NM", + description: "Device Not Available (No \ + Math Coprocessor)", + irqtype: "Fault", + source: "Floating-point or WAIT/FWAIT \ + instruction.", + }, + InterruptDescription { + vector: 8, + mnemonic: "#DF", + description: "Double Fault", + irqtype: "Abort", + source: "Any instruction that can \ + generate an exception, an NMI, \ + or an INTR.", + }, + InterruptDescription { + vector: 9, + mnemonic: "", + description: "Coprocessor Segment Overrun", + irqtype: "Fault", + source: "Floating-point instruction.", + }, + InterruptDescription { + vector: 10, + mnemonic: "#TS", + description: "Invalid TSS", + irqtype: "Fault", + source: "Task switch or TSS access.", + }, + InterruptDescription { + vector: 11, + mnemonic: "#NP", + description: "Segment Not Present", + irqtype: "Fault", + source: "Loading segment registers or \ + accessing system segments.", + }, + InterruptDescription { + vector: 12, + mnemonic: "#SS", + description: "Stack-Segment Fault", + irqtype: "Fault", + source: "Stack operations and SS register \ + loads.", + }, + InterruptDescription { + vector: 13, + mnemonic: "#GP", + description: "General Protection", + irqtype: "Fault", + source: "Any memory reference and other \ + protection checks.", + }, + InterruptDescription { + vector: 14, + mnemonic: "#PF", + description: "Page Fault", + irqtype: "Fault", + source: "Any memory reference.", + }, + InterruptDescription { + vector: 15, + mnemonic: "", + description: "RESERVED", + irqtype: "", + source: "None.", + }, + InterruptDescription { + vector: 16, + mnemonic: "#MF", + description: "x87 FPU Floating-Point", + irqtype: "Fault", + source: "x87 FPU instructions.", + }, + InterruptDescription { + vector: 17, + mnemonic: "#AC", + description: "Alignment Check", + irqtype: "Fault", + source: "Unaligned memory access.", + }, + InterruptDescription { + vector: 18, + mnemonic: "#MC", + description: "Machine Check", + irqtype: "Abort", + source: "Internal machine error.", + }, + InterruptDescription { + vector: 19, + mnemonic: "#XM", + description: "SIMD Floating-Point", + irqtype: "Fault", + source: "SSE SIMD instructions.", + }, + InterruptDescription { + vector: 20, + mnemonic: "#VE", + description: "Virtualization", + irqtype: "Fault", + source: "EPT violation.", + }, +]; + +/// Enable Interrupts. +pub unsafe fn enable() { + asm!("sti"); +} + +/// Disable Interrupts. +pub unsafe fn disable() { + asm!("cli"); +} + +/// Generate a software interrupt. +/// This is a macro argument needs to be an immediate. +#[macro_export] +macro_rules! int { + ( $x:expr ) => { + { + asm!("int $0" :: "N" ($x)); + } + }; +} diff --git a/src/shared/mod.rs b/src/shared/mod.rs new file mode 100644 index 0000000..42a7aa4 --- /dev/null +++ b/src/shared/mod.rs @@ -0,0 +1,24 @@ +pub mod control_regs; +pub mod descriptor; +pub mod dtables; +pub mod io; +pub mod irq; +pub mod msr; +pub mod paging; +pub mod flags; +pub mod segmentation; +pub mod task; + +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum PrivilegeLevel { + Ring0 = 0, + Ring1 = 1, + Ring2 = 2, + Ring3 = 3, +} + +#[inline(always)] +pub unsafe fn halt() { + asm!("hlt" :::: "volatile", "intel"); +} diff --git a/src/msr.rs b/src/shared/msr.rs index 6b4613a..1520232 100644 --- a/src/msr.rs +++ b/src/shared/msr.rs @@ -10,10 +10,8 @@ pub unsafe fn wrmsr(msr: u32, value: u64) { /// Read 64 bits msr register. #[allow(unused_mut)] pub unsafe fn rdmsr(msr: u32) -> u64 { - let mut low: u32; - let mut high: u32; + let (high, low): (u32, u32); asm!("rdmsr" : "={eax}" (low), "={edx}" (high) : "{ecx}" (msr) : "memory" : "volatile"); - ((high as u64) << 32) | (low as u64) } diff --git a/src/shared/paging.rs b/src/shared/paging.rs new file mode 100644 index 0000000..41705a9 --- /dev/null +++ b/src/shared/paging.rs @@ -0,0 +1,48 @@ +//! Description of the data-structures for IA-32e paging mode. + +use core::fmt; + +/// Represent a virtual (linear) memory address +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct VAddr(usize); + +impl VAddr { + /// Convert to `usize` + pub const fn as_usize(&self) -> usize { + self.0 + } + /// Convert from `usize` + pub const fn from_usize(v: usize) -> Self { + VAddr(v) + } +} + +impl fmt::Binary for VAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Display for VAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::LowerHex for VAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Octal for VAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::UpperHex for VAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} diff --git a/src/shared/segmentation.rs b/src/shared/segmentation.rs new file mode 100644 index 0000000..7f895ae --- /dev/null +++ b/src/shared/segmentation.rs @@ -0,0 +1,262 @@ +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 { + /// 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 << 3, + /// Table Indicator (TI) 1 means LDT is used. + const TI_LDT = 1 << 3, + } +} + +/// Reload code segment register. +/// Note this is special since we can not directly move +/// to %cs. Instead we push the new segment selector +/// and return value on the stack and use lretq +/// to reload cs and continue at 1:. +pub unsafe fn set_cs(sel: SegmentSelector) { + + #[cfg(target_arch="x86")] + #[inline(always)] + unsafe fn inner(sel: SegmentSelector) { + asm!("pushl $0; \ + pushl $$1f; \ + lretl; \ + 1:" :: "ri" (sel.bits() as usize) : "{rax}" "memory"); + } + + #[cfg(target_arch="x86_64")] + #[inline(always)] + unsafe fn inner(sel: SegmentSelector) { + asm!("pushq $0; \ + leaq 1f(%rip), %rax; \ + pushq %rax; \ + lretq; \ + 1:" :: "ri" (sel.bits() as usize) : "{rax}" "memory"); + } + + inner(sel) +} + + +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) : "memory"); +} + +/// Reload data segment register. +pub unsafe fn load_ds(sel: SegmentSelector) { + asm!("movw $0, %ds " :: "r" (sel) : "memory"); +} + +/// Reload es segment register. +pub unsafe fn load_es(sel: SegmentSelector) { + asm!("movw $0, %es " :: "r" (sel) : "memory"); +} + +/// Reload fs segment register. +pub unsafe fn load_fs(sel: SegmentSelector) { + asm!("movw $0, %fs " :: "r" (sel) : "memory"); +} + +/// Reload gs segment register. +pub unsafe fn load_gs(sel: SegmentSelector) { + asm!("movw $0, %gs " :: "r" (sel) : "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 | 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 } + } +} diff --git a/src/shared/task.rs b/src/shared/task.rs new file mode 100644 index 0000000..b87c032 --- /dev/null +++ b/src/shared/task.rs @@ -0,0 +1,9 @@ +//! Helpers to program the task state segment. +//! See Intel 3a, Chapter 7 + +pub use shared::segmentation; + +/// Load the task state register. +pub unsafe fn load_tr(sel: segmentation::SegmentSelector) { + asm!("ltr $0" :: "r" (sel)); +} |