cpu: Port call_interrupt_vector to Rust
This commit is contained in:
parent
48e6843a87
commit
4ef09445e1
|
@ -177,9 +177,6 @@ function V86Starter(options)
|
|||
return dest;
|
||||
},
|
||||
|
||||
"call_interrupt_vector": function(interrupt_nr, is_software_int, has_error_code, error_code) {
|
||||
cpu.call_interrupt_vector(interrupt_nr, is_software_int, !!has_error_code, error_code);
|
||||
},
|
||||
"far_jump": function(eip, selector, is_call) { return cpu.far_jump(eip, selector, !!is_call); },
|
||||
"far_return": function(eip, selector, stack_adjust) { return cpu.far_return(eip, selector, stack_adjust); },
|
||||
"iret16": function() { return cpu.iret16(); },
|
||||
|
@ -258,6 +255,10 @@ function V86Starter(options)
|
|||
"ceil": Math.ceil,
|
||||
"fabs": Math.abs,
|
||||
"abs": Math.abs,
|
||||
|
||||
"do_task_switch": (selector, error_code) => cpu.do_task_switch(selector, error_code),
|
||||
"get_tss_stack_addr": (dpl) => cpu.get_tss_stack_addr(dpl),
|
||||
"switch_cs_real_mode": (selector) => cpu.switch_cs_real_mode(selector),
|
||||
};
|
||||
|
||||
const wasm_globals = {
|
||||
|
|
337
src/cpu.js
337
src/cpu.js
|
@ -305,6 +305,8 @@ CPU.prototype.wasm_patch = function(wm)
|
|||
this.trigger_np = get_import("trigger_np");
|
||||
this.trigger_ss = get_import("trigger_ss");
|
||||
|
||||
this.call_interrupt_vector = get_import("call_interrupt_vector");
|
||||
|
||||
this.do_many_cycles_native = get_import("do_many_cycles_native");
|
||||
this.cycle_internal = get_import("cycle_internal");
|
||||
|
||||
|
@ -1452,341 +1454,6 @@ CPU.prototype.jit_clear_cache = function()
|
|||
}
|
||||
};
|
||||
|
||||
CPU.prototype.call_interrupt_vector = function(interrupt_nr, is_software_int, has_error_code, error_code)
|
||||
{
|
||||
//dbg_log("int " + h(interrupt_nr, 2) + " (" + (is_software_int ? "soft" : "hard") + "ware)", LOG_CPU);
|
||||
CPU_LOG_VERBOSE && this.debug.dump_state("int " + h(interrupt_nr) + " start" +
|
||||
" (" + (is_software_int ? "soft" : "hard") + "ware)");
|
||||
CPU_LOG_VERBOSE && this.debug.dump_regs();
|
||||
|
||||
this.debug.debug_interrupt(interrupt_nr);
|
||||
|
||||
dbg_assert(typeof has_error_code === "boolean");
|
||||
dbg_assert(has_error_code === false || typeof error_code === "number");
|
||||
|
||||
// we have to leave hlt_loop at some point, this is a
|
||||
// good place to do it
|
||||
//this.in_hlt && dbg_log("Leave HLT loop", LOG_CPU);
|
||||
this.in_hlt[0] = +false;
|
||||
|
||||
if(this.protected_mode[0])
|
||||
{
|
||||
if(this.vm86_mode() && (this.cr[4] & CR4_VME))
|
||||
{
|
||||
throw this.debug.unimpl("VME");
|
||||
}
|
||||
|
||||
if(this.vm86_mode() && is_software_int && this.getiopl() < 3)
|
||||
{
|
||||
dbg_log("call_interrupt_vector #GP. vm86 && software int && iopl < 3", LOG_CPU);
|
||||
dbg_trace(LOG_CPU);
|
||||
this.trigger_gp_non_raising(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if((interrupt_nr << 3 | 7) > this.idtr_size[0])
|
||||
{
|
||||
dbg_log(interrupt_nr, LOG_CPU);
|
||||
dbg_trace(LOG_CPU);
|
||||
throw this.debug.unimpl("#GP handler");
|
||||
}
|
||||
|
||||
var addr = this.idtr_offset[0] + (interrupt_nr << 3) | 0;
|
||||
dbg_assert((addr & 0xFFF) < 0xFF8);
|
||||
|
||||
if(this.cr[0] & CR0_PG)
|
||||
{
|
||||
addr = this.translate_address_system_read(addr);
|
||||
}
|
||||
|
||||
var base = this.read16(addr) | this.read16(addr + 6 | 0) << 16;
|
||||
var selector = this.read16(addr + 2 | 0);
|
||||
var access = this.read8(addr + 5 | 0);
|
||||
var dpl = access >> 5 & 3;
|
||||
var type = access & 31;
|
||||
|
||||
if((access & 0x80) === 0)
|
||||
{
|
||||
// present bit not set
|
||||
throw this.debug.unimpl("#NP handler");
|
||||
}
|
||||
|
||||
if(is_software_int && dpl < this.cpl[0])
|
||||
{
|
||||
dbg_log("#gp software interrupt (" + h(interrupt_nr, 2) + ") and dpl < cpl", LOG_CPU);
|
||||
dbg_trace(LOG_CPU);
|
||||
this.trigger_gp_non_raising(interrupt_nr << 3 | 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if(type === 5)
|
||||
{
|
||||
// task gate
|
||||
dbg_log("interrupt to task gate: int=" + h(interrupt_nr, 2) + " sel=" + h(selector, 4) + " dpl=" + dpl, LOG_CPU);
|
||||
dbg_trace(LOG_CPU);
|
||||
|
||||
this.do_task_switch(selector, error_code);
|
||||
CPU_LOG_VERBOSE && this.debug.dump_state("int end");
|
||||
return;
|
||||
}
|
||||
|
||||
if((type & ~1 & ~8) !== 6)
|
||||
{
|
||||
// invalid type
|
||||
dbg_trace(LOG_CPU);
|
||||
dbg_log("invalid type: " + h(type));
|
||||
dbg_log(h(addr) + " " + h(base >>> 0) + " " + h(selector));
|
||||
throw this.debug.unimpl("#GP handler");
|
||||
}
|
||||
|
||||
var is_trap = (type & 1) === 1;
|
||||
var is_16 = (type & 8) === 0;
|
||||
|
||||
var info = this.lookup_segment_selector(selector);
|
||||
|
||||
dbg_assert((base >>> 0) <= info.effective_limit);
|
||||
dbg_assert(info.is_valid);
|
||||
|
||||
if(info.is_null)
|
||||
{
|
||||
dbg_log("is null");
|
||||
throw this.debug.unimpl("#GP handler");
|
||||
}
|
||||
if(!info.is_executable || info.dpl > this.cpl[0])
|
||||
{
|
||||
dbg_log("not exec");
|
||||
throw this.debug.unimpl("#GP handler");
|
||||
}
|
||||
if(!info.is_present)
|
||||
{
|
||||
// kvm-unit-test
|
||||
dbg_log("not present");
|
||||
this.trigger_np(interrupt_nr << 3 | 2);
|
||||
return;
|
||||
}
|
||||
|
||||
var old_flags = this.get_eflags();
|
||||
|
||||
//dbg_log("interrupt " + h(interrupt_nr, 2) + " (" + (is_software_int ? "soft" : "hard") + "ware) from cpl=" + this.cpl[0] + " vm=" + (this.flags[0] & flag_vm) + " cs:eip=" + h(this.sreg[reg_cs], 4) + ":" + h(this.get_real_eip(), 8) + " to cpl="
|
||||
|
||||
if(!info.dc_bit && info.dpl < this.cpl[0])
|
||||
{
|
||||
// inter privilege level interrupt
|
||||
// interrupt from vm86 mode
|
||||
|
||||
//dbg_log("Inter privilege interrupt gate=" + h(selector, 4) + ":" + h(base >>> 0, 8) + " trap=" + is_trap + " 16bit=" + is_16, LOG_CPU);
|
||||
//this.debug.dump_regs();
|
||||
var tss_stack_addr = this.get_tss_stack_addr(info.dpl);
|
||||
|
||||
if(this.tss_size_32[0])
|
||||
{
|
||||
var new_esp = this.read32s(tss_stack_addr);
|
||||
var new_ss = this.read16(tss_stack_addr + 4 | 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var new_esp = this.read16(tss_stack_addr);
|
||||
var new_ss = this.read16(tss_stack_addr + 2 | 0);
|
||||
}
|
||||
var ss_info = this.lookup_segment_selector(new_ss);
|
||||
|
||||
// Disabled: Incorrect handling of direction bit
|
||||
// See http://css.csail.mit.edu/6.858/2014/readings/i386/s06_03.htm
|
||||
//if(!((new_esp >>> 0) <= ss_info.effective_limit))
|
||||
// debugger;
|
||||
//dbg_assert((new_esp >>> 0) <= ss_info.effective_limit);
|
||||
dbg_assert(ss_info.is_valid && !ss_info.is_system && ss_info.is_writable);
|
||||
|
||||
if(ss_info.is_null)
|
||||
{
|
||||
throw this.debug.unimpl("#TS handler");
|
||||
}
|
||||
if(ss_info.rpl !== info.dpl) // xxx: 0 in v86 mode
|
||||
{
|
||||
throw this.debug.unimpl("#TS handler");
|
||||
}
|
||||
if(ss_info.dpl !== info.dpl || !ss_info.rw_bit)
|
||||
{
|
||||
throw this.debug.unimpl("#TS handler");
|
||||
}
|
||||
if(!ss_info.is_present)
|
||||
{
|
||||
throw this.debug.unimpl("#TS handler");
|
||||
}
|
||||
|
||||
var old_esp = this.reg32s[reg_esp];
|
||||
var old_ss = this.sreg[reg_ss];
|
||||
|
||||
if(old_flags & flag_vm)
|
||||
{
|
||||
//dbg_log("return from vm86 mode");
|
||||
//this.debug.dump_regs();
|
||||
dbg_assert(info.dpl === 0, "switch to non-0 dpl from vm86 mode");
|
||||
}
|
||||
|
||||
var stack_space = (is_16 ? 2 : 4) * (5 + (has_error_code === true) + 4 * ((old_flags & flag_vm) === flag_vm));
|
||||
var new_stack_pointer = ss_info.base + (ss_info.size ? new_esp - stack_space : (new_esp - stack_space & 0xFFFF));
|
||||
|
||||
// XXX: with new cpl or with cpl 0?
|
||||
this.translate_address_system_write(new_stack_pointer);
|
||||
this.translate_address_system_write(ss_info.base + new_esp - 1);
|
||||
|
||||
// no exceptions below
|
||||
|
||||
this.cpl[0] = info.dpl;
|
||||
this.cpl_changed();
|
||||
|
||||
this.update_cs_size(info.size);
|
||||
|
||||
this.flags[0] &= ~flag_vm & ~flag_rf;
|
||||
|
||||
if(!this.switch_seg(reg_ss, new_ss)) dbg_assert(false); // XXX
|
||||
this.set_stack_reg(new_esp);
|
||||
|
||||
if(old_flags & flag_vm)
|
||||
{
|
||||
if(is_16)
|
||||
{
|
||||
dbg_assert(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.push32(this.sreg[reg_gs]);
|
||||
this.push32(this.sreg[reg_fs]);
|
||||
this.push32(this.sreg[reg_ds]);
|
||||
this.push32(this.sreg[reg_es]);
|
||||
}
|
||||
}
|
||||
|
||||
if(is_16)
|
||||
{
|
||||
this.push16(old_ss);
|
||||
this.push16(old_esp);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.push32(old_ss);
|
||||
this.push32(old_esp);
|
||||
}
|
||||
}
|
||||
else if(info.dc_bit || info.dpl === this.cpl[0])
|
||||
{
|
||||
// intra privilege level interrupt
|
||||
|
||||
//dbg_log("Intra privilege interrupt gate=" + h(selector, 4) + ":" + h(base >>> 0, 8) +
|
||||
// " trap=" + is_trap + " 16bit=" + is_16 +
|
||||
// " cpl=" + this.cpl[0] + " dpl=" + info.dpl + " conforming=" + +info.dc_bit, LOG_CPU);
|
||||
//this.debug.dump_regs_short();
|
||||
|
||||
if(this.flags[0] & flag_vm)
|
||||
{
|
||||
dbg_assert(false, "check error code");
|
||||
this.trigger_gp_non_raising(selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
var stack_space = (is_16 ? 2 : 4) * (3 + (has_error_code === true));
|
||||
|
||||
// XXX: with current cpl or with cpl 0?
|
||||
if(!this.writable_or_pagefault(this.get_stack_pointer(-stack_space), stack_space))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// no exceptions below
|
||||
}
|
||||
else
|
||||
{
|
||||
throw this.debug.unimpl("#GP handler");
|
||||
}
|
||||
|
||||
if(is_16)
|
||||
{
|
||||
this.push16(old_flags);
|
||||
this.push16(this.sreg[reg_cs]);
|
||||
this.push16(this.get_real_eip());
|
||||
|
||||
if(has_error_code === true)
|
||||
{
|
||||
this.push16(error_code);
|
||||
}
|
||||
|
||||
base &= 0xFFFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.push32(old_flags);
|
||||
this.push32(this.sreg[reg_cs]);
|
||||
this.push32(this.get_real_eip());
|
||||
|
||||
if(has_error_code === true)
|
||||
{
|
||||
this.push32(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
if(old_flags & flag_vm)
|
||||
{
|
||||
if(
|
||||
!this.switch_seg(reg_gs, 0) ||
|
||||
!this.switch_seg(reg_fs, 0) ||
|
||||
!this.switch_seg(reg_ds, 0) ||
|
||||
!this.switch_seg(reg_es, 0)
|
||||
)
|
||||
{
|
||||
// can't fail
|
||||
dbg_assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
this.sreg[reg_cs] = selector & ~3 | this.cpl[0];
|
||||
dbg_assert((this.sreg[reg_cs] & 3) === this.cpl[0]);
|
||||
|
||||
this.update_cs_size(info.size);
|
||||
|
||||
this.segment_limits[reg_cs] = info.effective_limit;
|
||||
this.segment_offsets[reg_cs] = info.base;
|
||||
|
||||
this.instruction_pointer[0] = this.get_seg(reg_cs) + base | 0;
|
||||
|
||||
this.flags[0] &= ~flag_nt & ~flag_vm & ~flag_rf & ~flag_trap;
|
||||
|
||||
if(!is_trap)
|
||||
{
|
||||
// clear int flag for interrupt gates
|
||||
this.flags[0] &= ~flag_interrupt;
|
||||
}
|
||||
else
|
||||
{
|
||||
if((this.flags[0] & flag_interrupt) && !(old_flags & flag_interrupt))
|
||||
{
|
||||
this.handle_irqs();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// call 4 byte cs:ip interrupt vector from ivt at cpu.memory 0
|
||||
|
||||
var index = interrupt_nr << 2;
|
||||
var new_ip = this.read16(index);
|
||||
var new_cs = this.read16(index + 2 | 0);
|
||||
|
||||
// push flags, cs:ip
|
||||
this.push16(this.get_eflags());
|
||||
this.push16(this.sreg[reg_cs]);
|
||||
this.push16(this.get_real_eip());
|
||||
|
||||
this.flags[0] &= ~flag_interrupt;
|
||||
|
||||
this.switch_cs_real_mode(new_cs);
|
||||
this.instruction_pointer[0] = this.get_seg(reg_cs) + new_ip | 0;
|
||||
}
|
||||
|
||||
//dbg_log("int to:", LOG_CPU);
|
||||
CPU_LOG_VERBOSE && this.debug.dump_state("int end");
|
||||
};
|
||||
|
||||
CPU.prototype.iret16 = function()
|
||||
{
|
||||
this.iret(true);
|
||||
|
|
|
@ -54,6 +54,5 @@ extern void io_port_write16(int32_t, int32_t);
|
|||
extern void io_port_write32(int32_t, int32_t);
|
||||
extern int32_t convert_f64_to_i32(double_t);
|
||||
extern void jit_clear_func(int32_t index);
|
||||
extern void call_interrupt_vector(int32_t interrupt_nr, bool is_software_int, bool has_error_code, int32_t error_code);
|
||||
extern void throw_cpu_exception(void);
|
||||
extern double_t microtick(void);
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
#![allow(non_upper_case_globals)]
|
||||
|
||||
extern "C" {
|
||||
#[no_mangle]
|
||||
fn call_interrupt_vector(interrupt: i32, is_software: bool, has_error: bool, error: i32);
|
||||
#[no_mangle]
|
||||
fn cpu_exception_hook(interrupt: i32) -> bool;
|
||||
#[no_mangle]
|
||||
fn do_task_switch(selector: i32, error_code: i32);
|
||||
#[no_mangle]
|
||||
fn get_tss_stack_addr(dpl: u8) -> u32;
|
||||
#[no_mangle]
|
||||
fn switch_cs_real_mode(selector: i32);
|
||||
#[no_mangle]
|
||||
fn dbg_trace();
|
||||
//#[no_mangle]
|
||||
//fn logop(addr: i32, op: i32);
|
||||
|
@ -26,6 +30,7 @@ use cpu2::memory::{
|
|||
};
|
||||
use cpu2::misc_instr::{
|
||||
adjust_stack_reg, get_stack_pointer, getaf, getcf, getof, getpf, getsf, getzf, pop16, pop32s,
|
||||
push16, push32,
|
||||
};
|
||||
use cpu2::modrm::{resolve_modrm16, resolve_modrm32};
|
||||
use page::Page;
|
||||
|
@ -339,6 +344,335 @@ impl SegmentDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct InterruptDescriptor {
|
||||
raw: u64,
|
||||
}
|
||||
|
||||
impl InterruptDescriptor {
|
||||
pub fn offset(&self) -> i32 { (self.raw & 0xffff | self.raw >> 32 & 0xffff0000) as i32 }
|
||||
pub fn selector(&self) -> u16 { (self.raw >> 16 & 0xffff) as u16 }
|
||||
pub fn access_byte(&self) -> u8 { (self.raw >> 40 & 0xff) as u8 }
|
||||
pub fn dpl(&self) -> u8 { (self.access_byte() >> 5 & 3) as u8 }
|
||||
pub fn gate_type(&self) -> u8 { self.access_byte() & 31 }
|
||||
pub fn is_32(&self) -> bool { self.access_byte() & 8 == 8 }
|
||||
pub fn is_present(&self) -> bool { self.access_byte() & 0x80 == 0x80 }
|
||||
|
||||
const TASK_GATE: u8 = 0b101;
|
||||
const TRAP_GATE: u8 = 0b111;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn call_interrupt_vector(
|
||||
interrupt_nr: i32,
|
||||
is_software_int: bool,
|
||||
has_error_code: bool,
|
||||
error_code: i32,
|
||||
) {
|
||||
// we have to leave hlt_loop at some point, this is a
|
||||
// good place to do it
|
||||
*in_hlt = false;
|
||||
|
||||
if *protected_mode {
|
||||
if vm86_mode() && *cr.offset(4) & CR4_VME != 0 {
|
||||
panic!("Unimplemented: VME");
|
||||
}
|
||||
|
||||
if vm86_mode() && is_software_int && getiopl() < 3 {
|
||||
dbg_log!("call_interrupt_vector #GP. vm86 && software int && iopl < 3");
|
||||
dbg_trace();
|
||||
trigger_gp_non_raising(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if interrupt_nr << 3 | 7 > *idtr_size {
|
||||
dbg_log!("interrupt_nr={:x} idtr_size={:x}", interrupt_nr, *idtr_size);
|
||||
dbg_trace();
|
||||
panic!("Unimplemented: #GP handler");
|
||||
}
|
||||
|
||||
let descriptor_address = return_on_pagefault!(translate_address_system_read(
|
||||
*idtr_offset + (interrupt_nr << 3)
|
||||
));
|
||||
|
||||
let descriptor = InterruptDescriptor {
|
||||
raw: read64s(descriptor_address) as u64,
|
||||
};
|
||||
|
||||
let mut offset = descriptor.offset();
|
||||
let selector = descriptor.selector() as i32;
|
||||
let dpl = descriptor.dpl();
|
||||
let gate_type = descriptor.gate_type();
|
||||
|
||||
if !descriptor.is_present() {
|
||||
// present bit not set
|
||||
panic!("Unimplemented: #NP handler");
|
||||
}
|
||||
|
||||
if is_software_int && dpl < *cpl {
|
||||
dbg_log!("#gp software interrupt ({:x}) and dpl < cpl", interrupt_nr);
|
||||
dbg_trace();
|
||||
trigger_gp_non_raising(interrupt_nr << 3 | 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if gate_type == InterruptDescriptor::TASK_GATE {
|
||||
// task gate
|
||||
dbg_log!(
|
||||
"interrupt to task gate: int={:x} sel={:x} dpl={}",
|
||||
interrupt_nr,
|
||||
selector,
|
||||
dpl
|
||||
);
|
||||
dbg_trace();
|
||||
|
||||
do_task_switch(selector, error_code);
|
||||
return;
|
||||
}
|
||||
|
||||
if gate_type & !1 & !8 != 6 {
|
||||
// invalid gate_type
|
||||
dbg_trace();
|
||||
dbg_log!("invalid gate_type: 0b{:b}", gate_type);
|
||||
dbg_log!(
|
||||
"addr={:x} offset={:x} selector={:x}",
|
||||
descriptor_address,
|
||||
offset,
|
||||
selector
|
||||
);
|
||||
panic!("Unimplemented: #GP handler");
|
||||
}
|
||||
|
||||
let is_trap = gate_type & 7 == InterruptDescriptor::TRAP_GATE;
|
||||
let is_16 = !descriptor.is_32();
|
||||
|
||||
let cs_segment_descriptor = match return_on_pagefault!(lookup_segment_selector(selector)) {
|
||||
Ok((desc, _)) => desc,
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("is null");
|
||||
panic!("Unimplemented: #GP handler");
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
dbg_log!("is invalid");
|
||||
panic!("Unimplemented: #GP handler (error code)");
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
dbg_assert!(offset as u32 <= cs_segment_descriptor.effective_limit());
|
||||
|
||||
if !cs_segment_descriptor.is_executable() || cs_segment_descriptor.dpl() > *cpl {
|
||||
dbg_log!("not exec");
|
||||
panic!("Unimplemented: #GP handler");
|
||||
}
|
||||
if !cs_segment_descriptor.is_present() {
|
||||
// kvm-unit-test
|
||||
dbg_log!("not present");
|
||||
trigger_np(interrupt_nr << 3 | 2);
|
||||
return;
|
||||
}
|
||||
|
||||
let old_flags = get_eflags();
|
||||
|
||||
if !cs_segment_descriptor.is_dc() && cs_segment_descriptor.dpl() < *cpl {
|
||||
// inter privilege level interrupt
|
||||
// interrupt from vm86 mode
|
||||
|
||||
if old_flags & FLAG_VM != 0 && cs_segment_descriptor.dpl() == 0 {
|
||||
panic!("Unimplemented: #GP handler for non-0 cs segment dpl when in vm86 mode");
|
||||
}
|
||||
|
||||
let tss_stack_addr = get_tss_stack_addr(cs_segment_descriptor.dpl());
|
||||
|
||||
let new_esp = read32s(tss_stack_addr);
|
||||
let new_ss = read16(tss_stack_addr + if *tss_size_32 { 4 } else { 2 });
|
||||
let (ss_segment_descriptor, ss_segment_selector) =
|
||||
match return_on_pagefault!(lookup_segment_selector(new_ss)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Err(_) => {
|
||||
panic!("Unimplemented: #TS handler");
|
||||
},
|
||||
};
|
||||
|
||||
// Disabled: Incorrect handling of direction bit
|
||||
// See http://css.csail.mit.edu/6.858/2014/readings/i386/s06_03.htm
|
||||
//if !((new_esp >>> 0) <= ss_segment_descriptor.effective_limit())
|
||||
// debugger;
|
||||
//dbg_assert!((new_esp >>> 0) <= ss_segment_descriptor.effective_limit());
|
||||
dbg_assert!(!ss_segment_descriptor.is_system() && ss_segment_descriptor.is_writable());
|
||||
|
||||
if ss_segment_selector.rpl() != cs_segment_descriptor.dpl() {
|
||||
panic!("Unimplemented: #TS handler");
|
||||
}
|
||||
if ss_segment_descriptor.dpl() != cs_segment_descriptor.dpl()
|
||||
|| !ss_segment_descriptor.is_rw()
|
||||
{
|
||||
panic!("Unimplemented: #TS handler");
|
||||
}
|
||||
if !ss_segment_descriptor.is_present() {
|
||||
panic!("Unimplemented: #TS handler");
|
||||
}
|
||||
|
||||
let old_esp = *reg32s.offset(ESP as isize);
|
||||
let old_ss = *sreg.offset(SS as isize) as i32;
|
||||
|
||||
let error_code_space = if has_error_code == true { 1 } else { 0 };
|
||||
let vm86_space = if (old_flags & FLAG_VM) == FLAG_VM {
|
||||
1
|
||||
}
|
||||
else {
|
||||
0
|
||||
};
|
||||
let bytes_per_arg = if is_16 { 2 } else { 4 };
|
||||
|
||||
let stack_space = bytes_per_arg * (5 + error_code_space + 4 * vm86_space);
|
||||
let new_stack_pointer = ss_segment_descriptor.base() + if ss_segment_descriptor.is_32()
|
||||
{
|
||||
new_esp - stack_space
|
||||
}
|
||||
else {
|
||||
new_esp - stack_space & 0xFFFF
|
||||
};
|
||||
|
||||
return_on_pagefault!(translate_address_system_write(new_stack_pointer));
|
||||
return_on_pagefault!(translate_address_system_write(
|
||||
ss_segment_descriptor.base() + new_esp - 1
|
||||
));
|
||||
|
||||
*cpl = cs_segment_descriptor.dpl();
|
||||
cpl_changed();
|
||||
|
||||
update_cs_size(cs_segment_descriptor.is_32());
|
||||
|
||||
*flags &= !FLAG_VM & !FLAG_RF;
|
||||
|
||||
if !switch_seg(SS, new_ss) {
|
||||
dbg_assert!(false);
|
||||
} // XXX
|
||||
set_stack_reg(new_esp);
|
||||
|
||||
if old_flags & FLAG_VM != 0 {
|
||||
if is_16 {
|
||||
dbg_assert!(false);
|
||||
}
|
||||
else {
|
||||
push32(*sreg.offset(GS as isize) as i32).unwrap();
|
||||
push32(*sreg.offset(FS as isize) as i32).unwrap();
|
||||
push32(*sreg.offset(DS as isize) as i32).unwrap();
|
||||
push32(*sreg.offset(ES as isize) as i32).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if is_16 {
|
||||
push16(old_ss).unwrap();
|
||||
push16(old_esp).unwrap();
|
||||
}
|
||||
else {
|
||||
push32(old_ss).unwrap();
|
||||
push32(old_esp).unwrap();
|
||||
}
|
||||
}
|
||||
else if cs_segment_descriptor.is_dc() || cs_segment_descriptor.dpl() == *cpl {
|
||||
// intra privilege level interrupt
|
||||
|
||||
//dbg_log!("Intra privilege interrupt gate=" + h(selector, 4) + ":" + h(offset >>> 0, 8) +
|
||||
// " trap=" + is_trap + " 16bit=" + is_16 +
|
||||
// " cpl=" + *cpl + " dpl=" + segment_descriptor.dpl() + " conforming=" + +segment_descriptor.dc_bit(), );
|
||||
//debug.dump_regs_short();
|
||||
|
||||
if *flags & FLAG_VM != 0 {
|
||||
dbg_assert!(false, "check error code");
|
||||
trigger_gp_non_raising(selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
let bytes_per_arg = if is_16 { 2 } else { 4 };
|
||||
let error_code_space = if has_error_code == true { 1 } else { 0 };
|
||||
|
||||
let stack_space = bytes_per_arg * (3 + error_code_space);
|
||||
|
||||
// XXX: with current cpl or with cpl 0?
|
||||
return_on_pagefault!(writable_or_pagefault(
|
||||
get_stack_pointer(-stack_space),
|
||||
stack_space
|
||||
));
|
||||
|
||||
// no exceptions below
|
||||
}
|
||||
else {
|
||||
panic!("Unimplemented: #GP handler");
|
||||
}
|
||||
|
||||
if is_16 {
|
||||
push16(old_flags).unwrap();
|
||||
push16(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push16(get_real_eip()).unwrap();
|
||||
|
||||
if has_error_code == true {
|
||||
push16(error_code).unwrap();
|
||||
}
|
||||
|
||||
offset &= 0xFFFF;
|
||||
}
|
||||
else {
|
||||
push32(old_flags).unwrap();
|
||||
push32(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push32(get_real_eip()).unwrap();
|
||||
|
||||
if has_error_code == true {
|
||||
push32(error_code).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
if old_flags & FLAG_VM != 0 {
|
||||
if !switch_seg(GS, 0) || !switch_seg(FS, 0) || !switch_seg(DS, 0) || !switch_seg(ES, 0)
|
||||
{
|
||||
// can't fail
|
||||
dbg_assert!(false);
|
||||
}
|
||||
}
|
||||
|
||||
*sreg.offset(CS as isize) = (selector as u16) & !3 | *cpl as u16;
|
||||
dbg_assert!((*sreg.offset(CS as isize) & 3) == *cpl as u16);
|
||||
|
||||
update_cs_size(cs_segment_descriptor.is_32());
|
||||
|
||||
*segment_limits.offset(CS as isize) = cs_segment_descriptor.effective_limit();
|
||||
*segment_offsets.offset(CS as isize) = cs_segment_descriptor.base();
|
||||
|
||||
*instruction_pointer = get_seg(CS) + offset;
|
||||
|
||||
*flags &= !FLAG_NT & !FLAG_VM & !FLAG_RF & !FLAG_TRAP;
|
||||
|
||||
if !is_trap {
|
||||
// clear int flag for interrupt gates
|
||||
*flags &= !FLAG_INTERRUPT;
|
||||
}
|
||||
else {
|
||||
if *flags & FLAG_INTERRUPT != 0 && old_flags & FLAG_INTERRUPT == 0 {
|
||||
handle_irqs();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// call 4 byte cs:ip interrupt vector from ivt at cpu.memory 0
|
||||
|
||||
let index = (interrupt_nr << 2) as u32;
|
||||
let new_ip = read16(index);
|
||||
let new_cs = read16(index + 2);
|
||||
|
||||
// push flags, cs:ip
|
||||
push16(get_eflags()).unwrap();
|
||||
push16(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push16(get_real_eip()).unwrap();
|
||||
|
||||
*flags &= !FLAG_INTERRUPT;
|
||||
|
||||
switch_cs_real_mode(new_cs);
|
||||
*instruction_pointer = get_seg(CS) + new_ip;
|
||||
}
|
||||
}
|
||||
|
||||
//pub fn call_indirect1(f: fn(u16), x: u16) { f(x); }
|
||||
|
||||
pub unsafe fn after_block_boundary() { jit_block_boundary = true; }
|
||||
|
|
|
@ -17,8 +17,6 @@ extern "C" {
|
|||
#[no_mangle]
|
||||
fn far_return(eip: i32, selector: i32, stack_adjust: i32);
|
||||
#[no_mangle]
|
||||
fn call_interrupt_vector(interrupt: i32, is_software: bool, has_error: bool, error: i32);
|
||||
#[no_mangle]
|
||||
fn iret16();
|
||||
#[no_mangle]
|
||||
fn iret32();
|
||||
|
|
Loading…
Reference in a new issue