Skip to content

Commit

Permalink
Implement access byte in segment descriptor cache, set dpl<cpl segmen…
Browse files Browse the repository at this point in the history
…ts to null in iret (#978)
  • Loading branch information
copy committed Feb 4, 2024
1 parent 704f9a6 commit 4697142
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 19 deletions.
22 changes: 20 additions & 2 deletions src/cpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function CPU(bus, wm, next_tick_immediately)
this.segment_is_null = v86util.view(Uint8Array, memory, 724, 8);
this.segment_offsets = v86util.view(Int32Array, memory, 736, 8);
this.segment_limits = v86util.view(Uint32Array, memory, 768, 8);
this.segment_access_bytes = v86util.view(Uint8Array, memory, 512, 8);

/**
* Wheter or not in protected mode
Expand Down Expand Up @@ -334,7 +335,7 @@ CPU.prototype.get_state = function()
var state = [];

state[0] = this.memory_size[0];
state[1] = this.segment_is_null;
state[1] = new Uint8Array([...this.segment_is_null, ...this.segment_access_bytes]);
state[2] = this.segment_offsets;
state[3] = this.segment_limits;
state[4] = this.protected_mode[0];
Expand Down Expand Up @@ -470,9 +471,25 @@ CPU.prototype.set_state = function(state)
console.warn("Note: Memory size mismatch. we=" + this.mem8.length + " state=" + this.memory_size[0]);
}

this.segment_is_null.set(state[1]);
if(state[1].length === 8)
{
// NOTE: support for old state images; delete this when bumping STATE_VERSION
this.segment_is_null.set(state[1]);
this.segment_access_bytes.fill(0x80 | (3 << 5) | 0x10 | 0x02);
this.segment_access_bytes[REG_CS] = 0x80 | (3 << 5) | 0x10 | 0x08 | 0x02;
}
else if(state[1].length === 16)
{
this.segment_is_null.set(state[1].subarray(0, 8));
this.segment_access_bytes.set(state[1].subarray(8, 16));
}
else
{
dbg_assert("Unexpected cpu segment state length:" + state[1].length);
}
this.segment_offsets.set(state[2]);
this.segment_limits.set(state[3]);

this.protected_mode[0] = state[4];
this.idtr_offset[0] = state[5];
this.idtr_size[0] = state[6];
Expand Down Expand Up @@ -1270,6 +1287,7 @@ CPU.prototype.load_multiboot_option_rom = function(buffer, initrd, cmdline)
cpu.segment_is_null[i] = 0;
cpu.segment_offsets[i] = 0;
cpu.segment_limits[i] = 0xFFFFFFFF;
// cpu.segment_access_bytes[i]
// Value doesn't matter, OS isn't allowed to reload without setting
// up a proper GDT
cpu.sreg[i] = 0xB002;
Expand Down
72 changes: 55 additions & 17 deletions src/rust/cpu/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,9 +686,17 @@ pub unsafe fn iret(is_16: bool) {
*flags = *flags & !FLAG_VIF & !FLAG_VIP | (new_flags & (FLAG_VIF | FLAG_VIP));
}

// XXX: Set segment to 0 if it's not usable in the new cpl
// XXX: Use cached segment information
// ...
for reg in [ES, DS, FS, GS] {
let access = *segment_access_bytes.offset(reg as isize);
let dpl = access >> 5 & 3;
let executable = access & 8 == 8;
let conforming = access & 4 == 4;
if dpl < *cpl && !(executable && conforming) {
dbg_log!("set segment to null sreg={} dpl={} executable={} conforming={}", reg, dpl, executable, conforming);
*segment_is_null.offset(reg as isize) = true;
*sreg.offset(reg as isize) = 0;
}
}
}
else if cs_selector.rpl() == *cpl {
// same privilege return
Expand Down Expand Up @@ -718,6 +726,7 @@ pub unsafe fn iret(is_16: bool) {

*segment_limits.offset(CS as isize) = cs_descriptor.effective_limit();
*segment_offsets.offset(CS as isize) = cs_descriptor.base();
*segment_access_bytes.offset(CS as isize) = cs_descriptor.access_byte();

*instruction_pointer = new_eip + get_seg_cs();

Expand Down Expand Up @@ -1008,6 +1017,7 @@ pub unsafe fn call_interrupt_vector(

*segment_limits.offset(CS as isize) = cs_segment_descriptor.effective_limit();
*segment_offsets.offset(CS as isize) = cs_segment_descriptor.base();
*segment_access_bytes.offset(CS as isize) = cs_segment_descriptor.access_byte();

*instruction_pointer = get_seg_cs() + offset;

Expand Down Expand Up @@ -1308,6 +1318,7 @@ pub unsafe fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool
*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();
*segment_access_bytes.offset(CS as isize) = cs_info.access_byte();
*sreg.offset(CS as isize) = cs_selector as u16 & !3 | *cpl as u16;
dbg_assert!(*sreg.offset(CS as isize) & 3 == *cpl as u16);

Expand Down Expand Up @@ -1374,6 +1385,7 @@ pub unsafe fn far_jump(eip: i32, selector: i32, is_call: bool, is_osize_32: bool

*segment_is_null.offset(CS as isize) = false;
*segment_limits.offset(CS as isize) = info.effective_limit();
*segment_access_bytes.offset(CS as isize) = info.access_byte();

*segment_offsets.offset(CS as isize) = info.base();
*sreg.offset(CS as isize) = selector as u16 & !3 | *cpl as u16;
Expand Down Expand Up @@ -1486,18 +1498,20 @@ pub unsafe fn far_return(eip: i32, selector: i32, stack_adjust: i32, is_osize_32
}
set_stack_reg(temp_esp + stack_adjust);

//if(is_osize_32)
//{
// adjust_stack_reg(2 * 4);
//}
//else
//{
// adjust_stack_reg(2 * 2);
//}
//if(is_osize_32)
//{
// adjust_stack_reg(2 * 4);
//}
//else
//{
// adjust_stack_reg(2 * 2);
//}

//throw debug.unimpl("privilege change");

//throw debug.unimpl("privilege change");
//adjust_stack_reg(stack_adjust);

//adjust_stack_reg(stack_adjust);
// TODO: invalidate segments that are not accessible at this cpl (see iret)
}
else {
if is_osize_32 {
Expand All @@ -1514,6 +1528,7 @@ pub unsafe fn far_return(eip: i32, selector: i32, stack_adjust: i32, is_osize_32

*segment_is_null.offset(CS as isize) = false;
*segment_limits.offset(CS as isize) = info.effective_limit();
*segment_access_bytes.offset(CS as isize) = info.access_byte();

*segment_offsets.offset(CS as isize) = info.base();
*sreg.offset(CS as isize) = selector as u16;
Expand Down Expand Up @@ -1659,6 +1674,7 @@ pub unsafe fn do_task_switch(selector: i32, error_code: Option<i32>) {
*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();
*segment_access_bytes.offset(CS as isize) = new_cs_descriptor.access_byte();
*sreg.offset(CS as isize) = new_cs as u16;

*cpl = new_cs_descriptor.dpl();
Expand Down Expand Up @@ -2370,8 +2386,14 @@ pub unsafe fn lookup_segment_selector(
#[inline(never)]
pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
dbg_assert!(reg >= 0 && reg <= 5);
dbg_assert!(reg != CS);
dbg_assert!(selector_raw >= 0 && selector_raw < 0x10000);

if vm86_mode() {
// TODO: Should set segment_limits and segment_access_bytes if ever implemented in get_seg
// (only vm86, not in real mode)
}

if !*protected_mode || vm86_mode() {
*sreg.offset(reg as isize) = selector_raw as u16;
*segment_is_null.offset(reg as isize) = false;
Expand Down Expand Up @@ -2406,10 +2428,12 @@ pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
}
else if selector_unusable == SelectorNullOrInvalid::OutsideOfTableLimit {
dbg_log!(
"#GP for loading invalid in seg={} sel={:x}",
"#GP for loading invalid in seg={} sel={:x} gdt_limit={:x}",
reg,
selector_raw
selector_raw,
*gdtr_size,
);
dbg_trace();
trigger_gp(selector_raw & !3);
return false;
}
Expand Down Expand Up @@ -2449,10 +2473,20 @@ pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
&& (selector.rpl() > descriptor.dpl() || *cpl > descriptor.dpl()))
{
dbg_log!(
"#GP for loading invalid in seg {} sel={:x}",
"#GP for loading invalid in seg {} sel={:x} sys={} readable={} dc={} exec={} rpl={} dpl={} cpl={} present={} paging={}",
reg,
selector_raw,
descriptor.is_system(),
descriptor.is_readable(),
descriptor.is_dc(),
descriptor.is_executable(),
selector.rpl(),
descriptor.dpl(),
*cpl,
descriptor.is_present(),
*cr & CR0_PG != 0,
);
dbg_trace();
trigger_gp(selector_raw & !3);
return false;
}
Expand All @@ -2471,6 +2505,7 @@ pub unsafe fn switch_seg(reg: i32, selector_raw: i32) -> bool {
*segment_is_null.offset(reg as isize) = false;
*segment_limits.offset(reg as isize) = descriptor.effective_limit();
*segment_offsets.offset(reg as isize) = descriptor.base();
*segment_access_bytes.offset(reg as isize) = descriptor.access_byte();
*sreg.offset(reg as isize) = selector_raw as u16;

update_state_flags();
Expand Down Expand Up @@ -2587,7 +2622,7 @@ pub unsafe fn get_seg(segment: i32) -> OrPageFault<i32> {
dbg_assert!(segment >= 0 && segment < 8);
if *segment_is_null.offset(segment as isize) {
dbg_assert!(segment != CS && segment != SS);
dbg_log!("#gp: Access null segment");
dbg_log!("#gp: Access null segment {}", segment);
dbg_trace();
dbg_assert!(!in_jit);
trigger_gp(0);
Expand Down Expand Up @@ -2620,6 +2655,7 @@ pub unsafe fn set_cr0(cr0: i32) {
}

*protected_mode = (*cr & CR0_PE) == CR0_PE;
*segment_access_bytes.offset(CS as isize) = 0x80 | 0x10 | 0x08 | 0x02; // P dpl0 S E RW
}

pub unsafe fn set_cr3(mut cr3: i32) {
Expand Down Expand Up @@ -4213,6 +4249,7 @@ pub unsafe fn reset_cpu() {
*segment_is_null.offset(i) = false;
*segment_limits.offset(i) = 0;
*segment_offsets.offset(i) = 0;
*segment_access_bytes.offset(i) = 0x80 | (0 << 5) | 0x10 | 0x02; // P dpl0 S RW

*reg32.offset(i) = 0;

Expand All @@ -4223,6 +4260,7 @@ pub unsafe fn reset_cpu() {

*fpu_st.offset(i) = ::softfloat::F80::ZERO;
}
*segment_access_bytes.offset(CS as isize) = 0x80 | (0 << 5) | 0x10 | 0x08 | 0x02; // P dpl0 S E RW

for i in 0..4 {
*reg_pdpte.offset(i) = 0
Expand Down
3 changes: 3 additions & 0 deletions src/rust/cpu/global_pointers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub const state_flags: *mut CachedStateFlags = 108 as *mut CachedStateFlags;
pub const last_result: *mut i32 = 112 as *mut i32;
pub const flags: *mut i32 = 120 as *mut i32;

pub const segment_access_bytes: *mut u8 = 512 as *mut u8; // TODO: reorder below segment_limits

pub const page_fault: *mut bool = 540 as *mut bool;

pub const apic_enabled: *mut bool = 548 as *mut bool;
Expand Down Expand Up @@ -47,6 +49,7 @@ pub const svga_dirty_bitmap_max_offset: *mut u32 = 720 as *mut u32;
pub const segment_is_null: *mut bool = 724 as *mut bool;
pub const segment_offsets: *mut i32 = 736 as *mut i32;
pub const segment_limits: *mut u32 = 768 as *mut u32;

pub const protected_mode: *mut bool = 800 as *mut bool;
pub const is_32: *mut bool = 804 as *mut bool;
pub const stack_size_32: *mut bool = 808 as *mut bool;
Expand Down
4 changes: 4 additions & 0 deletions src/rust/cpu/instructions_0f.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1340,13 +1340,15 @@ pub unsafe fn instr_0F34() {
*segment_is_null.offset(CS as isize) = false;
*segment_limits.offset(CS as isize) = -1i32 as u32;
*segment_offsets.offset(CS as isize) = 0;
*segment_access_bytes.offset(CS as isize) = 0x80 | (0 << 5) | 0x10 | 0x08 | 0x02; // P dpl0 S E RW
update_cs_size(true);
*cpl = 0;
cpl_changed();
*sreg.offset(SS as isize) = (seg + 8) as u16;
*segment_is_null.offset(SS as isize) = false;
*segment_limits.offset(SS as isize) = -1i32 as u32;
*segment_offsets.offset(SS as isize) = 0;
*segment_access_bytes.offset(SS as isize) = 0x80 | (0 << 5) | 0x10 | 0x02; // P dpl0 S RW
*stack_size_32 = true;
update_state_flags();
return;
Expand All @@ -1367,13 +1369,15 @@ pub unsafe fn instr_0F35() {
*segment_is_null.offset(CS as isize) = false;
*segment_limits.offset(CS as isize) = -1i32 as u32;
*segment_offsets.offset(CS as isize) = 0;
*segment_access_bytes.offset(CS as isize) = 0x80 | (3 << 5) | 0x10 | 0x08 | 0x02; // P dpl3 S E RW
update_cs_size(true);
*cpl = 3;
cpl_changed();
*sreg.offset(SS as isize) = (seg + 24 | 3) as u16;
*segment_is_null.offset(SS as isize) = false;
*segment_limits.offset(SS as isize) = -1i32 as u32;
*segment_offsets.offset(SS as isize) = 0;
*segment_access_bytes.offset(SS as isize) = 0x80 | (3 << 5) | 0x10 | 0x02; // P dpl3 S RW
*stack_size_32 = true;
update_state_flags();
return;
Expand Down

0 comments on commit 4697142

Please sign in to comment.