Port load_tr to rust
This commit is contained in:
parent
bd50bb8dae
commit
4344954da3
|
@ -137,7 +137,6 @@ function V86Starter(options)
|
|||
"cpuid": function() { return cpu.cpuid(); },
|
||||
|
||||
"load_ldt": function() { return cpu.load_ldt.apply(cpu, arguments); },
|
||||
"load_tr": function() { return cpu.load_tr.apply(cpu, arguments); },
|
||||
|
||||
"log_from_wasm": function(offset, len) {
|
||||
const str = v86util.read_sized_string_from_mem(wasm_memory, offset, len);
|
||||
|
|
51
src/cpu.js
51
src/cpu.js
|
@ -2237,57 +2237,6 @@ CPU.prototype.switch_seg = function(reg, selector)
|
|||
return true;
|
||||
};
|
||||
|
||||
CPU.prototype.load_tr = function(selector)
|
||||
{
|
||||
var info = this.lookup_segment_selector(selector);
|
||||
|
||||
dbg_assert(info.is_valid);
|
||||
//dbg_log("load tr: " + h(selector, 4) + " offset=" + h(info.base >>> 0, 8) + " limit=" + h(info.effective_limit >>> 0, 8), LOG_CPU);
|
||||
|
||||
if(!info.from_gdt)
|
||||
{
|
||||
throw this.debug.unimpl("TR can only be loaded from GDT");
|
||||
}
|
||||
|
||||
if(info.is_null)
|
||||
{
|
||||
dbg_log("#GP(0) | tried to load null selector (ltr)");
|
||||
throw this.debug.unimpl("#GP handler");
|
||||
}
|
||||
|
||||
if(!info.is_system)
|
||||
{
|
||||
dbg_log("#GP | ltr: not a system entry");
|
||||
throw this.debug.unimpl("#GP handler (happens when running kvm-unit-test without ACPI)");
|
||||
}
|
||||
|
||||
if(info.type !== 9 && info.type !== 1)
|
||||
{
|
||||
// 0xB: busy 386 TSS (GP)
|
||||
// 0x9: 386 TSS
|
||||
// 0x3: busy 286 TSS (GP)
|
||||
// 0x1: 286 TSS (??)
|
||||
dbg_log("#GP | ltr: invalid type (type = " + h(info.type) + ")");
|
||||
throw this.debug.unimpl("#GP handler");
|
||||
}
|
||||
|
||||
if(!info.is_present)
|
||||
{
|
||||
dbg_log("#NT | present bit not set (ltr)");
|
||||
throw this.debug.unimpl("#NT handler");
|
||||
}
|
||||
|
||||
this.tss_size_32[0] = info.type === 9;
|
||||
this.segment_offsets[reg_tr] = info.base;
|
||||
this.segment_limits[reg_tr] = info.effective_limit;
|
||||
this.sreg[reg_tr] = selector;
|
||||
|
||||
// Mark task as busy
|
||||
this.write8(info.table_offset + 5 | 0, this.read8(info.table_offset + 5 | 0) | 2);
|
||||
|
||||
//dbg_log("tsr at " + h(info.base) + "; (" + info.effective_limit + " bytes)");
|
||||
};
|
||||
|
||||
CPU.prototype.load_ldt = function(selector)
|
||||
{
|
||||
var info = this.lookup_segment_selector(selector);
|
||||
|
|
|
@ -312,7 +312,7 @@ impl SegmentSelector {
|
|||
#[derive(PartialEq)]
|
||||
pub enum SelectorNullOrInvalid {
|
||||
IsNull,
|
||||
IsInvalid,
|
||||
OutsideOfTableLimit,
|
||||
}
|
||||
|
||||
pub struct SegmentDescriptor {
|
||||
|
@ -344,6 +344,11 @@ impl SegmentDescriptor {
|
|||
pub fn effective_limit(&self) -> u32 {
|
||||
if self.flags() & 8 == 8 { self.limit() << 12 | 0xFFF } else { self.limit() }
|
||||
}
|
||||
pub fn set_busy(&self) -> SegmentDescriptor {
|
||||
SegmentDescriptor {
|
||||
raw: self.raw | 2 << 40,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InterruptDescriptor {
|
||||
|
@ -514,12 +519,12 @@ pub unsafe fn iret(is_16: bool) {
|
|||
// protected mode return
|
||||
|
||||
let (cs_descriptor, cs_selector) = match return_on_pagefault!(lookup_segment_selector(new_cs)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
panic!("Unimplemented: CS selector is null");
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
SelectorNullOrInvalid::OutsideOfTableLimit => {
|
||||
panic!("Unimplemented: CS selector is invalid");
|
||||
},
|
||||
},
|
||||
|
@ -567,7 +572,7 @@ pub unsafe fn iret(is_16: bool) {
|
|||
|
||||
let (ss_descriptor, ss_selector) =
|
||||
match return_on_pagefault!(lookup_segment_selector(temp_ss)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("#GP for loading 0 in SS sel={:x}", temp_ss);
|
||||
|
@ -575,7 +580,7 @@ pub unsafe fn iret(is_16: bool) {
|
|||
trigger_gp(0);
|
||||
return;
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
SelectorNullOrInvalid::OutsideOfTableLimit => {
|
||||
dbg_log!("#GP for loading invalid in SS sel={:x}", temp_ss);
|
||||
trigger_gp(temp_ss & !3);
|
||||
return;
|
||||
|
@ -750,13 +755,13 @@ pub unsafe fn call_interrupt_vector(
|
|||
}
|
||||
|
||||
let cs_segment_descriptor = match return_on_pagefault!(lookup_segment_selector(selector)) {
|
||||
Ok((desc, _)) => desc,
|
||||
Ok((desc, _, _)) => desc,
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("is null");
|
||||
panic!("Unimplemented: #GP handler");
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
SelectorNullOrInvalid::OutsideOfTableLimit => {
|
||||
dbg_log!("is invalid");
|
||||
panic!("Unimplemented: #GP handler (error code)");
|
||||
},
|
||||
|
@ -793,7 +798,7 @@ pub unsafe fn call_interrupt_vector(
|
|||
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),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(_) => {
|
||||
panic!("Unimplemented: #TS handler");
|
||||
},
|
||||
|
@ -1012,14 +1017,14 @@ pub unsafe fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool
|
|||
}
|
||||
|
||||
let (info, cs_selector) = match return_on_pagefault!(lookup_segment_selector(selector)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("#gp null cs");
|
||||
trigger_gp(0);
|
||||
return;
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
SelectorNullOrInvalid::OutsideOfTableLimit => {
|
||||
dbg_log!("#gp invalid cs: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
|
@ -1051,14 +1056,14 @@ pub unsafe fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool
|
|||
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),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("#gp null cs");
|
||||
trigger_gp(0);
|
||||
return;
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
SelectorNullOrInvalid::OutsideOfTableLimit => {
|
||||
dbg_log!("#gp invalid cs: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
|
@ -1106,12 +1111,12 @@ pub unsafe fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool
|
|||
|
||||
let (ss_info, ss_selector) =
|
||||
match return_on_pagefault!(lookup_segment_selector(new_ss)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
panic!("null ss: {}", new_ss);
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
SelectorNullOrInvalid::OutsideOfTableLimit => {
|
||||
panic!("invalid ss: {}", new_ss);
|
||||
},
|
||||
},
|
||||
|
@ -1348,14 +1353,14 @@ pub unsafe fn far_return(eip: i32, selector: i32, stack_adjust: i32, is_osize_32
|
|||
}
|
||||
|
||||
let (info, cs_selector) = match return_on_pagefault!(lookup_segment_selector(selector)) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(selector_unusable) => match selector_unusable {
|
||||
SelectorNullOrInvalid::IsNull => {
|
||||
dbg_log!("far return: #gp null cs");
|
||||
trigger_gp(0);
|
||||
return;
|
||||
},
|
||||
SelectorNullOrInvalid::IsInvalid => {
|
||||
SelectorNullOrInvalid::OutsideOfTableLimit => {
|
||||
dbg_log!("far return: #gp invalid cs: {:x}", selector);
|
||||
trigger_gp(selector & !3);
|
||||
return;
|
||||
|
@ -2086,7 +2091,7 @@ pub unsafe fn is_asize_32() -> bool {
|
|||
|
||||
pub unsafe fn lookup_segment_selector(
|
||||
selector: i32,
|
||||
) -> OrPageFault<Result<(SegmentDescriptor, SegmentSelector), SelectorNullOrInvalid>> {
|
||||
) -> OrPageFault<Result<(SegmentDescriptor, SegmentSelector, i32), SelectorNullOrInvalid>> {
|
||||
let selector = SegmentSelector::of_u16(selector as u16);
|
||||
|
||||
if selector.is_null() {
|
||||
|
@ -2104,15 +2109,16 @@ pub unsafe fn lookup_segment_selector(
|
|||
};
|
||||
|
||||
if selector.descriptor_offset() > table_limit {
|
||||
return Ok(Err(SelectorNullOrInvalid::IsInvalid));
|
||||
return Ok(Err(SelectorNullOrInvalid::OutsideOfTableLimit));
|
||||
}
|
||||
|
||||
let descriptor_address =
|
||||
translate_address_system_read(selector.descriptor_offset() as i32 + table_offset as i32)?;
|
||||
let descriptor_address = selector.descriptor_offset() as i32 + table_offset as i32;
|
||||
|
||||
let descriptor = SegmentDescriptor::of_u64(read64s(descriptor_address) as u64);
|
||||
let descriptor = SegmentDescriptor::of_u64(read64s(translate_address_system_read(
|
||||
descriptor_address,
|
||||
)?) as u64);
|
||||
|
||||
Ok(Ok((descriptor, selector)))
|
||||
Ok(Ok((descriptor, selector, descriptor_address)))
|
||||
}
|
||||
|
||||
pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
|
||||
|
@ -2132,7 +2138,7 @@ pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
|
|||
|
||||
let (descriptor, selector) =
|
||||
match return_on_pagefault!(lookup_segment_selector(selector_raw), false) {
|
||||
Ok((desc, sel)) => (desc, sel),
|
||||
Ok((desc, sel, _)) => (desc, sel),
|
||||
Err(selector_unusable) => {
|
||||
// The selector couldn't be used to fetch a descriptor, so we handle all of those
|
||||
// cases
|
||||
|
@ -2149,7 +2155,7 @@ pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
else if selector_unusable == SelectorNullOrInvalid::IsInvalid {
|
||||
else if selector_unusable == SelectorNullOrInvalid::OutsideOfTableLimit {
|
||||
dbg_log!(
|
||||
"#GP for loading invalid in seg={} sel={:x}",
|
||||
reg,
|
||||
|
@ -2221,6 +2227,56 @@ pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
|
|||
true
|
||||
}
|
||||
|
||||
pub unsafe fn load_tr(selector: i32) {
|
||||
let (descriptor, selector, descriptor_address) =
|
||||
match return_on_pagefault!(lookup_segment_selector(selector)) {
|
||||
Ok((desc, sel, addr)) => (desc, sel, addr),
|
||||
Err(SelectorNullOrInvalid::IsNull) => {
|
||||
panic!("TODO: null TR");
|
||||
},
|
||||
Err(SelectorNullOrInvalid::OutsideOfTableLimit) => {
|
||||
panic!("TODO: TR selector outside of table limit");
|
||||
},
|
||||
};
|
||||
|
||||
dbg_log!(
|
||||
"load tr: {:x} offset={:x} limit={:x} is32={}",
|
||||
selector.raw,
|
||||
descriptor.base(),
|
||||
descriptor.effective_limit(),
|
||||
descriptor.system_type() == 9,
|
||||
);
|
||||
|
||||
dbg_assert!(selector.is_gdt(), "TODO: TR can only be loaded from GDT");
|
||||
|
||||
if !descriptor.is_system() {
|
||||
panic!("#GP | ltr: not a system entry (happens when running kvm-unit-test without ACPI)");
|
||||
}
|
||||
|
||||
if descriptor.system_type() != 9 && descriptor.system_type() != 1 {
|
||||
// 0xB: busy 386 TSS (GP)
|
||||
// 0x9: 386 TSS
|
||||
// 0x3: busy 286 TSS (GP)
|
||||
// 0x1: 286 TSS (??)
|
||||
panic!(
|
||||
"#GP | ltr: invalid type (type = 0x{:x})",
|
||||
descriptor.system_type()
|
||||
);
|
||||
}
|
||||
|
||||
if !descriptor.is_present() {
|
||||
panic!("#NT | present bit not set (ltr)");
|
||||
}
|
||||
|
||||
*tss_size_32 = descriptor.system_type() == 9;
|
||||
*segment_limits.offset(TR as isize) = descriptor.effective_limit();
|
||||
*segment_offsets.offset(TR as isize) = descriptor.base();
|
||||
*sreg.offset(TR as isize) = selector.raw;
|
||||
|
||||
// Mark task as busy
|
||||
safe_write64(descriptor_address, descriptor.set_busy().raw).unwrap();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn log_segment_null(segment: i32) {
|
||||
dbg_assert!(segment >= 0 && segment < 8);
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
extern "C" {
|
||||
fn get_rand_int() -> i32;
|
||||
fn cpuid();
|
||||
fn load_tr(v: i32);
|
||||
fn load_ldt(v: i32);
|
||||
}
|
||||
|
||||
|
|
|
@ -442,7 +442,7 @@ pub unsafe fn lar(selector: i32, original: i32) -> i32 {
|
|||
dbg_log!("lar: invalid selector={:x}: null or invalid", selector);
|
||||
return original;
|
||||
},
|
||||
Ok(Ok((desc, sel))) => {
|
||||
Ok(Ok((desc, sel, _))) => {
|
||||
*flags_changed &= !FLAG_ZERO;
|
||||
let dpl_bad = desc.dpl() < *cpl || desc.dpl() < sel.rpl();
|
||||
|
||||
|
@ -497,7 +497,7 @@ pub unsafe fn lsl(selector: i32, original: i32) -> i32 {
|
|||
dbg_log!("lsl: invalid selector={:x}: null or invalid", selector);
|
||||
return original;
|
||||
},
|
||||
Ok(Ok((desc, sel))) => {
|
||||
Ok(Ok((desc, sel, _))) => {
|
||||
*flags_changed &= !FLAG_ZERO;
|
||||
let dpl_bad = desc.dpl() < *cpl || desc.dpl() < sel.rpl();
|
||||
|
||||
|
@ -531,7 +531,7 @@ pub unsafe fn verr(selector: i32) {
|
|||
*flags &= !FLAG_ZERO;
|
||||
dbg_log!("verr -> invalid. selector={:x}", selector);
|
||||
},
|
||||
Ok((desc, sel)) => {
|
||||
Ok((desc, sel, _)) => {
|
||||
if desc.is_system()
|
||||
|| !desc.is_readable()
|
||||
|| (!desc.is_conforming_executable()
|
||||
|
@ -555,7 +555,7 @@ pub unsafe fn verw(selector: i32) {
|
|||
*flags &= !FLAG_ZERO;
|
||||
dbg_log!("verw -> invalid. selector={:x}", selector);
|
||||
},
|
||||
Ok((desc, sel)) => {
|
||||
Ok((desc, sel, _)) => {
|
||||
if desc.is_system()
|
||||
|| !desc.is_writable()
|
||||
|| desc.dpl() < *cpl
|
||||
|
|
Loading…
Reference in a new issue