cpu: Port call_interrupt_vector to Rust

This commit is contained in:
Amaan Cheval 2018-09-15 18:37:49 +05:30 committed by Fabian
parent 48e6843a87
commit 4ef09445e1
5 changed files with 342 additions and 343 deletions

View file

@ -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 = {

View file

@ -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);

View file

@ -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);

View file

@ -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; }

View file

@ -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();