Port load_tr to rust

This commit is contained in:
Fabian 2021-03-30 05:24:08 -05:00
parent bd50bb8dae
commit 4344954da3
5 changed files with 84 additions and 81 deletions

View file

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

View file

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

View file

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

View file

@ -3,7 +3,6 @@
extern "C" {
fn get_rand_int() -> i32;
fn cpuid();
fn load_tr(v: i32);
fn load_ldt(v: i32);
}

View file

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