pub mod disasm;
mod exec;
mod fpexec;
pub mod fpu;
pub mod isa;
pub mod paging;
pub mod prot;
#[cfg(test)]
mod tests;
#[cfg(all(test, feature = "std"))]
mod firmware;
#[cfg(all(test, feature = "std"))]
mod conformance;
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::sync::{Arc, Weak};
use alloc::vec::Vec;
use core::fmt;
use crate::core::device::{
DebugTranslation, Device, DeviceClass, Initiator, PropertySpec, RealizeCtx, ResetKind, SinkPin,
};
use crate::core::error::{Error, Result};
use crate::core::props::{Props, ValueKind};
use crate::core::registry::Registry;
use crate::core::sched::{Budget, Consumed};
use crate::core::space::{AddressSpace, MemAttrs, RequesterId};
use crate::core::state::{ChunkReader, ChunkWriter, Sink, Source};
use crate::core::sync::{self, AtomicBool, AtomicU32, LockRank, Ordering};
use crate::core::value::Width;
use crate::core::wire::{
FanIn, IntAck, IntAckCycle, IntAckHandlers, IntAckResponse, Level, LocalController, Resolve,
WireId, WireSink,
};
use exec::{Exec, State};
pub mod flags {
pub const CF: u32 = 0x0001;
pub const PF: u32 = 0x0004;
pub const AF: u32 = 0x0010;
pub const ZF: u32 = 0x0040;
pub const SF: u32 = 0x0080;
pub const TF: u32 = 0x0100;
pub const IF: u32 = 0x0200;
pub const DF: u32 = 0x0400;
pub const OF: u32 = 0x0800;
pub const IOPL: u32 = 0x3000;
pub const IOPL_SHIFT: u32 = 12;
pub const NT: u32 = 0x4000;
pub const RF: u32 = 0x0001_0000;
pub const VM: u32 = 0x0002_0000;
pub const AC: u32 = 0x0004_0000;
pub const DEFINED: u32 = CF | PF | AF | ZF | SF | TF | IF | DF | OF;
pub const RESERVED_SET: u32 = 0xf002;
pub const DEFINED_386: u32 = DEFINED | IOPL | NT | RF | VM;
pub const DEFINED_486: u32 = DEFINED_386 | AC;
pub const ALWAYS_SET: u32 = 0x0002;
pub const POPF_FORBIDDEN: u32 = VM | RF;
pub const LOW_BYTE: u32 = CF | PF | AF | ZF | SF;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Variant {
I8086,
I8088,
I80386,
I80486,
X86_64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Features {
pub extras_486: bool,
pub cr4: bool,
pub msr: bool,
pub pae: bool,
pub long: bool,
pub nx: bool,
pub syscall: bool,
pub cmov: bool,
pub pse: bool,
pub pge: bool,
pub fpu: bool,
pub cx8: bool,
pub fxsr: bool,
pub sse: bool,
pub sse2: bool,
}
impl Features {
pub const NONE: Features = Features {
extras_486: false,
cr4: false,
msr: false,
pae: false,
long: false,
nx: false,
syscall: false,
cmov: false,
pse: false,
pge: false,
fpu: false,
cx8: false,
fxsr: false,
sse: false,
sse2: false,
};
pub const I80486: Features = Features {
extras_486: true,
fpu: true,
..Features::NONE
};
pub const I80486SX: Features = Features {
fpu: false,
..Features::I80486
};
pub const X86_64: Features = Features {
extras_486: true,
cr4: true,
msr: true,
pae: true,
long: true,
nx: true,
syscall: true,
cmov: true,
pse: true,
pge: true,
fpu: true,
cx8: true,
fxsr: true,
sse: true,
sse2: true,
};
pub fn validate(self) -> Result<()> {
let missing = |need: &str, want: &str| {
Err(Error::Property(alloc::format!(
"an x86 with `{want}` must also have `{need}`"
)))
};
if self.long && !self.pae {
return missing("pae", "long");
}
if self.pae && !self.cr4 {
return missing("cr4", "pae");
}
if (self.pse || self.pge) && !self.cr4 {
return missing("cr4", "pse or pge");
}
if self.nx && !self.long {
return missing("long", "nx");
}
if (self.msr || self.cr4) && !self.extras_486 {
return missing("extras_486", "cr4 or msr");
}
if self.sse2 && !self.sse {
return missing("sse", "sse2");
}
if self.sse && !self.fxsr {
return missing("fxsr", "sse");
}
if self.sse && !self.fpu {
return missing("fpu", "sse");
}
if self.fxsr && !self.cr4 {
return missing("cr4", "fxsr");
}
if self.long && !self.sse2 {
return missing("sse2", "long");
}
Ok(())
}
}
impl Variant {
#[must_use]
pub const fn queue_bytes(self) -> u8 {
match self {
Variant::I8086 => 6,
Variant::I8088 => 4,
Variant::I80386 | Variant::I80486 | Variant::X86_64 => 16,
}
}
#[must_use]
pub const fn bus_bytes(self) -> u8 {
match self {
Variant::I8086 => 2,
Variant::I8088 => 1,
Variant::I80386 | Variant::I80486 => 4,
Variant::X86_64 => 8,
}
}
#[must_use]
pub const fn bus_clocks(self) -> u32 {
match self {
Variant::I8086 | Variant::I8088 => 4,
Variant::I80386 | Variant::I80486 | Variant::X86_64 => 2,
}
}
#[must_use]
pub const fn map(self) -> isa::Gen {
match self {
Variant::I8086 | Variant::I8088 => isa::Gen::I8086,
Variant::I80386 | Variant::I80486 | Variant::X86_64 => isa::Gen::I386,
}
}
#[must_use]
pub const fn is_32bit(self) -> bool {
matches!(self, Variant::I80386 | Variant::I80486 | Variant::X86_64)
}
#[must_use]
pub const fn features(self) -> Features {
match self {
Variant::I8086 | Variant::I8088 | Variant::I80386 => Features::NONE,
Variant::I80486 => Features::I80486,
Variant::X86_64 => Features::X86_64,
}
}
#[must_use]
pub const fn flag_mask(self) -> u32 {
match self {
Variant::I8086 | Variant::I8088 => flags::DEFINED,
Variant::I80386 => flags::DEFINED_386,
Variant::I80486 | Variant::X86_64 => flags::DEFINED_486,
}
}
#[must_use]
pub const fn flag_fixed(self) -> u32 {
match self {
Variant::I8086 | Variant::I8088 => flags::RESERVED_SET,
Variant::I80386 | Variant::I80486 | Variant::X86_64 => flags::ALWAYS_SET,
}
}
#[must_use]
pub const fn reset_signature(self) -> u32 {
match self {
Variant::I8086 | Variant::I8088 => 0,
Variant::I80386 => 0x0000_0308,
Variant::I80486 => 0x0000_0480,
Variant::X86_64 => 0x0000_06f1,
}
}
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Variant::I8086 => "8086",
Variant::I8088 => "8088",
Variant::I80386 => "80386",
Variant::I80486 => "80486",
Variant::X86_64 => "x86-64",
}
}
pub const NAMES: &'static [&'static str] = &["8086", "8088", "80386", "80486", "x86-64"];
#[must_use]
pub fn from_name(name: &str) -> Option<Variant> {
match name {
"8086" => Some(Variant::I8086),
"8088" => Some(Variant::I8088),
"80386" | "386" | "i386" => Some(Variant::I80386),
"80486" | "486" | "i486" => Some(Variant::I80486),
"x86-64" | "x86_64" | "amd64" | "x64" => Some(Variant::X86_64),
_ => None,
}
}
}
impl fmt::Display for Variant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Config {
pub variant: Variant,
pub features: Features,
pub requester: RequesterId,
}
impl Config {
pub const I8088: Config = Config {
variant: Variant::I8088,
features: Features::NONE,
requester: RequesterId::ANONYMOUS,
};
pub const I8086: Config = Config {
variant: Variant::I8086,
..Config::I8088
};
pub const I80386: Config = Config {
variant: Variant::I80386,
..Config::I8088
};
pub const I80486: Config = Config {
variant: Variant::I80486,
features: Features::I80486,
..Config::I8088
};
pub const X86_64: Config = Config {
variant: Variant::X86_64,
features: Features::X86_64,
..Config::I8088
};
#[must_use]
pub const fn with_requester(mut self, id: RequesterId) -> Self {
self.requester = id;
self
}
#[must_use]
pub const fn with_variant(mut self, variant: Variant) -> Self {
self.variant = variant;
self.features = variant.features();
self
}
#[must_use]
pub const fn with_features(mut self, features: Features) -> Self {
self.features = features;
self
}
}
impl Default for Config {
fn default() -> Self {
Config::I8088
}
}
#[inline]
#[must_use]
pub const fn linear(segment: u16, offset: u16) -> u64 {
((((segment as u32) << 4).wrapping_add(offset as u32)) & 0xf_ffff) as u64
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Regs {
pub rax: u64,
pub rcx: u64,
pub rdx: u64,
pub rbx: u64,
pub rsp: u64,
pub rbp: u64,
pub rsi: u64,
pub rdi: u64,
pub r: [u64; 8],
pub rip: u64,
pub eflags: u32,
pub es: u16,
pub cs: u16,
pub ss: u16,
pub ds: u16,
pub fs: u16,
pub gs: u16,
}
const WORD_ORDER: [Reg; 8] = [
Reg::Ax,
Reg::Cx,
Reg::Dx,
Reg::Bx,
Reg::Sp,
Reg::Bp,
Reg::Si,
Reg::Di,
];
const DWORD_ORDER: [Reg; 8] = [
Reg::Eax,
Reg::Ecx,
Reg::Edx,
Reg::Ebx,
Reg::Esp,
Reg::Ebp,
Reg::Esi,
Reg::Edi,
];
impl Regs {
#[must_use]
pub const fn new() -> Regs {
Regs {
rax: 0,
rcx: 0,
rdx: 0,
rbx: 0,
rsp: 0,
rbp: 0,
rsi: 0,
rdi: 0,
r: [0; 8],
rip: 0,
eflags: flags::RESERVED_SET,
es: 0,
cs: 0xffff,
ss: 0,
ds: 0,
fs: 0,
gs: 0,
}
}
#[inline]
#[must_use]
pub const fn normalise_flags(variant: Variant, value: u32) -> u32 {
(value & variant.flag_mask()) | variant.flag_fixed()
}
#[inline]
#[must_use]
pub const fn flag(&self, mask: u32) -> bool {
self.eflags & mask != 0
}
#[inline]
#[must_use]
pub const fn iopl(&self) -> u8 {
((self.eflags & flags::IOPL) >> flags::IOPL_SHIFT) as u8
}
#[inline]
#[must_use]
pub const fn qword(&self, index: u8) -> u64 {
match index & 15 {
0 => self.rax,
1 => self.rcx,
2 => self.rdx,
3 => self.rbx,
4 => self.rsp,
5 => self.rbp,
6 => self.rsi,
7 => self.rdi,
n => self.r[(n - 8) as usize],
}
}
#[inline]
pub const fn set_qword(&mut self, index: u8, value: u64) {
match index & 15 {
0 => self.rax = value,
1 => self.rcx = value,
2 => self.rdx = value,
3 => self.rbx = value,
4 => self.rsp = value,
5 => self.rbp = value,
6 => self.rsi = value,
7 => self.rdi = value,
n => self.r[(n - 8) as usize] = value,
}
}
#[inline]
#[must_use]
pub const fn dword(&self, index: u8) -> u32 {
self.qword(index) as u32
}
#[inline]
pub const fn set_dword(&mut self, index: u8, value: u32) {
self.set_qword(index, value as u64);
}
#[inline]
#[must_use]
pub const fn word(&self, index: u8) -> u16 {
self.qword(index) as u16
}
#[inline]
pub const fn set_word(&mut self, index: u8, value: u16) {
let merged = (self.qword(index) & !0xffff) | value as u64;
self.set_qword(index, merged);
}
#[inline]
#[must_use]
pub const fn byte(&self, index: u8) -> u8 {
let word = self.word(index & 3);
if index & 4 == 0 {
word as u8
} else {
(word >> 8) as u8
}
}
#[inline]
pub const fn set_byte(&mut self, index: u8, value: u8) {
let word = self.word(index & 3);
let merged = if index & 4 == 0 {
(word & 0xff00) | value as u16
} else {
(word & 0x00ff) | ((value as u16) << 8)
};
self.set_word(index & 3, merged);
}
#[inline]
#[must_use]
pub const fn byte_rex(&self, index: u8) -> u8 {
self.qword(index) as u8
}
#[inline]
pub const fn set_byte_rex(&mut self, index: u8, value: u8) {
let merged = (self.qword(index) & !0xff) | value as u64;
self.set_qword(index, merged);
}
#[inline]
#[must_use]
pub const fn read(&self, index: u8, size: u8, rex: bool) -> u64 {
match size {
1 if rex => self.byte_rex(index) as u64,
1 => self.byte(index) as u64,
2 => self.word(index) as u64,
4 => self.dword(index) as u64,
_ => self.qword(index),
}
}
#[inline]
pub const fn write(&mut self, index: u8, size: u8, rex: bool, value: u64) {
match size {
1 if rex => self.set_byte_rex(index, value as u8),
1 => self.set_byte(index, value as u8),
2 => self.set_word(index, value as u16),
4 => self.set_dword(index, value as u32),
_ => self.set_qword(index, value),
}
}
#[inline]
#[must_use]
pub const fn segment(&self, index: u8) -> u16 {
match index {
0 => self.es,
1 => self.cs,
2 => self.ss,
3 => self.ds,
4 => self.fs,
5 => self.gs,
_ => 0,
}
}
#[inline]
pub const fn set_segment(&mut self, index: u8, value: u16) {
match index {
0 => self.es = value,
1 => self.cs = value,
2 => self.ss = value,
3 => self.ds = value,
4 => self.fs = value,
5 => self.gs = value,
_ => {}
}
}
}
impl Default for Regs {
fn default() -> Self {
Regs::new()
}
}
impl fmt::Display for Regs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"RAX:{:016x} RBX:{:016x} RCX:{:016x} RDX:{:016x} RSP:{:016x} RBP:{:016x} \
RSI:{:016x} RDI:{:016x} ES:{:04x} CS:{:04x} SS:{:04x} DS:{:04x} FS:{:04x} \
GS:{:04x} RIP:{:016x} F:{:08x}",
self.rax,
self.rbx,
self.rcx,
self.rdx,
self.rsp,
self.rbp,
self.rsi,
self.rdi,
self.es,
self.cs,
self.ss,
self.ds,
self.fs,
self.gs,
self.rip,
self.eflags
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Reg {
Eax,
Ecx,
Edx,
Ebx,
Esp,
Ebp,
Esi,
Edi,
Eip,
Eflags,
Es,
Cs,
Ss,
Ds,
Fs,
Gs,
Ax,
Cx,
Dx,
Bx,
Sp,
Bp,
Si,
Di,
Ip,
Flags,
Rax,
Rcx,
Rdx,
Rbx,
Rsp,
Rbp,
Rsi,
Rdi,
R8,
R9,
R10,
R11,
R12,
R13,
R14,
R15,
Rip,
}
impl Reg {
pub const ALL: &'static [Reg] = &[
Reg::Eax,
Reg::Ecx,
Reg::Edx,
Reg::Ebx,
Reg::Esp,
Reg::Ebp,
Reg::Esi,
Reg::Edi,
Reg::Eip,
Reg::Eflags,
Reg::Cs,
Reg::Ss,
Reg::Ds,
Reg::Es,
Reg::Fs,
Reg::Gs,
];
pub const NARROW: &'static [Reg] = &[
Reg::Ax,
Reg::Cx,
Reg::Dx,
Reg::Bx,
Reg::Sp,
Reg::Bp,
Reg::Si,
Reg::Di,
Reg::Ip,
Reg::Flags,
];
pub const WIDE: &'static [Reg] = &[
Reg::Rax,
Reg::Rcx,
Reg::Rdx,
Reg::Rbx,
Reg::Rsp,
Reg::Rbp,
Reg::Rsi,
Reg::Rdi,
Reg::R8,
Reg::R9,
Reg::R10,
Reg::R11,
Reg::R12,
Reg::R13,
Reg::R14,
Reg::R15,
Reg::Rip,
];
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Reg::Eax => "eax",
Reg::Ecx => "ecx",
Reg::Edx => "edx",
Reg::Ebx => "ebx",
Reg::Esp => "esp",
Reg::Ebp => "ebp",
Reg::Esi => "esi",
Reg::Edi => "edi",
Reg::Eip => "eip",
Reg::Eflags => "eflags",
Reg::Es => "es",
Reg::Cs => "cs",
Reg::Ss => "ss",
Reg::Ds => "ds",
Reg::Fs => "fs",
Reg::Gs => "gs",
Reg::Ax => "ax",
Reg::Cx => "cx",
Reg::Dx => "dx",
Reg::Bx => "bx",
Reg::Sp => "sp",
Reg::Bp => "bp",
Reg::Si => "si",
Reg::Di => "di",
Reg::Ip => "ip",
Reg::Flags => "flags",
Reg::Rax => "rax",
Reg::Rcx => "rcx",
Reg::Rdx => "rdx",
Reg::Rbx => "rbx",
Reg::Rsp => "rsp",
Reg::Rbp => "rbp",
Reg::Rsi => "rsi",
Reg::Rdi => "rdi",
Reg::R8 => "r8",
Reg::R9 => "r9",
Reg::R10 => "r10",
Reg::R11 => "r11",
Reg::R12 => "r12",
Reg::R13 => "r13",
Reg::R14 => "r14",
Reg::R15 => "r15",
Reg::Rip => "rip",
}
}
#[must_use]
pub const fn width(self) -> Width {
match self {
Reg::Eax
| Reg::Ecx
| Reg::Edx
| Reg::Ebx
| Reg::Esp
| Reg::Ebp
| Reg::Esi
| Reg::Edi
| Reg::Eip
| Reg::Eflags => Width::U32,
Reg::Rax
| Reg::Rcx
| Reg::Rdx
| Reg::Rbx
| Reg::Rsp
| Reg::Rbp
| Reg::Rsi
| Reg::Rdi
| Reg::R8
| Reg::R9
| Reg::R10
| Reg::R11
| Reg::R12
| Reg::R13
| Reg::R14
| Reg::R15
| Reg::Rip => Width::U64,
_ => Width::U16,
}
}
#[must_use]
pub const fn get(self, regs: &Regs) -> u64 {
match self {
Reg::Eax => regs.rax as u32 as u64,
Reg::Ecx => regs.rcx as u32 as u64,
Reg::Edx => regs.rdx as u32 as u64,
Reg::Ebx => regs.rbx as u32 as u64,
Reg::Esp => regs.rsp as u32 as u64,
Reg::Ebp => regs.rbp as u32 as u64,
Reg::Esi => regs.rsi as u32 as u64,
Reg::Edi => regs.rdi as u32 as u64,
Reg::Eip => regs.rip as u32 as u64,
Reg::Eflags => regs.eflags as u64,
Reg::Es => regs.es as u64,
Reg::Cs => regs.cs as u64,
Reg::Ss => regs.ss as u64,
Reg::Ds => regs.ds as u64,
Reg::Fs => regs.fs as u64,
Reg::Gs => regs.gs as u64,
Reg::Ax => regs.rax & 0xffff,
Reg::Cx => regs.rcx & 0xffff,
Reg::Dx => regs.rdx & 0xffff,
Reg::Bx => regs.rbx & 0xffff,
Reg::Sp => regs.rsp & 0xffff,
Reg::Bp => regs.rbp & 0xffff,
Reg::Si => regs.rsi & 0xffff,
Reg::Di => regs.rdi & 0xffff,
Reg::Ip => regs.rip & 0xffff,
Reg::Flags => (regs.eflags & 0xffff) as u64,
Reg::Rax => regs.rax,
Reg::Rcx => regs.rcx,
Reg::Rdx => regs.rdx,
Reg::Rbx => regs.rbx,
Reg::Rsp => regs.rsp,
Reg::Rbp => regs.rbp,
Reg::Rsi => regs.rsi,
Reg::Rdi => regs.rdi,
Reg::R8 => regs.r[0],
Reg::R9 => regs.r[1],
Reg::R10 => regs.r[2],
Reg::R11 => regs.r[3],
Reg::R12 => regs.r[4],
Reg::R13 => regs.r[5],
Reg::R14 => regs.r[6],
Reg::R15 => regs.r[7],
Reg::Rip => regs.rip,
}
}
pub const fn set(self, regs: &mut Regs, value: u64) {
match self {
Reg::Eax => regs.rax = value as u32 as u64,
Reg::Ecx => regs.rcx = value as u32 as u64,
Reg::Edx => regs.rdx = value as u32 as u64,
Reg::Ebx => regs.rbx = value as u32 as u64,
Reg::Esp => regs.rsp = value as u32 as u64,
Reg::Ebp => regs.rbp = value as u32 as u64,
Reg::Esi => regs.rsi = value as u32 as u64,
Reg::Edi => regs.rdi = value as u32 as u64,
Reg::Eip => regs.rip = value as u32 as u64,
Reg::Eflags => regs.eflags = value as u32,
Reg::Es => regs.es = value as u16,
Reg::Cs => regs.cs = value as u16,
Reg::Ss => regs.ss = value as u16,
Reg::Ds => regs.ds = value as u16,
Reg::Fs => regs.fs = value as u16,
Reg::Gs => regs.gs = value as u16,
Reg::Ax => regs.rax = (regs.rax & !0xffff) | (value & 0xffff),
Reg::Cx => regs.rcx = (regs.rcx & !0xffff) | (value & 0xffff),
Reg::Dx => regs.rdx = (regs.rdx & !0xffff) | (value & 0xffff),
Reg::Bx => regs.rbx = (regs.rbx & !0xffff) | (value & 0xffff),
Reg::Sp => regs.rsp = (regs.rsp & !0xffff) | (value & 0xffff),
Reg::Bp => regs.rbp = (regs.rbp & !0xffff) | (value & 0xffff),
Reg::Si => regs.rsi = (regs.rsi & !0xffff) | (value & 0xffff),
Reg::Di => regs.rdi = (regs.rdi & !0xffff) | (value & 0xffff),
Reg::Ip => regs.rip = (regs.rip & !0xffff) | (value & 0xffff),
Reg::Flags => regs.eflags = (regs.eflags & 0xffff_0000) | (value as u32 & 0xffff),
Reg::Rax => regs.rax = value,
Reg::Rcx => regs.rcx = value,
Reg::Rdx => regs.rdx = value,
Reg::Rbx => regs.rbx = value,
Reg::Rsp => regs.rsp = value,
Reg::Rbp => regs.rbp = value,
Reg::Rsi => regs.rsi = value,
Reg::Rdi => regs.rdi = value,
Reg::R8 => regs.r[0] = value,
Reg::R9 => regs.r[1] = value,
Reg::R10 => regs.r[2] = value,
Reg::R11 => regs.r[3] = value,
Reg::R12 => regs.r[4] = value,
Reg::R13 => regs.r[5] = value,
Reg::R14 => regs.r[6] = value,
Reg::R15 => regs.r[7] = value,
Reg::Rip => regs.rip = value,
}
}
#[must_use]
pub fn from_name(name: &str) -> Option<Reg> {
Reg::ALL
.iter()
.chain(Reg::NARROW.iter())
.chain(Reg::WIDE.iter())
.copied()
.find(|r| r.name() == name)
}
#[must_use]
pub const fn from_word_index(index: u8) -> Reg {
WORD_ORDER[(index & 7) as usize]
}
#[must_use]
pub const fn from_dword_index(index: u8) -> Reg {
DWORD_ORDER[(index & 7) as usize]
}
}
impl fmt::Display for Reg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Interrupt {
Intr,
Nmi,
}
#[derive(Debug)]
pub(crate) struct Lines {
intr: AtomicBool,
intr_vector: AtomicU32,
nmi_level: AtomicBool,
nmi_latch: AtomicBool,
reset: AtomicBool,
a20_mask: AtomicU32,
a20_wired: AtomicBool,
acks: IntAckHandlers,
init_pin: AtomicBool,
init_peer: AtomicBool,
init_latch: AtomicBool,
startup: AtomicU32,
intc: sync::Mutex<Option<Weak<dyn LocalController>>>,
has_intc: AtomicBool,
}
const NO_STARTUP: u32 = u32::MAX;
impl Default for Lines {
fn default() -> Lines {
Lines {
intr: AtomicBool::new(false),
intr_vector: AtomicU32::new(0),
nmi_level: AtomicBool::new(false),
nmi_latch: AtomicBool::new(false),
reset: AtomicBool::new(false),
a20_mask: AtomicU32::new(u32::MAX),
a20_wired: AtomicBool::new(false),
acks: IntAckHandlers::new(),
init_pin: AtomicBool::new(false),
init_peer: AtomicBool::new(false),
init_latch: AtomicBool::new(false),
startup: AtomicU32::new(NO_STARTUP),
intc: sync::Mutex::new(None),
has_intc: AtomicBool::new(false),
}
}
}
impl Lines {
fn set_intr(&self, asserted: bool) {
self.intr.store(asserted, Ordering::Release);
}
fn intr_asserted(&self) -> bool {
self.intr.load(Ordering::Acquire)
}
fn set_intr_vector(&self, vector: u8) {
self.intr_vector.store(u32::from(vector), Ordering::Release);
}
pub(crate) fn intr_vector(&self) -> u8 {
self.intr_vector.load(Ordering::Acquire) as u8
}
fn set_nmi(&self, asserted: bool) {
let previous = self.nmi_level.swap(asserted, Ordering::AcqRel);
if asserted && !previous {
self.nmi_latch.store(true, Ordering::Release);
}
}
pub(crate) fn nmi_pending(&self) -> bool {
self.nmi_latch.load(Ordering::Acquire)
}
pub(crate) fn take_nmi_pending(&self) -> bool {
self.nmi_latch.swap(false, Ordering::AcqRel)
}
pub(crate) fn intr_pending(&self) -> bool {
self.intr_asserted()
}
fn clear_nmi_latch(&self) {
self.nmi_latch.store(false, Ordering::Release);
}
fn request_reset(&self) {
self.reset.store(true, Ordering::Release);
}
fn take_reset_request(&self) -> bool {
self.reset.swap(false, Ordering::AcqRel)
}
fn set_a20(&self, open: bool) {
let mask = if open { u32::MAX } else { !(1u32 << 20) };
self.a20_mask.store(mask, Ordering::Release);
}
pub(crate) fn a20_mask(&self) -> u32 {
self.a20_mask.load(Ordering::Relaxed)
}
fn wire_a20(&self) {
self.a20_wired.store(true, Ordering::Release);
self.set_a20(false);
}
fn attach_ack(&self, ack: Weak<dyn IntAck>) {
self.acks.attach(ack);
}
fn set_init_pin(&self, asserted: bool) {
let previous = self.init_pin.swap(asserted, Ordering::AcqRel);
if asserted && !previous {
self.init_latch.store(true, Ordering::Release);
}
}
fn set_init_peer(&self, asserted: bool) {
self.init_peer.store(asserted, Ordering::Release);
}
fn request_init(&self) {
self.init_latch.store(true, Ordering::Release);
}
pub(crate) fn take_init_request(&self) -> bool {
if !self.init_latch.load(Ordering::Relaxed) {
return false;
}
self.init_latch.swap(false, Ordering::AcqRel)
}
pub(crate) fn init_held(&self) -> bool {
self.init_pin.load(Ordering::Acquire) || self.init_peer.load(Ordering::Acquire)
}
fn request_startup(&self, page: u8) {
self.startup.store(u32::from(page), Ordering::Release);
}
pub(crate) fn take_startup(&self) -> Option<u8> {
if self.startup.load(Ordering::Relaxed) == NO_STARTUP {
return None;
}
match self.startup.swap(NO_STARTUP, Ordering::AcqRel) {
NO_STARTUP => None,
page => Some(page as u8),
}
}
fn startup_pending(&self) -> Option<u8> {
match self.startup.load(Ordering::Acquire) {
NO_STARTUP => None,
page => Some(page as u8),
}
}
fn init_latched(&self) -> bool {
self.init_latch.load(Ordering::Acquire)
}
fn init_levels(&self) -> (bool, bool) {
(
self.init_pin.load(Ordering::Acquire),
self.init_peer.load(Ordering::Acquire),
)
}
fn restore_startup(&self, pin: bool, peer: bool, latch: bool, page: Option<u8>) {
self.init_pin.store(pin, Ordering::Release);
self.init_peer.store(peer, Ordering::Release);
self.init_latch.store(latch, Ordering::Release);
self.startup
.store(page.map_or(NO_STARTUP, u32::from), Ordering::Release);
}
pub(crate) fn has_local_controller(&self) -> bool {
self.has_intc.load(Ordering::Relaxed)
}
fn attach_intc(&self, peer: Weak<dyn LocalController>) {
*self.intc.lock() = Some(peer);
self.has_intc.store(true, Ordering::Release);
}
fn intc(&self) -> Option<Arc<dyn LocalController>> {
if !self.has_intc.load(Ordering::Relaxed) {
return None;
}
let peer = self.intc.lock().clone();
peer.and_then(|weak| weak.upgrade())
}
fn poll_intc(&self) {
let Some(intc) = self.intc() else { return };
let signal = intc.take_startup();
if signal.init {
self.request_init();
}
self.set_init_peer(signal.held);
if let Some(page) = signal.page {
self.request_startup(page);
}
}
pub(crate) fn base_register(&self) -> Option<u64> {
self.intc().map(|intc| intc.base_register())
}
pub(crate) fn set_base_register(&self, value: u64) -> bool {
match self.intc() {
Some(intc) => {
intc.set_base_register(value);
true
}
None => false,
}
}
pub(crate) fn acknowledge(&self) -> u8 {
match self.acks.run(IntAckCycle::vector_only()) {
IntAckResponse::Vector(vector) => vector as u8,
IntAckResponse::Autovector | IntAckResponse::Declined => self.intr_vector(),
}
}
fn snapshot(&self) -> (bool, bool, bool, u8) {
(
self.intr_asserted(),
self.nmi_level.load(Ordering::Acquire),
self.nmi_pending(),
self.intr_vector(),
)
}
fn restore(&self, (intr, level, latch, vector): (bool, bool, bool, u8)) {
self.intr.store(intr, Ordering::Release);
self.nmi_level.store(level, Ordering::Release);
self.nmi_latch.store(latch, Ordering::Release);
self.intr_vector.store(u32::from(vector), Ordering::Release);
}
}
#[derive(Debug)]
struct Session {
state: State,
memory: Option<Arc<AddressSpace>>,
io: Option<Arc<AddressSpace>>,
}
#[derive(Debug)]
pub struct X86 {
cfg: Config,
class: &'static DeviceClass,
lines: Arc<Lines>,
requester: AtomicU32,
iospace: String,
session: sync::Mutex<Session>,
pins: sync::Mutex<Vec<(String, Arc<InputPin>)>>,
}
impl X86 {
#[must_use]
pub fn new(cfg: Config) -> X86 {
X86 {
cfg,
class: &CLASS,
lines: Arc::new(Lines::default()),
requester: AtomicU32::new(cfg.requester.0),
iospace: String::new(),
session: sync::Mutex::with_rank(
LockRank::BUS,
Session {
state: State::new(cfg.variant),
memory: None,
io: None,
},
),
pins: sync::Mutex::new(Vec::new()),
}
}
pub fn from_props(props: &Props) -> Result<X86> {
X86::from_props_defaulting(props, Variant::I8088)
}
pub fn from_props_defaulting(props: &Props, default: Variant) -> Result<X86> {
let mut r = props.reader();
let named = r.optional_str("model")?;
let variant = r.or_enum("variant", default.name(), Variant::NAMES)?;
let variant = match named {
Some(name) => Variant::from_name(name)
.ok_or_else(|| Error::Property(alloc::format!("unknown x86 model `{name}`")))?,
None => Variant::from_name(variant).expect("the enum listed above"),
};
let _engine = r.or_enum("engine", "interp", &["interp"])?;
let iospace = r.optional_str("iospace")?.unwrap_or("").to_string();
let mut features = variant.features();
for name in [
"cpuid", "cr4", "msr", "pae", "long", "nx", "syscall", "cmov", "pse", "pge", "fpu",
"cx8", "fxsr", "sse", "sse2",
] {
let Some(on) = r.optional::<bool>(name)? else {
continue;
};
match name {
"cpuid" => features.extras_486 = on,
"cr4" => features.cr4 = on,
"msr" => features.msr = on,
"pae" => features.pae = on,
"long" => features.long = on,
"nx" => features.nx = on,
"syscall" => features.syscall = on,
"cmov" => features.cmov = on,
"pse" => features.pse = on,
"pge" => features.pge = on,
"fpu" => features.fpu = on,
"cx8" => features.cx8 = on,
"fxsr" => features.fxsr = on,
"sse" => features.sse = on,
_ => features.sse2 = on,
}
}
features.validate()?;
r.finish()?;
let mut cpu = X86::new(Config {
variant,
features,
requester: RequesterId::ANONYMOUS,
});
cpu.iospace = iospace;
Ok(cpu)
}
#[must_use]
pub fn as_i8086(mut self) -> X86 {
self.class = &I8086_CLASS;
self
}
#[must_use]
pub fn io_space_name(&self) -> &str {
&self.iospace
}
#[must_use]
pub fn config(&self) -> Config {
Config {
requester: RequesterId(self.requester.load(Ordering::Relaxed)),
..self.cfg
}
}
pub fn set_requester(&self, id: RequesterId) {
self.requester.store(id.0, Ordering::Relaxed);
}
#[must_use]
pub fn a20_open(&self) -> bool {
self.lines.a20_mask() == u32::MAX
}
pub fn set_a20(&self, open: bool) {
self.lines.set_a20(open);
}
pub fn attach_space(&self, space: Arc<AddressSpace>) {
self.session.lock().memory = Some(space);
}
pub fn attach_io_space(&self, space: Arc<AddressSpace>) {
self.session.lock().io = Some(space);
}
#[must_use]
pub fn space(&self) -> Option<Arc<AddressSpace>> {
self.session.lock().memory.clone()
}
#[must_use]
pub fn io_space(&self) -> Option<Arc<AddressSpace>> {
self.session.lock().io.clone()
}
#[must_use]
pub fn regs(&self) -> Regs {
self.session.lock().state.regs
}
pub fn set_regs(&self, regs: Regs) {
let mut session = self.session.lock();
session.state.regs = regs;
session.state.regs.eflags = Regs::normalise_flags(self.cfg.variant, regs.eflags);
if !session.state.sys.protected() {
for index in 0..isa::seg::COUNT as u8 {
let selector = session.state.regs.segment(index);
let entry = session.state.sys.seg_mut(index);
entry.selector = selector;
entry.base = u64::from(selector) << 4;
}
}
session.state.queue.flush();
}
#[must_use]
pub fn sys(&self) -> prot::Sys {
self.session.lock().state.sys
}
pub fn set_sys(&self, sys: prot::Sys) {
let mut session = self.session.lock();
session.state.sys = sys;
session.state.tlb.flush();
session.state.queue.flush();
}
#[must_use]
pub fn x87(&self) -> fpu::X87 {
self.session.lock().state.x87
}
pub fn set_x87(&self, x87: fpu::X87) {
self.session.lock().state.x87 = x87;
}
#[must_use]
pub fn sse(&self) -> fpu::Sse {
self.session.lock().state.sse
}
pub fn set_sse(&self, sse: fpu::Sse) {
self.session.lock().state.sse = sse;
}
#[must_use]
pub fn reg(&self, reg: Reg) -> u64 {
reg.get(&self.session.lock().state.regs)
}
pub fn set_reg(&self, reg: Reg, value: u64) {
let mut session = self.session.lock();
reg.set(&mut session.state.regs, value);
if matches!(reg, Reg::Eflags | Reg::Flags) {
let value = session.state.regs.eflags;
session.state.regs.eflags = Regs::normalise_flags(self.cfg.variant, value);
}
if matches!(reg, Reg::Cs | Reg::Eip | Reg::Ip) {
if reg == Reg::Cs && !session.state.sys.protected() {
let selector = session.state.regs.cs;
let entry = session.state.sys.seg_mut(isa::seg::CS);
entry.selector = selector;
entry.base = u64::from(selector) << 4;
}
session.state.queue.flush();
}
}
#[must_use]
pub fn cycles(&self) -> u64 {
self.session.lock().state.cycles
}
#[must_use]
pub fn is_halted(&self) -> bool {
self.session.lock().state.halted
}
#[must_use]
pub fn reset_pending(&self) -> bool {
self.session.lock().state.reset_pending
}
#[must_use]
pub fn prefetch_queue(&self) -> Vec<u8> {
self.session.lock().state.queue.contents()
}
pub fn set_prefetch_queue(&self, bytes: &[u8]) -> Result<()> {
let mut session = self.session.lock();
session.state.queue.install(bytes).map_err(|()| {
Error::Property(alloc::format!(
"the {} prefetch queue holds {} bytes, not {}",
self.cfg.variant,
self.cfg.variant.queue_bytes(),
bytes.len()
))
})
}
#[must_use]
pub fn bus_faults(&self) -> (u64, u64) {
let s = self.session.lock();
(s.state.faults, s.state.last_fault)
}
pub fn set_intr(&self, asserted: bool) {
self.lines.set_intr(asserted);
}
#[must_use]
pub fn intr_asserted(&self) -> bool {
self.lines.intr_asserted()
}
pub fn set_intr_vector(&self, vector: u8) {
self.lines.set_intr_vector(vector);
}
#[must_use]
pub fn intr_vector(&self) -> u8 {
self.lines.intr_vector()
}
pub fn set_nmi(&self, asserted: bool) {
self.lines.set_nmi(asserted);
}
pub fn pulse_nmi(&self) {
self.lines.set_nmi(true);
self.lines.set_nmi(false);
}
#[must_use]
pub fn nmi_pending(&self) -> bool {
self.lines.nmi_pending()
}
#[must_use]
pub fn interrupt_shadow(&self) -> bool {
self.session.lock().state.int_shadow
}
pub fn request_reset(&self) {
self.session.lock().state.reset_pending = true;
}
#[must_use]
pub fn reset_requested(&self) -> bool {
self.lines.reset.load(Ordering::Acquire)
}
pub fn request_init(&self) {
self.lines.request_init();
}
pub fn start_up(&self, page: u8) {
self.lines.request_startup(page);
}
#[must_use]
pub fn is_waiting_for_startup(&self) -> bool {
self.session.lock().state.wait_for_sipi
}
#[must_use]
pub fn init_requested(&self) -> bool {
self.lines.init_latched()
}
#[must_use]
pub fn init_held(&self) -> bool {
self.lines.init_held()
}
pub fn acknowledge(&self) -> u8 {
self.lines.acknowledge()
}
pub fn step(&self) -> u64 {
self.lines.poll_intc();
let reset = self.lines.take_reset_request();
let cfg = self.config();
let mut session = self.session.lock();
let Session { state, memory, io } = &mut *session;
state.reset_pending |= reset;
let Some(memory) = memory.clone() else {
return 0;
};
let io = io.clone();
Exec::new(state, &memory, io.as_deref(), &cfg, &self.lines).step()
}
pub fn run(&self, budget: u64) -> u64 {
let mut used = 0;
while used < budget {
let n = self.step();
if n == 0 {
break;
}
used += n;
}
used
}
pub fn run_budget(&self, ticks: u64) -> u64 {
let owed = self.session.lock().state.debt;
if owed >= ticks {
self.session.lock().state.debt = owed - ticks;
return ticks;
}
let allowance = ticks - owed;
let mut used = 0u64;
while used < allowance {
let n = self.step();
if n == 0 {
self.session.lock().state.debt = 0;
return ticks;
}
used += n;
}
self.session.lock().state.debt = used - allowance;
ticks
}
#[must_use]
pub fn cycle_debt(&self) -> u64 {
self.session.lock().state.debt
}
#[must_use]
pub fn translate_debug(&self, linear: u64) -> DebugTranslation {
let Some(space) = self.space() else {
return DebugTranslation::Identity;
};
let sys = self.session.lock().state.sys;
paging::debug_translate(&sys, self.cfg.features, &space, linear)
}
#[must_use]
pub fn disassemble(&self, cs: u16, eip: u64, count: usize) -> Vec<disasm::Disassembled> {
let Some(space) = self.space() else {
return Vec::new();
};
let (base, bits, legacy, sys) = {
let session = self.session.lock();
let seg = session.state.sys.seg(isa::seg::CS);
let legacy = !self.cfg.variant.is_32bit();
let base = if seg.selector == cs {
seg.base
} else {
u64::from(cs) << 4
};
let bits = if legacy {
isa::Bits::B16
} else if self.cfg.features.long && session.state.sys.long_mode() && seg.long() {
isa::Bits::B64
} else if seg.big() {
isa::Bits::B32
} else {
isa::Bits::B16
};
(base, bits, legacy, session.state.sys)
};
let map = self.cfg.variant.map();
let features = self.cfg.features;
disasm::disassemble_run_as(map, bits, cs, eip, count, |addr| {
let linear = if legacy {
base.wrapping_add(addr) & 0xf_ffff
} else {
base.wrapping_add(addr)
};
let addr = paging::debug_translate(&sys, features, &space, linear).phys(linear)?;
space
.read(addr, Width::U8, MemAttrs::DEBUG)
.ok()
.map(|v| v as u8)
})
}
}
pub static CLASS: DeviceClass = DeviceClass {
name: "cpu.x86",
version: 6,
summary: "Intel x86 CPU core: 8086/8088 real mode, 80386/80486 protected mode, or x86-64",
properties: &[
PropertySpec {
name: "variant",
kind: ValueKind::Str,
required: false,
summary: "\"8086\", \"8088\", \"80386\", \"80486\" (the default) or \"x86-64\"",
},
PropertySpec {
name: "cpuid",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: CPUID, BSWAP, XADD, CMPXCHG, INVLPG and CR0.WP",
},
PropertySpec {
name: "cr4",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: CR4 exists, which every extension below needs a bit in",
},
PropertySpec {
name: "msr",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: RDMSR, WRMSR, RDTSC and the model-specific registers",
},
PropertySpec {
name: "pae",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: physical address extension and the three-level walk",
},
PropertySpec {
name: "long",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: long mode, REX, and four-level paging",
},
PropertySpec {
name: "nx",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: EFER.NXE and the no-execute page bit",
},
PropertySpec {
name: "syscall",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: SYSCALL and SYSRET",
},
PropertySpec {
name: "cmov",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: the integer conditional moves",
},
PropertySpec {
name: "pse",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: CR4.PSE and 4 MiB pages in a two-level walk",
},
PropertySpec {
name: "pge",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: CR4.PGE and the global-page bit",
},
PropertySpec {
name: "fpu",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: the on-die x87 unit (a 486SX is a 486 with this off)",
},
PropertySpec {
name: "cx8",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: CMPXCHG8B, and CMPXCHG16B in long mode",
},
PropertySpec {
name: "fxsr",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: FXSAVE/FXRSTOR and CR4.OSFXSR",
},
PropertySpec {
name: "sse",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: the XMM registers, MXCSR and the single-precision SIMD",
},
PropertySpec {
name: "sse2",
kind: ValueKind::Bool,
required: false,
summary: "override the preset: the double-precision and packed-integer SIMD",
},
PropertySpec {
name: "model",
kind: ValueKind::Str,
required: false,
summary: "accepted as a synonym for \"variant\", which this class used to be called",
},
PropertySpec {
name: "engine",
kind: ValueKind::Str,
required: false,
summary: "which execution engine; only `interp` exists until phase 5",
},
PropertySpec {
name: "iospace",
kind: ValueKind::Str,
required: false,
summary: "the name of the separate address space IN and OUT reach",
},
],
construct: |props| {
Ok(Box::new(X86::from_props_defaulting(
props,
Variant::I80486,
)?))
},
};
pub static I8086_CLASS: DeviceClass = DeviceClass {
name: "cpu.i8086",
version: 6,
summary: "Intel 8086 / 8088 16-bit CPU core, real mode, hardware-checked interpreter",
properties: CLASS.properties,
construct: |props| {
Ok(Box::new(
X86::from_props_defaulting(props, Variant::I8088)?.as_i8086(),
))
},
};
pub fn register(reg: &mut Registry) -> Result<()> {
reg.add(&CLASS)?;
reg.add(&I8086_CLASS)
}
impl Device for X86 {
fn class(&self) -> &'static DeviceClass {
self.class
}
fn debug_translate(&self, va: u64) -> DebugTranslation {
self.translate_debug(va)
}
fn realize(&self, _ctx: &mut RealizeCtx<'_>) -> Result<()> {
Ok(())
}
fn sink(&self, port: &str, sources: &[WireId]) -> Option<SinkPin> {
let which = match port {
"intr" => Input::Intr,
"nmi" => Input::Nmi,
"reset" => Input::Reset,
"init" if self.cfg.variant.is_32bit() => Input::Init,
"a20" => Input::A20,
_ => return None,
};
if which == Input::A20 {
self.lines.wire_a20();
}
let pin = Arc::new(InputPin::new(Arc::clone(&self.lines), which, sources));
self.pins.lock().push((port.to_string(), Arc::clone(&pin)));
Some(SinkPin { sink: pin, line: 0 })
}
fn attach_int_ack(&self, port: &str, ack: Weak<dyn IntAck>) {
if port == "intr" {
self.lines.attach_ack(ack);
}
}
fn attach_local_controller(&self, port: &str, peer: Weak<dyn LocalController>) {
if matches!(port, "intr" | "nmi") {
self.lines.attach_intc(peer);
}
}
fn is_runnable(&self) -> bool {
true
}
fn run(&self, budget: Budget) -> Consumed {
Consumed::new(self.run_budget(budget.ticks))
}
fn reset(&self, kind: ResetKind) {
let mut session = self.session.lock();
if kind == ResetKind::Cold {
session.state = State::new(self.cfg.variant);
} else {
session.state.reset_pending = true;
session.state.halted = false;
session.state.int_shadow = false;
session.state.queue.flush();
}
drop(session);
if kind == ResetKind::Cold {
self.lines.restore((false, false, false, 0));
} else {
self.lines.clear_nmi_latch();
}
self.lines.take_reset_request();
self.lines.take_init_request();
self.lines.take_startup();
}
fn save(&self, w: &mut ChunkWriter<'_>) -> Result<()> {
let reset = self.lines.take_reset_request();
let state = {
let mut session = self.session.lock();
session.state.reset_pending |= reset;
session.state
};
for reg in Reg::ALL {
w.write_u32(reg.get(&state.regs) as u32)?;
}
w.write_u64(state.cycles)?;
for index in 0..isa::seg::COUNT as u8 {
let s = state.sys.seg(index);
w.write_u16(s.selector)?;
w.write_u64(s.base)?;
w.write_u32(s.limit)?;
w.write_u32(s.ar)?;
}
for s in [state.sys.ldtr, state.sys.task] {
w.write_u16(s.selector)?;
w.write_u64(s.base)?;
w.write_u32(s.limit)?;
w.write_u32(s.ar)?;
}
for t in [state.sys.gdtr, state.sys.idtr] {
w.write_u64(t.base)?;
w.write_u32(t.limit)?;
}
w.write_u32(state.sys.cr0)?;
w.write_u64(state.sys.cr2)?;
w.write_u64(state.sys.cr3)?;
for value in state.sys.dr {
w.write_u64(value)?;
}
for value in state.sys.test {
w.write_u32(value)?;
}
w.write_bool(state.halted)?;
w.write_bool(state.shutdown)?;
w.write_bool(state.reset_pending)?;
w.write_bool(state.int_shadow)?;
w.write_u8(state.open_bus)?;
w.write_u64(state.faults)?;
w.write_u64(state.last_fault)?;
let queue = state.queue.contents();
w.write_u8(queue.len() as u8)?;
for byte in queue {
w.write_u8(byte)?;
}
w.write_u64(state.debt)?;
let (intr, nmi_level, nmi_latch, vector) = self.lines.snapshot();
w.write_bool(intr)?;
w.write_bool(nmi_level)?;
w.write_bool(nmi_latch)?;
w.write_u8(vector)?;
w.write_bool(self.a20_open())?;
for reg in Reg::WIDE {
w.write_u64(reg.get(&state.regs))?;
}
w.write_u64(state.sys.cr4)?;
w.write_u64(state.sys.efer)?;
w.write_u64(state.sys.fs_base)?;
w.write_u64(state.sys.gs_base)?;
w.write_u64(state.sys.kernel_gs_base)?;
w.write_u64(state.sys.star)?;
w.write_u64(state.sys.lstar)?;
w.write_u64(state.sys.cstar)?;
w.write_u64(state.sys.sfmask)?;
for reg in state.x87.regs {
w.write_u64(reg.sig)?;
w.write_u16(reg.sign_exp)?;
}
w.write_u16(state.x87.control)?;
w.write_u16(state.x87.status)?;
w.write_u16(state.x87.tag)?;
w.write_u16(state.x87.last_op)?;
w.write_u64(state.x87.last_ip)?;
w.write_u64(state.x87.last_dp)?;
w.write_u16(state.x87.last_cs)?;
w.write_u16(state.x87.last_ds)?;
for reg in state.sse.xmm {
w.write_u64(reg[0])?;
w.write_u64(reg[1])?;
}
w.write_u32(state.sse.mxcsr)?;
w.write_bool(state.wait_for_sipi)?;
let (init_pin, init_peer) = self.lines.init_levels();
w.write_bool(init_pin)?;
w.write_bool(init_peer)?;
w.write_bool(self.lines.init_latched())?;
let page = self.lines.startup_pending();
w.write_bool(page.is_some())?;
w.write_u8(page.unwrap_or(0))?;
Ok(())
}
fn load(&self, r: &mut ChunkReader<'_>) -> Result<()> {
let mut state = State::new(self.cfg.variant);
for reg in Reg::ALL {
let value = r.read_u32()?;
reg.set(&mut state.regs, u64::from(value));
}
state.cycles = r.read_u64()?;
for index in 0..isa::seg::COUNT as u8 {
let s = state.sys.seg_mut(index);
s.selector = r.read_u16()?;
s.base = r.read_u64()?;
s.limit = r.read_u32()?;
s.ar = r.read_u32()?;
}
for slot in [0usize, 1] {
let s = prot::SegReg {
selector: r.read_u16()?,
base: r.read_u64()?,
limit: r.read_u32()?,
ar: r.read_u32()?,
};
if slot == 0 {
state.sys.ldtr = s;
} else {
state.sys.task = s;
}
}
for slot in [0usize, 1] {
let t = prot::TableReg {
base: r.read_u64()?,
limit: r.read_u32()?,
};
if slot == 0 {
state.sys.gdtr = t;
} else {
state.sys.idtr = t;
}
}
state.sys.cr0 = r.read_u32()?;
state.sys.cr2 = r.read_u64()?;
state.sys.cr3 = r.read_u64()?;
for i in 0..8 {
state.sys.dr[i] = r.read_u64()?;
}
for i in 0..8 {
state.sys.test[i] = r.read_u32()?;
}
state.halted = r.read_bool()?;
state.shutdown = r.read_bool()?;
state.reset_pending = r.read_bool()?;
state.int_shadow = r.read_bool()?;
state.open_bus = r.read_u8()?;
state.faults = r.read_u64()?;
state.last_fault = r.read_u64()?;
let len = r.read_u8()?;
let mut queue = Vec::with_capacity(usize::from(len));
for _ in 0..len {
queue.push(r.read_u8()?);
}
state.queue.install(&queue).map_err(|()| {
Error::State(alloc::format!(
"snapshot has a {len}-byte prefetch queue; an {} holds {}",
self.cfg.variant,
self.cfg.variant.queue_bytes()
))
})?;
state.debt = r.read_u64()?;
let intr = r.read_bool()?;
let nmi_level = r.read_bool()?;
let nmi_latch = r.read_bool()?;
let vector = r.read_u8()?;
let a20 = r.read_bool()?;
for reg in Reg::WIDE {
let wide = r.read_u64()?;
let value = match reg {
Reg::R8
| Reg::R9
| Reg::R10
| Reg::R11
| Reg::R12
| Reg::R13
| Reg::R14
| Reg::R15 => wide,
_ => (wide & !0xffff_ffff) | (reg.get(&state.regs) & 0xffff_ffff),
};
reg.set(&mut state.regs, value);
}
state.sys.cr4 = r.read_u64()?;
state.sys.efer = r.read_u64()?;
state.sys.fs_base = r.read_u64()?;
state.sys.gs_base = r.read_u64()?;
state.sys.kernel_gs_base = r.read_u64()?;
state.sys.star = r.read_u64()?;
state.sys.lstar = r.read_u64()?;
state.sys.cstar = r.read_u64()?;
state.sys.sfmask = r.read_u64()?;
for i in 0..8 {
let sig = r.read_u64()?;
let sign_exp = r.read_u16()?;
state.x87.regs[i] = crate::float::x87::F80::new(sign_exp, sig);
}
state.x87.control = r.read_u16()?;
state.x87.status = r.read_u16()?;
state.x87.tag = r.read_u16()?;
state.x87.last_op = r.read_u16()?;
state.x87.last_ip = r.read_u64()?;
state.x87.last_dp = r.read_u64()?;
state.x87.last_cs = r.read_u16()?;
state.x87.last_ds = r.read_u16()?;
for i in 0..16 {
let lo = r.read_u64()?;
let hi = r.read_u64()?;
state.sse.xmm[i] = [lo, hi];
}
state.sse.mxcsr = r.read_u32()?;
state.wait_for_sipi = r.read_bool()?;
let init_pin = r.read_bool()?;
let init_peer = r.read_bool()?;
let init_latch = r.read_bool()?;
let has_startup = r.read_bool()?;
let startup_page = r.read_u8()?;
let startup = has_startup.then_some(startup_page);
state.tlb.flush();
self.session.lock().state = state;
self.lines.restore((intr, nmi_level, nmi_latch, vector));
self.lines.set_a20(a20);
self.lines
.restore_startup(init_pin, init_peer, init_latch, startup);
Ok(())
}
}
impl Initiator for X86 {
fn requester(&self) -> RequesterId {
RequesterId(self.requester.load(Ordering::Relaxed))
}
}
impl crate::machine::Instance for X86 {
fn bind(&self, ctx: &crate::machine::BindCtx<'_>) -> Result<()> {
let memory = ctx.space().ok_or_else(|| Error::Config {
at: ctx.path().to_string(),
message: String::from("an x86 needs an address space to fetch from (`space = mem`)"),
})?;
self.attach_space(Arc::clone(memory));
if !self.iospace.is_empty() {
let io = ctx
.space_named(&self.iospace)
.ok_or_else(|| Error::Config {
at: ctx.path().to_string(),
message: alloc::format!(
"`iospace = \"{}\"` names no address space in this machine",
self.iospace
),
})?;
self.attach_io_space(Arc::clone(io));
}
self.set_requester(ctx.requester());
Ok(())
}
}
pub fn bind(bindings: &mut crate::machine::Bindings) -> Result<()> {
bindings.bind(CLASS.name, |props| {
Ok(Arc::new(X86::from_props_defaulting(
props,
Variant::I80486,
)?))
})?;
bindings.bind(I8086_CLASS.name, |props| {
Ok(Arc::new(
X86::from_props_defaulting(props, Variant::I8088)?.as_i8086(),
))
})
}
#[must_use]
pub fn schemas() -> Vec<crate::machine::validate::ClassSchema> {
alloc::vec![schema_for(CLASS.name), schema_for(I8086_CLASS.name)]
}
fn schema_for(name: &'static str) -> crate::machine::validate::ClassSchema {
use crate::machine::validate::{ClassSchema, PortDir, PropSchema};
ClassSchema::new(name)
.prop(PropSchema::new("variant", ValueKind::Str).values(Variant::NAMES))
.prop(PropSchema::new("model", ValueKind::Str).values(Variant::NAMES))
.prop(PropSchema::new("engine", ValueKind::Str).values(&["interp"]))
.prop(PropSchema::new("iospace", ValueKind::Str))
.port("intr", PortDir::In)
.port("nmi", PortDir::In)
.port("reset", PortDir::In)
.port("init", PortDir::In)
.port("a20", PortDir::In)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum Input {
Intr,
Nmi,
Reset,
Init,
A20,
}
#[derive(Debug)]
pub struct InputPin {
lines: Arc<Lines>,
which: Input,
inputs: FanIn,
resolve: Resolve,
}
impl InputPin {
fn new(lines: Arc<Lines>, which: Input, sources: &[WireId]) -> InputPin {
InputPin {
lines,
which,
inputs: FanIn::new(sources),
resolve: Resolve::Or,
}
}
#[must_use]
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
}
impl WireSink for InputPin {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
self.inputs.set(src, level);
let asserted = self.inputs.resolve(self.resolve).is_high();
match self.which {
Input::Intr => self.lines.set_intr(asserted),
Input::Nmi => self.lines.set_nmi(asserted),
Input::Reset => {
if asserted {
self.lines.request_reset();
}
}
Input::Init => self.lines.set_init_pin(asserted),
Input::A20 => self.lines.set_a20(asserted),
}
}
}
#[derive(Debug)]
pub struct InterruptPin {
lines: Arc<Lines>,
which: Interrupt,
inputs: FanIn,
resolve: Resolve,
}
impl InterruptPin {
#[must_use]
pub fn new(cpu: Arc<X86>, which: Interrupt, sources: &[WireId]) -> InterruptPin {
InterruptPin {
lines: Arc::clone(&cpu.lines),
which,
inputs: FanIn::new(sources),
resolve: Resolve::Or,
}
}
#[must_use]
pub fn with_resolve(mut self, resolve: Resolve) -> Self {
self.resolve = resolve;
self
}
#[must_use]
pub fn which(&self) -> Interrupt {
self.which
}
#[must_use]
pub fn inputs(&self) -> &FanIn {
&self.inputs
}
}
impl WireSink for InterruptPin {
fn set_level(&self, src: WireId, _line: u32, level: Level) {
self.inputs.set(src, level);
let asserted = self.inputs.resolve(self.resolve).is_high();
match self.which {
Interrupt::Intr => self.lines.set_intr(asserted),
Interrupt::Nmi => self.lines.set_nmi(asserted),
}
}
}
#[must_use]
pub fn describe_isa() -> String {
describe_isa_for(Variant::I8088)
}
#[must_use]
pub fn describe_isa_for(variant: Variant) -> String {
use core::fmt::Write as _;
let map = variant.map();
let mut out = String::new();
let mark = |class: isa::Class| match class {
isa::Class::Documented => ' ',
isa::Class::Alias => '=',
isa::Class::Undocumented => '*',
isa::Class::Undefined => '?',
isa::Class::Prefix => ':',
isa::Class::Escape => '~',
};
let row = |out: &mut String, prefix: &str, opcode: u8, insn: isa::Insn| {
if insn.group == isa::Grp::None {
let _ = writeln!(
out,
"{prefix}{opcode:02x} {}{:<7} {}",
mark(insn.class),
insn.op.mnemonic(),
insn.op.summary()
);
} else {
for reg in 0..8u8 {
let sub = isa::resolve_as(map, insn, reg);
let _ = writeln!(
out,
"{prefix}{opcode:02x}/{reg} {}{:<7} {}",
mark(sub.class),
sub.op.mnemonic(),
sub.op.summary()
);
}
}
};
for opcode in 0..=255u8 {
row(&mut out, "", opcode, isa::decode_as(map, opcode));
}
if matches!(map, isa::Gen::I386) {
for opcode in 0..=255u8 {
if !isa::LISTED_0F[opcode as usize] {
continue;
}
row(&mut out, "0f ", opcode, isa::decode_0f(opcode));
}
}
if variant.features().long {
let _ = writeln!(out, "\n-- 64-bit mode differs --");
let long_row = |out: &mut String, prefix: &str, opcode: u8, insn: isa::Insn| {
if matches!(insn.long, isa::L64::Same) {
return;
}
let now = insn.in_long();
let _ = writeln!(
out,
"{prefix}{opcode:02x} {:<7} -> {}{:<7} {}",
insn.op.mnemonic(),
mark(now.class),
now.op.mnemonic(),
now.op.summary()
);
};
for opcode in 0..=255u8 {
long_row(&mut out, "", opcode, isa::decode_as(map, opcode));
}
for opcode in 0..=255u8 {
long_row(&mut out, "0f ", opcode, isa::decode_0f(opcode));
}
}
out
}