use super::isa::seg;
use super::paging;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Selector(pub u16);
impl Selector {
pub const NULL: Selector = Selector(0);
#[inline]
#[must_use]
pub const fn index(self) -> u32 {
(self.0 >> 3) as u32
}
#[inline]
#[must_use]
pub const fn offset(self) -> u32 {
(self.0 & 0xfff8) as u32
}
#[inline]
#[must_use]
pub const fn is_ldt(self) -> bool {
self.0 & 4 != 0
}
#[inline]
#[must_use]
pub const fn rpl(self) -> u8 {
(self.0 & 3) as u8
}
#[inline]
#[must_use]
pub const fn is_null(self) -> bool {
self.0 & 0xfffc == 0
}
#[inline]
#[must_use]
pub const fn with_rpl(self, rpl: u8) -> Selector {
Selector((self.0 & 0xfffc) | (rpl as u16 & 3))
}
}
pub mod sys_type {
pub const TSS16_AVAIL: u8 = 1;
pub const LDT: u8 = 2;
pub const TSS16_BUSY: u8 = 3;
pub const CALL_GATE16: u8 = 4;
pub const TASK_GATE: u8 = 5;
pub const INT_GATE16: u8 = 6;
pub const TRAP_GATE16: u8 = 7;
pub const TSS32_AVAIL: u8 = 9;
pub const TSS32_BUSY: u8 = 11;
pub const CALL_GATE32: u8 = 12;
pub const INT_GATE32: u8 = 14;
pub const TRAP_GATE32: u8 = 15;
}
pub mod ar {
pub const MASK: u32 = 0x00f0_ff00;
pub const ACCESSED: u32 = 0x0000_0100;
pub const RW: u32 = 0x0000_0200;
pub const DC: u32 = 0x0000_0400;
pub const CODE: u32 = 0x0000_0800;
pub const S: u32 = 0x0000_1000;
pub const DPL: u32 = 0x0000_6000;
pub const DPL_SHIFT: u32 = 13;
pub const PRESENT: u32 = 0x0000_8000;
pub const AVL: u32 = 0x0010_0000;
pub const L: u32 = 0x0020_0000;
pub const DB: u32 = 0x0040_0000;
pub const GRANULAR: u32 = 0x0080_0000;
pub const REAL_DATA: u32 = PRESENT | S | RW | ACCESSED;
pub const REAL_CODE: u32 = PRESENT | S | CODE | RW | ACCESSED;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SegReg {
pub selector: u16,
pub base: u64,
pub limit: u32,
pub ar: u32,
}
impl SegReg {
#[must_use]
pub const fn real_data(selector: u16) -> SegReg {
SegReg {
selector,
base: (selector as u64) << 4,
limit: 0xffff,
ar: ar::REAL_DATA,
}
}
#[must_use]
pub const fn real_code(selector: u16) -> SegReg {
SegReg {
selector,
base: (selector as u64) << 4,
limit: 0xffff,
ar: ar::REAL_CODE,
}
}
#[must_use]
pub const fn null() -> SegReg {
SegReg {
selector: 0,
base: 0,
limit: 0,
ar: 0,
}
}
#[inline]
#[must_use]
pub const fn dpl(self) -> u8 {
((self.ar & ar::DPL) >> ar::DPL_SHIFT) as u8
}
#[inline]
#[must_use]
pub const fn present(self) -> bool {
self.ar & ar::PRESENT != 0
}
#[inline]
#[must_use]
pub const fn is_app(self) -> bool {
self.ar & ar::S != 0
}
#[inline]
#[must_use]
pub const fn is_code(self) -> bool {
self.is_app() && self.ar & ar::CODE != 0
}
#[inline]
#[must_use]
pub const fn is_data(self) -> bool {
self.is_app() && self.ar & ar::CODE == 0
}
#[inline]
#[must_use]
pub const fn rw(self) -> bool {
self.ar & ar::RW != 0
}
#[inline]
#[must_use]
pub const fn conforming(self) -> bool {
self.is_code() && self.ar & ar::DC != 0
}
#[inline]
#[must_use]
pub const fn expand_down(self) -> bool {
self.is_data() && self.ar & ar::DC != 0
}
#[inline]
#[must_use]
pub const fn big(self) -> bool {
self.ar & ar::DB != 0
}
#[inline]
#[must_use]
pub const fn long(self) -> bool {
self.ar & ar::L != 0
}
#[inline]
#[must_use]
pub const fn sys_type(self) -> u8 {
((self.ar >> 8) & 0xf) as u8
}
#[inline]
#[must_use]
pub const fn readable(self) -> bool {
if self.is_code() {
self.rw()
} else {
self.is_data()
}
}
#[inline]
#[must_use]
pub const fn writable(self) -> bool {
self.is_data() && self.rw()
}
#[must_use]
pub const fn in_bounds(self, offset: u64, size: u64) -> bool {
if size == 0 {
return true;
}
let last = match offset.checked_add(size - 1) {
Some(last) => last,
None => return false,
};
if last > 0xffff_ffff {
return false;
}
let limit = self.limit as u64;
if self.expand_down() {
let top: u64 = if self.big() { 0xffff_ffff } else { 0xffff };
offset > limit && last <= top
} else {
last <= limit
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct TableReg {
pub base: u64,
pub limit: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RawDesc {
pub low: u32,
pub high: u32,
pub upper: u32,
}
impl RawDesc {
#[must_use]
pub const fn base(self) -> u32 {
(self.low >> 16) | ((self.high & 0xff) << 16) | (self.high & 0xff00_0000)
}
#[must_use]
pub const fn limit(self) -> u32 {
let raw = (self.low & 0xffff) | (self.high & 0x000f_0000);
if self.high & ar::GRANULAR != 0 {
(raw << 12) | 0xfff
} else {
raw
}
}
#[must_use]
pub const fn ar(self) -> u32 {
self.high & ar::MASK
}
#[must_use]
pub const fn dpl(self) -> u8 {
((self.high & ar::DPL) >> ar::DPL_SHIFT) as u8
}
#[must_use]
pub const fn present(self) -> bool {
self.high & ar::PRESENT != 0
}
#[must_use]
pub const fn is_app(self) -> bool {
self.high & ar::S != 0
}
#[must_use]
pub const fn kind(self) -> u8 {
((self.high >> 8) & 0xf) as u8
}
#[must_use]
pub const fn base64(self) -> u64 {
(self.base() as u64) | ((self.upper as u64) << 32)
}
#[must_use]
pub const fn to_seg(self, selector: u16) -> SegReg {
SegReg {
selector,
base: self.base64(),
limit: self.limit(),
ar: self.ar(),
}
}
#[must_use]
pub const fn gate_selector(self) -> u16 {
(self.low >> 16) as u16
}
#[must_use]
pub const fn gate_offset(self) -> u32 {
(self.low & 0xffff) | (self.high & 0xffff_0000)
}
#[must_use]
pub const fn gate_offset64(self) -> u64 {
(self.gate_offset() as u64) | ((self.upper as u64) << 32)
}
#[must_use]
pub const fn gate_argc(self) -> u8 {
(self.high & 0x1f) as u8
}
#[must_use]
pub const fn gate_ist(self) -> u8 {
(self.high & 0x7) as u8
}
}
pub mod cr0 {
pub const PE: u32 = 1 << 0;
pub const MP: u32 = 1 << 1;
pub const EM: u32 = 1 << 2;
pub const TS: u32 = 1 << 3;
pub const ET: u32 = 1 << 4;
pub const NE: u32 = 1 << 5;
pub const WP: u32 = 1 << 16;
pub const AM: u32 = 1 << 18;
pub const NW: u32 = 1 << 29;
pub const CD: u32 = 1 << 30;
pub const PG: u32 = 1 << 31;
pub const MSW: u32 = PE | MP | EM | TS;
pub const VALID_386: u32 = PE | MP | EM | TS | ET | PG;
pub const VALID_486: u32 = VALID_386 | NE | WP | AM | NW | CD;
}
pub mod cr4 {
pub const VME: u64 = 1 << 0;
pub const PVI: u64 = 1 << 1;
pub const TSD: u64 = 1 << 2;
pub const DE: u64 = 1 << 3;
pub const PSE: u64 = 1 << 4;
pub const PAE: u64 = 1 << 5;
pub const MCE: u64 = 1 << 6;
pub const PGE: u64 = 1 << 7;
pub const PCE: u64 = 1 << 8;
pub const OSFXSR: u64 = 1 << 9;
pub const OSXMMEXCPT: u64 = 1 << 10;
}
pub mod efer {
pub const SCE: u64 = 1 << 0;
pub const LME: u64 = 1 << 8;
pub const LMA: u64 = 1 << 10;
pub const NXE: u64 = 1 << 11;
pub const WRITABLE: u64 = SCE | LME | NXE;
}
pub mod msr {
pub const TSC: u32 = 0x10;
pub const APIC_BASE: u32 = 0x1b;
pub const EFER: u32 = 0xc000_0080;
pub const STAR: u32 = 0xc000_0081;
pub const LSTAR: u32 = 0xc000_0082;
pub const CSTAR: u32 = 0xc000_0083;
pub const SFMASK: u32 = 0xc000_0084;
pub const FS_BASE: u32 = 0xc000_0100;
pub const GS_BASE: u32 = 0xc000_0101;
pub const KERNEL_GS_BASE: u32 = 0xc000_0102;
}
pub mod apic_base {
pub const BSP: u64 = 1 << 8;
pub const X2APIC: u64 = 1 << 10;
pub const ENABLE: u64 = 1 << 11;
pub const BASE: u64 = 0x0000_00ff_ffff_f000;
pub const WRITABLE: u64 = ENABLE | BASE;
}
#[inline]
#[must_use]
pub const fn canonical(addr: u64) -> bool {
((addr << 16) as i64 >> 16) as u64 == addr
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Sys {
pub segs: [SegReg; seg::COUNT],
pub gdtr: TableReg,
pub idtr: TableReg,
pub ldtr: SegReg,
pub task: SegReg,
pub cr0: u32,
pub cr2: u64,
pub cr3: u64,
pub cr4: u64,
pub efer: u64,
pub fs_base: u64,
pub gs_base: u64,
pub kernel_gs_base: u64,
pub star: u64,
pub lstar: u64,
pub cstar: u64,
pub sfmask: u64,
pub dr: [u64; 8],
pub test: [u32; 8],
}
impl Sys {
#[must_use]
pub fn reset() -> Sys {
let mut segs = [SegReg::real_data(0); seg::COUNT];
segs[seg::CS as usize] = SegReg {
selector: 0xf000,
base: 0xffff_0000,
limit: 0xffff,
ar: ar::REAL_CODE,
};
Sys {
segs,
gdtr: TableReg { base: 0, limit: 0 },
idtr: TableReg {
base: 0,
limit: 0x3ff,
},
ldtr: SegReg::null(),
task: SegReg::null(),
cr0: 0,
cr2: 0,
cr3: 0,
cr4: 0,
efer: 0,
fs_base: 0,
gs_base: 0,
kernel_gs_base: 0,
star: 0,
lstar: 0,
cstar: 0,
sfmask: 0,
dr: [0, 0, 0, 0, 0, 0, 0xffff_0ff0, 0x0000_0400],
test: [0; 8],
}
}
#[must_use]
pub fn reset_8086() -> Sys {
let mut sys = Sys::reset();
sys.segs = [SegReg::real_data(0); seg::COUNT];
sys.segs[seg::CS as usize] = SegReg::real_code(0xffff);
sys.dr = [0; 8];
sys
}
#[inline]
#[must_use]
pub const fn protected(&self) -> bool {
self.cr0 & cr0::PE != 0
}
#[inline]
#[must_use]
pub const fn paging(&self) -> bool {
self.cr0 & cr0::PG != 0
}
#[inline]
#[must_use]
pub const fn long_mode(&self) -> bool {
self.efer & efer::LMA != 0
}
#[inline]
#[must_use]
pub const fn sixty_four(&self) -> bool {
self.long_mode() && self.seg(seg::CS).long()
}
#[inline]
#[must_use]
pub const fn paging_mode(&self, features: super::Features) -> paging::Mode {
if self.cr0 & cr0::PG == 0 {
return paging::Mode::Off;
}
if !features.pae || self.cr4 & cr4::PAE == 0 {
return paging::Mode::Legacy;
}
if self.long_mode() {
paging::Mode::Ia32e
} else {
paging::Mode::Pae
}
}
#[must_use]
pub fn tables(&self, features: super::Features) -> paging::Tables {
paging::Tables {
mode: self.paging_mode(features),
cr3: self.cr3,
pse: features.pse && self.cr4 & cr4::PSE != 0,
nxe: features.nx && self.efer & efer::NXE != 0,
wp: features.extras_486 && self.cr0 & cr0::WP != 0,
}
}
#[inline]
#[must_use]
pub const fn seg(&self, index: u8) -> SegReg {
self.segs[(index as usize) % seg::COUNT]
}
#[inline]
pub const fn seg_mut(&mut self, index: u8) -> &mut SegReg {
&mut self.segs[(index as usize) % seg::COUNT]
}
}
impl Default for Sys {
fn default() -> Self {
Sys::reset()
}
}
pub mod tss32 {
pub const BACK_LINK: u64 = 0x00;
pub const ESP0: u64 = 0x04;
pub const SS0: u64 = 0x08;
pub const CR3: u64 = 0x1c;
pub const EIP: u64 = 0x20;
pub const EFLAGS: u64 = 0x24;
pub const EAX: u64 = 0x28;
pub const ES: u64 = 0x48;
pub const LDT: u64 = 0x60;
pub const TRAP: u64 = 0x64;
pub const IOMAP_BASE: u64 = 0x66;
pub const MIN_LIMIT: u64 = 0x67;
}
#[must_use]
pub const fn tss32_stack(level: u8) -> (u64, u64) {
let base = 4 + (level as u64 & 3) * 8;
(base, base + 4)
}
pub const TSS_SEG_ORDER: [u8; 6] = [seg::ES, seg::CS, seg::SS, seg::DS, seg::FS, seg::GS];
use super::exec::{Ex, Exec, Fault, VEC_GP, VEC_NP, VEC_SS, VEC_TS, VEC_UD};
use super::flags;
use super::isa::{Fields, Op};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum SwitchKind {
Return,
}
impl Exec<'_> {
pub(super) fn require_protected(&self) -> Ex<()> {
if self.protected() {
Ok(())
} else {
Err(Fault::bare(VEC_UD))
}
}
pub(super) fn require_ring0(&self) -> Ex<()> {
if self.protected() && self.cpl() != 0 {
Err(Fault::gp(0))
} else {
Ok(())
}
}
const fn seg_fault_vector(sr: u8) -> u8 {
if sr == seg::SS { VEC_SS } else { VEC_GP }
}
pub(super) fn seg_linear(&mut self, sr: u8, offset: u64, size: u64, write: bool) -> Ex<u64> {
let s = self.state.sys.seg(sr);
let vector = Self::seg_fault_vector(sr);
if self.sixty_four() {
let base = match sr {
seg::FS => self.state.sys.fs_base,
seg::GS => self.state.sys.gs_base,
_ => 0,
};
let lin = base.wrapping_add(offset);
if !canonical(lin) {
return Err(Fault::coded(vector, 0));
}
return Ok(lin);
}
if self.protected() {
if !s.present() {
return Err(Fault::coded(vector, 0));
}
if write && !s.writable() {
return Err(Fault::coded(vector, 0));
}
if !write && !s.readable() {
return Err(Fault::coded(vector, 0));
}
}
if !s.in_bounds(offset, size) {
return Err(Fault::coded(vector, 0));
}
Ok(s.base.wrapping_add(offset))
}
pub(super) fn descriptor(&mut self, selector: u16, vector: u8) -> Ex<RawDesc> {
let sel = Selector(selector);
let table = if sel.is_ldt() {
let ldtr = self.state.sys.ldtr;
if !ldtr.present() {
return Err(Fault::coded(vector, u32::from(selector & 0xfffc)));
}
TableReg {
base: ldtr.base,
limit: ldtr.limit,
}
} else {
self.state.sys.gdtr
};
if sel.offset() + 7 > table.limit {
return Err(Fault::coded(vector, u32::from(selector & 0xfffc)));
}
let addr = table.base.wrapping_add(u64::from(sel.offset()));
let low = self.sys_read32(addr)?;
let high = self.sys_read32(addr.wrapping_add(4))?;
let system = high & ar::S == 0;
let upper = if self.state.sys.long_mode() && system {
if sel.offset() + 15 > table.limit {
return Err(Fault::coded(vector, u32::from(selector & 0xfffc)));
}
self.sys_read32(addr.wrapping_add(8))?
} else {
0
};
Ok(RawDesc { low, high, upper })
}
fn mark_accessed(&mut self, selector: u16, desc: RawDesc) -> Ex<()> {
if desc.high & ar::ACCESSED != 0 {
return Ok(());
}
let sel = Selector(selector);
let table = if sel.is_ldt() {
let ldtr = self.state.sys.ldtr;
TableReg {
base: ldtr.base,
limit: ldtr.limit,
}
} else {
self.state.sys.gdtr
};
let addr = table
.base
.wrapping_add(u64::from(sel.offset()))
.wrapping_add(4);
self.sys_write32(addr, desc.high | ar::ACCESSED)
}
fn set_sys_type(&mut self, selector: u16, kind: u8) -> Ex<()> {
let sel = Selector(selector);
let table = self.state.sys.gdtr;
let addr = table
.base
.wrapping_add(u64::from(sel.offset()))
.wrapping_add(4);
let high = self.sys_read32(addr)?;
let updated = (high & !0x0000_0f00) | (u32::from(kind) << 8);
self.sys_write32(addr, updated)
}
pub(super) fn load_segment(&mut self, index: u8, selector: u16) -> Ex<()> {
if index >= seg::COUNT as u8 {
return Err(Fault::bare(VEC_UD));
}
if !self.protected() {
let cached = self.state.sys.seg(index);
let entry = self.state.sys.seg_mut(index);
entry.selector = selector;
entry.base = u64::from(selector) << 4;
if !cached.present() {
entry.limit = 0xffff;
entry.ar = ar::REAL_DATA;
}
self.state.regs.set_segment(index, selector);
if index == seg::SS {
self.state.int_shadow = true;
}
return Ok(());
}
let sel = Selector(selector);
let cpl = self.cpl();
if index == seg::SS {
if sel.is_null() {
return Err(Fault::gp(0));
}
let desc = self.descriptor(selector, VEC_GP)?;
if !desc.is_app()
|| desc.high & ar::CODE != 0
|| desc.high & ar::RW == 0
|| sel.rpl() != cpl
|| desc.dpl() != cpl
{
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_SS, u32::from(selector & 0xfffc)));
}
self.mark_accessed(selector, desc)?;
*self.state.sys.seg_mut(index) = desc.to_seg(selector);
self.state.regs.ss = selector;
self.state.int_shadow = true;
return Ok(());
}
if sel.is_null() {
*self.state.sys.seg_mut(index) = SegReg::null();
self.state.regs.set_segment(index, selector);
return Ok(());
}
let desc = self.descriptor(selector, VEC_GP)?;
let readable_code = desc.is_app() && desc.high & ar::CODE != 0 && desc.high & ar::RW != 0;
let data = desc.is_app() && desc.high & ar::CODE == 0;
if !(readable_code || data) {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let conforming = desc.high & (ar::CODE | ar::DC) == (ar::CODE | ar::DC);
if !conforming {
let effective = if cpl > sel.rpl() { cpl } else { sel.rpl() };
if effective > desc.dpl() {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
self.mark_accessed(selector, desc)?;
*self.state.sys.seg_mut(index) = desc.to_seg(selector);
self.state.regs.set_segment(index, selector);
Ok(())
}
fn commit_cs(&mut self, selector: u16, desc: RawDesc, cpl: u8) {
let selector = Selector(selector).with_rpl(cpl).0;
*self.state.sys.seg_mut(seg::CS) = desc.to_seg(selector);
self.state.regs.cs = selector;
self.state.queue.flush();
}
pub(super) fn far_transfer(
&mut self,
selector: u16,
offset: u64,
is_call: bool,
opsize: u8,
) -> Ex<()> {
if !self.protected() {
if is_call {
let cs = self.state.regs.cs;
let ip = self.state.regs.rip;
self.push(u64::from(cs), opsize)?;
self.push(ip, opsize)?;
}
let entry = self.state.sys.seg_mut(seg::CS);
entry.selector = selector;
entry.base = u64::from(selector) << 4;
if !entry.present() {
entry.limit = 0xffff;
entry.ar = ar::REAL_CODE;
}
self.state.regs.cs = selector;
self.state.regs.rip = if opsize == 2 { offset & 0xffff } else { offset };
self.state.queue.flush();
return Ok(());
}
let sel = Selector(selector);
if sel.is_null() {
return Err(Fault::gp(0));
}
let desc = self.descriptor(selector, VEC_GP)?;
let cpl = self.cpl();
if desc.is_app() {
if desc.high & ar::CODE == 0 {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let conforming = desc.high & ar::DC != 0;
let ok = if conforming {
desc.dpl() <= cpl
} else {
desc.dpl() == cpl && sel.rpl() <= cpl
};
if !ok {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
let target = desc.to_seg(selector);
if self.state.sys.long_mode() && target.long() && target.big() {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if target.long() && self.state.sys.long_mode() {
if !canonical(offset) {
return Err(Fault::gp(0));
}
} else if !target.in_bounds(offset, 1) {
return Err(Fault::gp(0));
}
if is_call {
let cs = self.state.regs.cs;
let ip = self.state.regs.rip;
self.push(u64::from(cs), opsize)?;
self.push(ip, opsize)?;
}
self.mark_accessed(selector, desc)?;
self.commit_cs(selector, desc, cpl);
self.state.regs.rip = offset;
return Ok(());
}
if self.state.sys.long_mode()
&& matches!(
desc.kind(),
sys_type::TASK_GATE
| sys_type::TSS16_AVAIL
| sys_type::TSS16_BUSY
| sys_type::TSS32_AVAIL
| sys_type::TSS32_BUSY
)
{
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
match desc.kind() {
sys_type::CALL_GATE16 | sys_type::CALL_GATE32 => {
self.call_gate(selector, desc, is_call)
}
sys_type::TASK_GATE => {
if desc.dpl() < cpl || desc.dpl() < sel.rpl() {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
self.switch_task(desc.gate_selector(), is_call, None)
}
sys_type::TSS32_AVAIL | sys_type::TSS16_AVAIL => {
if desc.dpl() < cpl || desc.dpl() < sel.rpl() {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
self.switch_task(selector, is_call, None)
}
_ => Err(Fault::gp(u32::from(selector & 0xfffc))),
}
}
fn call_gate(&mut self, gate_sel: u16, gate: RawDesc, is_call: bool) -> Ex<()> {
let cpl = self.cpl();
let sel = Selector(gate_sel);
if gate.dpl() < cpl || gate.dpl() < sel.rpl() {
return Err(Fault::gp(u32::from(gate_sel & 0xfffc)));
}
if !gate.present() {
return Err(Fault::coded(VEC_NP, u32::from(gate_sel & 0xfffc)));
}
let gate32 = gate.kind() == sys_type::CALL_GATE32;
let size = if gate32 { 4u8 } else { 2u8 };
let target_sel = gate.gate_selector();
if Selector(target_sel).is_null() {
return Err(Fault::gp(0));
}
let target = self.descriptor(target_sel, VEC_GP)?;
if !target.is_app() || target.high & ar::CODE == 0 || target.dpl() > cpl {
return Err(Fault::gp(u32::from(target_sel & 0xfffc)));
}
if !target.present() {
return Err(Fault::coded(VEC_NP, u32::from(target_sel & 0xfffc)));
}
let conforming = target.high & ar::DC != 0;
let long = self.state.sys.long_mode();
let offset = if long {
gate.gate_offset64()
} else if gate32 {
u64::from(gate.gate_offset())
} else {
u64::from(gate.gate_offset() & 0xffff)
};
let size = if long { 8u8 } else { size };
if !conforming && target.dpl() < cpl && is_call {
let new_cpl = target.dpl();
let (ss_sel, new_sp) = self.tss_stack(new_cpl)?;
let ss_desc = self.validate_stack(ss_sel, new_cpl)?;
let argc = gate.gate_argc();
let mut args = [0u64; 31];
for (i, slot) in args.iter_mut().enumerate().take(argc as usize) {
let off = self.sp().wrapping_add(u64::from(size) * (i as u64));
*slot = self.read_mem(seg::SS, off, size)?;
}
let old_ss = self.state.regs.ss;
let old_sp = self.sp();
let old_cs = self.state.regs.cs;
let old_ip = self.state.regs.rip;
self.commit_cs(target_sel, target, new_cpl);
*self.state.sys.seg_mut(seg::SS) = ss_desc.to_seg(ss_sel);
self.state.regs.ss = ss_sel;
self.set_sp(new_sp);
self.push(u64::from(old_ss), size)?;
self.push(old_sp, size)?;
for i in (0..argc as usize).rev() {
self.push(args[i], size)?;
}
self.push(u64::from(old_cs), size)?;
self.push(old_ip, size)?;
self.state.regs.rip = offset;
return Ok(());
}
if is_call {
let cs = self.state.regs.cs;
let ip = self.state.regs.rip;
self.push(u64::from(cs), size)?;
self.push(ip, size)?;
}
let new_cpl = if conforming {
cpl
} else if target.dpl() > cpl {
target.dpl()
} else {
cpl
};
self.commit_cs(target_sel, target, new_cpl);
self.state.regs.rip = offset;
Ok(())
}
fn tss_stack(&mut self, level: u8) -> Ex<(u16, u64)> {
let tss = self.state.sys.task;
if !tss.present() {
return Err(Fault::coded(VEC_TS, u32::from(tss.selector & 0xfffc)));
}
let wide =
tss.sys_type() == sys_type::TSS32_AVAIL || tss.sys_type() == sys_type::TSS32_BUSY;
let (esp_off, ss_off) = if wide {
tss32_stack(level)
} else {
let base = 2 + (u64::from(level) & 3) * 4;
(base, base + 2)
};
if ss_off + 3 > u64::from(tss.limit) {
return Err(Fault::coded(VEC_TS, u32::from(tss.selector & 0xfffc)));
}
let sp = if wide {
u64::from(self.sys_read32(tss.base.wrapping_add(esp_off))?)
} else {
u64::from(self.sys_read16(tss.base.wrapping_add(esp_off))?)
};
let ss = self.sys_read16(tss.base.wrapping_add(ss_off))? as u16;
Ok((ss, sp))
}
fn tss_rsp(&mut self, level: u8) -> Ex<u64> {
let tss = self.state.sys.task;
if !tss.present() {
return Err(Fault::coded(VEC_TS, u32::from(tss.selector & 0xfffc)));
}
let offset = 4 + u64::from(level & 3) * 8;
if offset + 7 > u64::from(tss.limit) {
return Err(Fault::coded(VEC_TS, u32::from(tss.selector & 0xfffc)));
}
self.sys_read(tss.base.wrapping_add(offset), 8)
}
fn tss_ist(&mut self, index: u8) -> Ex<u64> {
let tss = self.state.sys.task;
if !tss.present() || index == 0 || index > 7 {
return Err(Fault::coded(VEC_TS, u32::from(tss.selector & 0xfffc)));
}
let offset = 36 - 8 + u64::from(index) * 8;
if offset + 7 > u64::from(tss.limit) {
return Err(Fault::coded(VEC_TS, u32::from(tss.selector & 0xfffc)));
}
self.sys_read(tss.base.wrapping_add(offset), 8)
}
fn validate_stack(&mut self, selector: u16, level: u8) -> Ex<RawDesc> {
let sel = Selector(selector);
if sel.is_null() || sel.rpl() != level {
return Err(Fault::coded(VEC_TS, u32::from(selector & 0xfffc)));
}
let desc = self.descriptor(selector, VEC_TS)?;
if !desc.is_app()
|| desc.high & ar::CODE != 0
|| desc.high & ar::RW == 0
|| desc.dpl() != level
{
return Err(Fault::coded(VEC_TS, u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_SS, u32::from(selector & 0xfffc)));
}
Ok(desc)
}
pub(super) fn return_far(&mut self, opsize: u8, extra: u64) -> Ex<()> {
let ip = self.pop(opsize)?;
let selector = self.pop(opsize)? as u16;
if !self.protected() {
let entry = self.state.sys.seg_mut(seg::CS);
entry.selector = selector;
entry.base = u64::from(selector) << 4;
self.state.regs.cs = selector;
self.state.regs.rip = if opsize == 2 { ip & 0xffff } else { ip };
let sp = self.sp().wrapping_add(extra);
self.set_sp(sp);
self.state.queue.flush();
return Ok(());
}
let sp = self.sp().wrapping_add(extra);
self.set_sp(sp);
let sel = Selector(selector);
if sel.is_null() {
return Err(Fault::gp(0));
}
let cpl = self.cpl();
if sel.rpl() < cpl {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let desc = self.descriptor(selector, VEC_GP)?;
if !desc.is_app() || desc.high & ar::CODE == 0 {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let conforming = desc.high & ar::DC != 0;
let ok = if conforming {
desc.dpl() <= sel.rpl()
} else {
desc.dpl() == sel.rpl()
};
if !ok {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
if sel.rpl() > cpl {
let new_sp = self.pop(opsize)?;
let new_ss = self.pop(opsize)? as u16;
let ss_desc = self.validate_return_stack(new_ss, sel.rpl())?;
self.commit_cs(selector, desc, sel.rpl());
*self.state.sys.seg_mut(seg::SS) = ss_desc.to_seg(new_ss);
self.state.regs.ss = new_ss;
self.set_sp(new_sp.wrapping_add(extra));
self.state.regs.rip = if opsize == 2 { ip & 0xffff } else { ip };
self.drop_privileged_segments(sel.rpl());
return Ok(());
}
self.commit_cs(selector, desc, cpl);
self.state.regs.rip = if opsize == 2 { ip & 0xffff } else { ip };
Ok(())
}
fn validate_return_stack(&mut self, selector: u16, level: u8) -> Ex<RawDesc> {
let sel = Selector(selector);
if sel.is_null() || sel.rpl() != level {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let desc = self.descriptor(selector, VEC_GP)?;
if !desc.is_app()
|| desc.high & ar::CODE != 0
|| desc.high & ar::RW == 0
|| desc.dpl() != level
{
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_SS, u32::from(selector & 0xfffc)));
}
Ok(desc)
}
fn drop_privileged_segments(&mut self, cpl: u8) {
for index in [seg::ES, seg::DS, seg::FS, seg::GS] {
let s = self.state.sys.seg(index);
if !s.present() {
continue;
}
let conforming = s.is_code() && s.ar & ar::DC != 0;
if !conforming && s.dpl() < cpl {
*self.state.sys.seg_mut(index) = SegReg::null();
self.state.regs.set_segment(index, 0);
}
}
}
pub(super) fn iret(&mut self, opsize: u8) -> Ex<()> {
if !self.protected() {
let ip = self.pop(opsize)?;
let cs = self.pop(opsize)? as u16;
let fl = self.pop(opsize)?;
let entry = self.state.sys.seg_mut(seg::CS);
entry.selector = cs;
entry.base = u64::from(cs) << 4;
self.state.regs.cs = cs;
self.state.regs.rip = if opsize == 2 { ip & 0xffff } else { ip };
let kept: u32 = if opsize == 2 { 0xffff_0000 } else { 0 };
let old = self.state.regs.eflags;
self.set_flags((fl as u32 & !kept) | (old & kept));
self.state.queue.flush();
return Ok(());
}
if self.flag(flags::NT) {
let tss = self.state.sys.task;
let back = self.sys_read16(tss.base)? as u16;
return self.switch_task(back, false, Some(SwitchKind::Return));
}
let ip = self.pop(opsize)?;
let selector = self.pop(opsize)? as u16;
let fl = self.pop(opsize)?;
let cpl = self.cpl();
let sel = Selector(selector);
if sel.is_null() {
return Err(Fault::gp(0));
}
if sel.rpl() < cpl {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let desc = self.descriptor(selector, VEC_GP)?;
if !desc.is_app() || desc.high & ar::CODE == 0 {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let conforming = desc.high & ar::DC != 0;
let ok = if conforming {
desc.dpl() <= sel.rpl()
} else {
desc.dpl() == sel.rpl()
};
if !ok {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
let long = self.state.sys.long_mode();
let outward = sel.rpl() > cpl;
if long || outward {
let new_sp = self.pop(opsize)?;
let new_ss = self.pop(opsize)? as u16;
let new_cpl = sel.rpl();
self.commit_cs(selector, desc, new_cpl);
if long && Selector(new_ss).is_null() {
*self.state.sys.seg_mut(seg::SS) = SegReg {
selector: new_ss,
base: 0,
limit: 0,
ar: ar::PRESENT | ar::S | ar::RW | ar::ACCESSED,
};
} else {
let ss_desc = self.validate_return_stack(new_ss, new_cpl)?;
*self.state.sys.seg_mut(seg::SS) = ss_desc.to_seg(new_ss);
}
self.state.regs.ss = new_ss;
self.set_sp(new_sp);
} else {
self.commit_cs(selector, desc, cpl);
}
self.state.regs.rip = match opsize {
2 => ip & 0xffff,
4 => ip & 0xffff_ffff,
_ => ip,
};
self.restore_flags(fl, cpl, opsize);
if outward {
self.drop_privileged_segments(sel.rpl());
}
Ok(())
}
fn restore_flags(&mut self, value: u64, cpl: u8, opsize: u8) {
let old = self.state.regs.eflags;
let value = value as u32;
let mut keep = flags::VM;
if cpl > self.state.regs.iopl() {
keep |= flags::IF;
}
if cpl > 0 {
keep |= flags::IOPL;
}
if opsize == 2 {
keep |= 0xffff_0000;
}
self.set_flags(((value & !keep) | (old & keep)) & !flags::RF);
}
pub(super) fn check_software_gate(&mut self, vector: u8) -> Ex<()> {
let stride = if self.state.sys.long_mode() { 16 } else { 8 };
let offset = u32::from(vector) * stride;
let idtr = self.state.sys.idtr;
if offset + stride - 1 > idtr.limit {
return Err(Fault::gp(u32::from(vector) * 8 + 2));
}
let high = self.sys_read32(idtr.base.wrapping_add(u64::from(offset)).wrapping_add(4))?;
let dpl = ((high & ar::DPL) >> ar::DPL_SHIFT) as u8;
if dpl < self.cpl() {
return Err(Fault::gp(u32::from(vector) * 8 + 2));
}
Ok(())
}
pub(super) fn take_interrupt(&mut self, vector: u8, error: Option<u32>) -> Ex<()> {
self.state.halted = false;
if self.legacy() {
return self.legacy_interrupt(vector);
}
if self.protected() {
return self.protected_interrupt(vector, error);
}
self.real_interrupt(vector)
}
fn legacy_interrupt(&mut self, vector: u8) -> Ex<()> {
let base = u32::from(vector) << 2;
let target_ip = self.read_ivt(base);
let target_cs = self.read_ivt(base.wrapping_add(2));
let saved = u64::from(self.state.regs.eflags);
self.push(saved, 2)?;
self.set_flag(flags::IF | flags::TF, false);
let cs = self.state.regs.cs;
let ip = self.state.regs.rip & 0xffff;
self.push(u64::from(cs), 2)?;
self.push(ip, 2)?;
self.state.regs.cs = target_cs as u16;
self.state.regs.rip = target_ip;
let entry = self.state.sys.seg_mut(seg::CS);
entry.selector = target_cs as u16;
entry.base = (target_cs & 0xffff) << 4;
self.state.queue.flush();
Ok(())
}
fn read_ivt(&mut self, offset: u32) -> u64 {
let low = super::linear(0, offset as u16);
let high = super::linear(0, (offset as u16).wrapping_add(1));
let lo = self.phys_read(low, 1);
let hi = self.phys_read(high, 1);
lo | (hi << 8)
}
fn real_interrupt(&mut self, vector: u8) -> Ex<()> {
let offset = u32::from(vector) * 4;
let idtr = self.state.sys.idtr;
if offset + 3 > idtr.limit {
return Err(Fault::gp(u32::from(vector) * 8 + 2));
}
let entry_addr = idtr.base.wrapping_add(u64::from(offset));
let target_ip = u64::from(self.sys_read16(entry_addr)?);
let target_cs = self.sys_read16(entry_addr.wrapping_add(2))? as u16;
let saved = u64::from(self.state.regs.eflags);
let cs = self.state.regs.cs;
let ip = self.state.regs.rip & 0xffff;
self.push(saved, 2)?;
self.push(u64::from(cs), 2)?;
self.push(ip, 2)?;
self.set_flag(flags::IF | flags::TF | flags::AC | flags::RF, false);
self.state.regs.cs = target_cs;
self.state.regs.rip = target_ip;
let entry = self.state.sys.seg_mut(seg::CS);
entry.selector = target_cs;
entry.base = u64::from(target_cs) << 4;
self.state.queue.flush();
Ok(())
}
#[allow(clippy::too_many_lines)]
fn protected_interrupt(&mut self, vector: u8, error: Option<u32>) -> Ex<()> {
let long = self.state.sys.long_mode();
let stride: u32 = if long { 16 } else { 8 };
let index = u32::from(vector) * stride;
let idtr = self.state.sys.idtr;
let table_error = u32::from(vector) * 8 + 2;
if index + stride - 1 > idtr.limit {
return Err(Fault::gp(table_error));
}
let addr = idtr.base.wrapping_add(u64::from(index));
let gate = RawDesc {
low: self.sys_read32(addr)?,
high: self.sys_read32(addr.wrapping_add(4))?,
upper: if long {
self.sys_read32(addr.wrapping_add(8))?
} else {
0
},
};
if gate.is_app() {
return Err(Fault::gp(table_error));
}
let kind = gate.kind();
if kind == sys_type::TASK_GATE && !long {
if !gate.present() {
return Err(Fault::coded(VEC_NP, table_error));
}
self.switch_task(gate.gate_selector(), true, None)?;
if let Some(code) = error {
self.push(u64::from(code), 4)?;
}
return Ok(());
}
let gate32 = matches!(kind, sys_type::INT_GATE32 | sys_type::TRAP_GATE32);
let gate16 = matches!(kind, sys_type::INT_GATE16 | sys_type::TRAP_GATE16);
if !gate32 && (!gate16 || long) {
return Err(Fault::gp(table_error));
}
if !gate.present() {
return Err(Fault::coded(VEC_NP, table_error));
}
let interrupt_gate = matches!(kind, sys_type::INT_GATE16 | sys_type::INT_GATE32);
let size = if long {
8u8
} else if gate32 {
4u8
} else {
2u8
};
let target_sel = gate.gate_selector();
if Selector(target_sel).is_null() {
return Err(Fault::gp(0));
}
let target = self.descriptor(target_sel, VEC_GP)?;
let cpl = self.cpl();
if !target.is_app() || target.high & ar::CODE == 0 || target.dpl() > cpl {
return Err(Fault::gp(u32::from(target_sel & 0xfffc)));
}
if !target.present() {
return Err(Fault::coded(VEC_NP, u32::from(target_sel & 0xfffc)));
}
let conforming = target.high & ar::DC != 0;
let offset = if long {
gate.gate_offset64()
} else if gate32 {
u64::from(gate.gate_offset())
} else {
u64::from(gate.gate_offset() & 0xffff)
};
let old_flags = u64::from(self.state.regs.eflags);
let old_cs = self.state.regs.cs;
let old_ip = self.state.regs.rip;
let ist = if long { gate.gate_ist() } else { 0 };
let switching = (!conforming && target.dpl() < cpl) || ist != 0;
if switching {
let new_cpl = if conforming { cpl } else { target.dpl() };
let (ss_sel, new_sp) = if ist != 0 {
(0u16, self.tss_ist(ist)?)
} else if long {
(u16::from(new_cpl), self.tss_rsp(new_cpl)?)
} else {
self.tss_stack(new_cpl)?
};
let old_ss = self.state.regs.ss;
let old_sp = self.sp();
self.commit_cs(target_sel, target, new_cpl);
if long {
*self.state.sys.seg_mut(seg::SS) = SegReg {
selector: ss_sel,
base: 0,
limit: 0,
ar: ar::PRESENT | ar::S | ar::RW | ar::ACCESSED,
};
} else {
let ss_desc = self.validate_stack(ss_sel, new_cpl)?;
*self.state.sys.seg_mut(seg::SS) = ss_desc.to_seg(ss_sel);
}
self.state.regs.ss = ss_sel;
self.set_sp(new_sp);
self.push(u64::from(old_ss), size)?;
self.push(old_sp, size)?;
self.push(old_flags, size)?;
self.push(u64::from(old_cs), size)?;
self.push(old_ip, size)?;
} else if long {
let new_cpl = if conforming {
cpl
} else {
target.dpl().max(cpl)
};
let old_ss = self.state.regs.ss;
let old_sp = self.sp();
self.commit_cs(target_sel, target, new_cpl);
self.push(u64::from(old_ss), size)?;
self.push(old_sp, size)?;
self.push(old_flags, size)?;
self.push(u64::from(old_cs), size)?;
self.push(old_ip, size)?;
} else {
let new_cpl = if conforming {
cpl
} else if target.dpl() > cpl {
target.dpl()
} else {
cpl
};
self.commit_cs(target_sel, target, new_cpl);
self.push(old_flags, size)?;
self.push(u64::from(old_cs), size)?;
self.push(old_ip, size)?;
}
if let Some(code) = error {
self.push(u64::from(code), size)?;
}
self.state.regs.rip = offset;
let mut clear = flags::TF | flags::NT | flags::VM | flags::RF;
if interrupt_gate {
clear |= flags::IF;
}
self.state.regs.eflags &= !clear;
Ok(())
}
#[allow(clippy::too_many_lines)]
pub(super) fn switch_task(
&mut self,
selector: u16,
nested: bool,
kind: Option<SwitchKind>,
) -> Ex<()> {
let returning = matches!(kind, Some(SwitchKind::Return));
let sel = Selector(selector);
if sel.is_null() || sel.is_ldt() {
return Err(Fault::coded(VEC_TS, u32::from(selector & 0xfffc)));
}
let desc = self.descriptor(selector, VEC_GP)?;
if desc.is_app() {
return Err(Fault::coded(VEC_TS, u32::from(selector & 0xfffc)));
}
let wide = match desc.kind() {
sys_type::TSS32_AVAIL => true,
sys_type::TSS32_BUSY if returning => true,
sys_type::TSS16_AVAIL => false,
sys_type::TSS16_BUSY if returning => false,
_ => return Err(Fault::coded(VEC_TS, u32::from(selector & 0xfffc))),
};
if !wide {
return Err(Fault::coded(VEC_TS, u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
let new_tss = desc.to_seg(selector);
if u64::from(new_tss.limit) < tss32::MIN_LIMIT {
return Err(Fault::coded(VEC_TS, u32::from(selector & 0xfffc)));
}
let outgoing = self.state.sys.task;
if outgoing.present() {
self.save_task_state(outgoing)?;
if returning {
self.set_sys_type(outgoing.selector, sys_type::TSS32_AVAIL)?;
}
}
let base = new_tss.base;
let cr3 = self.sys_read32(base.wrapping_add(tss32::CR3))?;
let eip = self.sys_read32(base.wrapping_add(tss32::EIP))?;
let eflags = self.sys_read32(base.wrapping_add(tss32::EFLAGS))?;
let mut gpr = [0u32; 8];
for (i, slot) in gpr.iter_mut().enumerate() {
*slot = self.sys_read32(base.wrapping_add(tss32::EAX + 4 * i as u64))?;
}
let mut selectors = [0u16; 6];
for (i, slot) in selectors.iter_mut().enumerate() {
*slot = self.sys_read16(base.wrapping_add(tss32::ES + 4 * i as u64))? as u16;
}
let ldt = self.sys_read16(base.wrapping_add(tss32::LDT))? as u16;
if !returning && nested {
let previous = u32::from(self.state.sys.task.selector);
self.sys_write32(base, previous)?;
}
if !returning {
self.set_sys_type(selector, sys_type::TSS32_BUSY)?;
}
self.state.sys.task = SegReg {
ar: (new_tss.ar & !0x0000_0f00) | (u32::from(sys_type::TSS32_BUSY) << 8),
..new_tss
};
self.state.sys.cr0 |= cr0::TS;
if self.state.sys.paging() {
self.state.sys.cr3 = u64::from(cr3);
self.state.tlb.flush();
}
for (i, value) in gpr.iter().enumerate() {
self.state.regs.set_dword(i as u8, *value);
}
self.state.regs.rip = u64::from(eip);
let mut flags_value = eflags;
if !returning && nested {
flags_value |= flags::NT;
} else if returning {
flags_value &= !flags::NT;
}
self.set_flags(flags_value);
self.load_ldtr(ldt)?;
let cs_sel = selectors[1];
let cs_desc = self.descriptor(cs_sel, VEC_TS)?;
if !cs_desc.is_app() || cs_desc.high & ar::CODE == 0 {
return Err(Fault::coded(VEC_TS, u32::from(cs_sel & 0xfffc)));
}
*self.state.sys.seg_mut(seg::CS) = cs_desc.to_seg(cs_sel);
self.state.regs.cs = cs_sel;
self.state.queue.flush();
for (i, index) in TSS_SEG_ORDER.iter().enumerate() {
if *index == seg::CS {
continue;
}
let value = selectors[i];
if *index == seg::SS {
let stack = self.validate_stack(value, (cs_sel & 3) as u8)?;
*self.state.sys.seg_mut(seg::SS) = stack.to_seg(value);
self.state.regs.ss = value;
} else {
self.load_segment(*index, value)?;
}
}
Ok(())
}
fn save_task_state(&mut self, tss: SegReg) -> Ex<()> {
let base = tss.base;
let eip = self.state.regs.rip;
self.sys_write32(base.wrapping_add(tss32::EIP), eip as u32)?;
let eflags = self.state.regs.eflags;
self.sys_write32(base.wrapping_add(tss32::EFLAGS), eflags)?;
for i in 0..8u8 {
let value = self.state.regs.dword(i);
self.sys_write32(base.wrapping_add(tss32::EAX + 4 * u64::from(i)), value)?;
}
for (i, index) in TSS_SEG_ORDER.iter().enumerate() {
let value = u32::from(self.state.regs.segment(*index));
self.sys_write32(base.wrapping_add(tss32::ES + 4 * i as u64), value)?;
}
let ldt = u32::from(self.state.sys.ldtr.selector);
self.sys_write32(base.wrapping_add(tss32::LDT), ldt)
}
fn load_ldtr(&mut self, selector: u16) -> Ex<()> {
let sel = Selector(selector);
if sel.is_null() {
self.state.sys.ldtr = SegReg::null();
return Ok(());
}
if sel.is_ldt() {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let desc = self.descriptor(selector, VEC_GP)?;
if desc.is_app() || desc.kind() != sys_type::LDT {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
self.state.sys.ldtr = desc.to_seg(selector);
Ok(())
}
pub(super) fn load_table_register(&mut self, f: &Fields) -> Ex<()> {
self.require_ring0()?;
if f.rm_is_register() {
return Err(Fault::bare(VEC_UD));
}
let (sr, off) = self.ea();
let limit = self.read_mem(sr, off, 2)? as u32;
let width = if self.sixty_four() { 8u8 } else { 4 };
let base = self.read_mem(sr, off.wrapping_add(2), width)?;
let base = if f.opsize == 2 && !self.sixty_four() {
base & 0x00ff_ffff
} else {
base
};
let reg = TableReg { base, limit };
if f.insn.op == Op::LGDT {
self.state.sys.gdtr = reg;
} else {
self.state.sys.idtr = reg;
}
Ok(())
}
pub(super) fn store_table_register(&mut self, f: &Fields) -> Ex<()> {
if f.rm_is_register() {
return Err(Fault::bare(VEC_UD));
}
let reg = if f.insn.op == Op::SGDT {
self.state.sys.gdtr
} else {
self.state.sys.idtr
};
let (sr, off) = self.ea();
self.write_mem(sr, off, 2, u64::from(reg.limit & 0xffff))?;
let width = if self.sixty_four() { 8u8 } else { 4 };
let base = if f.opsize == 2 && !self.sixty_four() {
reg.base & 0x00ff_ffff
} else {
reg.base
};
self.write_mem(sr, off.wrapping_add(2), width, base)
}
pub(super) fn load_system_selector(&mut self, f: &Fields) -> Ex<()> {
self.require_protected()?;
self.require_ring0()?;
let selector = self.read_arg(f, f.insn.dst, 2)? as u16;
if f.insn.op == Op::LLDT {
return self.load_ldtr(selector);
}
let sel = Selector(selector);
if sel.is_null() || sel.is_ldt() {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
let desc = self.descriptor(selector, VEC_GP)?;
let kinds: &[u8] = if self.state.sys.long_mode() {
&[sys_type::TSS32_AVAIL]
} else {
&[sys_type::TSS16_AVAIL, sys_type::TSS32_AVAIL]
};
if desc.is_app() || !kinds.contains(&desc.kind()) {
return Err(Fault::gp(u32::from(selector & 0xfffc)));
}
if !desc.present() {
return Err(Fault::coded(VEC_NP, u32::from(selector & 0xfffc)));
}
let busy = if desc.kind() == sys_type::TSS32_AVAIL {
sys_type::TSS32_BUSY
} else {
sys_type::TSS16_BUSY
};
self.set_sys_type(selector, busy)?;
self.state.sys.task = SegReg {
ar: (desc.ar() & !0x0000_0f00) | (u32::from(busy) << 8),
..desc.to_seg(selector)
};
Ok(())
}
pub(super) fn lmsw(&mut self, f: &Fields) -> Ex<()> {
self.require_ring0()?;
let value = self.read_arg(f, f.insn.dst, 2)?;
let old = self.state.sys.cr0;
self.state.sys.cr0 = (old & !cr0::MSW) | (value as u32 & cr0::MSW) | (old & cr0::PE);
Ok(())
}
fn probe(&mut self, selector: u16) -> Ex<Option<RawDesc>> {
let sel = Selector(selector);
if sel.is_null() {
return Ok(None);
}
let table = if sel.is_ldt() {
let ldtr = self.state.sys.ldtr;
if !ldtr.present() {
return Ok(None);
}
TableReg {
base: ldtr.base,
limit: ldtr.limit,
}
} else {
self.state.sys.gdtr
};
if sel.offset() + 7 > table.limit {
return Ok(None);
}
let addr = table.base.wrapping_add(u64::from(sel.offset()));
let low = self.sys_read32(addr)?;
let high = self.sys_read32(addr.wrapping_add(4))?;
Ok(Some(RawDesc {
low,
high,
upper: 0,
}))
}
fn descriptor_visible(&self, desc: RawDesc, sel: Selector) -> bool {
let conforming = desc.is_app() && desc.high & (ar::CODE | ar::DC) == (ar::CODE | ar::DC);
let effective = if self.cpl() > sel.rpl() {
self.cpl()
} else {
sel.rpl()
};
conforming || desc.dpl() >= effective
}
pub(super) fn lar_lsl(&mut self, f: &Fields) -> Ex<()> {
self.require_protected()?;
let selector = self.read_arg(f, f.insn.src, 2)? as u16;
let Some(desc) = self.probe(selector)? else {
self.set_flag(flags::ZF, false);
return Ok(());
};
let visible = self.descriptor_visible(desc, Selector(selector));
let usable = if f.insn.op == Op::LAR {
desc.is_app()
|| matches!(
desc.kind(),
sys_type::TSS16_AVAIL
| sys_type::LDT
| sys_type::TSS16_BUSY
| sys_type::CALL_GATE16
| sys_type::TASK_GATE
| sys_type::TSS32_AVAIL
| sys_type::TSS32_BUSY
| sys_type::CALL_GATE32
)
} else {
desc.is_app()
|| matches!(
desc.kind(),
sys_type::TSS16_AVAIL
| sys_type::LDT
| sys_type::TSS16_BUSY
| sys_type::TSS32_AVAIL
| sys_type::TSS32_BUSY
)
};
if !visible || !usable {
self.set_flag(flags::ZF, false);
return Ok(());
}
self.set_flag(flags::ZF, true);
let value = u64::from(if f.insn.op == Op::LAR {
desc.ar()
} else {
desc.limit()
});
let opsize = f.opsize;
self.write_arg(f, f.insn.dst, opsize, value)
}
pub(super) fn verify(&mut self, f: &Fields) -> Ex<()> {
self.require_protected()?;
let selector = self.read_arg(f, f.insn.dst, 2)? as u16;
let Some(desc) = self.probe(selector)? else {
self.set_flag(flags::ZF, false);
return Ok(());
};
if !desc.is_app() || !desc.present() {
self.set_flag(flags::ZF, false);
return Ok(());
}
let visible = self.descriptor_visible(desc, Selector(selector));
let code = desc.high & ar::CODE != 0;
let ok = if f.insn.op == Op::VERR {
visible && (!code || desc.high & ar::RW != 0)
} else {
visible && !code && desc.high & ar::RW != 0
};
self.set_flag(flags::ZF, ok);
Ok(())
}
pub(super) fn arpl(&mut self, f: &Fields) -> Ex<()> {
self.require_protected()?;
let dst = self.read_arg(f, f.insn.dst, 2)? as u16;
let src = self.read_arg(f, f.insn.src, 2)? as u16;
if dst & 3 < src & 3 {
self.set_flag(flags::ZF, true);
let raised = (dst & 0xfffc) | (src & 3);
self.write_arg(f, f.insn.dst, 2, u64::from(raised))?;
} else {
self.set_flag(flags::ZF, false);
}
Ok(())
}
pub(super) fn read_control(&mut self, index: u8) -> Ex<u64> {
self.require_ring0()?;
match index {
0 => Ok(u64::from(self.state.sys.cr0)),
2 => Ok(self.state.sys.cr2),
3 => Ok(self.state.sys.cr3),
4 if self.cfg.features.cr4 => Ok(self.state.sys.cr4),
_ => Err(Fault::bare(VEC_UD)),
}
}
pub(super) fn write_control(&mut self, index: u8, value: u64) -> Ex<()> {
self.require_ring0()?;
match index {
0 => {
let valid = if self.cfg.features.extras_486 {
cr0::VALID_486
} else {
cr0::VALID_386
};
let old = self.state.sys.cr0;
let new = (value as u32) & valid;
if new & cr0::PG != 0 && new & cr0::PE == 0 {
return Err(Fault::gp(0));
}
let lme = self.state.sys.efer & efer::LME != 0;
if new & cr0::PG != 0 && old & cr0::PG == 0 && lme {
if self.state.sys.cr4 & cr4::PAE == 0 {
return Err(Fault::gp(0));
}
self.state.sys.efer |= efer::LMA;
} else if new & cr0::PG == 0 && old & cr0::PG != 0 {
self.state.sys.efer &= !efer::LMA;
}
self.state.sys.cr0 = new;
if (old ^ new) & (cr0::PG | cr0::WP | cr0::PE) != 0 {
self.state.tlb.flush();
}
self.state.queue.flush();
Ok(())
}
2 => {
self.state.sys.cr2 = value;
Ok(())
}
3 => {
self.state.sys.cr3 = value;
self.state.tlb.flush();
Ok(())
}
4 if self.cfg.features.cr4 => {
let old = self.state.sys.cr4;
let valid = cr4::VME
| cr4::PVI
| cr4::TSD
| cr4::DE
| cr4::PSE
| cr4::PAE
| cr4::MCE
| cr4::PGE
| cr4::PCE
| cr4::OSFXSR
| cr4::OSXMMEXCPT;
let new = value & valid;
if self.state.sys.long_mode() && new & cr4::PAE == 0 {
return Err(Fault::gp(0));
}
self.state.sys.cr4 = new;
if (old ^ new) & (cr4::PAE | cr4::PSE | cr4::PGE) != 0 {
self.state.tlb.flush();
}
Ok(())
}
_ => Err(Fault::bare(VEC_UD)),
}
}
pub(super) fn rdmsr(&mut self) -> Ex<()> {
if !self.cfg.features.msr {
return Err(Fault::bare(VEC_UD));
}
self.require_ring0()?;
let index = self.state.regs.rcx as u32;
let value = self.msr_read(index)?;
self.state.regs.set_dword(0, value as u32);
self.state.regs.set_dword(2, (value >> 32) as u32);
Ok(())
}
pub(super) fn wrmsr(&mut self) -> Ex<()> {
if !self.cfg.features.msr {
return Err(Fault::bare(VEC_UD));
}
self.require_ring0()?;
let index = self.state.regs.rcx as u32;
let value =
u64::from(self.state.regs.rax as u32) | (u64::from(self.state.regs.rdx as u32) << 32);
self.msr_write(index, value)
}
pub(super) fn rdtsc(&mut self) -> Ex<()> {
if !self.cfg.features.msr {
return Err(Fault::bare(VEC_UD));
}
if self.state.sys.cr4 & cr4::TSD != 0 {
self.require_ring0()?;
}
let tsc = self.state.cycles;
self.state.regs.set_dword(0, tsc as u32);
self.state.regs.set_dword(2, (tsc >> 32) as u32);
Ok(())
}
fn msr_read(&mut self, index: u32) -> Ex<u64> {
match index {
msr::TSC => return Ok(self.state.cycles),
msr::APIC_BASE => return self.lines.base_register().ok_or(Fault::gp(0)),
_ => {}
}
let sys = &self.state.sys;
let value = match index {
msr::EFER if self.cfg.features.long => sys.efer,
msr::STAR if self.cfg.features.syscall => sys.star,
msr::LSTAR if self.cfg.features.syscall => sys.lstar,
msr::CSTAR if self.cfg.features.syscall => sys.cstar,
msr::SFMASK if self.cfg.features.syscall => sys.sfmask,
msr::FS_BASE if self.cfg.features.long => sys.fs_base,
msr::GS_BASE if self.cfg.features.long => sys.gs_base,
msr::KERNEL_GS_BASE if self.cfg.features.long => sys.kernel_gs_base,
_ => return Err(Fault::gp(0)),
};
Ok(value)
}
fn msr_write(&mut self, index: u32, value: u64) -> Ex<()> {
match index {
msr::TSC => {
self.state.cycles = value;
Ok(())
}
msr::APIC_BASE => {
if value & !(apic_base::WRITABLE | apic_base::BSP) != 0 {
return Err(Fault::gp(0));
}
let Some(current) = self.lines.base_register() else {
return Err(Fault::gp(0));
};
let merged = (value & apic_base::WRITABLE) | (current & apic_base::BSP);
if !self.lines.set_base_register(merged) {
return Err(Fault::gp(0));
}
Ok(())
}
msr::EFER if self.cfg.features.long => {
let old = self.state.sys.efer;
let mut new = value & efer::WRITABLE;
if !self.cfg.features.nx {
new &= !efer::NXE;
}
if !self.cfg.features.syscall {
new &= !efer::SCE;
}
if (old ^ new) & efer::LME != 0 && self.state.sys.cr0 & cr0::PG != 0 {
return Err(Fault::gp(0));
}
self.state.sys.efer = new | (old & efer::LMA);
if (old ^ new) & efer::NXE != 0 {
self.state.tlb.flush();
}
Ok(())
}
msr::STAR if self.cfg.features.syscall => {
self.state.sys.star = value;
Ok(())
}
msr::LSTAR if self.cfg.features.syscall => {
if !canonical(value) {
return Err(Fault::gp(0));
}
self.state.sys.lstar = value;
Ok(())
}
msr::CSTAR if self.cfg.features.syscall => {
if !canonical(value) {
return Err(Fault::gp(0));
}
self.state.sys.cstar = value;
Ok(())
}
msr::SFMASK if self.cfg.features.syscall => {
self.state.sys.sfmask = value;
Ok(())
}
msr::FS_BASE if self.cfg.features.long => {
if !canonical(value) {
return Err(Fault::gp(0));
}
self.state.sys.fs_base = value;
self.state.sys.seg_mut(seg::FS).base = value;
Ok(())
}
msr::GS_BASE if self.cfg.features.long => {
if !canonical(value) {
return Err(Fault::gp(0));
}
self.state.sys.gs_base = value;
self.state.sys.seg_mut(seg::GS).base = value;
Ok(())
}
msr::KERNEL_GS_BASE if self.cfg.features.long => {
if !canonical(value) {
return Err(Fault::gp(0));
}
self.state.sys.kernel_gs_base = value;
Ok(())
}
_ => Err(Fault::gp(0)),
}
}
fn syscall_seg(selector: u16, code: bool, long: bool) -> SegReg {
let mut ar = ar::PRESENT | ar::S | ar::RW | ar::ACCESSED | ar::GRANULAR;
if code {
ar |= ar::CODE;
if long {
ar |= ar::L;
} else {
ar |= ar::DB;
}
} else {
ar |= ar::DB;
}
SegReg {
selector,
base: 0,
limit: 0xffff_ffff,
ar,
}
}
pub(super) fn syscall(&mut self) -> Ex<()> {
if !self.cfg.features.syscall || self.state.sys.efer & efer::SCE == 0 || !self.sixty_four()
{
return Err(Fault::bare(VEC_UD));
}
let sys = self.state.sys;
let cs_sel = ((sys.star >> 32) as u16) & 0xfffc;
self.state.regs.rcx = self.state.regs.rip;
self.state.regs.r[3] = u64::from(self.state.regs.eflags);
self.state.regs.rip = sys.lstar;
*self.state.sys.seg_mut(seg::CS) = Self::syscall_seg(cs_sel, true, true);
*self.state.sys.seg_mut(seg::SS) = Self::syscall_seg(cs_sel + 8, false, true);
self.state.regs.cs = cs_sel;
self.state.regs.ss = cs_sel + 8;
let masked = self.state.regs.eflags & !(sys.sfmask as u32) & !flags::RF;
self.set_flags(masked);
self.state.queue.flush();
Ok(())
}
pub(super) fn sysret(&mut self, opsize: u8) -> Ex<()> {
if !self.cfg.features.syscall || self.state.sys.efer & efer::SCE == 0 || !self.sixty_four()
{
return Err(Fault::bare(VEC_UD));
}
self.require_ring0()?;
let sys = self.state.sys;
let base = (sys.star >> 48) as u16;
let wide = opsize == 8;
let cs_sel = (base + if wide { 16 } else { 0 }) | 3;
let ss_sel = (base + 8) | 3;
let target = self.state.regs.rcx;
if wide && !canonical(target) {
return Err(Fault::gp(0));
}
*self.state.sys.seg_mut(seg::CS) = Self::syscall_seg(cs_sel, true, wide);
*self.state.sys.seg_mut(seg::SS) = Self::syscall_seg(ss_sel, false, false);
self.state.regs.cs = cs_sel;
self.state.regs.ss = ss_sel;
self.state.regs.rip = if wide { target } else { target & 0xffff_ffff };
let restored = (self.state.regs.r[3] as u32) & !(flags::RF | flags::VM);
self.set_flags(restored);
self.state.queue.flush();
Ok(())
}
pub(super) fn swapgs(&mut self) -> Ex<()> {
if !self.sixty_four() {
return Err(Fault::bare(VEC_UD));
}
self.require_ring0()?;
let sys = &mut self.state.sys;
core::mem::swap(&mut sys.gs_base, &mut sys.kernel_gs_base);
sys.seg_mut(seg::GS).base = sys.gs_base;
Ok(())
}
pub(super) fn read_debug(&mut self, index: u8) -> Ex<u64> {
self.require_ring0()?;
Ok(self.state.sys.dr[usize::from(Self::debug_index(index))])
}
pub(super) fn write_debug(&mut self, index: u8, value: u64) -> Ex<()> {
self.require_ring0()?;
self.state.sys.dr[usize::from(Self::debug_index(index))] = value;
Ok(())
}
const fn debug_index(index: u8) -> u8 {
match index & 7 {
4 => 6,
5 => 7,
other => other,
}
}
pub(super) fn read_test(&mut self, index: u8) -> Ex<u64> {
self.require_ring0()?;
if index < 6 {
return Err(Fault::bare(VEC_UD));
}
Ok(u64::from(self.state.sys.test[(index & 7) as usize]))
}
pub(super) fn write_test(&mut self, index: u8, value: u64) -> Ex<()> {
self.require_ring0()?;
if index < 6 {
return Err(Fault::bare(VEC_UD));
}
self.state.sys.test[(index & 7) as usize] = value as u32;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_selector_splits_into_index_table_and_privilege() {
let s = Selector(0x0023);
assert_eq!(s.index(), 4);
assert_eq!(s.offset(), 0x20);
assert!(!s.is_ldt());
assert_eq!(s.rpl(), 3);
assert!(!s.is_null());
let local = Selector(0x000f);
assert!(local.is_ldt());
assert_eq!(local.index(), 1);
assert!(Selector(0).is_null());
assert!(Selector(3).is_null());
assert!(!Selector(8).is_null());
}
#[test]
fn a_descriptor_reassembles_its_base_from_three_fields() {
let d = RawDesc {
low: 0x5678_ffff,
high: 0x00cf_9a12 | 0x3400_0000,
upper: 0,
};
assert_eq!(d.base(), 0x3412_5678);
assert_eq!(d.limit(), 0xffff_ffff);
assert!(d.present());
assert!(d.is_app());
assert_eq!(d.dpl(), 0);
}
#[test]
fn granularity_fills_the_low_twelve_bits_with_ones() {
let d = RawDesc {
low: 0x0000_0000,
high: 0x0080_9200,
upper: 0,
};
assert_eq!(d.limit(), 0xfff);
}
#[test]
fn an_expand_down_segment_covers_the_top_of_its_range() {
let s = SegReg {
selector: 0x10,
base: 0,
limit: 0x0fff,
ar: ar::PRESENT | ar::S | ar::RW | ar::DC,
};
assert!(s.expand_down());
assert!(!s.in_bounds(0x0fff, 1));
assert!(s.in_bounds(0x1000, 1));
assert!(s.in_bounds(0xfffe, 2));
assert!(!s.in_bounds(0xffff, 2));
}
#[test]
fn an_ordinary_segment_rejects_an_access_that_straddles_its_limit() {
let s = SegReg::real_data(0x1000);
assert_eq!(s.base, 0x1_0000);
assert!(s.in_bounds(0xfffe, 2));
assert!(!s.in_bounds(0xffff, 2));
assert!(s.in_bounds(0xffff, 1));
assert!(!s.in_bounds(0xffff_ffff, 4));
}
#[test]
fn a_code_segment_is_never_writable_however_its_flags_read() {
let code = SegReg {
selector: 8,
base: 0,
limit: 0xffff,
ar: ar::PRESENT | ar::S | ar::CODE | ar::RW,
};
assert!(code.readable());
assert!(!code.writable());
let data = SegReg::real_data(0);
assert!(data.readable());
assert!(data.writable());
}
#[test]
fn the_reset_code_segment_addresses_the_top_of_the_space() {
let sys = Sys::reset();
let cs = sys.seg(seg::CS);
assert_eq!(cs.selector, 0xf000);
assert_eq!(cs.base, 0xffff_0000);
assert_eq!(cs.base.wrapping_add(0xfff0), 0xffff_fff0);
assert!(!sys.protected());
assert!(!sys.paging());
}
#[test]
fn a_gate_splits_its_offset_around_the_access_byte() {
let g = RawDesc {
low: 0x0008_1234,
high: 0xabcd_8e00,
upper: 0,
};
assert_eq!(g.gate_selector(), 8);
assert_eq!(g.gate_offset(), 0xabcd_1234);
assert_eq!(g.kind(), sys_type::INT_GATE32);
assert!(g.present());
assert!(!g.is_app());
}
}