Port do_task_switch to rust

This commit is contained in:
Fabian 2021-03-30 14:43:14 -05:00
parent acb4e5f2dd
commit 7e903f09dd
5 changed files with 250 additions and 546 deletions

View file

@ -136,8 +136,6 @@ function V86Starter(options)
"cpuid": function() { return cpu.cpuid(); },
"load_ldt": function() { return cpu.load_ldt.apply(cpu, arguments); },
"log_from_wasm": function(offset, len) {
const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
dbg_log(str, LOG_CPU);
@ -155,9 +153,6 @@ function V86Starter(options)
},
"jit_clear_func": (wasm_table_index) => cpu.jit_clear_func(wasm_table_index),
"jit_clear_all_funcs": () => cpu.jit_clear_all_funcs(),
"do_task_switch": (selector, has_error_code, error_code) => {
cpu.do_task_switch(selector, has_error_code, error_code);
},
"__indirect_function_table": wasm_table,
};

View file

@ -668,7 +668,10 @@ CPU.prototype.reset = function()
this.instruction_pointer[0] = 0xFFFF0;
this.switch_cs_real_mode(0xF000);
if(!this.switch_seg(reg_ss, 0x30)) dbg_assert(false);
this.sreg[reg_ss] = 0x30;
this.segment_is_null[reg_ss] = 0;
this.segment_offsets[reg_ss] = 0x30 << 4;
this.stack_size_32[0] = +false;
this.reg32[reg_esp] = 0x100;
if(this.devices.virtio)
@ -1018,7 +1021,7 @@ CPU.prototype.load_multiboot = function(buffer)
this.cr[0] = 1;
this.protected_mode[0] = +true;
this.flags[0] = flags_default;
this.update_cs_size(true);
this.is_32[0] = +true;
this.stack_size_32[0] = +true;
for(var i = 0; i < 6; i++)
@ -1542,230 +1545,6 @@ CPU.prototype.run_hardware_timers = function(now)
}
};
CPU.prototype.cpl_changed = function()
{
this.last_virt_eip[0] = -1;
};
CPU.prototype.do_task_switch = function(selector, has_error_code, error_code)
{
dbg_assert(this.tss_size_32[0], "TODO");
dbg_log("do_task_switch sel=" + h(selector), LOG_CPU);
var descriptor = this.lookup_segment_selector(selector);
dbg_assert((descriptor.type | 2) === 3 || (descriptor.type | 2) === 0xb);
var tss_is_16 = descriptor.type <= 3;
var tss_is_busy = (descriptor.type & 2) === 2;
if(!descriptor.is_valid || descriptor.is_null || !descriptor.from_gdt)
{
throw this.debug.unimpl("#GP handler");
}
if((descriptor.access & 31) === 0xB)
{
// is busy
throw this.debug.unimpl("#GP handler");
}
if(!descriptor.is_present)
{
throw this.debug.unimpl("#NP handler");
}
if(descriptor.effective_limit < 103)
{
throw this.debug.unimpl("#NP handler");
}
var tsr_size = this.segment_limits[reg_tr];
var tsr_offset = this.segment_offsets[reg_tr];
var old_eflags = this.get_eflags();
if(tss_is_busy)
{
old_eflags &= ~flag_nt;
}
if(!this.writable_or_pagefault(tsr_offset, 0x66))
{
return;
}
//this.safe_write32(tsr_offset + TSR_CR3, this.cr[3]);
// TODO: Write 16 bit values if old tss is 16 bit
this.safe_write32(tsr_offset + TSR_EIP, this.get_real_eip());
this.safe_write32(tsr_offset + TSR_EFLAGS, old_eflags);
this.safe_write32(tsr_offset + TSR_EAX, this.reg32[reg_eax]);
this.safe_write32(tsr_offset + TSR_ECX, this.reg32[reg_ecx]);
this.safe_write32(tsr_offset + TSR_EDX, this.reg32[reg_edx]);
this.safe_write32(tsr_offset + TSR_EBX, this.reg32[reg_ebx]);
this.safe_write32(tsr_offset + TSR_ESP, this.reg32[reg_esp]);
this.safe_write32(tsr_offset + TSR_EBP, this.reg32[reg_ebp]);
this.safe_write32(tsr_offset + TSR_ESI, this.reg32[reg_esi]);
this.safe_write32(tsr_offset + TSR_EDI, this.reg32[reg_edi]);
this.safe_write32(tsr_offset + TSR_ES, this.sreg[reg_es]);
this.safe_write32(tsr_offset + TSR_CS, this.sreg[reg_cs]);
this.safe_write32(tsr_offset + TSR_SS, this.sreg[reg_ss]);
this.safe_write32(tsr_offset + TSR_DS, this.sreg[reg_ds]);
this.safe_write32(tsr_offset + TSR_FS, this.sreg[reg_fs]);
this.safe_write32(tsr_offset + TSR_GS, this.sreg[reg_gs]);
//this.safe_write32(tsr_offset + TSR_LDT, this.sreg[reg_ldtr]);
if(true /* is jump or call or int */)
{
// mark as busy
this.write8(descriptor.table_offset + 5 | 0, this.read8(descriptor.table_offset + 5 | 0) | 2);
}
//var new_tsr_size = descriptor.effective_limit;
var new_tsr_offset = descriptor.base;
dbg_assert(!tss_is_16, "unimplemented");
if(true /* is call or int */)
{
this.safe_write16(new_tsr_offset + TSR_BACKLINK, this.sreg[reg_tr]);
}
var new_cr3 = this.safe_read32s(new_tsr_offset + TSR_CR3);
this.flags[0] &= ~flag_vm;
var new_eip = this.safe_read32s(new_tsr_offset + TSR_EIP);
var new_cs = this.safe_read16(new_tsr_offset + TSR_CS);
var info = this.lookup_segment_selector(new_cs);
if(info.is_null)
{
dbg_log("null cs", LOG_CPU);
throw this.debug.unimpl("#TS handler");
}
if(!info.is_valid)
{
dbg_log("invalid cs: " + h(selector), LOG_CPU);
throw this.debug.unimpl("#TS handler");
}
if(info.is_system)
{
throw this.debug.unimpl("#TS handler");
}
if(!info.is_executable)
{
throw this.debug.unimpl("#TS handler");
}
if(info.dc_bit && info.dpl > info.rpl)
{
dbg_log("cs conforming and dpl > rpl: " + h(selector), LOG_CPU);
throw this.debug.unimpl("#TS handler");
}
if(!info.dc_bit && info.dpl !== info.rpl)
{
dbg_log("cs non-conforming and dpl != rpl: " + h(selector), LOG_CPU);
throw this.debug.unimpl("#TS handler");
}
if(!info.is_present)
{
dbg_log("#NP for loading not-present in cs sel=" + h(selector, 4), LOG_CPU);
throw this.debug.unimpl("#TS handler");
}
this.segment_is_null[reg_cs] = 0;
this.segment_limits[reg_cs] = info.effective_limit;
this.segment_offsets[reg_cs] = info.base;
this.sreg[reg_cs] = new_cs;
this.cpl = info.dpl;
this.cpl_changed();
dbg_assert((this.sreg[reg_cs] & 3) === this.cpl);
dbg_assert((new_eip >>> 0) <= info.effective_limit, "todo: #gp");
this.update_cs_size(info.size);
var new_eflags = this.safe_read32s(new_tsr_offset + TSR_EFLAGS);
if(true /* is call or int */)
{
this.safe_write32(tsr_offset + TSR_BACKLINK, selector);
new_eflags |= flag_nt;
}
if(new_eflags & flag_vm)
{
throw this.debug.unimpl("task switch to VM mode");
}
this.update_eflags(new_eflags);
if(true /* call or int */)
{
this.flags[0] |= flag_nt;
}
var new_ldt = this.safe_read16(new_tsr_offset + TSR_LDT);
this.load_ldt(new_ldt);
this.reg32[reg_eax] = this.safe_read32s(new_tsr_offset + TSR_EAX);
this.reg32[reg_ecx] = this.safe_read32s(new_tsr_offset + TSR_ECX);
this.reg32[reg_edx] = this.safe_read32s(new_tsr_offset + TSR_EDX);
this.reg32[reg_ebx] = this.safe_read32s(new_tsr_offset + TSR_EBX);
this.reg32[reg_esp] = this.safe_read32s(new_tsr_offset + TSR_ESP);
this.reg32[reg_ebp] = this.safe_read32s(new_tsr_offset + TSR_EBP);
this.reg32[reg_esi] = this.safe_read32s(new_tsr_offset + TSR_ESI);
this.reg32[reg_edi] = this.safe_read32s(new_tsr_offset + TSR_EDI);
if(
!this.switch_seg(reg_es, this.safe_read16(new_tsr_offset + TSR_ES)) ||
!this.switch_seg(reg_ss, this.safe_read16(new_tsr_offset + TSR_SS)) ||
!this.switch_seg(reg_ds, this.safe_read16(new_tsr_offset + TSR_DS)) ||
!this.switch_seg(reg_fs, this.safe_read16(new_tsr_offset + TSR_FS)) ||
!this.switch_seg(reg_gs, this.safe_read16(new_tsr_offset + TSR_GS))
)
{
// XXX: Should be checked before side effects
dbg_assert(false);
}
this.instruction_pointer[0] = this.get_seg_cs() + new_eip | 0;
this.segment_offsets[reg_tr] = descriptor.base;
this.segment_limits[reg_tr] = descriptor.effective_limit;
this.sreg[reg_tr] = selector;
this.cr[3] = new_cr3;
dbg_assert((this.cr[3] & 0xFFF) === 0);
this.clear_tlb();
this.cr[0] |= CR0_TS;
if(has_error_code !== false)
{
if(tss_is_16)
{
this.push16(error_code & 0xFFFF);
}
else
{
this.push32(error_code);
}
}
};
CPU.prototype.hlt_op = function()
{
if(this.cpl[0])
@ -1996,291 +1775,6 @@ CPU.prototype.cpuid = function()
this.reg32[reg_ebx] = ebx;
};
CPU.prototype.update_cs_size = function(new_size)
{
new_size = Boolean(new_size);
if(Boolean(this.is_32[0]) !== new_size)
{
this.is_32[0] = +new_size;
}
};
/**
* @param {number} selector
*/
CPU.prototype.lookup_segment_selector = function(selector)
{
dbg_assert(typeof selector === "number" && selector >= 0 && selector < 0x10000);
var is_gdt = (selector & 4) === 0,
selector_offset = selector & ~7,
info,
table_offset,
table_limit;
info = {
rpl: selector & 3,
from_gdt: is_gdt,
is_null: false,
is_valid: true,
base: 0,
access: 0,
flags: 0,
type: 0,
dpl: 0,
is_system: false,
is_present: false,
is_executable: false,
rw_bit: false,
dc_bit: false,
size: false,
is_conforming_executable: false,
// limit after applying granularity
effective_limit: 0,
is_writable: false,
is_readable: false,
table_offset: 0,
raw0: 0,
raw1: 0,
};
if(is_gdt)
{
table_offset = this.gdtr_offset[0];
table_limit = this.gdtr_size[0];
}
else
{
table_offset = this.segment_offsets[reg_ldtr];
table_limit = this.segment_limits[reg_ldtr];
}
if(is_gdt && selector_offset === 0)
{
info.is_null = true;
return info;
}
// limit is the number of entries in the table minus one
if((selector | 7) > table_limit)
{
dbg_log("Selector " + h(selector, 4) + " is outside of the " +
(is_gdt ? "g" : "l") + "dt limits", LOG_CPU);
info.is_valid = false;
return info;
}
table_offset = table_offset + selector_offset | 0;
if(this.cr[0] & CR0_PG)
{
table_offset = this.translate_address_system_read(table_offset);
}
info.table_offset = table_offset;
info.base = this.read16(table_offset + 2 | 0) | this.read8(table_offset + 4 | 0) << 16 |
this.read8(table_offset + 7 | 0) << 24;
info.access = this.read8(table_offset + 5 | 0);
info.flags = this.read8(table_offset + 6 | 0) >> 4;
info.raw0 = this.read32s(table_offset | 0);
info.raw1 = this.read32s(table_offset + 4 | 0);
//this.write8(table_offset + 5 | 0, info.access | 1);
// used if system
info.type = info.access & 0xF;
info.dpl = info.access >> 5 & 3;
info.is_system = (info.access & 0x10) === 0;
info.is_present = (info.access & 0x80) === 0x80;
info.is_executable = (info.access & 8) === 8;
info.rw_bit = (info.access & 2) === 2;
info.dc_bit = (info.access & 4) === 4;
info.is_conforming_executable = info.dc_bit && info.is_executable;
info.size = (info.flags & 4) === 4;
var limit = this.read16(table_offset) |
(this.read8(table_offset + 6 | 0) & 0xF) << 16;
if(info.flags & 8)
{
// granularity set
info.effective_limit = (limit << 12 | 0xFFF) >>> 0;
}
else
{
info.effective_limit = limit;
}
info.is_writable = info.rw_bit && !info.is_executable;
info.is_readable = info.rw_bit || !info.is_executable;
return info;
};
/**
* Returns false if changing was aborted due to an exception
*
* @param {number} reg
* @param {number} selector
*/
CPU.prototype.switch_seg = function(reg, selector)
{
dbg_assert(reg >= 0 && reg <= 5);
dbg_assert(typeof selector === "number" && selector < 0x10000 && selector >= 0);
if(!this.protected_mode[0] || this.vm86_mode())
{
this.sreg[reg] = selector;
this.segment_is_null[reg] = 0;
this.segment_offsets[reg] = selector << 4;
if(reg === reg_ss)
{
this.stack_size_32[0] = +false;
}
return true;
}
var info = this.lookup_segment_selector(selector);
if(reg === reg_ss)
{
if(info.is_null)
{
dbg_log("#GP for loading 0 in SS sel=" + h(selector, 4), LOG_CPU);
dbg_trace(LOG_CPU);
this.trigger_gp(0);
return false;
}
if(!info.is_valid ||
info.is_system ||
info.rpl !== this.cpl[0] ||
!info.is_writable ||
info.dpl !== this.cpl[0])
{
dbg_log("#GP for loading invalid in SS sel=" + h(selector, 4), LOG_CPU);
dbg_trace(LOG_CPU);
this.trigger_gp(selector & ~3);
return false;
}
if(!info.is_present)
{
dbg_log("#SS for loading non-present in SS sel=" + h(selector, 4), LOG_CPU);
dbg_trace(LOG_CPU);
this.trigger_ss(selector & ~3);
return false;
}
this.stack_size_32[0] = info.size;
}
else if(reg === reg_cs)
{
// handled by switch_cs_real_mode, far_return or far_jump
dbg_assert(false);
}
else
{
// es, ds, fs, gs
if(info.is_null)
{
//dbg_log("0 loaded in seg=" + reg + " sel=" + h(selector, 4), LOG_CPU);
//dbg_trace(LOG_CPU);
this.sreg[reg] = selector;
this.segment_is_null[reg] = 1;
return true;
}
if(!info.is_valid ||
info.is_system ||
!info.is_readable ||
(!info.is_conforming_executable &&
(info.rpl > info.dpl || this.cpl[0] > info.dpl))
) {
dbg_log("#GP for loading invalid in seg " + reg + " sel=" + h(selector, 4), LOG_CPU);
this.debug.dump_state();
this.debug.dump_regs();
dbg_trace(LOG_CPU);
this.trigger_gp(selector & ~3);
return false;
}
if(!info.is_present)
{
dbg_log("#NP for loading not-present in seg " + reg + " sel=" + h(selector, 4), LOG_CPU);
dbg_trace(LOG_CPU);
this.trigger_np(selector & ~3);
return false;
}
}
this.segment_is_null[reg] = 0;
this.segment_limits[reg] = info.effective_limit;
//this.segment_infos[reg] = 0; // TODO
this.segment_offsets[reg] = info.base;
this.sreg[reg] = selector;
return true;
};
CPU.prototype.load_ldt = function(selector)
{
var info = this.lookup_segment_selector(selector);
if(info.is_null)
{
// invalid
this.segment_offsets[reg_ldtr] = 0;
this.segment_limits[reg_ldtr] = 0;
return;
}
dbg_assert(info.is_valid);
if(!info.from_gdt)
{
throw this.debug.unimpl("LDTR can only be loaded from GDT");
}
if(!info.is_present)
{
dbg_log("lldt: present bit not set");
throw this.debug.unimpl("#GP handler");
}
if(!info.is_system)
{
dbg_log("lldt: not a system entry");
throw this.debug.unimpl("#GP handler");
}
if(info.type !== 2)
{
dbg_log("lldt: invalid type (" + info.type + ")");
throw this.debug.unimpl("#GP handler");
}
this.segment_offsets[reg_ldtr] = info.base;
this.segment_limits[reg_ldtr] = info.effective_limit;
this.sreg[reg_ldtr] = selector;
//dbg_log("ldt at " + h(info.base >>> 0) + "; (" + info.effective_limit + " bytes)", LOG_CPU);
};
// Closure Compiler's way of exporting
if(typeof window !== "undefined")
{

View file

@ -86,28 +86,6 @@ CPU.prototype.debug_init = function()
debug.step = step;
debug.run_until = run_until;
/**
* @param {string=} msg
*/
debug.unimpl = function(msg)
{
var s = "Unimplemented" + (msg ? ": " + msg : "");
debug.show(s);
if(DEBUG)
{
console.trace();
return s;
}
else
{
debug.show("Execution stopped");
return s;
}
//this.name = "Unimplemented";
};
function step()
{
if(!DEBUG) return;

View file

@ -2,7 +2,6 @@
extern "C" {
fn cpu_exception_hook(interrupt: i32) -> bool;
fn do_task_switch(selector: i32, has_error_code: bool, error_code: i32);
//fn logop(addr: i32, op: i32);
fn microtick() -> f64;
fn call_indirect1(f: i32, x: u16);
@ -170,6 +169,28 @@ pub const CR4_PGE: i32 = 1 << 7;
pub const CR4_OSFXSR: i32 = 1 << 9;
pub const CR4_OSXMMEXCPT: i32 = 1 << 10;
pub const TSR_BACKLINK: i32 = 0x00;
pub const TSR_CR3: i32 = 0x1C;
pub const TSR_EIP: i32 = 0x20;
pub const TSR_EFLAGS: i32 = 0x24;
pub const TSR_EAX: i32 = 0x28;
pub const TSR_ECX: i32 = 0x2c;
pub const TSR_EDX: i32 = 0x30;
pub const TSR_EBX: i32 = 0x34;
pub const TSR_ESP: i32 = 0x38;
pub const TSR_EBP: i32 = 0x3c;
pub const TSR_ESI: i32 = 0x40;
pub const TSR_EDI: i32 = 0x44;
pub const TSR_ES: i32 = 0x48;
pub const TSR_CS: i32 = 0x4c;
pub const TSR_SS: i32 = 0x50;
pub const TSR_DS: i32 = 0x54;
pub const TSR_FS: i32 = 0x58;
pub const TSR_GS: i32 = 0x5c;
pub const TSR_LDT: i32 = 0x60;
pub const IA32_SYSENTER_CS: i32 = 372;
pub const IA32_SYSENTER_ESP: i32 = 373;
pub const IA32_SYSENTER_EIP: i32 = 374;
@ -732,7 +753,7 @@ pub unsafe fn call_interrupt_vector(
);
dbg_trace();
do_task_switch(selector, error_code.is_some(), error_code.unwrap_or(0));
do_task_switch(selector, error_code);
return;
}
@ -1469,7 +1490,7 @@ pub unsafe fn far_return(eip: i32, selector: i32, stack_adjust: i32, is_osize_32
}
}
//dbg_assert(*cpl === info.dpl);
//dbg_assert(*cpl == info.dpl);
update_cs_size(info.is_32());
@ -1486,6 +1507,220 @@ pub unsafe fn far_return(eip: i32, selector: i32, stack_adjust: i32, is_osize_32
//CPU_LOG_VERBOSE && debug.dump_state("far ret end");
}
pub unsafe fn do_task_switch(selector: i32, error_code: Option<i32>) {
dbg_log!("do_task_switch sel={:x}", selector);
dbg_assert!(*tss_size_32, "TODO: 16-bit TSS in task switch");
let selector = SegmentSelector::of_u16(selector as u16);
let (descriptor, descriptor_address) =
match lookup_segment_selector(selector).expect("TODO: handle pagefault") {
Ok(desc) => desc,
Err(_) => {
panic!("#GP handler");
},
};
dbg_assert!(selector.is_gdt());
dbg_assert!((descriptor.system_type() & !2) == 1 || (descriptor.system_type() & !2) == 9);
let tss_is_16 = descriptor.system_type() <= 3;
let tss_is_busy = (descriptor.system_type() & 2) == 2;
if (descriptor.system_type() & 2) == 2 {
// is busy
panic!("#GP handler");
}
if !descriptor.is_present() {
panic!("#NP handler");
}
if descriptor.effective_limit() < 103 {
panic!("#NP handler");
}
let _tsr_size = *segment_limits.offset(TR as isize);
let tsr_offset = *segment_offsets.offset(TR as isize);
let mut old_eflags = get_eflags();
if tss_is_busy {
old_eflags &= !FLAG_NT;
}
writable_or_pagefault(tsr_offset, 0x66).unwrap();
//safe_write32(tsr_offset + TSR_CR3, *cr.offset(3));
// TODO: Write 16 bit values if old tss is 16 bit
safe_write32(tsr_offset + TSR_EIP, get_real_eip()).unwrap();
safe_write32(tsr_offset + TSR_EFLAGS, old_eflags).unwrap();
safe_write32(tsr_offset + TSR_EAX, read_reg32(EAX)).unwrap();
safe_write32(tsr_offset + TSR_ECX, read_reg32(ECX)).unwrap();
safe_write32(tsr_offset + TSR_EDX, read_reg32(EDX)).unwrap();
safe_write32(tsr_offset + TSR_EBX, read_reg32(EBX)).unwrap();
safe_write32(tsr_offset + TSR_ESP, read_reg32(ESP)).unwrap();
safe_write32(tsr_offset + TSR_EBP, read_reg32(EBP)).unwrap();
safe_write32(tsr_offset + TSR_ESI, read_reg32(ESI)).unwrap();
safe_write32(tsr_offset + TSR_EDI, read_reg32(EDI)).unwrap();
safe_write32(tsr_offset + TSR_ES, *sreg.offset(ES as isize) as i32).unwrap();
safe_write32(tsr_offset + TSR_CS, *sreg.offset(CS as isize) as i32).unwrap();
safe_write32(tsr_offset + TSR_SS, *sreg.offset(SS as isize) as i32).unwrap();
safe_write32(tsr_offset + TSR_DS, *sreg.offset(DS as isize) as i32).unwrap();
safe_write32(tsr_offset + TSR_FS, *sreg.offset(FS as isize) as i32).unwrap();
safe_write32(tsr_offset + TSR_GS, *sreg.offset(GS as isize) as i32).unwrap();
//safe_write32(tsr_offset + TSR_LDT, *sreg.offset(reg_ldtr));
if true
/* is jump or call or int */
{
safe_write64(descriptor_address, descriptor.set_busy().raw).unwrap();
}
//let new_tsr_size = descriptor.effective_limit;
let new_tsr_offset = descriptor.base();
dbg_assert!(!tss_is_16, "unimplemented");
if true
/* is call or int */
{
safe_write16(
new_tsr_offset + TSR_BACKLINK,
*sreg.offset(TR as isize) as i32,
)
.unwrap();
}
let new_cr3 = safe_read32s(new_tsr_offset + TSR_CR3).unwrap();
*flags &= !FLAG_VM;
let new_eip = safe_read32s(new_tsr_offset + TSR_EIP).unwrap();
let new_cs = safe_read16(new_tsr_offset + TSR_CS).unwrap();
let new_cs_selector = SegmentSelector::of_u16(new_cs as u16);
let new_cs_descriptor =
match lookup_segment_selector(new_cs_selector).expect("TODO: handle pagefault") {
Ok((desc, _)) => desc,
Err(SelectorNullOrInvalid::IsNull) => {
dbg_log!("null cs");
panic!("#TS handler");
},
Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
dbg_log!("invalid cs: {:x}", new_cs);
panic!("#TS handler");
},
};
if new_cs_descriptor.is_system() {
panic!("#TS handler");
}
if !new_cs_descriptor.is_executable() {
panic!("#TS handler");
}
if new_cs_descriptor.is_dc() && new_cs_descriptor.dpl() > new_cs_selector.rpl() {
dbg_log!("cs conforming and dpl > rpl: {:x}", selector.raw);
panic!("#TS handler");
}
if !new_cs_descriptor.is_dc() && new_cs_descriptor.dpl() != new_cs_selector.rpl() {
dbg_log!("cs non-conforming and dpl != rpl: {:x}", selector.raw);
panic!("#TS handler");
}
if !new_cs_descriptor.is_present() {
dbg_log!("#NP for loading not-present in cs sel={:x}", selector.raw);
panic!("#TS handler");
}
*segment_is_null.offset(CS as isize) = false;
*segment_limits.offset(CS as isize) = new_cs_descriptor.effective_limit();
*segment_offsets.offset(CS as isize) = new_cs_descriptor.base();
*sreg.offset(CS as isize) = new_cs as u16;
*cpl = new_cs_descriptor.dpl();
cpl_changed();
dbg_assert!((*sreg.offset(CS as isize) & 3) as u8 == *cpl);
dbg_assert!(
new_eip as u32 <= new_cs_descriptor.effective_limit(),
"todo: #gp"
);
update_cs_size(new_cs_descriptor.is_32());
let mut new_eflags = safe_read32s(new_tsr_offset + TSR_EFLAGS).unwrap();
if true
/* is call or int */
{
safe_write32(tsr_offset + TSR_BACKLINK, selector.raw as i32).unwrap();
new_eflags |= FLAG_NT;
}
if new_eflags & FLAG_VM != 0 {
panic!("task switch to VM mode");
}
update_eflags(new_eflags);
if true
/* call or int */
{
*flags |= FLAG_NT;
}
let new_ldt = safe_read16(new_tsr_offset + TSR_LDT).unwrap();
load_ldt(new_ldt).unwrap();
write_reg32(EAX, safe_read32s(new_tsr_offset + TSR_EAX).unwrap());
write_reg32(ECX, safe_read32s(new_tsr_offset + TSR_ECX).unwrap());
write_reg32(EDX, safe_read32s(new_tsr_offset + TSR_EDX).unwrap());
write_reg32(EBX, safe_read32s(new_tsr_offset + TSR_EBX).unwrap());
write_reg32(ESP, safe_read32s(new_tsr_offset + TSR_ESP).unwrap());
write_reg32(EBP, safe_read32s(new_tsr_offset + TSR_EBP).unwrap());
write_reg32(ESI, safe_read32s(new_tsr_offset + TSR_ESI).unwrap());
write_reg32(EDI, safe_read32s(new_tsr_offset + TSR_EDI).unwrap());
if !switch_seg(ES, safe_read16(new_tsr_offset + TSR_ES).unwrap())
|| !switch_seg(SS, safe_read16(new_tsr_offset + TSR_SS).unwrap())
|| !switch_seg(DS, safe_read16(new_tsr_offset + TSR_DS).unwrap())
|| !switch_seg(FS, safe_read16(new_tsr_offset + TSR_FS).unwrap())
|| !switch_seg(GS, safe_read16(new_tsr_offset + TSR_GS).unwrap())
{
// XXX: Should be checked before side effects
dbg_assert!(false);
}
*instruction_pointer = get_seg_cs() + new_eip;
*segment_offsets.offset(TR as isize) = descriptor.base();
*segment_limits.offset(TR as isize) = descriptor.effective_limit();
*sreg.offset(TR as isize) = selector.raw;
*cr.offset(3) = new_cr3;
dbg_assert!((*cr.offset(3) & 0xFFF) == 0);
clear_tlb();
*cr.offset(0) |= CR0_TS;
if let Some(error_code) = error_code {
if tss_is_16 {
push16(error_code & 0xFFFF).unwrap();
}
else {
push32(error_code).unwrap();
}
}
}
pub unsafe fn after_block_boundary() { jit_block_boundary = true; }
#[no_mangle]
@ -2285,19 +2520,19 @@ pub unsafe fn load_tr(selector: i32) {
safe_write64(descriptor_address, descriptor.set_busy().raw).unwrap();
}
pub unsafe fn load_ldt(selector: i32) {
pub unsafe fn load_ldt(selector: i32) -> OrPageFault<()> {
let selector = SegmentSelector::of_u16(selector as u16);
if selector.is_null() {
*segment_limits.offset(LDTR as isize) = 0;
*segment_offsets.offset(LDTR as isize) = 0;
*sreg.offset(LDTR as isize) = selector.raw;
return;
return Ok(());
}
dbg_assert!(selector.is_gdt(), "TODO: LDT can only be loaded from GDT");
let (descriptor, _) = match return_on_pagefault!(lookup_segment_selector(selector)) {
let (descriptor, _) = match lookup_segment_selector(selector)? {
Ok((desc, addr)) => (desc, addr),
Err(SelectorNullOrInvalid::IsNull) => {
panic!("TODO: null TR");
@ -2325,6 +2560,8 @@ pub unsafe fn load_ldt(selector: i32) {
*segment_limits.offset(LDTR as isize) = descriptor.effective_limit();
*segment_offsets.offset(LDTR as isize) = descriptor.base();
*sreg.offset(LDTR as isize) = selector.raw;
Ok(())
}
#[no_mangle]

View file

@ -101,7 +101,7 @@ pub unsafe fn instr16_0F00_2_mem(addr: i32) {
trigger_gp(0);
}
else {
load_ldt(return_on_pagefault!(safe_read16(addr)));
return_on_pagefault!(load_ldt(return_on_pagefault!(safe_read16(addr))));
};
}
#[no_mangle]
@ -115,7 +115,7 @@ pub unsafe fn instr16_0F00_2_reg(r: i32) {
trigger_gp(0);
}
else {
load_ldt(read_reg16(r));
return_on_pagefault!(load_ldt(read_reg16(r)));
};
}
#[no_mangle]