Skip to main content

native_v86_core/cpu/
cpu.rs

1#![allow(non_upper_case_globals)]
2
3use crate::config;
4use crate::cpu::fpu::fpu_set_tag_word;
5use crate::cpu::global_pointers::*;
6use crate::cpu::memory;
7use crate::cpu::misc_instr::{
8    adjust_stack_reg, get_stack_pointer, getaf, getcf, getof, getpf, getsf, getzf, pop16, pop32s,
9    push16, push32,
10};
11use crate::cpu::modrm::{resolve_modrm16, resolve_modrm32};
12use crate::cpu::{apic, ioapic, pic};
13use crate::dbg::dbg_trace;
14use crate::gen;
15use crate::jit;
16use crate::jit::is_near_end_of_page;
17use crate::opstats;
18use crate::page::Page;
19use crate::paging::OrPageFault;
20use crate::prefix;
21use crate::profiler;
22use crate::profiler::stat;
23use crate::softfloat;
24use crate::state_flags::CachedStateFlags;
25
26use std::collections::HashSet;
27use std::ptr;
28
29mod wasm {
30    extern "C" {
31        pub fn call_indirect1(f: i32, x: u16);
32    }
33}
34
35pub mod js {
36    #[link(wasm_import_module = "env")]
37    extern "C" {
38        pub fn cpu_exception_hook(interrupt: i32) -> bool;
39        pub fn microtick() -> f64;
40        pub fn run_hardware_timers(acpi_enabled: bool, t: f64) -> f64;
41        pub fn cpu_event_halt();
42        pub fn stop_idling();
43
44        pub fn io_port_read8(port: i32) -> i32;
45        pub fn io_port_read16(port: i32) -> i32;
46        pub fn io_port_read32(port: i32) -> i32;
47
48        pub fn io_port_write8(port: i32, value: i32);
49        pub fn io_port_write16(port: i32, value: i32);
50        pub fn io_port_write32(port: i32, value: i32);
51
52        pub fn get_rand_int() -> i32;
53    }
54}
55
56/// The offset for our generated functions in the wasm table. Every index less than this is
57/// reserved for rustc's indirect functions
58pub const WASM_TABLE_OFFSET: u32 = 1024;
59
60#[derive(Copy, Clone)]
61#[repr(C)]
62#[repr(align(16))]
63pub union reg128 {
64    pub i8: [i8; 16],
65    pub i16: [i16; 8],
66    pub i32: [i32; 4],
67    pub i64: [i64; 2],
68    pub u8: [u8; 16],
69    pub u16: [u16; 8],
70    pub u32: [u32; 4],
71    pub u64: [u64; 2],
72    pub f32: [f32; 4],
73    pub f64: [f64; 2],
74}
75
76pub const CHECK_MISSED_ENTRY_POINTS: bool = false;
77
78pub const INTERPRETER_ITERATION_LIMIT: u32 = 100_001;
79
80// How often, in milliseconds, to yield to the browser for rendering and running events
81pub const TIME_PER_FRAME: f64 = 1.0;
82
83pub const FLAG_SUB: i32 = -0x8000_0000;
84pub const FLAG_CARRY: i32 = 1;
85pub const FLAG_PARITY: i32 = 4;
86pub const FLAG_ADJUST: i32 = 16;
87pub const FLAG_ZERO: i32 = 64;
88pub const FLAG_SIGN: i32 = 128;
89pub const FLAG_TRAP: i32 = 256;
90pub const FLAG_INTERRUPT: i32 = 512;
91pub const FLAG_DIRECTION: i32 = 1024;
92pub const FLAG_OVERFLOW: i32 = 2048;
93pub const FLAG_IOPL: i32 = 1 << 12 | 1 << 13;
94pub const FLAG_NT: i32 = 1 << 14;
95pub const FLAG_RF: i32 = 1 << 16;
96pub const FLAG_VM: i32 = 1 << 17;
97pub const FLAG_AC: i32 = 1 << 18;
98pub const FLAG_VIF: i32 = 1 << 19;
99pub const FLAG_VIP: i32 = 1 << 20;
100pub const FLAG_ID: i32 = 1 << 21;
101pub const FLAGS_DEFAULT: i32 = 1 << 1;
102pub const FLAGS_MASK: i32 = FLAG_CARRY
103    | FLAG_PARITY
104    | FLAG_ADJUST
105    | FLAG_ZERO
106    | FLAG_SIGN
107    | FLAG_TRAP
108    | FLAG_INTERRUPT
109    | FLAG_DIRECTION
110    | FLAG_OVERFLOW
111    | FLAG_IOPL
112    | FLAG_NT
113    | FLAG_RF
114    | FLAG_VM
115    | FLAG_AC
116    | FLAG_VIF
117    | FLAG_VIP
118    | FLAG_ID;
119pub const FLAGS_ALL: i32 =
120    FLAG_CARRY | FLAG_PARITY | FLAG_ADJUST | FLAG_ZERO | FLAG_SIGN | FLAG_OVERFLOW;
121pub const OPSIZE_8: i32 = 7;
122pub const OPSIZE_16: i32 = 15;
123pub const OPSIZE_32: i32 = 31;
124
125pub const EAX: i32 = 0;
126pub const ECX: i32 = 1;
127pub const EDX: i32 = 2;
128pub const EBX: i32 = 3;
129pub const ESP: i32 = 4;
130pub const EBP: i32 = 5;
131pub const ESI: i32 = 6;
132pub const EDI: i32 = 7;
133
134pub const AX: i32 = 0;
135pub const CX: i32 = 1;
136pub const DX: i32 = 2;
137pub const BX: i32 = 3;
138pub const SP: i32 = 4;
139pub const BP: i32 = 5;
140pub const SI: i32 = 6;
141pub const DI: i32 = 7;
142
143pub const AL: i32 = 0;
144pub const CL: i32 = 1;
145pub const DL: i32 = 2;
146pub const BL: i32 = 3;
147pub const AH: i32 = 4;
148pub const CH: i32 = 5;
149pub const DH: i32 = 6;
150pub const BH: i32 = 7;
151
152pub const ES: i32 = 0;
153pub const CS: i32 = 1;
154pub const SS: i32 = 2;
155pub const DS: i32 = 3;
156pub const FS: i32 = 4;
157pub const GS: i32 = 5;
158pub const TR: i32 = 6;
159
160pub const LDTR: i32 = 7;
161pub const PAGE_TABLE_PRESENT_MASK: i32 = 1 << 0;
162pub const PAGE_TABLE_RW_MASK: i32 = 1 << 1;
163pub const PAGE_TABLE_USER_MASK: i32 = 1 << 2;
164pub const PAGE_TABLE_ACCESSED_MASK: i32 = 1 << 5;
165pub const PAGE_TABLE_DIRTY_MASK: i32 = 1 << 6;
166pub const PAGE_TABLE_PSE_MASK: i32 = 1 << 7;
167pub const PAGE_TABLE_GLOBAL_MASK: i32 = 1 << 8;
168pub const MMAP_BLOCK_BITS: i32 = 17;
169pub const MMAP_BLOCK_SIZE: i32 = 1 << MMAP_BLOCK_BITS;
170pub const CR0_PE: i32 = 1;
171pub const CR0_MP: i32 = 1 << 1;
172pub const CR0_EM: i32 = 1 << 2;
173pub const CR0_TS: i32 = 1 << 3;
174pub const CR0_ET: i32 = 1 << 4;
175pub const CR0_WP: i32 = 1 << 16;
176pub const CR0_AM: i32 = 1 << 18;
177pub const CR0_NW: i32 = 1 << 29;
178pub const CR0_CD: i32 = 1 << 30;
179pub const CR0_PG: i32 = 1 << 31;
180pub const CR4_VME: i32 = 1;
181pub const CR4_PVI: i32 = 1 << 1;
182pub const CR4_TSD: i32 = 1 << 2;
183pub const CR4_PSE: i32 = 1 << 4;
184pub const CR4_DE: i32 = 1 << 3;
185pub const CR4_PAE: i32 = 1 << 5;
186pub const CR4_PGE: i32 = 1 << 7;
187pub const CR4_OSFXSR: i32 = 1 << 9;
188pub const CR4_OSXMMEXCPT: i32 = 1 << 10;
189pub const CR4_SMEP: i32 = 1 << 20;
190
191pub const TSR_BACKLINK: i32 = 0x00;
192pub const TSR_CR3: i32 = 0x1C;
193pub const TSR_EIP: i32 = 0x20;
194pub const TSR_EFLAGS: i32 = 0x24;
195
196pub const TSR_EAX: i32 = 0x28;
197pub const TSR_ECX: i32 = 0x2c;
198pub const TSR_EDX: i32 = 0x30;
199pub const TSR_EBX: i32 = 0x34;
200pub const TSR_ESP: i32 = 0x38;
201pub const TSR_EBP: i32 = 0x3c;
202pub const TSR_ESI: i32 = 0x40;
203pub const TSR_EDI: i32 = 0x44;
204
205pub const TSR_ES: i32 = 0x48;
206pub const TSR_CS: i32 = 0x4c;
207pub const TSR_SS: i32 = 0x50;
208pub const TSR_DS: i32 = 0x54;
209pub const TSR_FS: i32 = 0x58;
210pub const TSR_GS: i32 = 0x5c;
211pub const TSR_LDT: i32 = 0x60;
212
213pub const IA32_TIME_STAMP_COUNTER: i32 = 0x10;
214pub const IA32_PLATFORM_ID: i32 = 0x17;
215pub const IA32_APIC_BASE: i32 = 0x1B;
216pub const MSR_TEST_CTRL: i32 = 0x33;
217pub const MSR_SMI_COUNT: i32 = 0x34;
218pub const IA32_FEAT_CTL: i32 = 0x3A;
219pub const IA32_SPEC_CTRL: i32 = 0x48;
220pub const IA32_BIOS_UPDT_TRIG: i32 = 0x79;
221pub const IA32_BIOS_SIGN_ID: i32 = 0x8B;
222pub const IA32_PMC0: i32 = 0xC1;
223pub const IA32_PMC1: i32 = 0xC2;
224pub const MSR_PLATFORM_INFO: i32 = 0xCE;
225pub const MSR_TSX_FORCE_ABORT: i32 = 0x10F;
226pub const IA32_TSX_CTRL: i32 = 0x122;
227pub const IA32_MCU_OPT_CTRL: i32 = 0x123;
228pub const MISC_FEATURE_ENABLES: i32 = 0x140;
229pub const IA32_SYSENTER_CS: i32 = 0x174;
230pub const IA32_SYSENTER_ESP: i32 = 0x175;
231pub const IA32_SYSENTER_EIP: i32 = 0x176;
232pub const IA32_MCG_CAP: i32 = 0x179;
233pub const IA32_PERFEVTSEL0: i32 = 0x186;
234pub const IA32_PERFEVTSEL1: i32 = 0x187;
235pub const IA32_MISC_ENABLE: i32 = 0x1A0;
236pub const IA32_PAT: i32 = 0x277;
237pub const IA32_RTIT_CTL: i32 = 0x570;
238pub const MSR_PKG_C2_RESIDENCY: i32 = 0x60D;
239pub const IA32_KERNEL_GS_BASE: i32 = 0xC0000101u32 as i32;
240pub const MSR_AMD64_LS_CFG: i32 = 0xC0011020u32 as i32;
241pub const MSR_AMD64_DE_CFG: i32 = 0xC0011029u32 as i32;
242
243pub const IA32_APIC_BASE_BSP: i32 = 1 << 8;
244pub const IA32_APIC_BASE_EXTD: i32 = 1 << 10;
245pub const IA32_APIC_BASE_EN: i32 = 1 << 11;
246
247pub const IOAPIC_MEM_ADDRESS: u32 = 0xFEC00000;
248pub const IOAPIC_MEM_SIZE: u32 = 32;
249pub const APIC_MEM_ADDRESS: u32 = 0xFEE00000;
250pub const APIC_MEM_SIZE: u32 = 0x1000;
251
252pub const MXCSR_MASK: i32 = 0xffff;
253pub const MXCSR_FZ: i32 = 1 << 15;
254pub const MXCSR_DAZ: i32 = 1 << 6;
255pub const MXCSR_RC_SHIFT: i32 = 13;
256
257pub const VALID_TLB_ENTRY_MAX: i32 = 10000;
258pub const TLB_VALID: i32 = 1 << 0;
259pub const TLB_READONLY: i32 = 1 << 1;
260pub const TLB_NO_USER: i32 = 1 << 2;
261pub const TLB_IN_MAPPED_RANGE: i32 = 1 << 3;
262pub const TLB_GLOBAL: i32 = 1 << 4;
263pub const TLB_HAS_CODE: i32 = 1 << 5;
264pub const IVT_SIZE: u32 = 0x400;
265pub const CPU_EXCEPTION_DE: i32 = 0;
266pub const CPU_EXCEPTION_DB: i32 = 1;
267pub const CPU_EXCEPTION_NMI: i32 = 2;
268pub const CPU_EXCEPTION_BP: i32 = 3;
269pub const CPU_EXCEPTION_OF: i32 = 4;
270pub const CPU_EXCEPTION_BR: i32 = 5;
271pub const CPU_EXCEPTION_UD: i32 = 6;
272pub const CPU_EXCEPTION_NM: i32 = 7;
273pub const CPU_EXCEPTION_DF: i32 = 8;
274pub const CPU_EXCEPTION_TS: i32 = 10;
275pub const CPU_EXCEPTION_NP: i32 = 11;
276pub const CPU_EXCEPTION_SS: i32 = 12;
277pub const CPU_EXCEPTION_GP: i32 = 13;
278pub const CPU_EXCEPTION_PF: i32 = 14;
279pub const CPU_EXCEPTION_MF: i32 = 16;
280pub const CPU_EXCEPTION_AC: i32 = 17;
281pub const CPU_EXCEPTION_MC: i32 = 18;
282pub const CPU_EXCEPTION_XM: i32 = 19;
283pub const CPU_EXCEPTION_VE: i32 = 20;
284
285pub const CHECK_TLB_INVARIANTS: bool = false;
286
287pub const DEBUG: bool = cfg!(debug_assertions);
288
289pub const LOOP_COUNTER: i32 = 100_003;
290
291// should probably be kept in sync with APIC_TIMER_FREQ in apic.js
292pub const TSC_RATE: f64 = 1_000_000.0;
293
294pub static mut cpuid_level: u32 = 0x16;
295
296pub static mut jit_block_boundary: bool = false;
297
298const TSC_ENABLE_IMPRECISE_BROWSER_WORKAROUND: bool = true;
299
300#[cfg(debug_assertions)]
301const TSC_VERBOSE_LOGGING: bool = false;
302#[cfg(debug_assertions)]
303pub static mut tsc_last_extra: u64 = 0;
304
305// the last value returned by rdtsc
306pub static mut tsc_last_value: u64 = 0;
307// the smallest difference between two rdtsc readings (depends on the browser's performance.now resolution)
308pub static mut tsc_resolution: u64 = u64::MAX;
309// how many times rdtsc was called and had to return the same value (due to browser's performance.now resolution)
310pub static mut tsc_number_of_same_readings: u64 = 0;
311// how often rdtsc was previously called without its value changing, used for interpolating quick
312// consecutive calls between rdtsc (when it's called faster than the browser's performance.now
313// changes)
314pub static mut tsc_speed: u64 = 1;
315
316// used for restoring the state
317pub static mut tsc_offset: u64 = 0;
318
319pub struct Code {
320    pub wasm_table_index: jit::WasmTableIndex,
321    pub state_flags: CachedStateFlags,
322    pub state_table: [u16; 0x1000],
323}
324
325pub static mut tlb_data: [i32; 0x100000] = [0; 0x100000];
326pub static mut tlb_code: [Option<ptr::NonNull<Code>>; 0x100000] = [None; 0x100000];
327
328pub static mut valid_tlb_entries: [i32; 10000] = [0; 10000];
329pub static mut valid_tlb_entries_count: i32 = 0;
330
331pub static mut in_jit: bool = false;
332
333pub enum JitExitReason {
334    None,
335    CpuException { code: i32, error_code: Option<i32> },
336    SelfModifyingCodeBail,
337}
338
339pub static mut jit_exit_reason: JitExitReason = JitExitReason::None;
340
341pub enum LastJump {
342    Interrupt {
343        phys_addr: u32,
344        int: u8,
345        software: bool,
346        error: Option<u32>,
347    },
348    Compiled {
349        phys_addr: u32,
350    },
351    Interpreted {
352        phys_addr: u32,
353    },
354    None,
355}
356impl LastJump {
357    pub fn phys_address(&self) -> Option<u32> {
358        match self {
359            LastJump::Interrupt { phys_addr, .. } => Some(*phys_addr),
360            LastJump::Compiled { phys_addr } => Some(*phys_addr),
361            LastJump::Interpreted { phys_addr } => Some(*phys_addr),
362            LastJump::None => None,
363        }
364    }
365    pub fn name(&self) -> &'static str {
366        match self {
367            LastJump::Interrupt { .. } => "interrupt",
368            LastJump::Compiled { .. } => "compiled",
369            LastJump::Interpreted { .. } => "interpreted",
370            LastJump::None => "none",
371        }
372    }
373}
374pub static mut debug_last_jump: LastJump = LastJump::None;
375
376#[derive(Copy, Clone)]
377pub struct SegmentSelector {
378    raw: u16,
379}
380
381impl SegmentSelector {
382    pub fn of_u16(raw: u16) -> SegmentSelector {
383        SegmentSelector { raw }
384    }
385    pub fn rpl(&self) -> u8 {
386        (self.raw & 3) as u8
387    }
388    pub fn is_gdt(&self) -> bool {
389        (self.raw & 4) == 0
390    }
391    pub fn descriptor_offset(&self) -> u16 {
392        (self.raw & !7) as u16
393    }
394
395    pub fn is_null(&self) -> bool {
396        self.is_gdt() && self.descriptor_offset() == 0
397    }
398}
399
400// Used to indicate early that the selector cannot be used to fetch a descriptor
401#[derive(PartialEq)]
402pub enum SelectorNullOrInvalid {
403    IsNull,
404    OutsideOfTableLimit,
405}
406
407pub struct SegmentDescriptor {
408    pub raw: u64,
409}
410
411impl SegmentDescriptor {
412    pub fn of_u64(raw: u64) -> SegmentDescriptor {
413        SegmentDescriptor { raw }
414    }
415    pub fn base(&self) -> i32 {
416        ((self.raw >> 16) & 0xffff | (self.raw & 0xff_00000000) >> 16 | (self.raw >> 56 << 24))
417            as i32
418    }
419    pub fn limit(&self) -> u32 {
420        (self.raw & 0xffff | ((self.raw >> 48) & 0xf) << 16) as u32
421    }
422    pub fn access_byte(&self) -> u8 {
423        ((self.raw >> 40) & 0xff) as u8
424    }
425    pub fn flags(&self) -> u8 {
426        ((self.raw >> 48 >> 4) & 0xf) as u8
427    }
428
429    pub fn is_system(&self) -> bool {
430        self.access_byte() & 0x10 == 0
431    }
432    pub fn system_type(&self) -> u8 {
433        self.access_byte() & 0xF
434    }
435
436    pub fn accessed(&self) -> bool {
437        self.access_byte() & 1 == 1
438    }
439    pub fn is_rw(&self) -> bool {
440        self.access_byte() & 2 == 2
441    }
442    pub fn is_dc(&self) -> bool {
443        self.access_byte() & 4 == 4
444    }
445    pub fn is_executable(&self) -> bool {
446        self.access_byte() & 8 == 8
447    }
448    pub fn is_present(&self) -> bool {
449        self.access_byte() & 0x80 == 0x80
450    }
451    pub fn is_writable(&self) -> bool {
452        self.is_rw() && !self.is_executable()
453    }
454    pub fn is_readable(&self) -> bool {
455        self.is_rw() || !self.is_executable()
456    }
457    pub fn is_conforming_executable(&self) -> bool {
458        self.is_dc() && self.is_executable()
459    }
460    pub fn dpl(&self) -> u8 {
461        (self.access_byte() >> 5) & 3
462    }
463    pub fn is_32(&self) -> bool {
464        self.flags() & 4 == 4
465    }
466    pub fn effective_limit(&self) -> u32 {
467        if self.flags() & 8 == 8 {
468            self.limit() << 12 | 0xFFF
469        } else {
470            self.limit()
471        }
472    }
473    pub fn set_busy(&self) -> SegmentDescriptor {
474        SegmentDescriptor {
475            raw: self.raw | 2 << 40,
476        }
477    }
478    pub fn clear_busy(&self) -> SegmentDescriptor {
479        SegmentDescriptor {
480            raw: self.raw & !(2 << 40),
481        }
482    }
483    pub fn set_accessed(&self) -> SegmentDescriptor {
484        SegmentDescriptor {
485            raw: self.raw | 1 << 40,
486        }
487    }
488}
489
490pub struct InterruptDescriptor {
491    raw: u64,
492}
493
494impl InterruptDescriptor {
495    pub fn of_u64(raw: u64) -> InterruptDescriptor {
496        InterruptDescriptor { raw }
497    }
498    pub fn offset(&self) -> i32 {
499        (self.raw & 0xffff | self.raw >> 32 & 0xffff0000) as i32
500    }
501    pub fn selector(&self) -> u16 {
502        (self.raw >> 16 & 0xffff) as u16
503    }
504    pub fn access_byte(&self) -> u8 {
505        (self.raw >> 40 & 0xff) as u8
506    }
507    pub fn dpl(&self) -> u8 {
508        (self.access_byte() >> 5 & 3) as u8
509    }
510    pub fn gate_type(&self) -> u8 {
511        self.access_byte() & 7
512    }
513    pub fn is_32(&self) -> bool {
514        self.access_byte() & 8 == 8
515    }
516    pub fn is_present(&self) -> bool {
517        self.access_byte() & 0x80 == 0x80
518    }
519    pub fn reserved_zeros_are_valid(&self) -> bool {
520        self.access_byte() & 16 == 0
521    }
522
523    const TASK_GATE: u8 = 0b101;
524    const INTERRUPT_GATE: u8 = 0b110;
525    const TRAP_GATE: u8 = 0b111;
526}
527
528pub unsafe fn switch_cs_real_mode(selector: i32) {
529    dbg_assert!(!*protected_mode || vm86_mode());
530
531    *sreg.offset(CS as isize) = selector as u16;
532    *segment_is_null.offset(CS as isize) = false;
533    *segment_offsets.offset(CS as isize) = selector << 4;
534    update_cs_size(false);
535}
536
537unsafe fn get_tss_ss_esp(dpl: u8) -> OrPageFault<(i32, i32)> {
538    Ok(if *tss_size_32 {
539        let tss_stack_offset = ((dpl << 3) + 4) as u32;
540        if tss_stack_offset + 7 > *segment_limits.offset(TR as isize) {
541            panic!("#TS handler");
542        }
543        let addr = translate_address_system_read(
544            *segment_offsets.offset(TR as isize) + tss_stack_offset as i32,
545        )?;
546        dbg_assert!(addr & 0xFFF <= 0x1000 - 6);
547        (memory::read16(addr + 4), memory::read32s(addr))
548    } else {
549        let tss_stack_offset = ((dpl << 2) + 2) as u32;
550        if tss_stack_offset + 3 > *segment_limits.offset(TR as isize) {
551            panic!("#TS handler");
552        }
553        let addr = translate_address_system_read(
554            *segment_offsets.offset(TR as isize) + tss_stack_offset as i32,
555        )?;
556        dbg_assert!(addr & 0xFFF <= 0x1000 - 4);
557        (memory::read16(addr + 2), memory::read16(addr))
558    })
559}
560
561pub unsafe fn iret16() {
562    iret(true);
563}
564pub unsafe fn iret32() {
565    iret(false);
566}
567
568pub unsafe fn iret(is_16: bool) {
569    if vm86_mode() && getiopl() < 3 {
570        // vm86 mode, iopl != 3
571        dbg_log!("#gp iret vm86 mode, iopl != 3");
572        trigger_gp(0);
573        return;
574    }
575
576    let (new_eip, new_cs, mut new_flags) = if is_16 {
577        (
578            return_on_pagefault!(safe_read16(get_stack_pointer(0))),
579            return_on_pagefault!(safe_read16(get_stack_pointer(2))),
580            return_on_pagefault!(safe_read16(get_stack_pointer(4))),
581        )
582    } else {
583        (
584            return_on_pagefault!(safe_read32s(get_stack_pointer(0))),
585            return_on_pagefault!(safe_read16(get_stack_pointer(4))),
586            return_on_pagefault!(safe_read32s(get_stack_pointer(8))),
587        )
588    };
589
590    if !*protected_mode || (vm86_mode() && getiopl() == 3) {
591        if new_eip as u32 & 0xFFFF0000 != 0 {
592            panic!("#GP handler");
593        }
594
595        switch_cs_real_mode(new_cs);
596        *instruction_pointer = get_seg_cs() + new_eip;
597
598        if is_16 {
599            update_eflags(new_flags | *flags & !0xFFFF);
600            adjust_stack_reg(3 * 2);
601        } else {
602            if !*protected_mode {
603                update_eflags((new_flags & 0x257FD5) | (*flags & 0x1A0000));
604            } else {
605                update_eflags(new_flags);
606            }
607            adjust_stack_reg(3 * 4);
608        }
609
610        update_state_flags();
611        handle_irqs();
612        return;
613    }
614
615    dbg_assert!(!vm86_mode());
616
617    if *flags & FLAG_NT != 0 {
618        // nested task: return to the task linked through the back-link field of the current tss
619        let tss_offset = *segment_offsets.offset(TR as isize);
620        let backlink = return_on_pagefault!(safe_read16(tss_offset + TSR_BACKLINK));
621        do_task_switch(backlink, None, TaskSwitchSource::Iret);
622        return;
623    }
624
625    if new_flags & FLAG_VM != 0 {
626        if *cpl == 0 {
627            // return to virtual 8086 mode
628
629            // vm86 cannot be set in 16 bit flag
630            dbg_assert!(!is_16);
631
632            let temp_esp = return_on_pagefault!(safe_read32s(get_stack_pointer(12)));
633            let temp_ss = return_on_pagefault!(safe_read16(get_stack_pointer(16)));
634
635            let new_es = return_on_pagefault!(safe_read16(get_stack_pointer(20)));
636            let new_ds = return_on_pagefault!(safe_read16(get_stack_pointer(24)));
637            let new_fs = return_on_pagefault!(safe_read16(get_stack_pointer(28)));
638            let new_gs = return_on_pagefault!(safe_read16(get_stack_pointer(32)));
639
640            // no exceptions below
641
642            update_eflags(new_flags);
643            *flags |= FLAG_VM;
644
645            switch_cs_real_mode(new_cs);
646            *instruction_pointer = get_seg_cs() + (new_eip & 0xFFFF);
647
648            if !switch_seg(ES, new_es)
649                || !switch_seg(DS, new_ds)
650                || !switch_seg(FS, new_fs)
651                || !switch_seg(GS, new_gs)
652            {
653                // XXX: Should be checked before side effects
654                dbg_assert!(false);
655            }
656
657            adjust_stack_reg(9 * 4); // 9 dwords: eip, cs, flags, esp, ss, es, ds, fs, gs
658
659            write_reg32(ESP, temp_esp);
660            if !switch_seg(SS, temp_ss) {
661                // XXX
662                dbg_assert!(false);
663            }
664
665            *cpl = 3;
666            cpl_changed();
667
668            update_cs_size(false);
669            update_state_flags();
670
671            // iret end
672            return;
673        } else {
674            dbg_log!("vm86 flag ignored because cpl != 0");
675            new_flags &= !FLAG_VM;
676        }
677    }
678
679    // protected mode return
680
681    let cs_selector = SegmentSelector::of_u16(new_cs as u16);
682    let cs_descriptor = match return_on_pagefault!(lookup_segment_selector(cs_selector)) {
683        Ok((desc, _)) => desc,
684        Err(SelectorNullOrInvalid::IsNull) => panic!("Unimplemented: CS selector is null"),
685        Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
686            panic!("Unimplemented: CS selector is invalid")
687        }
688    };
689
690    if new_eip as u32 > cs_descriptor.effective_limit() {
691        dbg_log!(
692            "#gp iret: new_eip > cs_descriptor.effective_limit, new_eip={:x} cs_descriptor.effective_limit={:x}",
693            new_eip as u32,
694            cs_descriptor.effective_limit()
695        );
696        trigger_gp(new_cs & !3);
697        return;
698    }
699
700    if !cs_descriptor.is_present() {
701        panic!("not present");
702    }
703    if !cs_descriptor.is_executable() {
704        panic!("not exec");
705    }
706    if cs_selector.rpl() < *cpl {
707        panic!("rpl < cpl");
708    }
709    if cs_descriptor.is_dc() && cs_descriptor.dpl() > cs_selector.rpl() {
710        panic!("conforming and dpl > rpl");
711    }
712
713    if !cs_descriptor.is_dc() && cs_selector.rpl() != cs_descriptor.dpl() {
714        dbg_log!(
715            "#gp iret: non-conforming cs and rpl != dpl, dpl={} rpl={}",
716            cs_descriptor.dpl(),
717            cs_selector.rpl()
718        );
719        trigger_gp(new_cs & !3);
720        return;
721    }
722
723    if cs_selector.rpl() > *cpl {
724        // outer privilege return
725        let (temp_esp, temp_ss) = if is_16 {
726            (
727                return_on_pagefault!(safe_read16(get_stack_pointer(6))),
728                return_on_pagefault!(safe_read16(get_stack_pointer(8))),
729            )
730        } else {
731            (
732                return_on_pagefault!(safe_read32s(get_stack_pointer(12))),
733                return_on_pagefault!(safe_read16(get_stack_pointer(16))),
734            )
735        };
736
737        let ss_selector = SegmentSelector::of_u16(temp_ss as u16);
738        let ss_descriptor = match return_on_pagefault!(lookup_segment_selector(ss_selector)) {
739            Ok((desc, _)) => desc,
740            Err(SelectorNullOrInvalid::IsNull) => {
741                dbg_log!("#GP for loading 0 in SS sel={:x}", temp_ss);
742                dbg_trace();
743                trigger_gp(0);
744                return;
745            }
746            Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
747                dbg_log!("#GP for loading invalid in SS sel={:x}", temp_ss);
748                trigger_gp(temp_ss & !3);
749                return;
750            }
751        };
752        let new_cpl = cs_selector.rpl();
753
754        if ss_descriptor.is_system()
755            || ss_selector.rpl() != new_cpl
756            || !ss_descriptor.is_writable()
757            || ss_descriptor.dpl() != new_cpl
758        {
759            dbg_log!("#GP for loading invalid in SS sel={:x}", temp_ss);
760            dbg_trace();
761            trigger_gp(temp_ss & !3);
762            return;
763        }
764
765        if !ss_descriptor.is_present() {
766            dbg_log!("#SS for loading non-present in SS sel={:x}", temp_ss);
767            dbg_trace();
768            trigger_ss(temp_ss & !3);
769            return;
770        }
771
772        // no exceptions below
773
774        if is_16 {
775            update_eflags(new_flags | *flags & !0xFFFF);
776        } else {
777            update_eflags(new_flags);
778        }
779
780        *cpl = cs_selector.rpl();
781        cpl_changed();
782
783        if !switch_seg(SS, temp_ss) {
784            // XXX
785            dbg_assert!(false);
786        }
787
788        set_stack_reg(temp_esp);
789
790        if *cpl == 0 && !is_16 {
791            *flags = *flags & !FLAG_VIF & !FLAG_VIP | (new_flags & (FLAG_VIF | FLAG_VIP));
792        }
793
794        for reg in [ES, DS, FS, GS] {
795            let access = *segment_access_bytes.offset(reg as isize);
796            let dpl = access >> 5 & 3;
797            let executable = access & 8 == 8;
798            let conforming = access & 4 == 4;
799            if dpl < *cpl && !(executable && conforming) {
800                //dbg_log!(
801                //    "set segment to null sreg={} dpl={} executable={} conforming={}",
802                //    reg,
803                //    dpl,
804                //    executable,
805                //    conforming
806                //);
807                *segment_is_null.offset(reg as isize) = true;
808                *sreg.offset(reg as isize) = 0;
809            }
810        }
811    } else if cs_selector.rpl() == *cpl {
812        // same privilege return
813        // no exceptions below
814        if is_16 {
815            adjust_stack_reg(3 * 2);
816            update_eflags(new_flags | *flags & !0xFFFF);
817        } else {
818            adjust_stack_reg(3 * 4);
819            update_eflags(new_flags);
820        }
821
822        // update vip and vif, which are not changed by update_eflags
823        if *cpl == 0 && !is_16 {
824            *flags = *flags & !FLAG_VIF & !FLAG_VIP | (new_flags & (FLAG_VIF | FLAG_VIP));
825        }
826    } else {
827        dbg_assert!(false);
828    }
829
830    *sreg.offset(CS as isize) = new_cs as u16;
831    dbg_assert!((new_cs & 3) == *cpl as i32);
832
833    update_cs_size(cs_descriptor.is_32());
834
835    *segment_limits.offset(CS as isize) = cs_descriptor.effective_limit();
836    *segment_offsets.offset(CS as isize) = cs_descriptor.base();
837    *segment_access_bytes.offset(CS as isize) = cs_descriptor.access_byte();
838
839    *instruction_pointer = new_eip + get_seg_cs();
840
841    update_state_flags();
842
843    // iret end
844
845    handle_irqs();
846}
847
848pub unsafe fn call_interrupt_vector(
849    interrupt_nr: i32,
850    is_software_int: bool,
851    error_code: Option<i32>,
852) {
853    if *protected_mode {
854        if vm86_mode() && *cr.offset(4) & CR4_VME != 0 {
855            panic!("Unimplemented: VME");
856        }
857
858        if vm86_mode() && is_software_int && getiopl() < 3 {
859            dbg_log!("call_interrupt_vector #GP. vm86 && software int && iopl < 3");
860            dbg_trace();
861            trigger_gp(0);
862            return;
863        }
864
865        if interrupt_nr << 3 | 7 > *idtr_size {
866            dbg_log!("interrupt_nr={:x} idtr_size={:x}", interrupt_nr, *idtr_size);
867            dbg_trace();
868            panic!("Unimplemented: #GP handler");
869        }
870
871        let descriptor_address = return_on_pagefault!(translate_address_system_read(
872            *idtr_offset + (interrupt_nr << 3)
873        ));
874
875        let descriptor = InterruptDescriptor::of_u64(memory::read64s(descriptor_address) as u64);
876
877        let mut offset = descriptor.offset();
878        let selector = descriptor.selector() as i32;
879        let dpl = descriptor.dpl();
880        let gate_type = descriptor.gate_type();
881
882        if is_software_int && dpl < *cpl {
883            dbg_log!("#gp software interrupt ({:x}) and dpl < cpl", interrupt_nr);
884            dbg_trace();
885            trigger_gp(interrupt_nr << 3 | 2);
886            return;
887        }
888
889        if gate_type != InterruptDescriptor::TRAP_GATE
890            && gate_type != InterruptDescriptor::INTERRUPT_GATE
891            && gate_type != InterruptDescriptor::TASK_GATE
892        {
893            // invalid gate_type
894            dbg_log!(
895                "gate type invalid. gate_type=0b{:b} raw={:b}",
896                gate_type,
897                descriptor.raw
898            );
899            dbg_trace();
900            panic!("Unimplemented: #GP handler");
901        }
902
903        if !descriptor.reserved_zeros_are_valid() {
904            dbg_log!(
905                "reserved 0s violated. gate_type=0b{:b} raw={:b}",
906                gate_type,
907                descriptor.raw
908            );
909            dbg_trace();
910            panic!("Unimplemented: #GP handler");
911        }
912
913        if !descriptor.is_present() {
914            // present bit not set
915            dbg_log!("#np int descriptor not present, int={}", interrupt_nr);
916            trigger_np(interrupt_nr << 3 | 2);
917            return;
918        }
919
920        if gate_type == InterruptDescriptor::TASK_GATE {
921            // task gate
922            dbg_log!(
923                "interrupt to task gate: int={:x} sel={:x} dpl={}",
924                interrupt_nr,
925                selector,
926                dpl
927            );
928            dbg_trace();
929            dbg_assert!(offset == 0, "TODO: Check this (likely #GP)");
930            do_task_switch(selector, error_code, TaskSwitchSource::CallOrInt);
931            return;
932        }
933
934        let cs_segment_descriptor = match return_on_pagefault!(lookup_segment_selector(
935            SegmentSelector::of_u16(selector as u16)
936        )) {
937            Ok((desc, _)) => desc,
938            Err(SelectorNullOrInvalid::IsNull) => {
939                dbg_log!("is null");
940                panic!("Unimplemented: #GP handler");
941            }
942            Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
943                dbg_log!("is invalid");
944                panic!("Unimplemented: #GP handler (error code)");
945            }
946        };
947
948        dbg_assert!(offset as u32 <= cs_segment_descriptor.effective_limit());
949
950        if !cs_segment_descriptor.is_executable() || cs_segment_descriptor.dpl() > *cpl {
951            dbg_log!("not exec");
952            panic!("Unimplemented: #GP handler");
953        }
954        if !cs_segment_descriptor.is_present() {
955            // kvm-unit-test
956            dbg_log!("not present");
957            trigger_np(interrupt_nr << 3 | 2);
958            return;
959        }
960
961        let old_flags = get_eflags();
962
963        if !cs_segment_descriptor.is_dc() && cs_segment_descriptor.dpl() < *cpl {
964            // inter privilege level interrupt
965            // interrupt from vm86 mode
966
967            if old_flags & FLAG_VM != 0 && cs_segment_descriptor.dpl() != 0 {
968                panic!("Unimplemented: #GP handler for non-0 cs segment dpl when in vm86 mode");
969            }
970
971            let (new_ss, new_esp) =
972                return_on_pagefault!(get_tss_ss_esp(cs_segment_descriptor.dpl()));
973
974            let ss_segment_selector = SegmentSelector::of_u16(new_ss as u16);
975            let ss_segment_descriptor =
976                match return_on_pagefault!(lookup_segment_selector(ss_segment_selector)) {
977                    Ok((desc, _)) => desc,
978                    Err(
979                        SelectorNullOrInvalid::IsNull | SelectorNullOrInvalid::OutsideOfTableLimit,
980                    ) => {
981                        panic!("Unimplemented: #TS handler");
982                    }
983                };
984
985            if ss_segment_descriptor.is_dc() {
986                dbg_assert!(new_esp as u32 > ss_segment_descriptor.effective_limit());
987            } else {
988                dbg_assert!(new_esp as u32 - 1 <= ss_segment_descriptor.effective_limit());
989            }
990            dbg_assert!(!ss_segment_descriptor.is_system() && ss_segment_descriptor.is_writable());
991
992            if ss_segment_selector.rpl() != cs_segment_descriptor.dpl() {
993                panic!("Unimplemented: #TS handler");
994            }
995            if ss_segment_descriptor.dpl() != cs_segment_descriptor.dpl()
996                || !ss_segment_descriptor.is_rw()
997            {
998                panic!("Unimplemented: #TS handler");
999            }
1000            if !ss_segment_descriptor.is_present() {
1001                panic!("Unimplemented: #TS handler");
1002            }
1003
1004            let old_esp = read_reg32(ESP);
1005            let old_ss = *sreg.offset(SS as isize) as i32;
1006
1007            let error_code_space = if error_code.is_some() { 1 } else { 0 };
1008            let vm86_space = if (old_flags & FLAG_VM) == FLAG_VM {
1009                4
1010            } else {
1011                0
1012            };
1013            let bytes_per_arg = if descriptor.is_32() { 4 } else { 2 };
1014
1015            let stack_space = bytes_per_arg * (5 + error_code_space + vm86_space);
1016            let new_stack_pointer = ss_segment_descriptor.base()
1017                + if ss_segment_descriptor.is_32() {
1018                    new_esp - stack_space
1019                } else {
1020                    new_esp - stack_space & 0xFFFF
1021                };
1022
1023            return_on_pagefault!(translate_address_system_write(new_stack_pointer));
1024            return_on_pagefault!(translate_address_system_write(
1025                ss_segment_descriptor.base() + new_esp - 1
1026            ));
1027
1028            // no exceptions below
1029            *cpl = cs_segment_descriptor.dpl();
1030            cpl_changed();
1031
1032            update_cs_size(cs_segment_descriptor.is_32());
1033
1034            *flags &= !FLAG_VM & !FLAG_RF;
1035
1036            if !switch_seg(SS, new_ss) {
1037                // XXX
1038                dbg_assert!(false);
1039            }
1040            set_stack_reg(new_esp);
1041
1042            // XXX: #SS if stack would cross stack limit
1043
1044            if old_flags & FLAG_VM != 0 {
1045                if !descriptor.is_32() {
1046                    dbg_assert!(false);
1047                } else {
1048                    push32(*sreg.offset(GS as isize) as i32).unwrap();
1049                    push32(*sreg.offset(FS as isize) as i32).unwrap();
1050                    push32(*sreg.offset(DS as isize) as i32).unwrap();
1051                    push32(*sreg.offset(ES as isize) as i32).unwrap();
1052                }
1053            }
1054
1055            if descriptor.is_32() {
1056                push32(old_ss).unwrap();
1057                push32(old_esp).unwrap();
1058            } else {
1059                push16(old_ss).unwrap();
1060                push16(old_esp & 0xFFFF).unwrap();
1061            }
1062        } else if cs_segment_descriptor.is_dc() || cs_segment_descriptor.dpl() == *cpl {
1063            // intra privilege level interrupt
1064
1065            //dbg_log!("Intra privilege interrupt gate=" + h(selector, 4) + ":" + h(offset >>> 0, 8) +
1066            //        " gate_type=" + gate_type + " 16bit=" + descriptor.is_32() +
1067            //        " cpl=" + *cpl + " dpl=" + segment_descriptor.dpl() + " conforming=" + +segment_descriptor.is_dc(), );
1068            //debug.dump_regs_short();
1069
1070            if *flags & FLAG_VM != 0 {
1071                dbg_assert!(false, "check error code");
1072                trigger_gp(selector & !3);
1073                return;
1074            }
1075
1076            let bytes_per_arg = if descriptor.is_32() { 4 } else { 2 };
1077            let error_code_space = if error_code.is_some() { 1 } else { 0 };
1078
1079            let stack_space = bytes_per_arg * (3 + error_code_space);
1080
1081            // XXX: with current cpl or with cpl 0?
1082            return_on_pagefault!(writable_or_pagefault(
1083                get_stack_pointer(-stack_space),
1084                stack_space
1085            ));
1086
1087        // no exceptions below
1088        } else {
1089            panic!("Unimplemented: #GP handler");
1090        }
1091
1092        // XXX: #SS if stack would cross stack limit
1093        if descriptor.is_32() {
1094            push32(old_flags).unwrap();
1095            push32(*sreg.offset(CS as isize) as i32).unwrap();
1096            push32(get_real_eip()).unwrap();
1097
1098            if let Some(ec) = error_code {
1099                push32(ec).unwrap();
1100            }
1101        } else {
1102            push16(old_flags & 0xFFFF).unwrap();
1103            push16(*sreg.offset(CS as isize) as i32).unwrap();
1104            push16(get_real_eip() & 0xFFFF).unwrap();
1105
1106            if let Some(ec) = error_code {
1107                dbg_assert!(ec >= 0 && ec < 0x10000);
1108                push16(ec).unwrap();
1109            }
1110
1111            offset &= 0xFFFF;
1112        }
1113
1114        if old_flags & FLAG_VM != 0 {
1115            if !switch_seg(GS, 0) || !switch_seg(FS, 0) || !switch_seg(DS, 0) || !switch_seg(ES, 0)
1116            {
1117                // can't fail
1118                dbg_assert!(false);
1119            }
1120        }
1121
1122        *sreg.offset(CS as isize) = (selector as u16) & !3 | *cpl as u16;
1123        dbg_assert!((*sreg.offset(CS as isize) & 3) == *cpl as u16);
1124
1125        update_cs_size(cs_segment_descriptor.is_32());
1126
1127        *segment_limits.offset(CS as isize) = cs_segment_descriptor.effective_limit();
1128        *segment_offsets.offset(CS as isize) = cs_segment_descriptor.base();
1129        *segment_access_bytes.offset(CS as isize) = cs_segment_descriptor.access_byte();
1130
1131        *instruction_pointer = get_seg_cs() + offset;
1132
1133        *flags &= !FLAG_NT & !FLAG_VM & !FLAG_RF & !FLAG_TRAP;
1134
1135        if gate_type == InterruptDescriptor::INTERRUPT_GATE {
1136            // clear int flag for interrupt gates
1137            *flags &= !FLAG_INTERRUPT;
1138        } else {
1139            if *flags & FLAG_INTERRUPT != 0 && old_flags & FLAG_INTERRUPT == 0 {
1140                handle_irqs();
1141            }
1142        }
1143
1144        update_state_flags();
1145    } else {
1146        // call 4 byte cs:ip interrupt vector from ivt at cpu.memory 0
1147
1148        let index = (interrupt_nr << 2) as u32;
1149        let new_ip = memory::read16(index);
1150        let new_cs = memory::read16(index + 2);
1151
1152        dbg_assert!(
1153            index | 3 <= IVT_SIZE,
1154            "Unimplemented: #GP for interrupt number out of IVT bounds"
1155        );
1156
1157        // XXX: #SS if stack would cross stack limit
1158
1159        // push flags, cs:ip
1160        push16(get_eflags() & 0xFFFF).unwrap();
1161        push16(*sreg.offset(CS as isize) as i32).unwrap();
1162        push16(get_real_eip() & 0xFFFF).unwrap();
1163
1164        *flags &= !FLAG_INTERRUPT & !FLAG_AC & !FLAG_TRAP;
1165
1166        switch_cs_real_mode(new_cs);
1167        *instruction_pointer = get_seg_cs() + new_ip;
1168        update_state_flags();
1169    }
1170}
1171
1172pub unsafe fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool) {
1173    dbg_assert!(selector < 0x10000 && selector >= 0);
1174
1175    if !*protected_mode || vm86_mode() {
1176        if is_call {
1177            if is_osize_32 {
1178                return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-8), 8));
1179
1180                push32(*sreg.offset(CS as isize) as i32).unwrap();
1181                push32(get_real_eip()).unwrap();
1182            } else {
1183                return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-4), 4));
1184
1185                push16(*sreg.offset(CS as isize) as i32).unwrap();
1186                push16(get_real_eip()).unwrap();
1187            }
1188        }
1189        switch_cs_real_mode(selector);
1190        *instruction_pointer = get_seg_cs() + eip;
1191        update_state_flags();
1192        return;
1193    }
1194
1195    let cs_selector = SegmentSelector::of_u16(selector as u16);
1196    let info = match return_on_pagefault!(lookup_segment_selector(cs_selector)) {
1197        Ok((desc, _)) => desc,
1198        Err(SelectorNullOrInvalid::IsNull) => {
1199            dbg_log!("#gp null cs");
1200            trigger_gp(0);
1201            return;
1202        }
1203        Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
1204            dbg_log!("#gp invalid cs: {:x}", selector);
1205            trigger_gp(selector & !3);
1206            return;
1207        }
1208    };
1209
1210    if info.is_system() {
1211        dbg_log!("system type cs: {:x}", selector);
1212
1213        if info.system_type() == 0xC || info.system_type() == 4 {
1214            // call gate
1215            dbg_assert!(is_call, "TODO: Jump through call gate");
1216
1217            let is_16 = info.system_type() == 4;
1218
1219            if info.dpl() < *cpl || info.dpl() < cs_selector.rpl() {
1220                dbg_log!("#gp cs gate dpl < cpl or dpl < rpl: {:x}", selector);
1221                trigger_gp(selector & !3);
1222                return;
1223            }
1224
1225            if !info.is_present() {
1226                dbg_log!("#NP for loading not-present in gate cs sel={:x}", selector);
1227                trigger_np(selector & !3);
1228                return;
1229            }
1230
1231            let cs_selector = (info.raw >> 16) as i32;
1232
1233            let cs_info = match return_on_pagefault!(lookup_segment_selector(
1234                SegmentSelector::of_u16(cs_selector as u16)
1235            )) {
1236                Ok((desc, _)) => desc,
1237                Err(SelectorNullOrInvalid::IsNull) => {
1238                    dbg_log!("#gp null cs");
1239                    trigger_gp(0);
1240                    return;
1241                }
1242                Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
1243                    dbg_log!("#gp invalid cs: {:x}", cs_selector);
1244                    trigger_gp(cs_selector & !3);
1245                    return;
1246                }
1247            };
1248
1249            if cs_info.is_system() {
1250                dbg_log!("#gp non-code cs: {:x}", cs_selector);
1251                trigger_gp(cs_selector & !3);
1252                return;
1253            }
1254
1255            if !cs_info.is_executable() {
1256                dbg_log!("#gp non-executable cs: {:x}", cs_selector);
1257                trigger_gp(cs_selector & !3);
1258                return;
1259            }
1260
1261            if cs_info.dpl() > *cpl {
1262                dbg_log!("#gp dpl > cpl: {:x}", cs_selector);
1263                trigger_gp(cs_selector & !3);
1264                return;
1265            }
1266
1267            if !cs_info.is_present() {
1268                dbg_log!("#NP for loading not-present in cs sel={:x}", cs_selector);
1269                trigger_np(cs_selector & !3);
1270                return;
1271            }
1272
1273            if !cs_info.is_dc() && cs_info.dpl() < *cpl {
1274                dbg_log!(
1275                    "more privilege call gate is_16={} from={} to={}",
1276                    is_16,
1277                    *cpl,
1278                    cs_info.dpl()
1279                );
1280                let (new_ss, new_esp) = return_on_pagefault!(get_tss_ss_esp(cs_info.dpl()));
1281
1282                let ss_selector = SegmentSelector::of_u16(new_ss as u16);
1283                let ss_info = match return_on_pagefault!(lookup_segment_selector(ss_selector)) {
1284                    Ok((desc, _)) => desc,
1285                    Err(SelectorNullOrInvalid::IsNull) => {
1286                        panic!("null ss: {}", new_ss);
1287                    }
1288                    Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
1289                        panic!("invalid ss: {}", new_ss);
1290                    }
1291                };
1292
1293                if ss_info.is_dc() {
1294                    dbg_assert!(new_esp as u32 > ss_info.effective_limit());
1295                } else {
1296                    dbg_assert!(new_esp as u32 - 1 <= ss_info.effective_limit());
1297                }
1298                dbg_assert!(!ss_info.is_system() && ss_info.is_writable());
1299
1300                if ss_selector.rpl() != cs_info.dpl()
1301                // xxx: 0 in v86 mode
1302                {
1303                    panic!("#TS handler");
1304                }
1305                if ss_info.dpl() != cs_info.dpl() || !ss_info.is_writable() {
1306                    panic!("#TS handler");
1307                }
1308                if !ss_info.is_present() {
1309                    panic!("#SS handler");
1310                }
1311
1312                let parameter_count = (info.raw >> 32 & 0x1F) as i32;
1313                let mut stack_space = if is_16 { 4 } else { 8 };
1314                if is_call {
1315                    stack_space += if is_16 {
1316                        4 + 2 * parameter_count
1317                    } else {
1318                        8 + 4 * parameter_count
1319                    };
1320                }
1321                if ss_info.is_32() {
1322                    return_on_pagefault!(writable_or_pagefault_cpl(
1323                        cs_info.dpl(),
1324                        ss_info.base() + new_esp - stack_space,
1325                        stack_space
1326                    ));
1327                } else {
1328                    return_on_pagefault!(writable_or_pagefault_cpl(
1329                        cs_info.dpl(),
1330                        ss_info.base() + (new_esp - stack_space & 0xFFFF),
1331                        stack_space
1332                    ));
1333                }
1334
1335                let old_esp = read_reg32(ESP);
1336                let old_ss = *sreg.offset(SS as isize);
1337                let old_stack_pointer = get_stack_pointer(0);
1338
1339                //dbg_log!("old_esp=" + h(old_esp));
1340
1341                *cpl = cs_info.dpl();
1342                cpl_changed();
1343
1344                update_cs_size(cs_info.is_32());
1345
1346                dbg_assert!(new_ss & 3 == cs_info.dpl() as i32);
1347                // XXX: Should be checked before side effects
1348                if !switch_seg(SS, new_ss) {
1349                    dbg_assert!(false);
1350                };
1351                set_stack_reg(new_esp);
1352
1353                //dbg_log!("parameter_count=" + parameter_count);
1354                //dbg_assert!(parameter_count == 0, "TODO");
1355
1356                if is_16 {
1357                    push16(old_ss as i32).unwrap();
1358                    push16(old_esp).unwrap();
1359                } else {
1360                    push32(old_ss as i32).unwrap();
1361                    push32(old_esp).unwrap();
1362                }
1363
1364                if is_call {
1365                    if is_16 {
1366                        for i in (0..parameter_count).rev() {
1367                            let parameter = safe_read16(old_stack_pointer + 2 * i).unwrap();
1368                            push16(parameter).unwrap();
1369                        }
1370
1371                        //writable_or_pagefault(get_stack_pointer(-4), 4);
1372                        push16(*sreg.offset(CS as isize) as i32).unwrap();
1373                        push16(get_real_eip()).unwrap();
1374                    } else {
1375                        for i in (0..parameter_count).rev() {
1376                            let parameter = safe_read32s(old_stack_pointer + 4 * i).unwrap();
1377                            push32(parameter).unwrap();
1378                        }
1379
1380                        //writable_or_pagefault(get_stack_pointer(-8), 8);
1381                        push32(*sreg.offset(CS as isize) as i32).unwrap();
1382                        push32(get_real_eip()).unwrap();
1383                    }
1384                }
1385            } else {
1386                dbg_log!(
1387                    "same privilege call gate is_16={} from={} to={} conforming={}",
1388                    is_16,
1389                    *cpl,
1390                    cs_info.dpl(),
1391                    cs_info.is_dc()
1392                );
1393
1394                if is_call {
1395                    if is_16 {
1396                        return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-4), 4));
1397
1398                        push16(*sreg.offset(CS as isize) as i32).unwrap();
1399                        push16(get_real_eip()).unwrap();
1400                    } else {
1401                        return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-8), 8));
1402
1403                        push32(*sreg.offset(CS as isize) as i32).unwrap();
1404                        push32(get_real_eip()).unwrap();
1405                    }
1406                }
1407
1408                dbg_assert!(*cpl == cs_info.dpl());
1409            }
1410
1411            // Note: eip from call is ignored
1412            let mut new_eip = (info.raw & 0xFFFF) as i32;
1413            if !is_16 {
1414                new_eip |= ((info.raw >> 32) & 0xFFFF0000) as i32;
1415            }
1416
1417            dbg_log!(
1418                "call gate eip={:x} cs={:x} conforming={}",
1419                new_eip as u32,
1420                cs_selector,
1421                cs_info.is_dc()
1422            );
1423            dbg_assert!((new_eip as u32) <= cs_info.effective_limit(), "todo: #gp");
1424
1425            update_cs_size(cs_info.is_32());
1426
1427            *segment_is_null.offset(CS as isize) = false;
1428            *segment_limits.offset(CS as isize) = cs_info.effective_limit();
1429            *segment_offsets.offset(CS as isize) = cs_info.base();
1430            *segment_access_bytes.offset(CS as isize) = cs_info.access_byte();
1431            *sreg.offset(CS as isize) = cs_selector as u16 & !3 | *cpl as u16;
1432            dbg_assert!(*sreg.offset(CS as isize) & 3 == *cpl as u16);
1433
1434            *instruction_pointer = get_seg_cs() + new_eip;
1435
1436            update_state_flags();
1437        } else if info.system_type() == 1 || info.system_type() == 9 {
1438            // available tss
1439            if info.dpl() < *cpl || info.dpl() < cs_selector.rpl() {
1440                dbg_log!("#gp tss dpl < cpl or dpl < rpl: {:x}", selector);
1441                trigger_gp(selector & !3);
1442                return;
1443            }
1444
1445            if !info.is_present() {
1446                dbg_log!("#NP for loading not-present tss sel={:x}", selector);
1447                trigger_np(selector & !3);
1448                return;
1449            }
1450
1451            do_task_switch(
1452                selector,
1453                None,
1454                if is_call {
1455                    TaskSwitchSource::CallOrInt
1456                } else {
1457                    TaskSwitchSource::Jump
1458                },
1459            );
1460        } else if info.system_type() == 5 {
1461            // task gate
1462            if info.dpl() < *cpl || info.dpl() < cs_selector.rpl() {
1463                dbg_log!("#gp task gate dpl < cpl or dpl < rpl: {:x}", selector);
1464                trigger_gp(selector & !3);
1465                return;
1466            }
1467
1468            if !info.is_present() {
1469                dbg_log!("#NP for loading not-present task gate sel={:x}", selector);
1470                trigger_np(selector & !3);
1471                return;
1472            }
1473
1474            let tss_selector = (info.raw >> 16) as i32 & 0xFFFF;
1475            do_task_switch(
1476                tss_selector,
1477                None,
1478                if is_call {
1479                    TaskSwitchSource::CallOrInt
1480                } else {
1481                    TaskSwitchSource::Jump
1482                },
1483            );
1484        } else {
1485            dbg_assert!(false, "TODO: #gp invalid system type");
1486        }
1487    } else {
1488        if !info.is_executable() {
1489            dbg_log!("#gp non-executable cs: {:x}", selector);
1490            trigger_gp(selector & !3);
1491            return;
1492        }
1493
1494        if info.is_dc() {
1495            // conforming code segment
1496            if info.dpl() > *cpl {
1497                dbg_log!("#gp cs dpl > cpl: {:x}", selector);
1498                trigger_gp(selector & !3);
1499                return;
1500            }
1501        } else {
1502            // non-conforming code segment
1503
1504            if cs_selector.rpl() > *cpl || info.dpl() != *cpl {
1505                dbg_log!("#gp cs rpl > cpl or dpl != cpl: {:x}", selector);
1506                trigger_gp(selector & !3);
1507                return;
1508            }
1509        }
1510
1511        if !info.is_present() {
1512            dbg_log!("#NP for loading not-present in cs sel={:x}", selector);
1513            dbg_trace();
1514            trigger_np(selector & !3);
1515            return;
1516        }
1517
1518        if is_call {
1519            if is_osize_32 {
1520                return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-8), 8));
1521
1522                push32(*sreg.offset(CS as isize) as i32).unwrap();
1523                push32(get_real_eip()).unwrap();
1524            } else {
1525                return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-4), 4));
1526
1527                push16(*sreg.offset(CS as isize) as i32).unwrap();
1528                push16(get_real_eip()).unwrap();
1529            }
1530        }
1531
1532        dbg_assert!((eip as u32) <= info.effective_limit(), "todo: #gp");
1533
1534        update_cs_size(info.is_32());
1535
1536        *segment_is_null.offset(CS as isize) = false;
1537        *segment_limits.offset(CS as isize) = info.effective_limit();
1538        *segment_access_bytes.offset(CS as isize) = info.access_byte();
1539
1540        *segment_offsets.offset(CS as isize) = info.base();
1541        *sreg.offset(CS as isize) = selector as u16 & !3 | *cpl as u16;
1542
1543        *instruction_pointer = get_seg_cs() + eip;
1544
1545        update_state_flags();
1546    }
1547}
1548
1549pub unsafe fn far_return(eip: i32, selector: i32, stack_adjust: i32, is_osize_32: bool) {
1550    dbg_assert!(selector < 0x10000 && selector >= 0);
1551
1552    if !*protected_mode {
1553        dbg_assert!(!*is_32);
1554    }
1555
1556    if !*protected_mode || vm86_mode() {
1557        switch_cs_real_mode(selector);
1558        *instruction_pointer = get_seg_cs() + eip;
1559        adjust_stack_reg(2 * (if is_osize_32 { 4 } else { 2 }) + stack_adjust);
1560        update_state_flags();
1561        return;
1562    }
1563
1564    let cs_selector = SegmentSelector::of_u16(selector as u16);
1565    let info = match return_on_pagefault!(lookup_segment_selector(cs_selector)) {
1566        Ok((desc, _)) => desc,
1567        Err(SelectorNullOrInvalid::IsNull) => {
1568            dbg_log!("far return: #gp null cs");
1569            trigger_gp(0);
1570            return;
1571        }
1572        Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
1573            dbg_log!("far return: #gp invalid cs: {:x}", selector);
1574            trigger_gp(selector & !3);
1575            return;
1576        }
1577    };
1578
1579    if info.is_system() {
1580        dbg_assert!(false, "is system in far return");
1581        trigger_gp(selector & !3);
1582        return;
1583    }
1584
1585    if !info.is_executable() {
1586        dbg_log!("non-executable cs: {:x}", selector);
1587        trigger_gp(selector & !3);
1588        return;
1589    }
1590
1591    if cs_selector.rpl() < *cpl {
1592        dbg_log!("cs rpl < cpl: {:x}", selector);
1593        trigger_gp(selector & !3);
1594        return;
1595    }
1596
1597    if info.is_dc() && info.dpl() > cs_selector.rpl() {
1598        dbg_log!("cs conforming and dpl > rpl: {:x}", selector);
1599        trigger_gp(selector & !3);
1600        return;
1601    }
1602
1603    if !info.is_dc() && info.dpl() != cs_selector.rpl() {
1604        dbg_log!("cs non-conforming and dpl != rpl: {:x}", selector);
1605        trigger_gp(selector & !3);
1606        return;
1607    }
1608
1609    if !info.is_present() {
1610        dbg_log!("#NP for loading not-present in cs sel={:x}", selector);
1611        dbg_trace();
1612        trigger_np(selector & !3);
1613        return;
1614    }
1615
1616    if cs_selector.rpl() > *cpl {
1617        dbg_log!(
1618            "far return privilege change cs: {:x} from={} to={} is_16={}",
1619            selector,
1620            *cpl,
1621            cs_selector.rpl(),
1622            is_osize_32
1623        );
1624
1625        let temp_esp;
1626        let temp_ss;
1627        if is_osize_32 {
1628            //dbg_log!("esp read from " + h(translate_address_system_read(get_stack_pointer(stack_adjust + 8))))
1629            temp_esp = safe_read32s(get_stack_pointer(stack_adjust + 8)).unwrap();
1630            //dbg_log!("esp=" + h(temp_esp));
1631            temp_ss = safe_read16(get_stack_pointer(stack_adjust + 12)).unwrap();
1632        } else {
1633            //dbg_log!("esp read from " + h(translate_address_system_read(get_stack_pointer(stack_adjust + 4))));
1634            temp_esp = safe_read16(get_stack_pointer(stack_adjust + 4)).unwrap();
1635            //dbg_log!("esp=" + h(temp_esp));
1636            temp_ss = safe_read16(get_stack_pointer(stack_adjust + 6)).unwrap();
1637        }
1638
1639        *cpl = cs_selector.rpl();
1640        cpl_changed();
1641
1642        // XXX: This failure should be checked before side effects
1643        if !switch_seg(SS, temp_ss) {
1644            dbg_assert!(false);
1645        }
1646        set_stack_reg(temp_esp + stack_adjust);
1647
1648        //if(is_osize_32)
1649        //{
1650        //    adjust_stack_reg(2 * 4);
1651        //}
1652        //else
1653        //{
1654        //    adjust_stack_reg(2 * 2);
1655        //}
1656
1657        //throw debug.unimpl("privilege change");
1658
1659        //adjust_stack_reg(stack_adjust);
1660
1661        for reg in [ES, DS, FS, GS] {
1662            let access = *segment_access_bytes.offset(reg as isize);
1663            let dpl = access >> 5 & 3;
1664            let executable = access & 8 == 8;
1665            let conforming = access & 4 == 4;
1666            if dpl < *cpl && !(executable && conforming) {
1667                *segment_is_null.offset(reg as isize) = true;
1668                *sreg.offset(reg as isize) = 0;
1669            }
1670        }
1671    } else {
1672        if is_osize_32 {
1673            adjust_stack_reg(2 * 4 + stack_adjust);
1674        } else {
1675            adjust_stack_reg(2 * 2 + stack_adjust);
1676        }
1677    }
1678
1679    //dbg_assert(*cpl == info.dpl);
1680
1681    update_cs_size(info.is_32());
1682
1683    *segment_is_null.offset(CS as isize) = false;
1684    *segment_limits.offset(CS as isize) = info.effective_limit();
1685    *segment_access_bytes.offset(CS as isize) = info.access_byte();
1686
1687    *segment_offsets.offset(CS as isize) = info.base();
1688    *sreg.offset(CS as isize) = selector as u16;
1689    dbg_assert!(selector & 3 == *cpl as i32);
1690
1691    *instruction_pointer = get_seg_cs() + eip;
1692
1693    update_state_flags();
1694}
1695
1696#[derive(Copy, Clone, PartialEq)]
1697pub enum TaskSwitchSource {
1698    Jump,
1699    CallOrInt,
1700    Iret,
1701}
1702
1703pub unsafe fn do_task_switch(selector: i32, error_code: Option<i32>, source: TaskSwitchSource) {
1704    dbg_log!("do_task_switch sel={:x}", selector);
1705
1706    dbg_assert!(*tss_size_32, "TODO: 16-bit TSS in task switch");
1707
1708    let selector = SegmentSelector::of_u16(selector as u16);
1709    let (descriptor, descriptor_address) =
1710        match lookup_segment_selector(selector).expect("TODO: handle pagefault") {
1711            Ok(desc) => desc,
1712            Err(_) => {
1713                panic!("#GP handler");
1714            }
1715        };
1716
1717    dbg_assert!(selector.is_gdt());
1718    dbg_assert!((descriptor.system_type() & !2) == 1 || (descriptor.system_type() & !2) == 9);
1719    let tss_is_16 = descriptor.system_type() <= 3;
1720    let tss_is_busy = (descriptor.system_type() & 2) == 2;
1721
1722    if source == TaskSwitchSource::Iret {
1723        if !tss_is_busy {
1724            // a task return must target a busy task
1725            panic!("#TS handler");
1726        }
1727    } else if tss_is_busy {
1728        // jump, call or int to a busy task
1729        panic!("#GP handler");
1730    }
1731
1732    if !descriptor.is_present() {
1733        panic!("#NP handler");
1734    }
1735
1736    if descriptor.effective_limit() < 103 {
1737        panic!("#NP handler");
1738    }
1739
1740    let _tsr_size = *segment_limits.offset(TR as isize);
1741    let tsr_offset = *segment_offsets.offset(TR as isize);
1742
1743    let mut old_eflags = get_eflags();
1744
1745    if tss_is_busy {
1746        old_eflags &= !FLAG_NT;
1747    }
1748
1749    writable_or_pagefault(tsr_offset, 0x66).unwrap();
1750
1751    //safe_write32(tsr_offset + TSR_CR3, *cr.offset(3));
1752
1753    // TODO: Write 16 bit values if old tss is 16 bit
1754    safe_write32(tsr_offset + TSR_EIP, get_real_eip()).unwrap();
1755    safe_write32(tsr_offset + TSR_EFLAGS, old_eflags).unwrap();
1756
1757    safe_write32(tsr_offset + TSR_EAX, read_reg32(EAX)).unwrap();
1758    safe_write32(tsr_offset + TSR_ECX, read_reg32(ECX)).unwrap();
1759    safe_write32(tsr_offset + TSR_EDX, read_reg32(EDX)).unwrap();
1760    safe_write32(tsr_offset + TSR_EBX, read_reg32(EBX)).unwrap();
1761
1762    safe_write32(tsr_offset + TSR_ESP, read_reg32(ESP)).unwrap();
1763    safe_write32(tsr_offset + TSR_EBP, read_reg32(EBP)).unwrap();
1764    safe_write32(tsr_offset + TSR_ESI, read_reg32(ESI)).unwrap();
1765    safe_write32(tsr_offset + TSR_EDI, read_reg32(EDI)).unwrap();
1766
1767    safe_write32(tsr_offset + TSR_ES, *sreg.offset(ES as isize) as i32).unwrap();
1768    safe_write32(tsr_offset + TSR_CS, *sreg.offset(CS as isize) as i32).unwrap();
1769    safe_write32(tsr_offset + TSR_SS, *sreg.offset(SS as isize) as i32).unwrap();
1770    safe_write32(tsr_offset + TSR_DS, *sreg.offset(DS as isize) as i32).unwrap();
1771    safe_write32(tsr_offset + TSR_FS, *sreg.offset(FS as isize) as i32).unwrap();
1772    safe_write32(tsr_offset + TSR_GS, *sreg.offset(GS as isize) as i32).unwrap();
1773
1774    //safe_write32(tsr_offset + TSR_LDT, *sreg.offset(reg_ldtr));
1775
1776    if source == TaskSwitchSource::Jump || source == TaskSwitchSource::Iret {
1777        // mark the old task as not busy
1778        let tr_selector = SegmentSelector::of_u16(*sreg.offset(TR as isize));
1779        let (tr_descriptor, tr_descriptor_address) =
1780            match lookup_segment_selector(tr_selector).expect("TODO: handle pagefault") {
1781                Ok(desc) => desc,
1782                Err(_) => {
1783                    panic!("#TS handler");
1784                }
1785            };
1786        safe_write64(tr_descriptor_address, tr_descriptor.clear_busy().raw).unwrap();
1787    }
1788
1789    if source != TaskSwitchSource::Iret {
1790        // jump, call and int mark the new task as busy (iret would not)
1791        safe_write64(descriptor_address, descriptor.set_busy().raw).unwrap();
1792    }
1793
1794    //let new_tsr_size = descriptor.effective_limit;
1795    let new_tsr_offset = descriptor.base();
1796
1797    dbg_assert!(!tss_is_16, "unimplemented");
1798
1799    if source == TaskSwitchSource::CallOrInt {
1800        safe_write16(
1801            new_tsr_offset + TSR_BACKLINK,
1802            *sreg.offset(TR as isize) as i32,
1803        )
1804        .unwrap();
1805    }
1806
1807    let new_cr3 = safe_read32s(new_tsr_offset + TSR_CR3).unwrap();
1808
1809    *flags &= !FLAG_VM;
1810
1811    let new_eip = safe_read32s(new_tsr_offset + TSR_EIP).unwrap();
1812    let new_cs = safe_read16(new_tsr_offset + TSR_CS).unwrap();
1813    let new_ldt = safe_read16(new_tsr_offset + TSR_LDT).unwrap();
1814
1815    let mut new_eflags = safe_read32s(new_tsr_offset + TSR_EFLAGS).unwrap();
1816    if source == TaskSwitchSource::CallOrInt {
1817        new_eflags |= FLAG_NT;
1818    }
1819
1820    load_ldt(new_ldt).unwrap();
1821
1822    let new_cpl;
1823    if new_eflags & FLAG_VM != 0 {
1824        *segment_is_null.offset(CS as isize) = false;
1825        *segment_offsets.offset(CS as isize) = new_cs << 4;
1826        *sreg.offset(CS as isize) = new_cs as u16;
1827        update_cs_size(false);
1828        new_cpl = 3;
1829    } else {
1830        let new_cs_selector = SegmentSelector::of_u16(new_cs as u16);
1831        let new_cs_descriptor =
1832            match lookup_segment_selector(new_cs_selector).expect("TODO: handle pagefault") {
1833                Ok((desc, _)) => desc,
1834                Err(SelectorNullOrInvalid::IsNull) => {
1835                    dbg_log!("null cs");
1836                    panic!("#TS handler");
1837                }
1838                Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
1839                    dbg_log!("invalid cs: {:x}", new_cs);
1840                    panic!("#TS handler");
1841                }
1842            };
1843
1844        if new_cs_descriptor.is_system() {
1845            panic!("#TS handler");
1846        }
1847
1848        if !new_cs_descriptor.is_executable() {
1849            panic!("#TS handler");
1850        }
1851
1852        if new_cs_descriptor.is_dc() && new_cs_descriptor.dpl() > new_cs_selector.rpl() {
1853            dbg_log!("cs conforming and dpl > rpl: {:x}", selector.raw);
1854            panic!("#TS handler");
1855        }
1856
1857        if !new_cs_descriptor.is_dc() && new_cs_descriptor.dpl() != new_cs_selector.rpl() {
1858            dbg_log!("cs non-conforming and dpl != rpl: {:x}", selector.raw);
1859            panic!("#TS handler");
1860        }
1861
1862        if !new_cs_descriptor.is_present() {
1863            dbg_log!("#NP for loading not-present in cs sel={:x}", selector.raw);
1864            panic!("#TS handler");
1865        }
1866
1867        *segment_is_null.offset(CS as isize) = false;
1868        *segment_limits.offset(CS as isize) = new_cs_descriptor.effective_limit();
1869        *segment_offsets.offset(CS as isize) = new_cs_descriptor.base();
1870        *segment_access_bytes.offset(CS as isize) = new_cs_descriptor.access_byte();
1871        *sreg.offset(CS as isize) = new_cs as u16;
1872
1873        dbg_assert!(
1874            new_eip as u32 <= new_cs_descriptor.effective_limit(),
1875            "todo: #gp"
1876        );
1877        update_cs_size(new_cs_descriptor.is_32());
1878
1879        new_cpl = new_cs_selector.rpl();
1880    }
1881
1882    *cpl = 0; // run update_eflags at cpl 0
1883    update_eflags(new_eflags);
1884
1885    if new_eflags & FLAG_VM != 0 {
1886        *flags |= FLAG_VM;
1887    }
1888    *cpl = new_cpl;
1889    cpl_changed();
1890
1891    if source == TaskSwitchSource::CallOrInt {
1892        *flags |= FLAG_NT;
1893    }
1894
1895    write_reg32(EAX, safe_read32s(new_tsr_offset + TSR_EAX).unwrap());
1896    write_reg32(ECX, safe_read32s(new_tsr_offset + TSR_ECX).unwrap());
1897    write_reg32(EDX, safe_read32s(new_tsr_offset + TSR_EDX).unwrap());
1898    write_reg32(EBX, safe_read32s(new_tsr_offset + TSR_EBX).unwrap());
1899
1900    write_reg32(ESP, safe_read32s(new_tsr_offset + TSR_ESP).unwrap());
1901    write_reg32(EBP, safe_read32s(new_tsr_offset + TSR_EBP).unwrap());
1902    write_reg32(ESI, safe_read32s(new_tsr_offset + TSR_ESI).unwrap());
1903    write_reg32(EDI, safe_read32s(new_tsr_offset + TSR_EDI).unwrap());
1904
1905    if !switch_seg(ES, safe_read16(new_tsr_offset + TSR_ES).unwrap())
1906        || !switch_seg(SS, safe_read16(new_tsr_offset + TSR_SS).unwrap())
1907        || !switch_seg(DS, safe_read16(new_tsr_offset + TSR_DS).unwrap())
1908        || !switch_seg(FS, safe_read16(new_tsr_offset + TSR_FS).unwrap())
1909        || !switch_seg(GS, safe_read16(new_tsr_offset + TSR_GS).unwrap())
1910    {
1911        // XXX: Should be checked before side effects
1912        dbg_assert!(false);
1913    }
1914
1915    *instruction_pointer = get_seg_cs()
1916        + if new_eflags & FLAG_VM != 0 {
1917            new_eip & 0xFFFF
1918        } else {
1919            new_eip
1920        };
1921
1922    *segment_offsets.offset(TR as isize) = descriptor.base();
1923    *segment_limits.offset(TR as isize) = descriptor.effective_limit();
1924    *sreg.offset(TR as isize) = selector.raw;
1925
1926    set_cr3(new_cr3);
1927
1928    *cr.offset(0) |= CR0_TS;
1929
1930    if let Some(error_code) = error_code {
1931        if tss_is_16 {
1932            push16(error_code & 0xFFFF).unwrap();
1933        } else {
1934            push32(error_code).unwrap();
1935        }
1936    }
1937
1938    update_state_flags();
1939}
1940
1941pub unsafe fn after_block_boundary() {
1942    jit_block_boundary = true;
1943}
1944
1945#[no_mangle]
1946pub fn track_jit_exit(phys_addr: u32) {
1947    unsafe {
1948        debug_last_jump = LastJump::Compiled { phys_addr };
1949    }
1950}
1951
1952#[no_mangle]
1953pub unsafe fn get_eflags() -> i32 {
1954    return *flags & !FLAGS_ALL
1955        | getcf() as i32
1956        | (getpf() as i32) << 2
1957        | (getaf() as i32) << 4
1958        | (getzf() as i32) << 6
1959        | (getsf() as i32) << 7
1960        | (getof() as i32) << 11;
1961}
1962
1963pub unsafe fn readable_or_pagefault(addr: i32, size: i32) -> OrPageFault<()> {
1964    dbg_assert!(size < 0x1000);
1965    dbg_assert!(size > 0);
1966
1967    let user = *cpl == 3;
1968    translate_address(addr, false, user, false, true)?;
1969
1970    let end = addr + size - 1 & !0xFFF;
1971    if addr & !0xFFF != end & !0xFFF {
1972        translate_address(end, false, user, false, true)?;
1973    }
1974
1975    return Ok(());
1976}
1977
1978pub unsafe fn writable_or_pagefault(addr: i32, size: i32) -> OrPageFault<()> {
1979    writable_or_pagefault_cpl(*cpl, addr, size)
1980}
1981
1982pub unsafe fn writable_or_pagefault_cpl(other_cpl: u8, addr: i32, size: i32) -> OrPageFault<()> {
1983    dbg_assert!(size < 0x1000);
1984    dbg_assert!(size > 0);
1985
1986    let user = other_cpl == 3;
1987    translate_address(addr, true, user, false, true)?;
1988
1989    let end = addr + size - 1 & !0xFFF;
1990    if addr & !0xFFF != end & !0xFFF {
1991        translate_address(end, true, user, false, true)?;
1992    }
1993
1994    return Ok(());
1995}
1996
1997pub fn translate_address_read_no_side_effects(address: i32) -> OrPageFault<u32> {
1998    unsafe { translate_address(address, false, *cpl == 3, false, false) }
1999}
2000pub fn translate_address_read(address: i32) -> OrPageFault<u32> {
2001    unsafe { translate_address(address, false, *cpl == 3, false, true) }
2002}
2003pub unsafe fn translate_address_read_jit(address: i32) -> OrPageFault<u32> {
2004    translate_address(address, false, *cpl == 3, true, true)
2005}
2006
2007pub unsafe fn translate_address_write(address: i32) -> OrPageFault<u32> {
2008    translate_address(address, true, *cpl == 3, false, true)
2009}
2010pub unsafe fn translate_address_write_jit(address: i32, wasm_table_index: u16) -> OrPageFault<u32> {
2011    let mut entry = tlb_data[(address as u32 >> 12) as usize];
2012    let user = *cpl == 3;
2013    if entry & (TLB_VALID | if user { TLB_NO_USER } else { 0 } | TLB_READONLY) != TLB_VALID {
2014        entry = do_page_walk(address, true, user, true, true)?.get();
2015    }
2016    let has_code = entry & TLB_HAS_CODE != 0;
2017    let phys_addr = ((entry & !0xFFF ^ address) as u32).wrapping_sub(memory::mem8 as u32);
2018    let page = Page::page_of(phys_addr);
2019    if !has_code {
2020        return Ok(phys_addr);
2021    }
2022    let is_smc = jit::jit_page_has_wasm_table_index(page, wasm_table_index);
2023    jit::jit_dirty_page(page);
2024    if !is_smc {
2025        return Ok(phys_addr);
2026    }
2027    dbg_log!(
2028        "SMC: write to addr phys={:x} virt={:x} of the running module {}, exiting",
2029        phys_addr,
2030        address as u32,
2031        wasm_table_index,
2032    );
2033    jit_exit_reason = JitExitReason::SelfModifyingCodeBail;
2034    Err(())
2035}
2036
2037pub unsafe fn translate_address_system_read(address: i32) -> OrPageFault<u32> {
2038    translate_address(address, false, false, false, true)
2039}
2040pub unsafe fn translate_address_system_write(address: i32) -> OrPageFault<u32> {
2041    translate_address(address, true, false, false, true)
2042}
2043
2044#[inline(always)]
2045pub unsafe fn translate_address(
2046    address: i32,
2047    for_writing: bool,
2048    user: bool,
2049    jit: bool,
2050    side_effects: bool,
2051) -> OrPageFault<u32> {
2052    let mut entry = tlb_data[(address as u32 >> 12) as usize];
2053    if entry
2054        & (TLB_VALID
2055            | if user { TLB_NO_USER } else { 0 }
2056            | if for_writing { TLB_READONLY } else { 0 })
2057        != TLB_VALID
2058    {
2059        entry = do_page_walk(address, for_writing, user, jit, side_effects)?.get();
2060    }
2061    Ok(((entry & !0xFFF ^ address) as u32).wrapping_sub(memory::mem8 as u32))
2062}
2063
2064pub unsafe fn translate_address_write_and_can_skip_dirty(address: i32) -> OrPageFault<(u32, bool)> {
2065    let mut entry = tlb_data[(address as u32 >> 12) as usize];
2066    let user = *cpl == 3;
2067    if entry & (TLB_VALID | if user { TLB_NO_USER } else { 0 } | TLB_READONLY) != TLB_VALID {
2068        entry = do_page_walk(address, true, user, false, true)?.get();
2069    }
2070    Ok((
2071        ((entry & !0xFFF ^ address) as u32).wrapping_sub(memory::mem8 as u32),
2072        entry & TLB_HAS_CODE == 0,
2073    ))
2074}
2075
2076// 32-bit paging:
2077// - 10 bits PD | 10 bits PT | 12 bits offset
2078// - 10 bits PD | 22 bits offset (4MB huge page)
2079//
2080// PAE paging:
2081// - 2 bits PDPT | 9 bits PD | 9 bits PT | 12 bits offset
2082// - 2 bits PDPT | 9 bits PD | 21 bits offset (2MB huge page)
2083//
2084// Note that PAE entries are 64-bit, and can describe physical addresses over 32
2085// bits. However, since we support only 32-bit physical addresses, we require
2086// the high half of the entry to be 0.
2087#[cold]
2088pub unsafe fn do_page_walk(
2089    addr: i32,
2090    for_writing: bool,
2091    user: bool,
2092    jit: bool,
2093    side_effects: bool,
2094) -> OrPageFault<std::num::NonZeroI32> {
2095    let global;
2096    let mut allow_user = true;
2097    let page = (addr as u32 >> 12) as i32;
2098    let high;
2099
2100    let cr0 = *cr;
2101    let cr4 = *cr.offset(4);
2102
2103    if cr0 & CR0_PG == 0 {
2104        // paging disabled
2105        high = addr as u32 & 0xFFFFF000;
2106        global = false
2107    } else {
2108        profiler::stat_increment(stat::TLB_MISS);
2109
2110        let pae = cr4 & CR4_PAE != 0;
2111
2112        let (page_dir_addr, page_dir_entry) = if pae {
2113            let pdpt_entry = *reg_pdpte.offset(((addr as u32) >> 30) as isize);
2114            if pdpt_entry as i32 & PAGE_TABLE_PRESENT_MASK == 0 {
2115                if side_effects {
2116                    trigger_pagefault(addr, false, for_writing, user, jit);
2117                }
2118                return Err(());
2119            }
2120
2121            let page_dir_addr =
2122                (pdpt_entry as u32 & 0xFFFFF000) + ((((addr as u32) >> 21) & 0x1FF) << 3);
2123            let page_dir_entry = memory::read64s(page_dir_addr);
2124            dbg_assert!(
2125                page_dir_entry as u64 & 0x7FFF_FFFF_0000_0000 == 0,
2126                "Unsupported: Page directory entry larger than 32 bits"
2127            );
2128            dbg_assert!(
2129                page_dir_entry & 0x8000_0000_0000_0000u64 as i64 == 0,
2130                "Unsupported: NX bit"
2131            );
2132
2133            (page_dir_addr, page_dir_entry as i32)
2134        } else {
2135            let page_dir_addr = *cr.offset(3) as u32 + (((addr as u32) >> 22) << 2);
2136            let page_dir_entry = memory::read32s(page_dir_addr);
2137            (page_dir_addr, page_dir_entry)
2138        };
2139
2140        if page_dir_entry & PAGE_TABLE_PRESENT_MASK == 0 {
2141            if side_effects {
2142                trigger_pagefault(addr, false, for_writing, user, jit);
2143            }
2144            return Err(());
2145        }
2146
2147        let kernel_write_override = !user && 0 == cr0 & CR0_WP;
2148        let mut allow_write = page_dir_entry & PAGE_TABLE_RW_MASK != 0;
2149        allow_user &= page_dir_entry & PAGE_TABLE_USER_MASK != 0;
2150
2151        if 0 != page_dir_entry & PAGE_TABLE_PSE_MASK && 0 != cr4 & CR4_PSE {
2152            // size bit is set
2153
2154            if for_writing && !allow_write && !kernel_write_override || user && !allow_user {
2155                if side_effects {
2156                    trigger_pagefault(addr, true, for_writing, user, jit);
2157                }
2158                return Err(());
2159            }
2160
2161            // set the accessed and dirty bits
2162
2163            let new_page_dir_entry = page_dir_entry
2164                | PAGE_TABLE_ACCESSED_MASK
2165                | if for_writing {
2166                    PAGE_TABLE_DIRTY_MASK
2167                } else {
2168                    0
2169                };
2170
2171            if side_effects && page_dir_entry != new_page_dir_entry {
2172                memory::write8(page_dir_addr, new_page_dir_entry);
2173            }
2174
2175            high = if pae {
2176                page_dir_entry as u32 & 0xFFE00000 | (addr & 0x1FF000) as u32
2177            } else {
2178                page_dir_entry as u32 & 0xFFC00000 | (addr & 0x3FF000) as u32
2179            };
2180            global = page_dir_entry & PAGE_TABLE_GLOBAL_MASK == PAGE_TABLE_GLOBAL_MASK
2181        } else {
2182            let (page_table_addr, page_table_entry) = if pae {
2183                let page_table_addr =
2184                    (page_dir_entry as u32 & 0xFFFFF000) + (((addr as u32 >> 12) & 0x1FF) << 3);
2185                let page_table_entry = memory::read64s(page_table_addr);
2186                dbg_assert!(
2187                    page_table_entry as u64 & 0x7FFF_FFFF_0000_0000 == 0,
2188                    "Unsupported: Page table entry larger than 32 bits"
2189                );
2190                dbg_assert!(
2191                    page_table_entry & 0x8000_0000_0000_0000u64 as i64 == 0,
2192                    "Unsupported: NX bit"
2193                );
2194
2195                (page_table_addr, page_table_entry as i32)
2196            } else {
2197                let page_table_addr =
2198                    (page_dir_entry as u32 & 0xFFFFF000) + (((addr as u32 >> 12) & 0x3FF) << 2);
2199                let page_table_entry = memory::read32s(page_table_addr);
2200                (page_table_addr, page_table_entry)
2201            };
2202
2203            let present = page_table_entry & PAGE_TABLE_PRESENT_MASK != 0;
2204            allow_write &= page_table_entry & PAGE_TABLE_RW_MASK != 0;
2205            allow_user &= page_table_entry & PAGE_TABLE_USER_MASK != 0;
2206
2207            if !present
2208                || for_writing && !allow_write && !kernel_write_override
2209                || user && !allow_user
2210            {
2211                if side_effects {
2212                    trigger_pagefault(addr, present, for_writing, user, jit);
2213                }
2214                return Err(());
2215            }
2216
2217            // Set the accessed and dirty bits
2218            // Note: dirty bit is only set on the page table entry
2219            let new_page_dir_entry = page_dir_entry | PAGE_TABLE_ACCESSED_MASK;
2220            if side_effects && new_page_dir_entry != page_dir_entry {
2221                memory::write8(page_dir_addr, new_page_dir_entry);
2222            }
2223            let new_page_table_entry = page_table_entry
2224                | PAGE_TABLE_ACCESSED_MASK
2225                | if for_writing {
2226                    PAGE_TABLE_DIRTY_MASK
2227                } else {
2228                    0
2229                };
2230            if side_effects && page_table_entry != new_page_table_entry {
2231                memory::write8(page_table_addr, new_page_table_entry);
2232            }
2233
2234            high = page_table_entry as u32 & 0xFFFFF000;
2235            global = page_table_entry & PAGE_TABLE_GLOBAL_MASK == PAGE_TABLE_GLOBAL_MASK
2236        }
2237    }
2238
2239    if side_effects && tlb_data[page as usize] == 0 {
2240        if valid_tlb_entries_count == VALID_TLB_ENTRY_MAX {
2241            profiler::stat_increment(stat::TLB_FULL);
2242            clear_tlb();
2243            // also clear global entries if tlb is almost full after clearing non-global pages
2244            if valid_tlb_entries_count > VALID_TLB_ENTRY_MAX * 3 / 4 {
2245                profiler::stat_increment(stat::TLB_GLOBAL_FULL);
2246                full_clear_tlb();
2247            }
2248        }
2249        dbg_assert!(valid_tlb_entries_count < VALID_TLB_ENTRY_MAX);
2250        valid_tlb_entries[valid_tlb_entries_count as usize] = page;
2251        valid_tlb_entries_count += 1;
2252    // TODO: Check that there are no duplicates in valid_tlb_entries
2253    // XXX: There will probably be duplicates due to invlpg deleting
2254    // entries from tlb_data but not from valid_tlb_entries
2255    } else if side_effects && CHECK_TLB_INVARIANTS {
2256        let mut found = false;
2257        for i in 0..valid_tlb_entries_count {
2258            if valid_tlb_entries[i as usize] == page {
2259                found = true;
2260                break;
2261            }
2262        }
2263        dbg_assert!(found);
2264    }
2265
2266    let is_in_mapped_range = memory::in_mapped_range(high);
2267    let has_code = if side_effects {
2268        !is_in_mapped_range && jit::jit_page_has_code(Page::page_of(high))
2269    } else {
2270        // If side_effects is false, don't call into jit::jit_page_has_code. This value is not used
2271        // anyway (we only get here by translate_address_read_no_side_effects, which only uses the
2272        // address part)
2273        true
2274    };
2275    let info_bits = TLB_VALID
2276        | if for_writing { 0 } else { TLB_READONLY }
2277        | if allow_user { 0 } else { TLB_NO_USER }
2278        | if is_in_mapped_range {
2279            TLB_IN_MAPPED_RANGE
2280        } else {
2281            0
2282        }
2283        | if global && 0 != cr4 & CR4_PGE {
2284            TLB_GLOBAL
2285        } else {
2286            0
2287        }
2288        | if has_code { TLB_HAS_CODE } else { 0 };
2289
2290    let tlb_entry = high.wrapping_add(memory::mem8 as u32) as i32 ^ page << 12 | info_bits as i32;
2291
2292    dbg_assert!((high ^ (page as u32) << 12) & 0xFFF == 0);
2293    if side_effects {
2294        // bake in the addition with memory::mem8 to save an instruction from the fast path
2295        // of memory accesses
2296        tlb_data[page as usize] = tlb_entry;
2297
2298        jit::update_tlb_code(Page::page_of(addr as u32), Page::page_of(high));
2299    }
2300
2301    Ok(if DEBUG {
2302        std::num::NonZeroI32::new(tlb_entry).unwrap()
2303    } else {
2304        std::num::NonZeroI32::new_unchecked(tlb_entry)
2305    })
2306}
2307
2308#[no_mangle]
2309pub unsafe fn full_clear_tlb() {
2310    profiler::stat_increment(stat::FULL_CLEAR_TLB);
2311    // clear tlb including global pages
2312    *last_virt_eip = -1;
2313    for i in 0..valid_tlb_entries_count {
2314        let page = valid_tlb_entries[i as usize];
2315        clear_tlb_code(page);
2316        tlb_data[page as usize] = 0;
2317    }
2318    valid_tlb_entries_count = 0;
2319
2320    if CHECK_TLB_INVARIANTS {
2321        #[allow(static_mut_refs)]
2322        for &entry in tlb_data.iter() {
2323            dbg_assert!(entry == 0);
2324        }
2325    };
2326}
2327
2328#[no_mangle]
2329pub unsafe fn clear_tlb() {
2330    profiler::stat_increment(stat::CLEAR_TLB);
2331    // clear tlb excluding global pages
2332    *last_virt_eip = -1;
2333    let mut global_page_offset = 0;
2334    for i in 0..valid_tlb_entries_count {
2335        let page = valid_tlb_entries[i as usize];
2336        let entry = tlb_data[page as usize];
2337        if 0 != entry & TLB_GLOBAL {
2338            // reinsert at the front
2339            valid_tlb_entries[global_page_offset as usize] = page;
2340            global_page_offset += 1;
2341        } else {
2342            clear_tlb_code(page);
2343            tlb_data[page as usize] = 0;
2344        }
2345    }
2346    valid_tlb_entries_count = global_page_offset;
2347
2348    if CHECK_TLB_INVARIANTS {
2349        #[allow(static_mut_refs)]
2350        for &entry in tlb_data.iter() {
2351            dbg_assert!(entry == 0 || 0 != entry & TLB_GLOBAL);
2352        }
2353    };
2354}
2355
2356#[no_mangle]
2357pub unsafe fn trigger_de_jit(eip_offset_in_page: i32) {
2358    dbg_log!("#de in jit mode");
2359    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
2360    *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
2361    jit_exit_reason = JitExitReason::CpuException {
2362        code: CPU_EXCEPTION_DE,
2363        error_code: None,
2364    }
2365}
2366
2367#[no_mangle]
2368pub unsafe fn trigger_ud_jit(eip_offset_in_page: i32) {
2369    dbg_log!("#ud in jit mode");
2370    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
2371    *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
2372    jit_exit_reason = JitExitReason::CpuException {
2373        code: CPU_EXCEPTION_UD,
2374        error_code: None,
2375    }
2376}
2377
2378#[no_mangle]
2379pub unsafe fn trigger_nm_jit(eip_offset_in_page: i32) {
2380    dbg_log!("#nm in jit mode");
2381    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
2382    *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
2383    jit_exit_reason = JitExitReason::CpuException {
2384        code: CPU_EXCEPTION_NM,
2385        error_code: None,
2386    }
2387}
2388
2389#[no_mangle]
2390pub unsafe fn trigger_gp_jit(code: i32, eip_offset_in_page: i32) {
2391    dbg_log!("#gp in jit mode");
2392    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
2393    *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
2394    jit_exit_reason = JitExitReason::CpuException {
2395        code: CPU_EXCEPTION_GP,
2396        error_code: Some(code),
2397    }
2398}
2399
2400#[no_mangle]
2401pub unsafe fn exit_jit() {
2402    #[allow(static_mut_refs)]
2403    let (code, error_code) = match std::mem::replace(&mut jit_exit_reason, JitExitReason::None) {
2404        JitExitReason::CpuException { code, error_code } => (code, error_code),
2405        JitExitReason::SelfModifyingCodeBail => return,
2406        JitExitReason::None => {
2407            dbg_assert!(false, "exit_jit without exit reason");
2408            return;
2409        }
2410    };
2411    if DEBUG {
2412        if js::cpu_exception_hook(code) {
2413            return;
2414        }
2415    }
2416    call_interrupt_vector(code, false, error_code);
2417}
2418
2419/// Pagefault handling with the jit works as follows:
2420/// - If the slow path is taken, it calls safe_{read,write}*_jit
2421/// - safe_{read,write}*_jit call translate_address_{read,write}_jit
2422/// - translate_address_{read,write}_jit do the normal page walk and call this method with
2423///   jit=true when a page fault happens
2424/// - this method prepares a page fault by setting cr2, and writes the error code
2425///   into jit_exit_reason. This method *doesn't* trigger the interrupt, as registers are
2426///   still stored in the wasm module
2427/// - back in the wasm module, the generated code detects the page fault, restores the registers
2428///   and finally calls exit_jit, which does the interrupt
2429///
2430/// Non-jit resets the instruction pointer and does the PF interrupt directly
2431pub unsafe fn trigger_pagefault(addr: i32, present: bool, write: bool, user: bool, jit: bool) {
2432    if config::LOG_PAGE_FAULTS {
2433        dbg_log!(
2434            "page fault{} w={} u={} p={} eip={:x} cr2={:x}",
2435            if jit { "jit" } else { "" },
2436            write as i32,
2437            user as i32,
2438            present as i32,
2439            *previous_ip,
2440            addr
2441        );
2442        dbg_trace();
2443    }
2444    profiler::stat_increment(stat::PAGE_FAULT);
2445    *cr.offset(2) = addr;
2446    // invalidate tlb entry
2447    let page = ((addr as u32) >> 12) as i32;
2448    clear_tlb_code(page);
2449    tlb_data[page as usize] = 0;
2450    let error_code = (user as i32) << 2 | (write as i32) << 1 | present as i32;
2451    if jit {
2452        jit_exit_reason = JitExitReason::CpuException {
2453            code: CPU_EXCEPTION_PF,
2454            error_code: Some(error_code),
2455        };
2456    } else {
2457        *instruction_pointer = *previous_ip;
2458        call_interrupt_vector(CPU_EXCEPTION_PF, false, Some(error_code));
2459    }
2460}
2461
2462pub fn tlb_set_has_code(physical_page: Page, has_code: bool) {
2463    for i in 0..unsafe { valid_tlb_entries_count } {
2464        let page = unsafe { valid_tlb_entries[i as usize] };
2465        let entry = unsafe { tlb_data[page as usize] };
2466        if 0 != entry {
2467            let tlb_physical_page = Page::of_u32(
2468                (entry as u32 >> 12 ^ page as u32) - (unsafe { memory::mem8 } as u32 >> 12),
2469            );
2470            if physical_page == tlb_physical_page {
2471                unsafe {
2472                    tlb_data[page as usize] = if has_code {
2473                        entry | TLB_HAS_CODE
2474                    } else {
2475                        entry & !TLB_HAS_CODE
2476                    }
2477                }
2478                if !has_code {
2479                    clear_tlb_code(page);
2480                }
2481            }
2482        }
2483    }
2484
2485    check_tlb_invariants();
2486}
2487pub fn tlb_set_has_code_multiple(physical_pages: &HashSet<Page>, has_code: bool) {
2488    let physical_pages: Vec<Page> = physical_pages.into_iter().copied().collect();
2489    for i in 0..unsafe { valid_tlb_entries_count } {
2490        let page = unsafe { valid_tlb_entries[i as usize] };
2491        let entry = unsafe { tlb_data[page as usize] };
2492        if 0 != entry {
2493            let tlb_physical_page = Page::of_u32(
2494                (entry as u32 >> 12 ^ page as u32) - (unsafe { memory::mem8 } as u32 >> 12),
2495            );
2496            if physical_pages.contains(&tlb_physical_page) {
2497                unsafe {
2498                    tlb_data[page as usize] = if has_code {
2499                        entry | TLB_HAS_CODE
2500                    } else {
2501                        entry & !TLB_HAS_CODE
2502                    }
2503                }
2504            }
2505        }
2506    }
2507
2508    check_tlb_invariants();
2509}
2510
2511pub fn check_tlb_invariants() {
2512    if !CHECK_TLB_INVARIANTS {
2513        return;
2514    }
2515
2516    for i in 0..unsafe { valid_tlb_entries_count } {
2517        let page = unsafe { valid_tlb_entries[i as usize] };
2518        let entry = unsafe { tlb_data[page as usize] };
2519
2520        if 0 == entry || 0 != entry & TLB_IN_MAPPED_RANGE {
2521            // there's no code in mapped memory
2522            continue;
2523        }
2524
2525        let target = (entry ^ page << 12) as u32 - unsafe { memory::mem8 } as u32;
2526        dbg_assert!(!memory::in_mapped_range(target));
2527
2528        let entry_has_code = entry & TLB_HAS_CODE != 0;
2529        let has_code = jit::jit_page_has_code(Page::page_of(target));
2530
2531        // If some code has been created in a page, the corresponding tlb entries must be marked
2532        dbg_assert!(!has_code || entry_has_code);
2533    }
2534}
2535
2536pub const DISABLE_EIP_TRANSLATION_OPTIMISATION: bool = false;
2537
2538pub unsafe fn read_imm8() -> OrPageFault<i32> {
2539    let eip = *instruction_pointer;
2540    if DISABLE_EIP_TRANSLATION_OPTIMISATION || 0 != eip & !0xFFF ^ *last_virt_eip {
2541        *eip_phys = (translate_address_read(eip)? ^ eip as u32) as i32;
2542        *last_virt_eip = eip & !0xFFF
2543    }
2544    dbg_assert!(!memory::in_mapped_range((*eip_phys ^ eip) as u32));
2545    let data8 = *memory::mem8.offset((*eip_phys ^ eip) as isize) as i32;
2546    *instruction_pointer = eip + 1;
2547    return Ok(data8);
2548}
2549
2550pub unsafe fn read_imm8s() -> OrPageFault<i32> {
2551    return Ok(read_imm8()? << 24 >> 24);
2552}
2553
2554pub unsafe fn read_imm16() -> OrPageFault<i32> {
2555    // Two checks in one comparison:
2556    // 1. Did the high 20 bits of eip change
2557    // or 2. Are the low 12 bits of eip 0xFFF (and this read crosses a page boundary)
2558    if DISABLE_EIP_TRANSLATION_OPTIMISATION
2559        || (*instruction_pointer ^ *last_virt_eip) as u32 > 0xFFE
2560    {
2561        return Ok(read_imm8()? | read_imm8()? << 8);
2562    } else {
2563        let data16 = memory::read16((*eip_phys ^ *instruction_pointer) as u32);
2564        *instruction_pointer = *instruction_pointer + 2;
2565        return Ok(data16);
2566    };
2567}
2568
2569pub unsafe fn read_imm32s() -> OrPageFault<i32> {
2570    // Analogue to the above comment
2571    if DISABLE_EIP_TRANSLATION_OPTIMISATION
2572        || (*instruction_pointer ^ *last_virt_eip) as u32 > 0xFFC
2573    {
2574        return Ok(read_imm16()? | read_imm16()? << 16);
2575    } else {
2576        let data32 = memory::read32s((*eip_phys ^ *instruction_pointer) as u32);
2577        *instruction_pointer = *instruction_pointer + 4;
2578        return Ok(data32);
2579    };
2580}
2581
2582pub unsafe fn is_osize_32() -> bool {
2583    dbg_assert!(!in_jit);
2584    return *is_32 != (*prefixes & prefix::PREFIX_MASK_OPSIZE == prefix::PREFIX_MASK_OPSIZE);
2585}
2586
2587pub unsafe fn is_asize_32() -> bool {
2588    dbg_assert!(!in_jit);
2589    return *is_32 != (*prefixes & prefix::PREFIX_MASK_ADDRSIZE == prefix::PREFIX_MASK_ADDRSIZE);
2590}
2591
2592pub unsafe fn lookup_segment_selector(
2593    selector: SegmentSelector,
2594) -> OrPageFault<Result<(SegmentDescriptor, i32), SelectorNullOrInvalid>> {
2595    if selector.is_null() {
2596        return Ok(Err(SelectorNullOrInvalid::IsNull));
2597    }
2598
2599    let (table_offset, table_limit) = if selector.is_gdt() {
2600        (*gdtr_offset as u32, *gdtr_size as u32)
2601    } else {
2602        (
2603            *segment_offsets.offset(LDTR as isize) as u32,
2604            *segment_limits.offset(LDTR as isize) as u32,
2605        )
2606    };
2607
2608    if selector.descriptor_offset() as u32 > table_limit {
2609        dbg_log!(
2610            "segment outside of table limit: selector={:x} offset={:x} isgdt={} table_limit={:x}",
2611            selector.raw,
2612            selector.descriptor_offset(),
2613            selector.is_gdt(),
2614            table_limit
2615        );
2616        return Ok(Err(SelectorNullOrInvalid::OutsideOfTableLimit));
2617    }
2618
2619    let descriptor_address = selector.descriptor_offset() as i32 + table_offset as i32;
2620
2621    let descriptor = SegmentDescriptor::of_u64(memory::read64s(translate_address_system_read(
2622        descriptor_address,
2623    )?) as u64);
2624
2625    Ok(Ok((descriptor, descriptor_address)))
2626}
2627
2628#[inline(never)]
2629pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
2630    dbg_assert!(reg >= 0 && reg <= 5);
2631    dbg_assert!(reg != CS);
2632    dbg_assert!(selector_raw >= 0 && selector_raw < 0x10000);
2633
2634    if vm86_mode() {
2635        // TODO: Should set segment_limits and segment_access_bytes if ever implemented in get_seg
2636        //       (only vm86, not in real mode)
2637    }
2638
2639    if !*protected_mode || vm86_mode() {
2640        *sreg.offset(reg as isize) = selector_raw as u16;
2641        *segment_is_null.offset(reg as isize) = false;
2642        *segment_offsets.offset(reg as isize) = selector_raw << 4;
2643
2644        if reg == SS {
2645            *stack_size_32 = false;
2646        }
2647        update_state_flags();
2648        return true;
2649    }
2650
2651    let selector = SegmentSelector::of_u16(selector_raw as u16);
2652    let (mut descriptor, descriptor_address) =
2653        match return_on_pagefault!(lookup_segment_selector(selector), false) {
2654            Ok(desc) => desc,
2655            Err(SelectorNullOrInvalid::IsNull) => {
2656                if reg == SS {
2657                    dbg_log!("#GP for loading 0 in SS sel={:x}", selector_raw);
2658                    trigger_gp(0);
2659                    return false;
2660                } else {
2661                    // es, ds, fs, gs
2662                    *sreg.offset(reg as isize) = selector_raw as u16;
2663                    *segment_is_null.offset(reg as isize) = true;
2664                    update_state_flags();
2665                    return true;
2666                }
2667            }
2668            Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
2669                dbg_log!(
2670                    "#GP for loading invalid in seg={} sel={:x}",
2671                    reg,
2672                    selector_raw,
2673                );
2674                dbg_trace();
2675                trigger_gp(selector_raw & !3);
2676                return false;
2677            }
2678        };
2679
2680    if reg == SS {
2681        if descriptor.is_system()
2682            || selector.rpl() != *cpl
2683            || !descriptor.is_writable()
2684            || descriptor.dpl() != *cpl
2685        {
2686            dbg_log!("#GP for loading invalid in SS sel={:x}", selector_raw);
2687            trigger_gp(selector_raw & !3);
2688            return false;
2689        }
2690
2691        if !descriptor.is_present() {
2692            dbg_log!("#SS for loading non-present in SS sel={:x}", selector_raw);
2693            trigger_ss(selector_raw & !3);
2694            return false;
2695        }
2696
2697        *stack_size_32 = descriptor.is_32();
2698    } else {
2699        if descriptor.is_system()
2700            || !descriptor.is_readable()
2701            || (!descriptor.is_conforming_executable()
2702                && (selector.rpl() > descriptor.dpl() || *cpl > descriptor.dpl()))
2703        {
2704            dbg_log!(
2705                "#GP for loading invalid in seg {} sel={:x} sys={} readable={} dc={} exec={} rpl={} dpl={} cpl={} present={} paging={}",
2706                reg,
2707                selector_raw,
2708                descriptor.is_system(),
2709                descriptor.is_readable(),
2710                descriptor.is_dc(),
2711                descriptor.is_executable(),
2712                selector.rpl(),
2713                descriptor.dpl(),
2714                *cpl,
2715                descriptor.is_present(),
2716                *cr & CR0_PG != 0,
2717            );
2718            dbg_trace();
2719            trigger_gp(selector_raw & !3);
2720            return false;
2721        }
2722
2723        if !descriptor.is_present() {
2724            dbg_log!(
2725                "#NP for loading not-present in seg {} sel={:x}",
2726                reg,
2727                selector_raw,
2728            );
2729            trigger_np(selector_raw & !3);
2730            return false;
2731        }
2732    }
2733
2734    if !descriptor.accessed() {
2735        descriptor = descriptor.set_accessed();
2736
2737        memory::write8(
2738            translate_address_system_write(descriptor_address + 5).unwrap(),
2739            descriptor.access_byte() as i32,
2740        );
2741    }
2742
2743    *segment_is_null.offset(reg as isize) = false;
2744    *segment_limits.offset(reg as isize) = descriptor.effective_limit();
2745    *segment_offsets.offset(reg as isize) = descriptor.base();
2746    *segment_access_bytes.offset(reg as isize) = descriptor.access_byte();
2747    *sreg.offset(reg as isize) = selector_raw as u16;
2748
2749    update_state_flags();
2750
2751    true
2752}
2753
2754pub unsafe fn load_tr(selector: i32) {
2755    let selector = SegmentSelector::of_u16(selector as u16);
2756    dbg_assert!(selector.is_gdt(), "TODO: TR can only be loaded from GDT");
2757
2758    let (descriptor, descriptor_address) =
2759        match return_on_pagefault!(lookup_segment_selector(selector)) {
2760            Ok((desc, addr)) => (desc, addr),
2761            Err(SelectorNullOrInvalid::IsNull) => {
2762                panic!("TODO: null TR");
2763            }
2764            Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
2765                panic!("TODO: TR selector outside of table limit");
2766            }
2767        };
2768
2769    //dbg_log!(
2770    //    "load tr: {:x} offset={:x} limit={:x} is32={}",
2771    //    selector.raw,
2772    //    descriptor.base(),
2773    //    descriptor.effective_limit(),
2774    //    descriptor.system_type() == 9,
2775    //);
2776
2777    if !descriptor.is_system() {
2778        panic!("#GP | ltr: not a system entry (happens when running kvm-unit-test without ACPI)");
2779    }
2780
2781    if descriptor.system_type() != 9 && descriptor.system_type() != 1 {
2782        // 0xB: busy 386 TSS (GP)
2783        // 0x9: 386 TSS
2784        // 0x3: busy 286 TSS (GP)
2785        // 0x1: 286 TSS (??)
2786        panic!(
2787            "#GP | ltr: invalid type (type = 0x{:x})",
2788            descriptor.system_type()
2789        );
2790    }
2791
2792    if !descriptor.is_present() {
2793        panic!("#NT | present bit not set (ltr)");
2794    }
2795
2796    *tss_size_32 = descriptor.system_type() == 9;
2797    *segment_limits.offset(TR as isize) = descriptor.effective_limit();
2798    *segment_offsets.offset(TR as isize) = descriptor.base();
2799    *sreg.offset(TR as isize) = selector.raw;
2800
2801    // Mark task as busy
2802    memory::write8(
2803        translate_address_system_write(descriptor_address + 5).unwrap(),
2804        descriptor.set_busy().access_byte() as i32,
2805    );
2806}
2807
2808pub unsafe fn load_ldt(selector: i32) -> OrPageFault<()> {
2809    let selector = SegmentSelector::of_u16(selector as u16);
2810
2811    if selector.is_null() {
2812        dbg_log!("lldt: null loaded");
2813        *segment_limits.offset(LDTR as isize) = 0;
2814        *segment_offsets.offset(LDTR as isize) = 0;
2815        *sreg.offset(LDTR as isize) = selector.raw;
2816        return Ok(());
2817    }
2818
2819    dbg_assert!(selector.is_gdt(), "TODO: LDT can only be loaded from GDT");
2820
2821    let (descriptor, _) = match lookup_segment_selector(selector)? {
2822        Ok((desc, addr)) => (desc, addr),
2823        Err(SelectorNullOrInvalid::IsNull) => {
2824            panic!("TODO: null TR");
2825        }
2826        Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
2827            panic!("TODO: TR selector outside of table limit");
2828        }
2829    };
2830
2831    if !descriptor.is_present() {
2832        panic!("#NT | present bit not set (lldt)");
2833    }
2834
2835    if !descriptor.is_system() {
2836        panic!("#GP | lldt: not a system entry");
2837    }
2838
2839    if descriptor.system_type() != 2 {
2840        panic!(
2841            "#GP | lldt: invalid type (type = 0x{:x})",
2842            descriptor.system_type()
2843        );
2844    }
2845
2846    dbg_log!(
2847        "lldt: {:x} offset={:x} limit={:x}",
2848        selector.raw,
2849        descriptor.base(),
2850        descriptor.effective_limit()
2851    );
2852    *segment_limits.offset(LDTR as isize) = descriptor.effective_limit();
2853    *segment_offsets.offset(LDTR as isize) = descriptor.base();
2854    *sreg.offset(LDTR as isize) = selector.raw;
2855
2856    Ok(())
2857}
2858
2859#[no_mangle]
2860#[cfg(feature = "profiler")]
2861pub unsafe fn log_segment_null(segment: i32) {
2862    dbg_assert!(segment >= 0 && segment < 8);
2863    if *segment_is_null.offset(segment as isize) {
2864        dbg_assert!(segment != CS && segment != SS);
2865        dbg_log!("#gp: Access null segment in jit");
2866    }
2867}
2868
2869pub unsafe fn get_seg(segment: i32) -> OrPageFault<i32> {
2870    dbg_assert!(segment >= 0 && segment < 8);
2871    if *segment_is_null.offset(segment as isize) {
2872        dbg_assert!(segment != CS && segment != SS);
2873        dbg_log!("#gp: Access null segment {}", segment);
2874        dbg_trace();
2875        dbg_assert!(!in_jit);
2876        trigger_gp(0);
2877        return Err(());
2878    }
2879    return Ok(*segment_offsets.offset(segment as isize));
2880}
2881
2882pub unsafe fn set_cr0(cr0: i32) {
2883    let old_cr0 = *cr;
2884
2885    if old_cr0 & CR0_AM == 0 && cr0 & CR0_AM != 0 {
2886        dbg_log!("Warning: Unimplemented: cr0 alignment mask");
2887    }
2888    if (cr0 & (CR0_PE | CR0_PG)) == CR0_PG {
2889        panic!("cannot load PG without PE");
2890    }
2891
2892    *cr = cr0;
2893    *cr |= CR0_ET;
2894
2895    if old_cr0 & (CR0_PG | CR0_WP) != cr0 & (CR0_PG | CR0_WP) {
2896        full_clear_tlb();
2897    }
2898
2899    if *cr.offset(4) & CR4_PAE != 0
2900        && old_cr0 & (CR0_CD | CR0_NW | CR0_PG) != cr0 & (CR0_CD | CR0_NW | CR0_PG)
2901    {
2902        load_pdpte(*cr.offset(3))
2903    }
2904
2905    *protected_mode = (*cr & CR0_PE) == CR0_PE;
2906    *segment_access_bytes.offset(CS as isize) = 0x80 | 0x10 | 0x08 | 0x02; // P dpl0 S E RW
2907}
2908
2909pub unsafe fn set_cr3(mut cr3: i32) {
2910    if false {
2911        dbg_log!("cr3 <- {:x}", cr3);
2912    }
2913    if *cr.offset(4) & CR4_PAE != 0 {
2914        cr3 &= !0b1111;
2915        load_pdpte(cr3);
2916    } else {
2917        cr3 &= !0b111111100111;
2918        dbg_assert!(cr3 & 0xFFF == 0, "TODO");
2919    }
2920    *cr.offset(3) = cr3;
2921    clear_tlb();
2922}
2923
2924pub unsafe fn load_pdpte(cr3: i32) {
2925    dbg_assert!(cr3 & 0b1111 == 0);
2926    for i in 0..4 {
2927        let mut pdpt_entry = memory::read64s(cr3 as u32 + 8 * i as u32) as u64;
2928        pdpt_entry &= !0b1110_0000_0000;
2929        dbg_assert!(pdpt_entry & 0b11000 == 0, "TODO");
2930        dbg_assert!(
2931            pdpt_entry as u64 & 0xFFFF_FFFF_0000_0000 == 0,
2932            "Unsupported: PDPT entry larger than 32 bits"
2933        );
2934        if pdpt_entry as i32 & PAGE_TABLE_PRESENT_MASK != 0 {
2935            dbg_assert!(
2936                pdpt_entry & 0b1_1110_0110 == 0,
2937                "TODO: #gp reserved bit in pdpte"
2938            );
2939        }
2940        *reg_pdpte.offset(i) = pdpt_entry;
2941    }
2942}
2943
2944pub unsafe fn cpl_changed() {
2945    *last_virt_eip = -1
2946}
2947
2948pub unsafe fn update_cs_size(new_size: bool) {
2949    if *is_32 != new_size {
2950        *is_32 = new_size;
2951    }
2952}
2953
2954#[inline(never)]
2955pub unsafe fn test_privileges_for_io(port: i32, size: i32) -> bool {
2956    if *protected_mode && (*cpl > getiopl() as u8 || (*flags & FLAG_VM != 0)) {
2957        if !*tss_size_32 {
2958            dbg_log!("#GP for port io, 16-bit TSS  port={:x} size={}", port, size);
2959            trigger_gp(0);
2960            return false;
2961        }
2962
2963        let tsr_size = *segment_limits.offset(TR as isize);
2964        let tsr_offset = *segment_offsets.offset(TR as isize);
2965
2966        if tsr_size >= 0x67 {
2967            dbg_assert!(tsr_offset + 0x64 + 2 & 0xFFF < 0xFFF);
2968
2969            let iomap_base = memory::read16(return_on_pagefault!(
2970                translate_address_system_read(tsr_offset + 0x64 + 2),
2971                false
2972            ));
2973            let high_port = port + size - 1;
2974
2975            if tsr_size >= (iomap_base + (high_port >> 3)) as u32 {
2976                let mask = ((1 << size) - 1) << (port & 7);
2977                let addr = return_on_pagefault!(
2978                    translate_address_system_read(tsr_offset + iomap_base + (port >> 3)),
2979                    false
2980                );
2981                let port_info = if mask & 0xFF00 != 0 {
2982                    memory::read16(addr)
2983                } else {
2984                    memory::read8(addr)
2985                };
2986
2987                dbg_assert!(addr & 0xFFF < 0xFFF);
2988
2989                if port_info & mask == 0 {
2990                    return true;
2991                }
2992            }
2993        }
2994
2995        dbg_log!("#GP for port io  port={:x} size={}", port, size);
2996        trigger_gp(0);
2997        return false;
2998    }
2999
3000    return true;
3001}
3002
3003pub unsafe fn popa16() {
3004    return_on_pagefault!(readable_or_pagefault(get_stack_pointer(0), 16));
3005
3006    write_reg16(DI, pop16().unwrap());
3007    write_reg16(SI, pop16().unwrap());
3008    write_reg16(BP, pop16().unwrap());
3009    adjust_stack_reg(2);
3010    write_reg16(BX, pop16().unwrap());
3011    write_reg16(DX, pop16().unwrap());
3012    write_reg16(CX, pop16().unwrap());
3013    write_reg16(AX, pop16().unwrap());
3014}
3015
3016pub unsafe fn popa32() {
3017    return_on_pagefault!(readable_or_pagefault(get_stack_pointer(0), 32));
3018
3019    write_reg32(EDI, pop32s().unwrap());
3020    write_reg32(ESI, pop32s().unwrap());
3021    write_reg32(EBP, pop32s().unwrap());
3022    adjust_stack_reg(4);
3023    write_reg32(EBX, pop32s().unwrap());
3024    write_reg32(EDX, pop32s().unwrap());
3025    write_reg32(ECX, pop32s().unwrap());
3026    write_reg32(EAX, pop32s().unwrap());
3027}
3028
3029pub fn get_state_flags() -> CachedStateFlags {
3030    unsafe { *state_flags }
3031}
3032
3033#[no_mangle]
3034pub fn get_seg_cs() -> i32 {
3035    unsafe { *segment_offsets.offset(CS as isize) }
3036}
3037
3038pub unsafe fn get_seg_ss() -> i32 {
3039    return *segment_offsets.offset(SS as isize);
3040}
3041
3042pub unsafe fn segment_prefix(default_segment: i32) -> i32 {
3043    let prefix = *prefixes & prefix::PREFIX_MASK_SEGMENT;
3044    if 0 != prefix {
3045        dbg_assert!(prefix != prefix::SEG_PREFIX_ZERO);
3046        prefix as i32 - 1
3047    } else {
3048        default_segment
3049    }
3050}
3051
3052pub unsafe fn get_seg_prefix(default_segment: i32) -> OrPageFault<i32> {
3053    dbg_assert!(!in_jit);
3054    let prefix = *prefixes & prefix::PREFIX_MASK_SEGMENT;
3055    if 0 != prefix {
3056        if prefix == prefix::SEG_PREFIX_ZERO {
3057            return Ok(0);
3058        } else {
3059            return get_seg(prefix as i32 - 1);
3060        }
3061    } else {
3062        return get_seg(default_segment);
3063    };
3064}
3065
3066pub unsafe fn get_seg_prefix_ds(offset: i32) -> OrPageFault<i32> {
3067    Ok(get_seg_prefix(DS)? + offset)
3068}
3069
3070pub unsafe fn get_seg_prefix_ss(offset: i32) -> OrPageFault<i32> {
3071    Ok(get_seg_prefix(SS)? + offset)
3072}
3073
3074pub unsafe fn modrm_resolve(modrm_byte: i32) -> OrPageFault<i32> {
3075    if is_asize_32() {
3076        resolve_modrm32(modrm_byte)
3077    } else {
3078        resolve_modrm16(modrm_byte)
3079    }
3080}
3081
3082pub unsafe fn run_instruction(opcode: i32) {
3083    gen::interpreter::run(opcode as u32)
3084}
3085pub unsafe fn run_instruction0f_16(opcode: i32) {
3086    gen::interpreter0f::run(opcode as u32)
3087}
3088pub unsafe fn run_instruction0f_32(opcode: i32) {
3089    gen::interpreter0f::run(opcode as u32 | 0x100)
3090}
3091
3092pub unsafe fn cycle_internal() {
3093    profiler::stat_increment(stat::CYCLE_INTERNAL);
3094    let mut jit_entry = None;
3095    let initial_eip = *instruction_pointer;
3096    let initial_state_flags = *state_flags;
3097
3098    match tlb_code[(initial_eip as u32 >> 12) as usize] {
3099        None => {}
3100        Some(c) => {
3101            let c = c.as_ref();
3102
3103            if initial_state_flags == c.state_flags {
3104                let state = c.state_table[initial_eip as usize & 0xFFF];
3105                if state != u16::MAX {
3106                    jit_entry = Some((c.wasm_table_index.to_u16(), state));
3107                } else {
3108                    profiler::stat_increment(if is_near_end_of_page(initial_eip as u32) {
3109                        stat::RUN_INTERPRETED_NEAR_END_OF_PAGE
3110                    } else {
3111                        stat::RUN_INTERPRETED_PAGE_HAS_CODE
3112                    })
3113                }
3114            } else {
3115                profiler::stat_increment(stat::RUN_INTERPRETED_DIFFERENT_STATE);
3116                let s = *state_flags;
3117                if c.state_flags.cpl3() != s.cpl3() {
3118                    profiler::stat_increment(stat::RUN_INTERPRETED_DIFFERENT_STATE_CPL3);
3119                }
3120                if c.state_flags.has_flat_segmentation() != s.has_flat_segmentation() {
3121                    profiler::stat_increment(stat::RUN_INTERPRETED_DIFFERENT_STATE_FLAT);
3122                }
3123                if c.state_flags.is_32() != s.is_32() {
3124                    profiler::stat_increment(stat::RUN_INTERPRETED_DIFFERENT_STATE_IS32);
3125                }
3126                if c.state_flags.ssize_32() != s.ssize_32() {
3127                    profiler::stat_increment(stat::RUN_INTERPRETED_DIFFERENT_STATE_SS32);
3128                }
3129            }
3130        }
3131    }
3132
3133    if let Some((wasm_table_index, initial_state)) = jit_entry {
3134        if jit::CHECK_JIT_STATE_INVARIANTS {
3135            match get_phys_eip() {
3136                Err(()) => dbg_assert!(false),
3137                Ok(phys_eip) => {
3138                    let entry = jit::jit_find_cache_entry(phys_eip, initial_state_flags);
3139                    dbg_assert!(entry.wasm_table_index.to_u16() == wasm_table_index);
3140                    dbg_assert!(entry.initial_state == initial_state);
3141                }
3142            }
3143        }
3144        profiler::stat_increment(stat::RUN_FROM_CACHE);
3145        let initial_instruction_counter = *instruction_counter;
3146        #[cfg(debug_assertions)]
3147        {
3148            in_jit = true;
3149        }
3150        wasm::call_indirect1(
3151            wasm_table_index as i32 + WASM_TABLE_OFFSET as i32,
3152            initial_state,
3153        );
3154        #[cfg(debug_assertions)]
3155        {
3156            in_jit = false;
3157        }
3158        profiler::stat_increment_by(
3159            stat::RUN_FROM_CACHE_STEPS,
3160            (*instruction_counter - initial_instruction_counter) as u64,
3161        );
3162        dbg_assert!(
3163            *instruction_counter != initial_instruction_counter,
3164            "Instruction counter didn't change"
3165        );
3166
3167        if cfg!(feature = "profiler") {
3168            dbg_assert!(match debug_last_jump {
3169                LastJump::Compiled { .. } => true,
3170                _ => false,
3171            });
3172            #[allow(static_mut_refs)]
3173            let last_jump_addr = debug_last_jump.phys_address().unwrap();
3174            let last_jump_opcode = if last_jump_addr != 0 {
3175                memory::read32s(last_jump_addr)
3176            } else {
3177                // Happens during exit due to loop iteration limit
3178                0
3179            };
3180
3181            opstats::record_opstat_jit_exit(last_jump_opcode as u32);
3182        }
3183
3184        if is_near_end_of_page(*instruction_pointer as u32) {
3185            profiler::stat_increment(stat::RUN_FROM_CACHE_EXIT_NEAR_END_OF_PAGE);
3186        } else if Page::page_of(initial_eip as u32) == Page::page_of(*instruction_pointer as u32) {
3187            profiler::stat_increment(stat::RUN_FROM_CACHE_EXIT_SAME_PAGE);
3188        } else {
3189            profiler::stat_increment(stat::RUN_FROM_CACHE_EXIT_DIFFERENT_PAGE);
3190        }
3191    } else {
3192        *previous_ip = initial_eip;
3193        let phys_addr = return_on_pagefault!(get_phys_eip());
3194
3195        match tlb_code[(initial_eip as u32 >> 12) as usize] {
3196            None => {}
3197            Some(c) => {
3198                let c = c.as_ref();
3199
3200                if initial_state_flags == c.state_flags
3201                    && c.state_table[initial_eip as usize & 0xFFF] != u16::MAX
3202                {
3203                    profiler::stat_increment(stat::RUN_INTERPRETED_PAGE_HAS_ENTRY_AFTER_PAGE_WALK);
3204                    return;
3205                }
3206            }
3207        }
3208
3209        #[cfg(feature = "profiler")]
3210        {
3211            if CHECK_MISSED_ENTRY_POINTS {
3212                jit::check_missed_entry_points(phys_addr, initial_state_flags);
3213            }
3214        }
3215
3216        let initial_instruction_counter = *instruction_counter;
3217        jit_run_interpreted(phys_addr);
3218
3219        jit::jit_increase_hotness_and_maybe_compile(
3220            initial_eip,
3221            phys_addr,
3222            get_seg_cs() as u32,
3223            initial_state_flags,
3224            *instruction_counter - initial_instruction_counter,
3225        );
3226
3227        profiler::stat_increment_by(
3228            stat::RUN_INTERPRETED_STEPS,
3229            (*instruction_counter - initial_instruction_counter) as u64,
3230        );
3231        dbg_assert!(
3232            *instruction_counter != initial_instruction_counter,
3233            "Instruction counter didn't change"
3234        );
3235    };
3236}
3237
3238pub unsafe fn get_phys_eip() -> OrPageFault<u32> {
3239    let eip = *instruction_pointer;
3240    if 0 != eip & !0xFFF ^ *last_virt_eip {
3241        *eip_phys = (translate_address_read(eip)? ^ eip as u32) as i32;
3242        *last_virt_eip = eip & !0xFFF
3243    }
3244    let phys_addr = (*eip_phys ^ eip) as u32;
3245    dbg_assert!(!memory::in_mapped_range(phys_addr));
3246    return Ok(phys_addr);
3247}
3248
3249unsafe fn jit_run_interpreted(mut phys_addr: u32) {
3250    profiler::stat_increment(stat::RUN_INTERPRETED);
3251    dbg_assert!(!memory::in_mapped_range(phys_addr));
3252
3253    jit_block_boundary = false;
3254    let mut i = 0;
3255
3256    loop {
3257        if CHECK_MISSED_ENTRY_POINTS {
3258            let entry = jit::jit_find_cache_entry(phys_addr, *state_flags);
3259            if entry != jit::CachedCode::NONE {
3260                profiler::stat_increment(
3261                    stat::RUN_INTERPRETED_MISSED_COMPILED_ENTRY_RUN_INTERPRETED,
3262                );
3263            }
3264        }
3265
3266        i += 1;
3267        let start_eip = *instruction_pointer;
3268        let opcode = *memory::mem8.offset(phys_addr as isize) as i32;
3269        *instruction_pointer += 1;
3270        dbg_assert!(*prefixes == 0);
3271        run_instruction(opcode | (*is_32 as i32) << 8);
3272        dbg_assert!(*prefixes == 0);
3273
3274        if jit_block_boundary
3275            || Page::page_of(start_eip as u32) != Page::page_of(*instruction_pointer as u32)
3276                // Limit the number of iterations, as jumps within the same page are not counted as
3277                // block boundaries for the interpreter, but only on the next backwards jump
3278            || (i >= INTERPRETER_ITERATION_LIMIT
3279                && (start_eip as u32) >= (*instruction_pointer as u32))
3280        {
3281            break;
3282        }
3283
3284        *previous_ip = *instruction_pointer;
3285        phys_addr = return_on_pagefault!(get_phys_eip()) as u32;
3286    }
3287
3288    if cfg!(debug_assertions) {
3289        debug_last_jump = LastJump::Interpreted { phys_addr };
3290    }
3291
3292    *instruction_counter += i;
3293}
3294
3295#[no_mangle]
3296pub fn update_state_flags() {
3297    unsafe {
3298        *state_flags = CachedStateFlags::of_u32(
3299            (*is_32 as u32) << 0
3300                | (*stack_size_32 as u32) << 1
3301                | ((*cpl == 3) as u32) << 2
3302                | (has_flat_segmentation() as u32) << 3,
3303        )
3304    }
3305}
3306
3307#[no_mangle]
3308pub unsafe fn has_flat_segmentation() -> bool {
3309    // cs/ss can't be null
3310    return *segment_offsets.offset(SS as isize) == 0
3311        && !*segment_is_null.offset(DS as isize)
3312        && *segment_offsets.offset(DS as isize) == 0
3313        && *segment_offsets.offset(CS as isize) == 0;
3314}
3315
3316pub unsafe fn run_prefix_instruction() {
3317    run_instruction(return_on_pagefault!(read_imm8()) | (is_osize_32() as i32) << 8);
3318}
3319
3320pub unsafe fn segment_prefix_op(seg: i32) {
3321    dbg_assert!(seg <= 5 && seg >= 0);
3322    *prefixes = *prefixes & !prefix::PREFIX_MASK_SEGMENT | (seg as u8 + 1);
3323    run_prefix_instruction();
3324    *prefixes = 0
3325}
3326
3327/// Execute a bounded number of guest instructions through the native interpreter.
3328/// This path deliberately avoids the browser/WASM JIT table.
3329#[no_mangle]
3330pub unsafe fn main_loop_native_interpreter(max_instructions: u32) -> u32 {
3331    let mut executed = 0;
3332    while executed < max_instructions && !*in_hlt {
3333        let phys_addr = match get_phys_eip() {
3334            Ok(address) => address,
3335            Err(()) => break,
3336        };
3337        *previous_ip = *instruction_pointer;
3338        let opcode = *memory::mem8.add(phys_addr as usize) as i32;
3339        *instruction_pointer = (*instruction_pointer).wrapping_add(1);
3340        run_instruction(opcode | (*is_32 as i32) << 8);
3341        *instruction_counter = (*instruction_counter).wrapping_add(1);
3342        executed += 1;
3343    }
3344    executed
3345}
3346
3347#[no_mangle]
3348pub unsafe fn main_loop() -> f64 {
3349    profiler::stat_increment(stat::MAIN_LOOP);
3350
3351    let start = js::microtick();
3352
3353    if *in_hlt {
3354        if *flags & FLAG_INTERRUPT != 0 {
3355            let t = js::run_hardware_timers(*acpi_enabled, start);
3356            handle_irqs();
3357            if *in_hlt {
3358                profiler::stat_increment(stat::MAIN_LOOP_IDLE);
3359                return t;
3360            }
3361        } else {
3362            // dead
3363            return 100.0;
3364        }
3365    }
3366
3367    loop {
3368        do_many_cycles_native();
3369
3370        let now = js::microtick();
3371        let t = js::run_hardware_timers(*acpi_enabled, now);
3372        handle_irqs();
3373        if *in_hlt {
3374            return t;
3375        }
3376
3377        if now - start > TIME_PER_FRAME {
3378            break;
3379        }
3380    }
3381
3382    return 0.0;
3383}
3384
3385pub unsafe fn do_many_cycles_native() {
3386    profiler::stat_increment(stat::DO_MANY_CYCLES);
3387    let initial_instruction_counter = *instruction_counter;
3388    while (*instruction_counter).wrapping_sub(initial_instruction_counter) < LOOP_COUNTER as u32
3389        && !*in_hlt
3390    {
3391        cycle_internal();
3392    }
3393}
3394
3395#[cold]
3396pub unsafe fn trigger_de() {
3397    dbg_log!("#de");
3398    *instruction_pointer = *previous_ip;
3399    if DEBUG {
3400        if js::cpu_exception_hook(CPU_EXCEPTION_DE) {
3401            return;
3402        }
3403    }
3404    call_interrupt_vector(CPU_EXCEPTION_DE, false, None);
3405}
3406
3407#[inline(never)]
3408pub unsafe fn trigger_ud() {
3409    dbg_log!("#ud");
3410    dbg_trace();
3411    *instruction_pointer = *previous_ip;
3412    if DEBUG {
3413        if js::cpu_exception_hook(CPU_EXCEPTION_UD) {
3414            return;
3415        }
3416    }
3417    call_interrupt_vector(CPU_EXCEPTION_UD, false, None);
3418}
3419
3420#[inline(never)]
3421pub unsafe fn trigger_nm() {
3422    dbg_log!("#nm eip={:x}", *previous_ip);
3423    dbg_trace();
3424    *instruction_pointer = *previous_ip;
3425    if DEBUG {
3426        if js::cpu_exception_hook(CPU_EXCEPTION_NM) {
3427            return;
3428        }
3429    }
3430    call_interrupt_vector(CPU_EXCEPTION_NM, false, None);
3431}
3432
3433#[inline(never)]
3434pub unsafe fn trigger_gp(code: i32) {
3435    dbg_log!("#gp");
3436    *instruction_pointer = *previous_ip;
3437    if DEBUG {
3438        if js::cpu_exception_hook(CPU_EXCEPTION_GP) {
3439            return;
3440        }
3441    }
3442    call_interrupt_vector(CPU_EXCEPTION_GP, false, Some(code));
3443}
3444
3445#[cold]
3446pub unsafe fn virt_boundary_read16(low: u32, high: u32) -> i32 {
3447    dbg_assert!(low & 0xFFF == 0xFFF);
3448    dbg_assert!(high & 0xFFF == 0);
3449    return memory::read8(low as u32) | memory::read8(high as u32) << 8;
3450}
3451
3452#[cold]
3453pub unsafe fn virt_boundary_read32s(low: u32, high: u32) -> i32 {
3454    dbg_assert!(low & 0xFFF >= 0xFFD);
3455    dbg_assert!(high - 3 & 0xFFF == low & 0xFFF);
3456    let mid;
3457    if 0 != low & 1 {
3458        if 0 != low & 2 {
3459            // 0xFFF
3460            mid = memory::read16(high - 2)
3461        } else {
3462            // 0xFFD
3463            mid = memory::read16(low + 1)
3464        }
3465    } else {
3466        // 0xFFE
3467        mid = virt_boundary_read16(low + 1, high - 1)
3468    }
3469    return memory::read8(low as u32) | mid << 8 | memory::read8(high as u32) << 24;
3470}
3471
3472#[cold]
3473pub unsafe fn virt_boundary_write16(low: u32, high: u32, value: i32) {
3474    dbg_assert!(low & 0xFFF == 0xFFF);
3475    dbg_assert!(high & 0xFFF == 0);
3476    memory::write8(low as u32, value);
3477    memory::write8(high as u32, value >> 8);
3478}
3479
3480#[cold]
3481pub unsafe fn virt_boundary_write32(low: u32, high: u32, value: i32) {
3482    dbg_assert!(low & 0xFFF >= 0xFFD);
3483    dbg_assert!(high - 3 & 0xFFF == low & 0xFFF);
3484    memory::write8(low as u32, value);
3485    if 0 != low & 1 {
3486        if 0 != low & 2 {
3487            // 0xFFF
3488            memory::write8((high - 2) as u32, value >> 8);
3489            memory::write8((high - 1) as u32, value >> 16);
3490        } else {
3491            // 0xFFD
3492            memory::write8((low + 1) as u32, value >> 8);
3493            memory::write8((low + 2) as u32, value >> 16);
3494        }
3495    } else {
3496        // 0xFFE
3497        memory::write8((low + 1) as u32, value >> 8);
3498        memory::write8((high - 1) as u32, value >> 16);
3499    }
3500    memory::write8(high as u32, value >> 24);
3501}
3502
3503pub unsafe fn safe_read8(addr: i32) -> OrPageFault<i32> {
3504    Ok(memory::read8(translate_address_read(addr)?))
3505}
3506
3507pub unsafe fn safe_read16(addr: i32) -> OrPageFault<i32> {
3508    if addr & 0xFFF == 0xFFF {
3509        Ok(safe_read8(addr)? | safe_read8(addr + 1)? << 8)
3510    } else {
3511        Ok(memory::read16(translate_address_read(addr)?))
3512    }
3513}
3514
3515pub unsafe fn safe_read32s(addr: i32) -> OrPageFault<i32> {
3516    if addr & 0xFFF >= 0xFFD {
3517        Ok(safe_read16(addr)? | safe_read16(addr + 2)? << 16)
3518    } else {
3519        Ok(memory::read32s(translate_address_read(addr)?))
3520    }
3521}
3522
3523pub unsafe fn safe_read_f32(addr: i32) -> OrPageFault<f32> {
3524    Ok(f32::from_bits(i32::cast_unsigned(safe_read32s(addr)?)))
3525}
3526
3527pub unsafe fn safe_read64s(addr: i32) -> OrPageFault<u64> {
3528    if addr & 0xFFF > 0x1000 - 8 {
3529        Ok(safe_read32s(addr)? as u32 as u64 | (safe_read32s(addr + 4)? as u32 as u64) << 32)
3530    } else {
3531        Ok(memory::read64s(translate_address_read(addr)?) as u64)
3532    }
3533}
3534
3535pub unsafe fn safe_read128s(addr: i32) -> OrPageFault<reg128> {
3536    if addr & 0xFFF > 0x1000 - 16 {
3537        Ok(reg128 {
3538            u64: [safe_read64s(addr)?, safe_read64s(addr + 8)?],
3539        })
3540    } else {
3541        Ok(memory::read128(translate_address_read(addr)?))
3542    }
3543}
3544
3545#[no_mangle]
3546#[cfg(feature = "profiler")]
3547pub fn report_safe_read_jit_slow(address: u32, entry: i32) {
3548    if entry & TLB_VALID == 0 {
3549        profiler::stat_increment(stat::SAFE_READ_SLOW_NOT_VALID);
3550    } else if entry & TLB_IN_MAPPED_RANGE != 0 {
3551        profiler::stat_increment(stat::SAFE_READ_SLOW_IN_MAPPED_RANGE);
3552    } else if entry & TLB_NO_USER != 0 {
3553        profiler::stat_increment(stat::SAFE_READ_SLOW_NOT_USER);
3554    } else if address & 0xFFF > 0x1000 - 16 {
3555        profiler::stat_increment(stat::SAFE_READ_SLOW_PAGE_CROSSED);
3556    } else {
3557        dbg_log!("Unexpected entry bit: {:x} (read at {:x})", entry, address);
3558        dbg_assert!(false);
3559    }
3560}
3561
3562#[no_mangle]
3563#[cfg(feature = "profiler")]
3564pub fn report_safe_write_jit_slow(address: u32, entry: i32) {
3565    if entry & TLB_VALID == 0 {
3566        profiler::stat_increment(stat::SAFE_WRITE_SLOW_NOT_VALID);
3567    } else if entry & TLB_IN_MAPPED_RANGE != 0 {
3568        profiler::stat_increment(stat::SAFE_WRITE_SLOW_IN_MAPPED_RANGE);
3569    } else if entry & TLB_HAS_CODE != 0 {
3570        profiler::stat_increment(stat::SAFE_WRITE_SLOW_HAS_CODE);
3571    } else if entry & TLB_READONLY != 0 {
3572        profiler::stat_increment(stat::SAFE_WRITE_SLOW_READ_ONLY);
3573    } else if entry & TLB_NO_USER != 0 {
3574        profiler::stat_increment(stat::SAFE_WRITE_SLOW_NOT_USER);
3575    } else if address & 0xFFF > 0x1000 - 16 {
3576        profiler::stat_increment(stat::SAFE_WRITE_SLOW_PAGE_CROSSED);
3577    } else {
3578        dbg_assert!(false);
3579    }
3580}
3581
3582#[no_mangle]
3583#[cfg(feature = "profiler")]
3584pub fn report_safe_read_write_jit_slow(address: u32, entry: i32) {
3585    if entry & TLB_VALID == 0 {
3586        profiler::stat_increment(stat::SAFE_READ_WRITE_SLOW_NOT_VALID);
3587    } else if entry & TLB_IN_MAPPED_RANGE != 0 {
3588        profiler::stat_increment(stat::SAFE_READ_WRITE_SLOW_IN_MAPPED_RANGE);
3589    } else if entry & TLB_HAS_CODE != 0 {
3590        profiler::stat_increment(stat::SAFE_READ_WRITE_SLOW_HAS_CODE);
3591    } else if entry & TLB_READONLY != 0 {
3592        profiler::stat_increment(stat::SAFE_READ_WRITE_SLOW_READ_ONLY);
3593    } else if entry & TLB_NO_USER != 0 {
3594        profiler::stat_increment(stat::SAFE_READ_WRITE_SLOW_NOT_USER);
3595    } else if address & 0xFFF > 0x1000 - 16 {
3596        profiler::stat_increment(stat::SAFE_READ_WRITE_SLOW_PAGE_CROSSED);
3597    } else {
3598        dbg_assert!(false);
3599    }
3600}
3601
3602#[repr(align(0x1000))]
3603struct ScratchBuffer([u8; 0x1000 * 2]);
3604static mut jit_paging_scratch_buffer: ScratchBuffer = ScratchBuffer([0; 2 * 0x1000]);
3605
3606pub unsafe fn safe_read_slow_jit(
3607    addr: i32,
3608    bitsize: i32,
3609    is_write: bool,
3610    eip_offset_in_page_and_wasm_table_index: i32,
3611) -> i32 {
3612    let wasm_table_index = (eip_offset_in_page_and_wasm_table_index >> 16) as u16;
3613    let eip_offset_in_page = eip_offset_in_page_and_wasm_table_index & 0xFFFF;
3614    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
3615    dbg_assert!(u32::from(wasm_table_index) < jit::WASM_TABLE_SIZE);
3616
3617    let crosses_page = (addr & 0xFFF) + bitsize / 8 > 0x1000;
3618    let addr_low = match if is_write {
3619        translate_address_write_jit(addr, wasm_table_index)
3620    } else {
3621        translate_address_read_jit(addr)
3622    } {
3623        Err(()) => {
3624            *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
3625            return 1;
3626        }
3627        Ok(addr) => addr,
3628    };
3629    if crosses_page {
3630        let boundary_addr = (addr | 0xFFF) + 1;
3631        let addr_high = match if is_write {
3632            translate_address_write_jit(boundary_addr, wasm_table_index)
3633        } else {
3634            translate_address_read_jit(boundary_addr)
3635        } {
3636            Err(()) => {
3637                *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
3638                return 1;
3639            }
3640            Ok(addr) => addr,
3641        };
3642        // TODO: Could check if virtual pages point to consecutive physical and go to fast path
3643        // do read, write into scratch buffer
3644
3645        let scratch = &raw mut jit_paging_scratch_buffer.0 as u32;
3646        dbg_assert!(scratch & 0xFFF == 0);
3647
3648        for s in addr_low..((addr_low | 0xFFF) + 1) {
3649            *(scratch as *mut u8).offset((s & 0xFFF) as isize) = memory::read8(s) as u8
3650        }
3651        for s in addr_high..(addr_high + (addr + bitsize / 8 & 0xFFF) as u32) {
3652            *(scratch as *mut u8).offset((0x1000 | s & 0xFFF) as isize) = memory::read8(s) as u8
3653        }
3654
3655        ((scratch as i32) ^ addr) & !0xFFF
3656    } else if memory::in_mapped_range(addr_low) {
3657        let scratch = &raw mut jit_paging_scratch_buffer.0[0];
3658
3659        match bitsize {
3660            128 => ptr::write_unaligned(
3661                scratch.offset(addr_low as isize & 0xFFF) as *mut reg128,
3662                memory::read128(addr_low),
3663            ),
3664            64 => ptr::write_unaligned(
3665                scratch.offset(addr_low as isize & 0xFFF) as *mut i64,
3666                memory::read64s(addr_low),
3667            ),
3668            32 => ptr::write_unaligned(
3669                scratch.offset(addr_low as isize & 0xFFF) as *mut i32,
3670                memory::read32s(addr_low),
3671            ),
3672            16 => ptr::write_unaligned(
3673                scratch.offset(addr_low as isize & 0xFFF) as *mut u16,
3674                memory::read16(addr_low) as u16,
3675            ),
3676            8 => {
3677                *(scratch.offset(addr_low as isize & 0xFFF) as *mut u8) =
3678                    memory::read8(addr_low) as u8
3679            }
3680            _ => {
3681                dbg_assert!(false);
3682            }
3683        }
3684
3685        ((scratch as i32) ^ addr) & !0xFFF
3686    } else {
3687        ((addr_low as i32 + memory::mem8 as i32) ^ addr) & !0xFFF
3688    }
3689}
3690
3691#[no_mangle]
3692pub unsafe fn safe_read8_slow_jit(addr: i32, eip: i32) -> i32 {
3693    safe_read_slow_jit(addr, 8, false, eip)
3694}
3695#[no_mangle]
3696pub unsafe fn safe_read16_slow_jit(addr: i32, eip: i32) -> i32 {
3697    safe_read_slow_jit(addr, 16, false, eip)
3698}
3699#[no_mangle]
3700pub unsafe fn safe_read32s_slow_jit(addr: i32, eip: i32) -> i32 {
3701    safe_read_slow_jit(addr, 32, false, eip)
3702}
3703#[no_mangle]
3704pub unsafe fn safe_read64s_slow_jit(addr: i32, eip: i32) -> i32 {
3705    safe_read_slow_jit(addr, 64, false, eip)
3706}
3707#[no_mangle]
3708pub unsafe fn safe_read128s_slow_jit(addr: i32, eip: i32) -> i32 {
3709    safe_read_slow_jit(addr, 128, false, eip)
3710}
3711
3712#[no_mangle]
3713pub unsafe fn get_phys_eip_slow_jit(addr: i32) -> i32 {
3714    match translate_address_read_jit(addr) {
3715        Err(()) => 1,
3716        Ok(addr_low) => {
3717            dbg_assert!(!memory::in_mapped_range(addr_low as u32)); // same assumption as in read_imm8
3718            ((addr_low as i32 + memory::mem8 as i32) ^ addr) & !0xFFF
3719        }
3720    }
3721}
3722
3723#[no_mangle]
3724pub unsafe fn safe_read_write8_slow_jit(addr: i32, eip_and_wasm_table_index: i32) -> i32 {
3725    safe_read_slow_jit(addr, 8, true, eip_and_wasm_table_index)
3726}
3727#[no_mangle]
3728pub unsafe fn safe_read_write16_slow_jit(addr: i32, eip_and_wasm_table_index: i32) -> i32 {
3729    safe_read_slow_jit(addr, 16, true, eip_and_wasm_table_index)
3730}
3731#[no_mangle]
3732pub unsafe fn safe_read_write32s_slow_jit(addr: i32, eip_and_wasm_table_index: i32) -> i32 {
3733    safe_read_slow_jit(addr, 32, true, eip_and_wasm_table_index)
3734}
3735#[no_mangle]
3736pub unsafe fn safe_read_write64_slow_jit(addr: i32, eip_and_wasm_table_index: i32) -> i32 {
3737    safe_read_slow_jit(addr, 64, true, eip_and_wasm_table_index)
3738}
3739
3740#[no_mangle]
3741pub unsafe fn readable_or_pagefault_jit(addr: i32, size: i32, eip_offset_in_page: i32) -> i32 {
3742    dbg_assert!(size > 0 && size < 0x1000);
3743    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
3744    let crosses_page = (addr & 0xFFF) + size > 0x1000;
3745    if translate_address_read_jit(addr).is_err()
3746        || crosses_page && translate_address_read_jit((addr | 0xFFF) + 1).is_err()
3747    {
3748        *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
3749        return 1;
3750    }
3751    0
3752}
3753
3754pub unsafe fn safe_write_slow_jit(
3755    addr: i32,
3756    bitsize: i32,
3757    value_low: u64,
3758    value_high: u64,
3759    eip_offset_in_page_and_wasm_table_index: i32,
3760) -> i32 {
3761    let wasm_table_index = (eip_offset_in_page_and_wasm_table_index >> 16) as u16;
3762    let eip_offset_in_page = eip_offset_in_page_and_wasm_table_index & 0xFFFF;
3763    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
3764    dbg_assert!(u32::from(wasm_table_index) < jit::WASM_TABLE_SIZE);
3765
3766    let crosses_page = (addr & 0xFFF) + bitsize / 8 > 0x1000;
3767    let addr_low = match translate_address_write_jit(addr, wasm_table_index) {
3768        Err(()) => {
3769            *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
3770            return 1;
3771        }
3772        Ok(x) => x,
3773    };
3774    if crosses_page {
3775        let addr_high = match translate_address_write_jit((addr | 0xFFF) + 1, wasm_table_index) {
3776            Err(()) => {
3777                *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
3778                return 1;
3779            }
3780            Ok(x) => x,
3781        };
3782        // TODO: Could check if virtual pages point to consecutive physical and go to fast path
3783
3784        // do write, return dummy pointer for fast path to write into
3785
3786        match bitsize {
3787            128 => safe_write128(
3788                addr,
3789                reg128 {
3790                    u64: [value_low, value_high],
3791                },
3792            )
3793            .unwrap(),
3794            64 => safe_write64(addr, value_low).unwrap(),
3795            32 => virt_boundary_write32(
3796                addr_low,
3797                addr_high | (addr as u32 + 3 & 3),
3798                value_low as i32,
3799            ),
3800            16 => virt_boundary_write16(addr_low, addr_high, value_low as i32),
3801            8 => {
3802                dbg_assert!(false);
3803            }
3804            _ => {
3805                dbg_assert!(false);
3806            }
3807        }
3808
3809        let scratch = &raw mut jit_paging_scratch_buffer.0 as u32;
3810        dbg_assert!(scratch & 0xFFF == 0);
3811        ((scratch as i32) ^ addr) & !0xFFF
3812    } else if memory::in_mapped_range(addr_low) {
3813        match bitsize {
3814            128 => memory::mmap_write128(addr_low, value_low, value_high),
3815            64 => memory::mmap_write64(addr_low, value_low),
3816            32 => memory::mmap_write32(addr_low, value_low as i32),
3817            16 => memory::mmap_write16(addr_low, (value_low & 0xFFFF) as i32),
3818            8 => memory::mmap_write8(addr_low, (value_low & 0xFF) as i32),
3819            _ => {
3820                dbg_assert!(false);
3821            }
3822        }
3823
3824        let scratch = &raw mut jit_paging_scratch_buffer.0 as u32;
3825        dbg_assert!(scratch & 0xFFF == 0);
3826        ((scratch as i32) ^ addr) & !0xFFF
3827    } else {
3828        ((addr_low as i32 + memory::mem8 as i32) ^ addr) & !0xFFF
3829    }
3830}
3831
3832#[no_mangle]
3833pub unsafe fn safe_write8_slow_jit(addr: i32, value: u32, eip_and_wasm_table_index: i32) -> i32 {
3834    safe_write_slow_jit(addr, 8, value as u64, 0, eip_and_wasm_table_index)
3835}
3836#[no_mangle]
3837pub unsafe fn safe_write16_slow_jit(addr: i32, value: u32, eip_and_wasm_table_index: i32) -> i32 {
3838    safe_write_slow_jit(addr, 16, value as u64, 0, eip_and_wasm_table_index)
3839}
3840#[no_mangle]
3841pub unsafe fn safe_write32_slow_jit(addr: i32, value: u32, eip_and_wasm_table_index: i32) -> i32 {
3842    safe_write_slow_jit(addr, 32, value as u64, 0, eip_and_wasm_table_index)
3843}
3844#[no_mangle]
3845pub unsafe fn safe_write64_slow_jit(addr: i32, value: u64, eip_and_wasm_table_index: i32) -> i32 {
3846    safe_write_slow_jit(addr, 64, value, 0, eip_and_wasm_table_index)
3847}
3848#[no_mangle]
3849pub unsafe fn safe_write128_slow_jit(
3850    addr: i32,
3851    low: u64,
3852    high: u64,
3853    eip_and_wasm_table_index: i32,
3854) -> i32 {
3855    safe_write_slow_jit(addr, 128, low, high, eip_and_wasm_table_index)
3856}
3857
3858#[no_mangle]
3859pub unsafe fn writable_or_pagefault_jit(
3860    addr: i32,
3861    size: i32,
3862    eip_offset_in_page_and_wasm_table_index: i32,
3863) -> i32 {
3864    let wasm_table_index = (eip_offset_in_page_and_wasm_table_index >> 16) as u16;
3865    let eip_offset_in_page = eip_offset_in_page_and_wasm_table_index & 0xFFFF;
3866    dbg_assert!(size > 0 && size < 0x1000);
3867    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
3868    dbg_assert!(u32::from(wasm_table_index) < jit::WASM_TABLE_SIZE);
3869    let crosses_page = (addr & 0xFFF) + size > 0x1000;
3870    if translate_address_write_jit(addr, wasm_table_index).is_err()
3871        || crosses_page
3872            && translate_address_write_jit((addr | 0xFFF) + 1, wasm_table_index).is_err()
3873    {
3874        *instruction_pointer = *instruction_pointer & !0xFFF | eip_offset_in_page;
3875        return 1;
3876    }
3877    0
3878}
3879
3880pub unsafe fn safe_write8(addr: i32, value: i32) -> OrPageFault<()> {
3881    let (phys_addr, can_skip_dirty_page) = translate_address_write_and_can_skip_dirty(addr)?;
3882    if memory::in_mapped_range(phys_addr) {
3883        memory::mmap_write8(phys_addr, value);
3884    } else {
3885        if !can_skip_dirty_page {
3886            jit::jit_dirty_page(Page::page_of(phys_addr));
3887        } else {
3888            dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
3889        }
3890        memory::write8_no_mmap_or_dirty_check(phys_addr, value);
3891    };
3892    Ok(())
3893}
3894
3895pub unsafe fn safe_write16(addr: i32, value: i32) -> OrPageFault<()> {
3896    let (phys_addr, can_skip_dirty_page) = translate_address_write_and_can_skip_dirty(addr)?;
3897    dbg_assert!(value >= 0 && value < 0x10000);
3898    if addr & 0xFFF == 0xFFF {
3899        virt_boundary_write16(phys_addr, translate_address_write(addr + 1)?, value);
3900    } else if memory::in_mapped_range(phys_addr) {
3901        memory::mmap_write16(phys_addr, value);
3902    } else {
3903        if !can_skip_dirty_page {
3904            jit::jit_dirty_page(Page::page_of(phys_addr));
3905        } else {
3906            dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
3907        }
3908        memory::write16_no_mmap_or_dirty_check(phys_addr, value);
3909    };
3910    Ok(())
3911}
3912
3913pub unsafe fn safe_write32(addr: i32, value: i32) -> OrPageFault<()> {
3914    let (phys_addr, can_skip_dirty_page) = translate_address_write_and_can_skip_dirty(addr)?;
3915    if addr & 0xFFF > 0x1000 - 4 {
3916        virt_boundary_write32(
3917            phys_addr,
3918            translate_address_write(addr + 3 & !3)? | (addr as u32 + 3 & 3),
3919            value,
3920        );
3921    } else if memory::in_mapped_range(phys_addr) {
3922        memory::mmap_write32(phys_addr, value);
3923    } else {
3924        if !can_skip_dirty_page {
3925            jit::jit_dirty_page(Page::page_of(phys_addr));
3926        } else {
3927            dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
3928        }
3929        memory::write32_no_mmap_or_dirty_check(phys_addr, value);
3930    };
3931    Ok(())
3932}
3933
3934pub unsafe fn safe_write64(addr: i32, value: u64) -> OrPageFault<()> {
3935    if addr & 0xFFF > 0x1000 - 8 {
3936        writable_or_pagefault(addr, 8)?;
3937        safe_write32(addr, value as i32).unwrap();
3938        safe_write32(addr + 4, (value >> 32) as i32).unwrap();
3939    } else {
3940        let (phys_addr, can_skip_dirty_page) = translate_address_write_and_can_skip_dirty(addr)?;
3941        if memory::in_mapped_range(phys_addr) {
3942            memory::mmap_write64(phys_addr, value);
3943        } else {
3944            if !can_skip_dirty_page {
3945                jit::jit_dirty_page(Page::page_of(phys_addr));
3946            } else {
3947                dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
3948            }
3949            memory::write64_no_mmap_or_dirty_check(phys_addr, value);
3950        }
3951    };
3952    Ok(())
3953}
3954
3955pub unsafe fn safe_write128(addr: i32, value: reg128) -> OrPageFault<()> {
3956    if addr & 0xFFF > 0x1000 - 16 {
3957        writable_or_pagefault(addr, 16)?;
3958        safe_write64(addr, value.u64[0]).unwrap();
3959        safe_write64(addr + 8, value.u64[1]).unwrap();
3960    } else {
3961        let (phys_addr, can_skip_dirty_page) = translate_address_write_and_can_skip_dirty(addr)?;
3962        if memory::in_mapped_range(phys_addr) {
3963            memory::mmap_write128(phys_addr, value.u64[0], value.u64[1]);
3964        } else {
3965            if !can_skip_dirty_page {
3966                jit::jit_dirty_page(Page::page_of(phys_addr));
3967            } else {
3968                dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
3969            }
3970            memory::write128_no_mmap_or_dirty_check(phys_addr, value);
3971        }
3972    };
3973    Ok(())
3974}
3975
3976#[inline(always)]
3977pub unsafe fn safe_read_write8(addr: i32, instruction: &dyn Fn(i32) -> i32) {
3978    let (phys_addr, can_skip_dirty_page) =
3979        return_on_pagefault!(translate_address_write_and_can_skip_dirty(addr));
3980    let x = memory::read8(phys_addr);
3981    let value = instruction(x);
3982    dbg_assert!(value >= 0 && value < 0x100);
3983    if memory::in_mapped_range(phys_addr) {
3984        memory::mmap_write8(phys_addr, value);
3985    } else {
3986        if !can_skip_dirty_page {
3987            jit::jit_dirty_page(Page::page_of(phys_addr));
3988        } else {
3989            dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
3990        }
3991        memory::write8_no_mmap_or_dirty_check(phys_addr, value);
3992    }
3993}
3994
3995#[inline(always)]
3996pub unsafe fn safe_read_write16(addr: i32, instruction: &dyn Fn(i32) -> i32) {
3997    let (phys_addr, can_skip_dirty_page) =
3998        return_on_pagefault!(translate_address_write_and_can_skip_dirty(addr));
3999    if phys_addr & 0xFFF == 0xFFF {
4000        let phys_addr_high = return_on_pagefault!(translate_address_write(addr + 1));
4001        let x = virt_boundary_read16(phys_addr, phys_addr_high);
4002        virt_boundary_write16(phys_addr, phys_addr_high, instruction(x));
4003    } else {
4004        let x = memory::read16(phys_addr);
4005        let value = instruction(x);
4006        dbg_assert!(value >= 0 && value < 0x10000);
4007        if memory::in_mapped_range(phys_addr) {
4008            memory::mmap_write16(phys_addr, value);
4009        } else {
4010            if !can_skip_dirty_page {
4011                jit::jit_dirty_page(Page::page_of(phys_addr));
4012            } else {
4013                dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
4014            }
4015            memory::write16_no_mmap_or_dirty_check(phys_addr, value);
4016        };
4017    }
4018}
4019
4020#[inline(always)]
4021pub unsafe fn safe_read_write32(addr: i32, instruction: &dyn Fn(i32) -> i32) {
4022    let (phys_addr, can_skip_dirty_page) =
4023        return_on_pagefault!(translate_address_write_and_can_skip_dirty(addr));
4024    if phys_addr & 0xFFF >= 0xFFD {
4025        let phys_addr_high = return_on_pagefault!(translate_address_write(addr + 3 & !3));
4026        let phys_addr_high = phys_addr_high | (addr as u32) + 3 & 3;
4027        let x = virt_boundary_read32s(phys_addr, phys_addr_high);
4028        virt_boundary_write32(phys_addr, phys_addr_high, instruction(x));
4029    } else {
4030        let x = memory::read32s(phys_addr);
4031        let value = instruction(x);
4032        if memory::in_mapped_range(phys_addr) {
4033            memory::mmap_write32(phys_addr, value);
4034        } else {
4035            if !can_skip_dirty_page {
4036                jit::jit_dirty_page(Page::page_of(phys_addr));
4037            } else {
4038                dbg_assert!(!jit::jit_page_has_code(Page::page_of(phys_addr as u32)));
4039            }
4040            memory::write32_no_mmap_or_dirty_check(phys_addr, value);
4041        };
4042    }
4043}
4044
4045fn get_reg8_index(index: i32) -> i32 {
4046    return index << 2 & 12 | index >> 2 & 1;
4047}
4048
4049pub unsafe fn read_reg8(index: i32) -> i32 {
4050    dbg_assert!(index >= 0 && index < 8);
4051    return *reg8.offset(get_reg8_index(index) as isize) as i32;
4052}
4053
4054pub unsafe fn write_reg8(index: i32, value: i32) {
4055    dbg_assert!(index >= 0 && index < 8);
4056    *reg8.offset(get_reg8_index(index) as isize) = value as u8;
4057}
4058
4059fn get_reg16_index(index: i32) -> i32 {
4060    return index << 1;
4061}
4062
4063pub unsafe fn read_reg16(index: i32) -> i32 {
4064    dbg_assert!(index >= 0 && index < 8);
4065    return *reg16.offset(get_reg16_index(index) as isize) as i32;
4066}
4067
4068pub unsafe fn write_reg16(index: i32, value: i32) {
4069    dbg_assert!(index >= 0 && index < 8);
4070    *reg16.offset(get_reg16_index(index) as isize) = value as u16;
4071}
4072
4073pub unsafe fn read_reg32(index: i32) -> i32 {
4074    dbg_assert!(index >= 0 && index < 8);
4075    *reg32.offset(index as isize)
4076}
4077
4078pub unsafe fn write_reg32(index: i32, value: i32) {
4079    dbg_assert!(index >= 0 && index < 8);
4080    *reg32.offset(index as isize) = value;
4081}
4082
4083pub unsafe fn read_mmx32s(r: i32) -> i32 {
4084    (*fpu_st.offset(r as isize)).mantissa as i32
4085}
4086
4087pub unsafe fn read_mmx64s(r: i32) -> u64 {
4088    (*fpu_st.offset(r as isize)).mantissa
4089}
4090
4091pub unsafe fn write_mmx_reg64(r: i32, data: u64) {
4092    *fpu_st.offset(r as isize) = softfloat::F80 {
4093        mantissa: data,
4094        sign_exponent: 0xFFFF,
4095    };
4096}
4097
4098pub unsafe fn read_xmm_f32(r: i32) -> f32 {
4099    return (*reg_xmm.offset(r as isize)).f32[0];
4100}
4101
4102pub unsafe fn read_xmm32(r: i32) -> i32 {
4103    return (*reg_xmm.offset(r as isize)).u32[0] as i32;
4104}
4105
4106pub unsafe fn read_xmm64s(r: i32) -> u64 {
4107    (*reg_xmm.offset(r as isize)).u64[0]
4108}
4109
4110pub unsafe fn read_xmm128s(r: i32) -> reg128 {
4111    return *reg_xmm.offset(r as isize);
4112}
4113
4114pub unsafe fn write_xmm_f32(r: i32, data: f32) {
4115    (*reg_xmm.offset(r as isize)).f32[0] = data;
4116}
4117
4118pub unsafe fn write_xmm32(r: i32, data: i32) {
4119    (*reg_xmm.offset(r as isize)).i32[0] = data;
4120}
4121
4122pub unsafe fn write_xmm64(r: i32, data: u64) {
4123    (*reg_xmm.offset(r as isize)).u64[0] = data
4124}
4125pub unsafe fn write_xmm_f64(r: i32, data: f64) {
4126    (*reg_xmm.offset(r as isize)).f64[0] = data
4127}
4128
4129pub unsafe fn write_xmm128(r: i32, i0: i32, i1: i32, i2: i32, i3: i32) {
4130    let x = reg128 {
4131        u32: [i0 as u32, i1 as u32, i2 as u32, i3 as u32],
4132    };
4133    *reg_xmm.offset(r as isize) = x;
4134}
4135
4136pub unsafe fn write_xmm128_2(r: i32, i0: u64, i1: u64) {
4137    *reg_xmm.offset(r as isize) = reg128 { u64: [i0, i1] };
4138}
4139
4140pub unsafe fn write_xmm_reg128(r: i32, data: reg128) {
4141    *reg_xmm.offset(r as isize) = data;
4142}
4143
4144/// Set the fpu tag word to valid and the top-of-stack to 0 on mmx instructions
4145#[no_mangle]
4146pub fn transition_fpu_to_mmx() {
4147    unsafe {
4148        fpu_set_tag_word(0);
4149        *fpu_stack_ptr = 0;
4150    }
4151}
4152
4153pub unsafe fn task_switch_test() -> bool {
4154    if 0 != *cr & (CR0_EM | CR0_TS) {
4155        trigger_nm();
4156        return false;
4157    } else {
4158        return true;
4159    };
4160}
4161
4162pub unsafe fn set_mxcsr(new_mxcsr: i32) {
4163    dbg_assert!(new_mxcsr & !MXCSR_MASK == 0); // checked by caller
4164
4165    if *mxcsr & MXCSR_DAZ == 0 && new_mxcsr & MXCSR_DAZ != 0 {
4166        dbg_log!("Warning: Unimplemented MXCSR bit: Denormals Are Zero");
4167    }
4168    if *mxcsr & MXCSR_FZ == 0 && new_mxcsr & MXCSR_FZ != 0 {
4169        dbg_log!("Warning: Unimplemented MXCSR bit: Flush To Zero");
4170    }
4171
4172    let rounding_mode = new_mxcsr >> MXCSR_RC_SHIFT & 3;
4173    if *mxcsr >> MXCSR_RC_SHIFT & 3 == 0 && rounding_mode != 0 {
4174        dbg_log!(
4175            "Warning: Unimplemented MXCSR rounding mode: {}",
4176            rounding_mode
4177        );
4178    }
4179
4180    let exception_mask = new_mxcsr >> 7 & 0b111111;
4181    if *mxcsr >> 7 & 0b111111 != exception_mask && exception_mask != 0b111111 {
4182        dbg_log!(
4183            "Warning: Unimplemented MXCSR exception mask: 0b{:b}",
4184            exception_mask
4185        );
4186    }
4187
4188    *mxcsr = new_mxcsr;
4189}
4190
4191#[no_mangle]
4192pub unsafe fn task_switch_test_jit(eip_offset_in_page: i32) {
4193    dbg_assert!(0 != *cr & (CR0_EM | CR0_TS));
4194    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
4195    trigger_nm_jit(eip_offset_in_page);
4196}
4197
4198pub unsafe fn task_switch_test_mmx() -> bool {
4199    if *cr.offset(4) & CR4_OSFXSR == 0 {
4200        dbg_log!("Warning: Unimplemented task switch test with cr4.osfxsr=0");
4201    }
4202    if 0 != *cr & CR0_EM {
4203        trigger_ud();
4204        return false;
4205    } else if 0 != *cr & CR0_TS {
4206        trigger_nm();
4207        return false;
4208    } else {
4209        return true;
4210    };
4211}
4212
4213#[no_mangle]
4214pub unsafe fn task_switch_test_mmx_jit(eip_offset_in_page: i32) {
4215    dbg_assert!(eip_offset_in_page >= 0 && eip_offset_in_page < 0x1000);
4216    if *cr.offset(4) & CR4_OSFXSR == 0 {
4217        dbg_log!("Warning: Unimplemented task switch test with cr4.osfxsr=0");
4218    }
4219    if 0 != *cr & CR0_EM {
4220        trigger_ud_jit(eip_offset_in_page);
4221    } else if 0 != *cr & CR0_TS {
4222        trigger_nm_jit(eip_offset_in_page);
4223    } else {
4224        dbg_assert!(false);
4225    }
4226}
4227
4228pub unsafe fn read_moffs() -> OrPageFault<i32> {
4229    // read 2 or 4 byte from ip, depending on address size attribute
4230    if is_asize_32() {
4231        read_imm32s()
4232    } else {
4233        read_imm16()
4234    }
4235}
4236
4237#[no_mangle]
4238pub unsafe fn get_real_eip() -> i32 {
4239    // Returns the 'real' instruction pointer, without segment offset
4240    return *instruction_pointer - get_seg_cs();
4241}
4242
4243pub unsafe fn get_stack_reg() -> i32 {
4244    if *stack_size_32 {
4245        return read_reg32(ESP);
4246    } else {
4247        return read_reg16(SP);
4248    };
4249}
4250
4251pub unsafe fn set_stack_reg(value: i32) {
4252    if *stack_size_32 {
4253        write_reg32(ESP, value)
4254    } else {
4255        write_reg16(SP, value)
4256    };
4257}
4258
4259pub unsafe fn get_reg_asize(reg: i32) -> i32 {
4260    dbg_assert!(reg == ECX || reg == ESI || reg == EDI);
4261    let r = read_reg32(reg);
4262    if is_asize_32() {
4263        return r;
4264    } else {
4265        return r & 0xFFFF;
4266    };
4267}
4268
4269pub unsafe fn set_reg_asize(is_asize_32: bool, reg: i32, value: i32) {
4270    dbg_assert!(reg == ECX || reg == ESI || reg == EDI);
4271    if is_asize_32 {
4272        write_reg32(reg, value)
4273    } else {
4274        write_reg16(reg, value)
4275    };
4276}
4277
4278pub unsafe fn decr_ecx_asize(is_asize_32: bool) -> i32 {
4279    return if is_asize_32 {
4280        write_reg32(ECX, read_reg32(ECX) - 1);
4281        read_reg32(ECX)
4282    } else {
4283        write_reg16(CX, read_reg16(CX) - 1);
4284        read_reg16(CX)
4285    };
4286}
4287
4288#[no_mangle]
4289pub unsafe fn set_tsc(low: u32, high: u32) {
4290    let new_value = low as u64 | (high as u64) << 32;
4291    let current_value = read_tsc();
4292    tsc_offset = current_value.wrapping_sub(new_value);
4293}
4294
4295#[no_mangle]
4296pub unsafe fn read_tsc() -> u64 {
4297    let value = ((js::microtick() * TSC_RATE) as u64).wrapping_sub(tsc_offset);
4298
4299    if !TSC_ENABLE_IMPRECISE_BROWSER_WORKAROUND {
4300        return value;
4301    }
4302
4303    if value == tsc_last_value {
4304        // If the browser returns the same value as last time, extrapolate based on the number of
4305        // rdtsc calls between the last two changes
4306        tsc_number_of_same_readings += 1;
4307        let extra = (tsc_number_of_same_readings * tsc_resolution) / tsc_speed;
4308        let extra = u64::min(extra, tsc_resolution - 1);
4309        #[cfg(debug_assertions)]
4310        {
4311            tsc_last_extra = extra;
4312        }
4313        return value + extra;
4314    }
4315
4316    #[cfg(debug_assertions)]
4317    if tsc_last_extra != 0 {
4318        if TSC_VERBOSE_LOGGING || tsc_last_extra >= tsc_resolution {
4319            dbg_log!(
4320                "rdtsc: jump from {}+{} to {} (diff {}, {}%)",
4321                tsc_last_value as u64,
4322                tsc_last_extra as u64,
4323                value,
4324                value - (tsc_last_value + tsc_last_extra),
4325                (100 * tsc_last_extra) / tsc_resolution,
4326            );
4327            dbg_assert!(tsc_last_extra < tsc_resolution, "XXX: Overshot tsc");
4328        }
4329        tsc_last_extra = 0;
4330    }
4331
4332    let d = value - tsc_last_value;
4333    if d < tsc_resolution {
4334        dbg_log!("rdtsc resolution: {}", d);
4335    }
4336    tsc_resolution = tsc_resolution.min(d);
4337    tsc_last_value = value;
4338    if tsc_number_of_same_readings != 0 {
4339        tsc_speed = tsc_number_of_same_readings;
4340        tsc_number_of_same_readings = 0;
4341    }
4342
4343    value
4344}
4345
4346pub unsafe fn vm86_mode() -> bool {
4347    return *flags & FLAG_VM == FLAG_VM;
4348}
4349
4350#[no_mangle]
4351pub unsafe fn getiopl() -> i32 {
4352    return *flags >> 12 & 3;
4353}
4354
4355#[no_mangle]
4356#[cfg(feature = "profiler")]
4357pub unsafe fn get_opstats_buffer(
4358    compiled: bool,
4359    jit_exit: bool,
4360    unguarded_register: bool,
4361    wasm_size: bool,
4362    opcode: u8,
4363    is_0f: bool,
4364    is_mem: bool,
4365    fixed_g: u8,
4366) -> f64 {
4367    {
4368        let index = (is_0f as usize) << 12
4369            | (opcode as usize) << 4
4370            | (is_mem as usize) << 3
4371            | fixed_g as usize;
4372        (if compiled {
4373            opstats::opstats_compiled_buffer[index]
4374        } else if jit_exit {
4375            opstats::opstats_jit_exit_buffer[index]
4376        } else if unguarded_register {
4377            opstats::opstats_unguarded_register_buffer[index]
4378        } else if wasm_size {
4379            opstats::opstats_wasm_size[index]
4380        } else {
4381            opstats::opstats_buffer[index]
4382        }) as f64
4383    }
4384}
4385
4386#[no_mangle]
4387#[cfg(not(feature = "profiler"))]
4388pub unsafe fn get_opstats_buffer() -> f64 {
4389    0.0
4390}
4391
4392pub fn clear_tlb_code(page: i32) {
4393    unsafe {
4394        if let Some(c) = tlb_code[page as usize] {
4395            drop(Box::from_raw(c.as_ptr()));
4396        }
4397        tlb_code[page as usize] = None;
4398    }
4399}
4400
4401pub unsafe fn invlpg(addr: i32) {
4402    let page = (addr as u32 >> 12) as i32;
4403    // Note: Doesn't remove this page from valid_tlb_entries: This isn't
4404    // necessary, because when valid_tlb_entries grows too large, it will be
4405    // empties by calling clear_tlb, which removes this entry as it isn't global.
4406    // This however means that valid_tlb_entries can contain some invalid entries
4407    clear_tlb_code(page);
4408    tlb_data[page as usize] = 0;
4409    *last_virt_eip = -1;
4410}
4411
4412#[no_mangle]
4413pub unsafe fn update_eflags(new_flags: i32) {
4414    let mut dont_update = FLAG_RF | FLAG_VM | FLAG_VIP | FLAG_VIF;
4415    let mut clear = !FLAG_VIP & !FLAG_VIF & FLAGS_MASK;
4416    if 0 != *flags & FLAG_VM {
4417        // other case needs to be handled in popf or iret
4418        dbg_assert!(getiopl() == 3);
4419        dont_update |= FLAG_IOPL;
4420        // don't clear vip or vif
4421        clear |= FLAG_VIP | FLAG_VIF
4422    } else {
4423        if !*protected_mode {
4424            dbg_assert!(*cpl == 0);
4425        }
4426        if 0 != *cpl {
4427            // cpl > 0
4428            // cannot update iopl
4429            dont_update |= FLAG_IOPL;
4430            if *cpl as i32 > getiopl() {
4431                // cpl > iopl
4432                // cannot update interrupt flag
4433                dont_update |= FLAG_INTERRUPT
4434            }
4435        }
4436    }
4437    *flags = (new_flags ^ (*flags ^ new_flags) & dont_update) & clear | FLAGS_DEFAULT;
4438    *flags_changed = 0;
4439
4440    if *flags & FLAG_TRAP != 0 {
4441        dbg_log!("Not supported: trap flag");
4442    }
4443    *flags &= !FLAG_TRAP;
4444}
4445
4446#[no_mangle]
4447pub unsafe fn get_valid_tlb_entries_count() -> i32 {
4448    if !cfg!(feature = "profiler") {
4449        return 0;
4450    }
4451    let mut result = 0;
4452    for i in 0..valid_tlb_entries_count {
4453        let page = valid_tlb_entries[i as usize];
4454        let entry = tlb_data[page as usize];
4455        if 0 != entry {
4456            result += 1
4457        }
4458    }
4459    return result;
4460}
4461
4462#[no_mangle]
4463pub unsafe fn get_valid_global_tlb_entries_count() -> i32 {
4464    if !cfg!(feature = "profiler") {
4465        return 0;
4466    }
4467    let mut result = 0;
4468    for i in 0..valid_tlb_entries_count {
4469        let page = valid_tlb_entries[i as usize];
4470        let entry = tlb_data[page as usize];
4471        if 0 != entry & TLB_GLOBAL {
4472            result += 1
4473        }
4474    }
4475    return result;
4476}
4477
4478#[inline(never)]
4479pub unsafe fn trigger_np(code: i32) {
4480    dbg_log!("#np");
4481    *instruction_pointer = *previous_ip;
4482    if DEBUG {
4483        if js::cpu_exception_hook(CPU_EXCEPTION_NP) {
4484            return;
4485        }
4486    }
4487    call_interrupt_vector(CPU_EXCEPTION_NP, false, Some(code));
4488}
4489
4490#[inline(never)]
4491pub unsafe fn trigger_ss(code: i32) {
4492    dbg_log!("#ss");
4493    *instruction_pointer = *previous_ip;
4494    if DEBUG {
4495        if js::cpu_exception_hook(CPU_EXCEPTION_SS) {
4496            return;
4497        }
4498    }
4499    call_interrupt_vector(CPU_EXCEPTION_SS, false, Some(code));
4500}
4501
4502#[no_mangle]
4503pub unsafe fn store_current_tsc() {
4504    *current_tsc = read_tsc();
4505}
4506
4507#[no_mangle]
4508pub unsafe fn handle_irqs() {
4509    if *flags & FLAG_INTERRUPT != 0 {
4510        if let Some(irq) = pic::pic_acknowledge_irq() {
4511            pic_call_irq(irq)
4512        } else if *acpi_enabled {
4513            if let Some(irq) = apic::acknowledge_irq() {
4514                pic_call_irq(irq)
4515            }
4516        }
4517    }
4518}
4519
4520unsafe fn pic_call_irq(interrupt_nr: u8) {
4521    *previous_ip = *instruction_pointer; // XXX: What if called after instruction (port IO)
4522    if *in_hlt {
4523        js::stop_idling();
4524        *in_hlt = false;
4525    }
4526    call_interrupt_vector(interrupt_nr as i32, false, None);
4527}
4528
4529#[no_mangle]
4530pub unsafe fn device_raise_irq(i: u8) {
4531    pic::set_irq(i);
4532    if *acpi_enabled {
4533        ioapic::set_irq(i);
4534    }
4535    handle_irqs()
4536}
4537
4538#[no_mangle]
4539pub unsafe fn device_lower_irq(i: u8) {
4540    pic::clear_irq(i);
4541    if *acpi_enabled {
4542        ioapic::clear_irq(i);
4543    }
4544    handle_irqs()
4545}
4546
4547pub fn io_port_read8(port: i32) -> i32 {
4548    unsafe {
4549        match port {
4550            0x20 => pic::port20_read() as i32,
4551            0x21 => pic::port21_read() as i32,
4552            0xA0 => pic::portA0_read() as i32,
4553            0xA1 => pic::portA1_read() as i32,
4554            0x4D0 => pic::port4D0_read() as i32,
4555            0x4D1 => pic::port4D1_read() as i32,
4556            _ => js::io_port_read8(port),
4557        }
4558    }
4559}
4560pub fn io_port_read16(port: i32) -> i32 {
4561    unsafe { js::io_port_read16(port) }
4562}
4563pub fn io_port_read32(port: i32) -> i32 {
4564    unsafe { js::io_port_read32(port) }
4565}
4566
4567pub fn io_port_write8(port: i32, value: i32) {
4568    unsafe {
4569        match port {
4570            0x20 | 0x21 | 0xA0 | 0xA1 | 0x4D0 | 0x4D1 => {
4571                match port {
4572                    0x20 => pic::port20_write(value as u8),
4573                    0x21 => pic::port21_write(value as u8),
4574                    0xA0 => pic::portA0_write(value as u8),
4575                    0xA1 => pic::portA1_write(value as u8),
4576                    0x4D0 => pic::port4D0_write(value as u8),
4577                    0x4D1 => pic::port4D1_write(value as u8),
4578                    _ => dbg_assert!(false),
4579                };
4580                handle_irqs()
4581            }
4582            _ => js::io_port_write8(port, value),
4583        }
4584    }
4585}
4586pub fn io_port_write16(port: i32, value: i32) {
4587    unsafe { js::io_port_write16(port, value) }
4588}
4589pub fn io_port_write32(port: i32, value: i32) {
4590    unsafe { js::io_port_write32(port, value) }
4591}
4592
4593#[no_mangle]
4594#[cfg(debug_assertions)]
4595pub unsafe fn check_page_switch(block_addr: u32, next_block_addr: u32) {
4596    let x = translate_address_read_jit(*instruction_pointer);
4597    if x != Ok(next_block_addr) {
4598        dbg_log!(
4599            "page switch from={:x} to={:x} prev_eip={:x} eip={:x} phys_eip={:x}",
4600            block_addr,
4601            next_block_addr,
4602            *previous_ip,
4603            *instruction_pointer,
4604            x.unwrap_or(0),
4605        );
4606    }
4607    dbg_assert!(next_block_addr & 0xFFF == *instruction_pointer as u32 & 0xFFF);
4608    dbg_assert!(x.is_ok());
4609    dbg_assert!(x == Ok(next_block_addr));
4610}
4611
4612#[no_mangle]
4613pub unsafe fn reset_cpu() {
4614    for i in 0..8 {
4615        *segment_is_null.offset(i) = false;
4616        *segment_limits.offset(i) = 0;
4617        *segment_offsets.offset(i) = 0;
4618        *segment_access_bytes.offset(i) = 0x80 | (0 << 5) | 0x10 | 0x02; // P dpl0 S RW
4619
4620        *reg32.offset(i) = 0;
4621
4622        *sreg.offset(i) = 0;
4623        *dreg.offset(i) = 0;
4624
4625        write_xmm128_2(i as i32, 0, 0);
4626
4627        *fpu_st.offset(i) = softfloat::F80::ZERO;
4628    }
4629    *segment_access_bytes.offset(CS as isize) = 0x80 | (0 << 5) | 0x10 | 0x08 | 0x02; // P dpl0 S E RW
4630
4631    for i in 0..4 {
4632        *reg_pdpte.offset(i) = 0
4633    }
4634
4635    *fpu_stack_empty = 0xFF;
4636    *fpu_stack_ptr = 0;
4637    *fpu_control_word = 0x37F;
4638    *fpu_status_word = 0;
4639    *fpu_ip = 0;
4640    *fpu_ip_selector = 0;
4641    *fpu_opcode = 0;
4642    *fpu_dp = 0;
4643    *fpu_dp_selector = 0;
4644
4645    *mxcsr = 0x1F80;
4646
4647    full_clear_tlb();
4648
4649    *protected_mode = false;
4650
4651    // http://www.sandpile.org/x86/initial.htm
4652    *idtr_size = 0;
4653    *idtr_offset = 0;
4654
4655    *gdtr_size = 0;
4656    *gdtr_offset = 0;
4657
4658    *cr = 1 << 30 | 1 << 29 | 1 << 4;
4659    *cr.offset(2) = 0;
4660    *cr.offset(3) = 0;
4661    *cr.offset(4) = 0;
4662    *dreg.offset(6) = 0xFFFF0FF0u32 as i32;
4663    *dreg.offset(7) = 0x400;
4664    *cpl = 0;
4665
4666    *is_32 = false;
4667    *stack_size_32 = false;
4668    *prefixes = 0;
4669
4670    *last_virt_eip = -1;
4671
4672    *instruction_counter = 0;
4673    *previous_ip = 0;
4674    *in_hlt = false;
4675
4676    *sysenter_cs = 0;
4677    *sysenter_esp = 0;
4678    *sysenter_eip = 0;
4679
4680    *flags = FLAGS_DEFAULT;
4681    *flags_changed = 0;
4682    *last_result = 0;
4683    *last_op1 = 0;
4684    *last_op_size = 0;
4685
4686    set_tsc(0, 0);
4687
4688    *instruction_pointer = 0xFFFF0;
4689    switch_cs_real_mode(0xF000);
4690
4691    switch_seg(SS, 0x30);
4692    write_reg32(ESP, 0x100);
4693
4694    update_state_flags();
4695
4696    jit::jit_clear_cache_js();
4697}
4698
4699#[no_mangle]
4700pub unsafe fn set_cpuid_level(level: u32) {
4701    cpuid_level = level
4702}