Port far_jump to Rust
This commit is contained in:
parent
3711ed7f23
commit
d735d5d34e
|
@ -117,7 +117,6 @@ function V86Starter(options)
|
|||
dbg_trace();
|
||||
},
|
||||
|
||||
"far_jump": function(eip, selector, is_call, is_osize_32) { return cpu.far_jump(eip, selector, !!is_call, !!is_osize_32); },
|
||||
"far_return": function(eip, selector, stack_adjust, is_osize_32) { return cpu.far_return(eip, selector, stack_adjust, !!is_osize_32); },
|
||||
"pic_acknowledge": function() { cpu.pic_acknowledge(); },
|
||||
|
||||
|
|
376
src/cpu.js
376
src/cpu.js
|
@ -1720,382 +1720,6 @@ CPU.prototype.far_return = function(eip, selector, stack_adjust, is_osize_32)
|
|||
CPU_LOG_VERBOSE && this.debug.dump_state("far ret end");
|
||||
};
|
||||
|
||||
CPU.prototype.far_jump = function(eip, selector, is_call, is_osize_32)
|
||||
{
|
||||
is_call = !!is_call;
|
||||
dbg_assert(typeof selector === "number" && selector < 0x10000 && selector >= 0);
|
||||
|
||||
//dbg_log("far " + ["jump", "call"][+is_call] + " eip=" + h(eip >>> 0, 8) + " cs=" + h(selector, 4), LOG_CPU);
|
||||
CPU_LOG_VERBOSE && this.debug.dump_state("far " + ["jump", "call"][+is_call]);
|
||||
|
||||
if(!this.protected_mode[0] || this.vm86_mode())
|
||||
{
|
||||
if(is_call)
|
||||
{
|
||||
if(is_osize_32)
|
||||
{
|
||||
if(!this.writable_or_pagefault(this.get_stack_pointer(-8), 8))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.push32(this.sreg[reg_cs]);
|
||||
this.push32(this.get_real_eip());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!this.writable_or_pagefault(this.get_stack_pointer(-4), 4))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.push16(this.sreg[reg_cs]);
|
||||
this.push16(this.get_real_eip());
|
||||
}
|
||||
}
|
||||
this.switch_cs_real_mode(selector);
|
||||
this.instruction_pointer[0] = this.get_seg_cs() + eip | 0;
|
||||
return;
|
||||
}
|
||||
|
||||
var info = this.lookup_segment_selector(selector);
|
||||
|
||||
if(info.is_null)
|
||||
{
|
||||
dbg_log("#gp null cs", LOG_CPU);
|
||||
this.trigger_gp(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!info.is_valid)
|
||||
{
|
||||
dbg_log("#gp invalid cs: " + h(selector), LOG_CPU);
|
||||
this.trigger_gp(selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(info.is_system)
|
||||
{
|
||||
dbg_assert(is_call, "TODO: Jump");
|
||||
|
||||
dbg_log("system type cs: " + h(selector), LOG_CPU);
|
||||
|
||||
if(info.type === 0xC || info.type === 4)
|
||||
{
|
||||
// call gate
|
||||
var is_16 = info.type === 4;
|
||||
|
||||
if(info.dpl < this.cpl[0] || info.dpl < info.rpl)
|
||||
{
|
||||
dbg_log("#gp cs gate dpl < cpl or dpl < rpl: " + h(selector), LOG_CPU);
|
||||
this.trigger_gp(selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!info.is_present)
|
||||
{
|
||||
dbg_log("#NP for loading not-present in gate cs sel=" + h(selector, 4), LOG_CPU);
|
||||
this.trigger_np(selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
var cs_selector = info.raw0 >>> 16;
|
||||
var cs_info = this.lookup_segment_selector(cs_selector);
|
||||
|
||||
if(cs_info.is_null)
|
||||
{
|
||||
dbg_log("#gp null cs", LOG_CPU);
|
||||
this.trigger_gp(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!cs_info.is_valid)
|
||||
{
|
||||
dbg_log("#gp invalid cs: " + h(cs_selector), LOG_CPU);
|
||||
this.trigger_gp(cs_selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!cs_info.is_executable)
|
||||
{
|
||||
dbg_log("#gp non-executable cs: " + h(cs_selector), LOG_CPU);
|
||||
this.trigger_gp(cs_selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(cs_info.dpl > this.cpl[0])
|
||||
{
|
||||
dbg_log("#gp dpl > cpl: " + h(cs_selector), LOG_CPU);
|
||||
this.trigger_gp(cs_selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!cs_info.is_present)
|
||||
{
|
||||
dbg_log("#NP for loading not-present in cs sel=" + h(cs_selector, 4), LOG_CPU);
|
||||
this.trigger_np(cs_selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!cs_info.dc_bit && cs_info.dpl < this.cpl[0])
|
||||
{
|
||||
dbg_log("more privilege call gate is_16=" + is_16 + " from=" + this.cpl[0] + " to=" + cs_info.dpl);
|
||||
var tss_stack_addr = this.get_tss_stack_addr(cs_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 !== cs_info.dpl) // xxx: 0 in v86 mode
|
||||
{
|
||||
throw this.debug.unimpl("#TS handler");
|
||||
}
|
||||
if(ss_info.dpl !== cs_info.dpl || !ss_info.rw_bit)
|
||||
{
|
||||
throw this.debug.unimpl("#TS handler");
|
||||
}
|
||||
if(!ss_info.is_present)
|
||||
{
|
||||
throw this.debug.unimpl("#SS handler");
|
||||
}
|
||||
|
||||
var parameter_count = info.raw1 & 0x1F;
|
||||
var stack_space = is_16 ? 4 : 8;
|
||||
if(is_call)
|
||||
{
|
||||
stack_space += is_16 ? 4 + 2 * parameter_count : 8 + 4 * parameter_count;
|
||||
}
|
||||
if(ss_info.size)
|
||||
{
|
||||
if(!this.writable_or_pagefault(ss_info.base + new_esp - stack_space | 0, stack_space)) // , cs_info.dpl
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!this.writable_or_pagefault(ss_info.base + (new_esp - stack_space & 0xFFFF) | 0, stack_space)) // , cs_info.dpl
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var old_esp = this.reg32[reg_esp];
|
||||
var old_ss = this.sreg[reg_ss];
|
||||
var old_stack_pointer = this.get_stack_pointer(0);
|
||||
|
||||
//dbg_log("old_esp=" + h(old_esp));
|
||||
|
||||
this.cpl[0] = cs_info.dpl;
|
||||
this.cpl_changed();
|
||||
|
||||
this.update_cs_size(cs_info.size);
|
||||
|
||||
// XXX: Should be checked before side effects
|
||||
if(!this.switch_seg(reg_ss, new_ss)) dbg_assert(false);
|
||||
this.set_stack_reg(new_esp);
|
||||
|
||||
//dbg_log("parameter_count=" + parameter_count);
|
||||
//dbg_assert(parameter_count === 0, "TODO");
|
||||
|
||||
if(is_16)
|
||||
{
|
||||
this.push16(old_ss);
|
||||
this.push16(old_esp);
|
||||
//dbg_log("old esp written to " + h(this.translate_address_system_read(this.get_stack_pointer(0))));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.push32(old_ss);
|
||||
this.push32(old_esp);
|
||||
//dbg_log("old esp written to " + h(this.translate_address_system_read(this.get_stack_pointer(0))));
|
||||
}
|
||||
|
||||
if(is_call)
|
||||
{
|
||||
if(is_16)
|
||||
{
|
||||
for(var i = parameter_count - 1; i >= 0; i--)
|
||||
{
|
||||
var parameter = this.safe_read16(old_stack_pointer + 2 * i);
|
||||
this.push16(parameter);
|
||||
}
|
||||
|
||||
//this.writable_or_pagefault(this.get_stack_pointer(-4), 4);
|
||||
this.push16(this.sreg[reg_cs]);
|
||||
this.push16(this.get_real_eip());
|
||||
}
|
||||
else
|
||||
{
|
||||
for(var i = parameter_count - 1; i >= 0; i--)
|
||||
{
|
||||
var parameter = this.safe_read32s(old_stack_pointer + 4 * i);
|
||||
this.push32(parameter);
|
||||
}
|
||||
|
||||
//this.writable_or_pagefault(this.get_stack_pointer(-8), 8);
|
||||
this.push32(this.sreg[reg_cs]);
|
||||
this.push32(this.get_real_eip());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dbg_log("same privilege call gate is_16=" + is_16 + " from=" + this.cpl[0] + " to=" + cs_info.dpl + " conforming=" + cs_info.dc_bit);
|
||||
// ok
|
||||
|
||||
if(is_call)
|
||||
{
|
||||
if(is_16)
|
||||
{
|
||||
if(!this.writable_or_pagefault(this.get_stack_pointer(-4), 4))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.push16(this.sreg[reg_cs]);
|
||||
this.push16(this.get_real_eip());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!this.writable_or_pagefault(this.get_stack_pointer(-8), 8))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.push32(this.sreg[reg_cs]);
|
||||
this.push32(this.get_real_eip());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: eip from call is ignored
|
||||
var new_eip = info.raw0 & 0xFFFF;
|
||||
if(!is_16)
|
||||
{
|
||||
new_eip |= info.raw1 & 0xFFFF0000;
|
||||
}
|
||||
|
||||
dbg_log("call gate eip=" + h(new_eip >>> 0) + " cs=" + h(cs_selector) + " conforming=" + cs_info.dc_bit);
|
||||
dbg_assert((new_eip >>> 0) <= cs_info.effective_limit, "todo: #gp");
|
||||
|
||||
this.update_cs_size(cs_info.size);
|
||||
|
||||
this.segment_is_null[reg_cs] = 0;
|
||||
this.segment_limits[reg_cs] = cs_info.effective_limit;
|
||||
//this.segment_infos[reg_cs] = 0; // TODO
|
||||
this.segment_offsets[reg_cs] = cs_info.base;
|
||||
this.sreg[reg_cs] = cs_selector & ~3 | this.cpl[0];
|
||||
dbg_assert((this.sreg[reg_cs] & 3) === this.cpl[0]);
|
||||
|
||||
this.instruction_pointer[0] = this.get_seg_cs() + new_eip | 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var types = { 9: "Available 386 TSS", 0xb: "Busy 386 TSS", 4: "286 Call Gate", 0xc: "386 Call Gate" };
|
||||
throw this.debug.unimpl("load system segment descriptor, type = " + (info.access & 15) + " (" + types[info.access & 15] + ")");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!info.is_executable)
|
||||
{
|
||||
dbg_log("#gp non-executable cs: " + h(selector), LOG_CPU);
|
||||
this.trigger_gp(selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(info.dc_bit)
|
||||
{
|
||||
// conforming code segment
|
||||
if(info.dpl > this.cpl[0])
|
||||
{
|
||||
dbg_log("#gp cs dpl > cpl: " + h(selector), LOG_CPU);
|
||||
this.trigger_gp(selector & ~3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-conforming code segment
|
||||
|
||||
if(info.rpl > this.cpl[0] || info.dpl !== this.cpl[0])
|
||||
{
|
||||
dbg_log("#gp cs rpl > cpl or dpl != cpl: " + h(selector), LOG_CPU);
|
||||
this.trigger_gp(selector & ~3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(!info.is_present)
|
||||
{
|
||||
dbg_log("#NP for loading not-present in cs sel=" + h(selector, 4), LOG_CPU);
|
||||
dbg_trace(LOG_CPU);
|
||||
this.trigger_np(selector & ~3);
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_call)
|
||||
{
|
||||
if(is_osize_32)
|
||||
{
|
||||
if(!this.writable_or_pagefault(this.get_stack_pointer(-8), 8))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.push32(this.sreg[reg_cs]);
|
||||
this.push32(this.get_real_eip());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!this.writable_or_pagefault(this.get_stack_pointer(-4), 4))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.push16(this.sreg[reg_cs]);
|
||||
this.push16(this.get_real_eip());
|
||||
}
|
||||
}
|
||||
|
||||
dbg_assert((eip >>> 0) <= info.effective_limit, "todo: #gp");
|
||||
|
||||
this.update_cs_size(info.size);
|
||||
|
||||
this.segment_is_null[reg_cs] = 0;
|
||||
this.segment_limits[reg_cs] = info.effective_limit;
|
||||
//this.segment_infos[reg_cs] = 0; // TODO
|
||||
|
||||
this.segment_offsets[reg_cs] = info.base;
|
||||
this.sreg[reg_cs] = selector & ~3 | this.cpl[0];
|
||||
|
||||
this.instruction_pointer[0] = this.get_seg_cs() + eip | 0;
|
||||
}
|
||||
|
||||
//dbg_log("far " + ["jump", "call"][+is_call] + " to:", LOG_CPU)
|
||||
CPU_LOG_VERBOSE && this.debug.dump_state("far " + ["jump", "call"][+is_call] + " end");
|
||||
};
|
||||
|
||||
CPU.prototype.do_task_switch = function(selector, has_error_code, error_code)
|
||||
{
|
||||
dbg_assert(this.tss_size_32[0], "TODO");
|
||||
|
|
|
@ -317,6 +317,8 @@ impl SegmentDescriptor {
|
|||
pub fn flags(&self) -> u8 { ((self.raw >> 48 >> 4) & 0xf) as u8 }
|
||||
|
||||
pub fn is_system(&self) -> bool { self.access_byte() & 0x10 == 0 }
|
||||
pub fn system_type(&self) -> u8 { self.access_byte() & 0xF }
|
||||
|
||||
pub fn is_rw(&self) -> bool { self.access_byte() & 2 == 2 }
|
||||
pub fn is_dc(&self) -> bool { self.access_byte() & 4 == 4 }
|
||||
pub fn is_executable(&self) -> bool { self.access_byte() & 8 == 8 }
|
||||
|
@ -972,6 +974,350 @@ pub unsafe fn call_interrupt_vector(
|
|||
}
|
||||
}
|
||||
|
||||
pub unsafe fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool) {
|
||||
dbg_assert!(selector < 0x10000 && selector >= 0);
|
||||
|
||||
//dbg_log("far " + ["jump", "call"][+is_call] + " eip=" + h(eip >>> 0, 8) + " cs=" + h(selector, 4), LOG_CPU);
|
||||
//CPU_LOG_VERBOSE && this.debug.dump_state("far " + ["jump", "call"][+is_call]);
|
||||
|
||||
if !*protected_mode || vm86_mode() {
|
||||
if is_call {
|
||||
if is_osize_32 {
|
||||
return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-8), 8));
|
||||
|
||||
push32(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push32(get_real_eip()).unwrap();
|
||||
}
|
||||
else {
|
||||
return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-4), 4));
|
||||
|
||||
push16(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push16(get_real_eip()).unwrap();
|
||||
}
|
||||
}
|
||||
switch_cs_real_mode(selector);
|
||||
*instruction_pointer = get_seg_cs() + eip;
|
||||
return;
|
||||
}
|
||||
|
||||
let (info, cs_selector) = match return_on_pagefault!(lookup_segment_selector(selector)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("#gp null cs");
|
||||
trigger_gp(0);
|
||||
return;
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
dbg_log!("#gp invalid cs: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if info.is_system() {
|
||||
dbg_assert!(is_call, "TODO: Jump");
|
||||
|
||||
dbg_log!("system type cs: {:x}", selector);
|
||||
|
||||
if info.system_type() == 0xC || info.system_type() == 4 {
|
||||
// call gate
|
||||
let is_16 = info.system_type() == 4;
|
||||
|
||||
if info.dpl() < *cpl || info.dpl() < cs_selector.rpl() {
|
||||
dbg_log!("#gp cs gate dpl < cpl or dpl < rpl: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
if !info.is_present() {
|
||||
dbg_log!("#NP for loading not-present in gate cs sel={:x}", selector);
|
||||
trigger_np(selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
let cs_selector = (info.raw >> 16) as i32;
|
||||
|
||||
let (cs_info, _) = match return_on_pagefault!(lookup_segment_selector(cs_selector)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("#gp null cs");
|
||||
trigger_gp(0);
|
||||
return;
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
dbg_log!("#gp invalid cs: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if !cs_info.is_executable() {
|
||||
dbg_log!("#gp non-executable cs: {:x}", cs_selector);
|
||||
trigger_gp(cs_selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
if cs_info.dpl() > *cpl {
|
||||
dbg_log!("#gp dpl > cpl: {:x}", cs_selector);
|
||||
trigger_gp(cs_selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
if !cs_info.is_present() {
|
||||
dbg_log!("#NP for loading not-present in cs sel={:x}", cs_selector);
|
||||
trigger_np(cs_selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
if !cs_info.is_dc() && cs_info.dpl() < *cpl {
|
||||
dbg_log!(
|
||||
"more privilege call gate is_16={} from={} to={}",
|
||||
is_16,
|
||||
*cpl,
|
||||
cs_info.dpl()
|
||||
);
|
||||
let tss_stack_addr = return_on_pagefault!(get_tss_stack_addr(cs_info.dpl()));
|
||||
|
||||
let new_esp;
|
||||
let new_ss;
|
||||
if *tss_size_32 {
|
||||
new_esp = read32s(tss_stack_addr);
|
||||
new_ss = read16(tss_stack_addr + 4);
|
||||
}
|
||||
else {
|
||||
new_esp = read16(tss_stack_addr);
|
||||
new_ss = read16(tss_stack_addr + 2);
|
||||
}
|
||||
|
||||
let (ss_info, ss_selector) =
|
||||
match return_on_pagefault!(lookup_segment_selector(new_ss)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
panic!("null ss: {}", new_ss);
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
panic!("invalid ss: {}", 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_system() && ss_info.is_writable());
|
||||
|
||||
if ss_selector.rpl() != cs_info.dpl()
|
||||
// xxx: 0 in v86 mode
|
||||
{
|
||||
panic!("#TS handler");
|
||||
}
|
||||
if ss_info.dpl() != cs_info.dpl() || !ss_info.is_writable() {
|
||||
panic!("#TS handler");
|
||||
}
|
||||
if !ss_info.is_present() {
|
||||
panic!("#SS handler");
|
||||
}
|
||||
|
||||
let parameter_count = (info.raw >> 32 & 0x1F) as i32;
|
||||
let mut stack_space = if is_16 { 4 } else { 8 };
|
||||
if is_call {
|
||||
stack_space +=
|
||||
if is_16 { 4 + 2 * parameter_count } else { 8 + 4 * parameter_count };
|
||||
}
|
||||
if ss_info.is_32() {
|
||||
return_on_pagefault!(writable_or_pagefault(
|
||||
ss_info.base() + new_esp - stack_space,
|
||||
stack_space
|
||||
)); // , cs_info.dpl
|
||||
}
|
||||
else {
|
||||
return_on_pagefault!(writable_or_pagefault(
|
||||
ss_info.base() + (new_esp - stack_space & 0xFFFF),
|
||||
stack_space
|
||||
)); // , cs_info.dpl
|
||||
}
|
||||
|
||||
let old_esp = *reg32.offset(ESP as isize);
|
||||
let old_ss = *sreg.offset(SS as isize);
|
||||
let old_stack_pointer = get_stack_pointer(0);
|
||||
|
||||
//dbg_log!("old_esp=" + h(old_esp));
|
||||
|
||||
*cpl = cs_info.dpl();
|
||||
cpl_changed();
|
||||
|
||||
update_cs_size(cs_info.is_32());
|
||||
|
||||
// XXX: Should be checked before side effects
|
||||
if !switch_seg(SS, new_ss) {
|
||||
dbg_assert!(false)
|
||||
};
|
||||
set_stack_reg(new_esp);
|
||||
|
||||
//dbg_log!("parameter_count=" + parameter_count);
|
||||
//dbg_assert!(parameter_count == 0, "TODO");
|
||||
|
||||
if is_16 {
|
||||
push16(old_ss as i32).unwrap();
|
||||
push16(old_esp).unwrap();
|
||||
}
|
||||
else {
|
||||
push32(old_ss as i32).unwrap();
|
||||
push32(old_esp).unwrap();
|
||||
}
|
||||
|
||||
if is_call {
|
||||
if is_16 {
|
||||
for i in (0..parameter_count).rev() {
|
||||
//for(var i = parameter_count - 1; i >= 0; i--)
|
||||
let parameter = safe_read16(old_stack_pointer + 2 * i).unwrap();
|
||||
push16(parameter).unwrap();
|
||||
}
|
||||
|
||||
//writable_or_pagefault(get_stack_pointer(-4), 4);
|
||||
push16(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push16(get_real_eip()).unwrap();
|
||||
}
|
||||
else {
|
||||
for i in (0..parameter_count).rev() {
|
||||
//for(var i = parameter_count - 1; i >= 0; i--)
|
||||
let parameter = safe_read32s(old_stack_pointer + 4 * i).unwrap();
|
||||
push32(parameter).unwrap();
|
||||
}
|
||||
|
||||
//writable_or_pagefault(get_stack_pointer(-8), 8);
|
||||
push32(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push32(get_real_eip()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
dbg_log!(
|
||||
"same privilege call gate is_16={} from={} to={} conforming={}",
|
||||
is_16,
|
||||
*cpl,
|
||||
cs_info.dpl(),
|
||||
cs_info.is_dc()
|
||||
);
|
||||
// ok
|
||||
|
||||
if is_call {
|
||||
if is_16 {
|
||||
return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-4), 4));
|
||||
|
||||
push16(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push16(get_real_eip()).unwrap();
|
||||
}
|
||||
else {
|
||||
return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-8), 8));
|
||||
|
||||
push32(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push32(get_real_eip()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note: eip from call is ignored
|
||||
let mut new_eip = (info.raw & 0xFFFF) as i32;
|
||||
if !is_16 {
|
||||
new_eip |= ((info.raw >> 32) & 0xFFFF0000) as i32;
|
||||
}
|
||||
|
||||
dbg_log!(
|
||||
"call gate eip={:x} cs={:x} conforming={}",
|
||||
new_eip as u32,
|
||||
cs_selector,
|
||||
cs_info.is_dc()
|
||||
);
|
||||
dbg_assert!((new_eip as u32) <= cs_info.effective_limit(), "todo: #gp");
|
||||
|
||||
update_cs_size(cs_info.is_32());
|
||||
|
||||
*segment_is_null.offset(CS as isize) = false;
|
||||
*segment_limits.offset(CS as isize) = cs_info.effective_limit();
|
||||
*segment_offsets.offset(CS as isize) = cs_info.base();
|
||||
*sreg.offset(CS as isize) = cs_selector as u16 & !3 | *cpl as u16;
|
||||
dbg_assert!(*sreg.offset(CS as isize) & 3 == *cpl as u16);
|
||||
|
||||
*instruction_pointer = get_seg_cs() + new_eip;
|
||||
}
|
||||
else {
|
||||
dbg_assert!(false);
|
||||
//var types = { 9: "Available 386 TSS", 0xb: "Busy 386 TSS", 4: "286 Call Gate", 0xc: "386 Call Gate" };
|
||||
//throw debug.unimpl("load system segment descriptor, type = " + (info.access & 15) + " (" + types[info.access & 15] + ")");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if !info.is_executable() {
|
||||
dbg_log!("#gp non-executable cs: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
if info.is_dc() {
|
||||
// conforming code segment
|
||||
if info.dpl() > *cpl {
|
||||
dbg_log!("#gp cs dpl > cpl: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// non-conforming code segment
|
||||
|
||||
if cs_selector.rpl() > *cpl || info.dpl() != *cpl {
|
||||
dbg_log!("#gp cs rpl > cpl or dpl != cpl: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if !info.is_present() {
|
||||
dbg_log!("#NP for loading not-present in cs sel={:x}", selector);
|
||||
dbg_trace();
|
||||
trigger_np(selector & !3);
|
||||
return;
|
||||
}
|
||||
|
||||
if is_call {
|
||||
if is_osize_32 {
|
||||
return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-8), 8));
|
||||
|
||||
push32(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push32(get_real_eip()).unwrap();
|
||||
}
|
||||
else {
|
||||
return_on_pagefault!(writable_or_pagefault(get_stack_pointer(-4), 4));
|
||||
|
||||
push16(*sreg.offset(CS as isize) as i32).unwrap();
|
||||
push16(get_real_eip()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
dbg_assert!((eip as u32) <= info.effective_limit(), "todo: #gp");
|
||||
|
||||
update_cs_size(info.is_32());
|
||||
|
||||
*segment_is_null.offset(CS as isize) = false;
|
||||
*segment_limits.offset(CS as isize) = info.effective_limit();
|
||||
|
||||
*segment_offsets.offset(CS as isize) = info.base();
|
||||
*sreg.offset(CS as isize) = selector as u16 & !3 | *cpl as u16;
|
||||
|
||||
*instruction_pointer = get_seg_cs() + eip;
|
||||
}
|
||||
|
||||
//dbg_log!("far " + ["jump", "call"][+is_call] + " to:", LOG_CPU)
|
||||
//CPU_LOG_VERBOSE && debug.dump_state("far " + ["jump", "call"][+is_call] + " end");
|
||||
}
|
||||
|
||||
//pub fn call_indirect1(f: fn(u16), x: u16) { f(x); }
|
||||
|
||||
pub unsafe fn after_block_boundary() { jit_block_boundary = true; }
|
||||
|
|
|
@ -4,8 +4,6 @@ extern "C" {
|
|||
#[no_mangle]
|
||||
fn arpl(seg: i32, r: i32) -> i32;
|
||||
#[no_mangle]
|
||||
fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool);
|
||||
#[no_mangle]
|
||||
fn far_return(eip: i32, selector: i32, stack_adjust: i32, is_osize_32: bool);
|
||||
|
||||
#[no_mangle]
|
||||
|
|
Loading…
Reference in a new issue