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
56pub 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
80pub 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
291pub 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
305pub static mut tsc_last_value: u64 = 0;
307pub static mut tsc_resolution: u64 = u64::MAX;
309pub static mut tsc_number_of_same_readings: u64 = 0;
311pub static mut tsc_speed: u64 = 1;
315
316pub 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#[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 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 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 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 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 dbg_assert!(false);
655 }
656
657 adjust_stack_reg(9 * 4); write_reg32(ESP, temp_esp);
660 if !switch_seg(SS, temp_ss) {
661 dbg_assert!(false);
663 }
664
665 *cpl = 3;
666 cpl_changed();
667
668 update_cs_size(false);
669 update_state_flags();
670
671 return;
673 } else {
674 dbg_log!("vm86 flag ignored because cpl != 0");
675 new_flags &= !FLAG_VM;
676 }
677 }
678
679 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 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 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 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 *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 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 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 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 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 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 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 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 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 *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 dbg_assert!(false);
1039 }
1040 set_stack_reg(new_esp);
1041
1042 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 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 return_on_pagefault!(writable_or_pagefault(
1083 get_stack_pointer(-stack_space),
1084 stack_space
1085 ));
1086
1087 } else {
1089 panic!("Unimplemented: #GP handler");
1090 }
1091
1092 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 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 *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 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 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 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 {
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 *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 if !switch_seg(SS, new_ss) {
1349 dbg_assert!(false);
1350 };
1351 set_stack_reg(new_esp);
1352
1353 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 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 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 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 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 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 if info.dpl() > *cpl {
1497 dbg_log!("#gp cs dpl > cpl: {:x}", selector);
1498 trigger_gp(selector & !3);
1499 return;
1500 }
1501 } else {
1502 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 temp_esp = safe_read32s(get_stack_pointer(stack_adjust + 8)).unwrap();
1630 temp_ss = safe_read16(get_stack_pointer(stack_adjust + 12)).unwrap();
1632 } else {
1633 temp_esp = safe_read16(get_stack_pointer(stack_adjust + 4)).unwrap();
1635 temp_ss = safe_read16(get_stack_pointer(stack_adjust + 6)).unwrap();
1637 }
1638
1639 *cpl = cs_selector.rpl();
1640 cpl_changed();
1641
1642 if !switch_seg(SS, temp_ss) {
1644 dbg_assert!(false);
1645 }
1646 set_stack_reg(temp_esp + stack_adjust);
1647
1648 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 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 panic!("#TS handler");
1726 }
1727 } else if tss_is_busy {
1728 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_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 if source == TaskSwitchSource::Jump || source == TaskSwitchSource::Iret {
1777 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 safe_write64(descriptor_address, descriptor.set_busy().raw).unwrap();
1792 }
1793
1794 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; 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 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#[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 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 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 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 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 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 } 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 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 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 *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 *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 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
2419pub 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 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 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 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 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 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 }
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 *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 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 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 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; }
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 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 || (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 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#[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 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 mid = memory::read16(high - 2)
3461 } else {
3462 mid = memory::read16(low + 1)
3464 }
3465 } else {
3466 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 memory::write8((high - 2) as u32, value >> 8);
3489 memory::write8((high - 1) as u32, value >> 16);
3490 } else {
3491 memory::write8((low + 1) as u32, value >> 8);
3493 memory::write8((low + 2) as u32, value >> 16);
3494 }
3495 } else {
3496 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 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)); ((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 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#[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); 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 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 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 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 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 dbg_assert!(getiopl() == 3);
4419 dont_update |= FLAG_IOPL;
4420 clear |= FLAG_VIP | FLAG_VIF
4422 } else {
4423 if !*protected_mode {
4424 dbg_assert!(*cpl == 0);
4425 }
4426 if 0 != *cpl {
4427 dont_update |= FLAG_IOPL;
4430 if *cpl as i32 > getiopl() {
4431 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; 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; *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; 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 *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}