diff --git a/src/rust/cpu/cpu.rs b/src/rust/cpu/cpu.rs index e28f3c56bd..26f99f17f6 100644 --- a/src/rust/cpu/cpu.rs +++ b/src/rust/cpu/cpu.rs @@ -164,6 +164,7 @@ pub const PAGE_TABLE_ACCESSED_MASK: i32 = 1 << 5; pub const PAGE_TABLE_DIRTY_MASK: i32 = 1 << 6; pub const PAGE_TABLE_PSE_MASK: i32 = 1 << 7; pub const PAGE_TABLE_GLOBAL_MASK: i32 = 1 << 8; +pub const PAGE_TABLE_NX_MASK: i64 = 1 << 63; pub const MMAP_BLOCK_BITS: i32 = 17; pub const MMAP_BLOCK_SIZE: i32 = 1 << MMAP_BLOCK_BITS; pub const CR0_PE: i32 = 1; @@ -236,9 +237,12 @@ pub const IA32_PAT: i32 = 0x277; pub const IA32_RTIT_CTL: i32 = 0x570; pub const MSR_PKG_C2_RESIDENCY: i32 = 0x60D; pub const IA32_KERNEL_GS_BASE: i32 = 0xC0000101u32 as i32; +pub const IA32_EFER: i32 = 0xC0000080u32 as i32; pub const MSR_AMD64_LS_CFG: i32 = 0xC0011020u32 as i32; pub const MSR_AMD64_DE_CFG: i32 = 0xC0011029u32 as i32; +pub const EFER_NXE: i32 = 1 << 11; + pub const IA32_APIC_BASE_BSP: i32 = 1 << 8; pub const IA32_APIC_BASE_EXTD: i32 = 1 << 10; pub const IA32_APIC_BASE_EN: i32 = 1 << 11; @@ -260,6 +264,7 @@ pub const TLB_NO_USER: i32 = 1 << 2; pub const TLB_IN_MAPPED_RANGE: i32 = 1 << 3; pub const TLB_GLOBAL: i32 = 1 << 4; pub const TLB_HAS_CODE: i32 = 1 << 5; +pub const TLB_NO_EXECUTE: i32 = 1 << 6; pub const IVT_SIZE: u32 = 0x400; pub const CPU_EXCEPTION_DE: i32 = 0; pub const CPU_EXCEPTION_DB: i32 = 1; @@ -1947,7 +1952,9 @@ pub unsafe fn do_page_walk( side_effects: bool, ) -> OrPageFault { let global; + let for_execute = false; let mut allow_user = true; + let mut has_nx_bit = false; let page = (addr as u32 >> 12) as i32; let high; @@ -1968,7 +1975,7 @@ pub unsafe fn do_page_walk( let pdpt_entry = *reg_pdpte.offset(((addr as u32) >> 30) as isize); if pdpt_entry as i32 & PAGE_TABLE_PRESENT_MASK == 0 { if side_effects { - trigger_pagefault(addr, false, for_writing, user, jit); + trigger_pagefault(addr, false, for_writing, user, false, jit); } return Err(()); } @@ -1980,10 +1987,7 @@ pub unsafe fn do_page_walk( page_dir_entry as u64 & 0x7FFF_FFFF_0000_0000 == 0, "Unsupported: Page directory entry larger than 32 bits" ); - dbg_assert!( - page_dir_entry & 0x8000_0000_0000_0000u64 as i64 == 0, - "Unsupported: NX bit" - ); + has_nx_bit = (*efer & EFER_NXE != 0) && (page_dir_entry & PAGE_TABLE_NX_MASK != 0); (page_dir_addr, page_dir_entry as i32) } @@ -1995,7 +1999,7 @@ pub unsafe fn do_page_walk( if page_dir_entry & PAGE_TABLE_PRESENT_MASK == 0 { if side_effects { - trigger_pagefault(addr, false, for_writing, user, jit); + trigger_pagefault(addr, false, for_writing, user, false, jit); } return Err(()); } @@ -2007,9 +2011,9 @@ pub unsafe fn do_page_walk( if 0 != page_dir_entry & PAGE_TABLE_PSE_MASK && 0 != cr4 & CR4_PSE { // size bit is set - if for_writing && !allow_write && !kernel_write_override || user && !allow_user { + if for_writing && !allow_write && !kernel_write_override || user && !allow_user || for_execute && has_nx_bit { if side_effects { - trigger_pagefault(addr, true, for_writing, user, jit); + trigger_pagefault(addr, true, for_writing, user, for_execute, jit); } return Err(()); } @@ -2041,10 +2045,7 @@ pub unsafe fn do_page_walk( page_table_entry as u64 & 0x7FFF_FFFF_0000_0000 == 0, "Unsupported: Page table entry larger than 32 bits" ); - dbg_assert!( - page_table_entry & 0x8000_0000_0000_0000u64 as i64 == 0, - "Unsupported: NX bit" - ); + has_nx_bit |= (*efer & EFER_NXE != 0) && (page_table_entry & PAGE_TABLE_NX_MASK != 0); (page_table_addr, page_table_entry as i32) } @@ -2062,9 +2063,10 @@ pub unsafe fn do_page_walk( if !present || for_writing && !allow_write && !kernel_write_override || user && !allow_user + || for_execute && has_nx_bit { if side_effects { - trigger_pagefault(addr, present, for_writing, user, jit); + trigger_pagefault(addr, present, for_writing, user, for_execute, jit); } return Err(()); } @@ -2130,7 +2132,8 @@ pub unsafe fn do_page_walk( | if allow_user { 0 } else { TLB_NO_USER } | if is_in_mapped_range { TLB_IN_MAPPED_RANGE } else { 0 } | if global && 0 != cr4 & CR4_PGE { TLB_GLOBAL } else { 0 } - | if has_code { TLB_HAS_CODE } else { 0 }; + | if has_code { TLB_HAS_CODE } else { 0 } + | if has_nx_bit { TLB_NO_EXECUTE } else { 0 }; let tlb_entry = (high + memory::mem8 as u32) as i32 ^ page << 12 | info_bits as i32; @@ -2256,14 +2259,15 @@ pub unsafe fn trigger_fault_end_jit() { /// and finally calls trigger_fault_end_jit, which does the interrupt /// /// Non-jit resets the instruction pointer and does the PF interrupt directly -pub unsafe fn trigger_pagefault(addr: i32, present: bool, write: bool, user: bool, jit: bool) { +pub unsafe fn trigger_pagefault(addr: i32, present: bool, write: bool, user: bool, execut: bool, jit: bool) { if config::LOG_PAGE_FAULTS { dbg_log!( - "page fault{} w={} u={} p={} eip={:x} cr2={:x}", + "page fault{} w={} u={} p={} e={} eip={:x} cr2={:x}", if jit { "jit" } else { "" }, write as i32, user as i32, present as i32, + execut as i32, *previous_ip, addr ); @@ -2275,7 +2279,7 @@ pub unsafe fn trigger_pagefault(addr: i32, present: bool, write: bool, user: boo let page = ((addr as u32) >> 12) as i32; clear_tlb_code(page); tlb_data[page as usize] = 0; - let error_code = (user as i32) << 2 | (write as i32) << 1 | present as i32; + let error_code = (execut as i32) << 4 | (user as i32) << 2 | (write as i32) << 1 | present as i32; if jit { jit_fault = Some((CPU_EXCEPTION_PF, Some(error_code))); } @@ -4484,6 +4488,8 @@ pub unsafe fn reset_cpu() { *last_op1 = 0; *last_op_size = 0; + *efer = 0; + set_tsc(0, 0); *instruction_pointer = 0xFFFF0; diff --git a/src/rust/cpu/global_pointers.rs b/src/rust/cpu/global_pointers.rs index c32bd005c6..238a191173 100644 --- a/src/rust/cpu/global_pointers.rs +++ b/src/rust/cpu/global_pointers.rs @@ -76,6 +76,8 @@ pub const sse_scratch_register: *mut reg128 = 1136 as *mut reg128; pub const fpu_st: *mut F80 = 1152 as *mut F80; +pub const efer: *mut i32 = 1280 as *mut i32; + pub fn get_reg32_offset(r: u32) -> u32 { dbg_assert!(r < 8); (unsafe { reg32.offset(r as isize) }) as u32 diff --git a/src/rust/cpu/instructions_0f.rs b/src/rust/cpu/instructions_0f.rs index cc1a4e23b8..eef485c2bc 100644 --- a/src/rust/cpu/instructions_0f.rs +++ b/src/rust/cpu/instructions_0f.rs @@ -1196,6 +1196,20 @@ pub unsafe fn instr_0F30() { IA32_SYSENTER_ESP => *sysenter_esp = low, IA32_FEAT_CTL => {}, // linux 5.x MSR_TEST_CTRL => {}, // linux 5.x + IA32_EFER => { + let supported_bits = EFER_NXE as i32; + + if (low & !supported_bits) != 0 { + dbg_log!("Writing unsupported bits to EFER: {:x}", low & !supported_bits); + dbg_assert!(false); + } + + if (low & EFER_NXE) != (*efer & EFER_NXE) { + full_clear_tlb(); + } + + *efer = (low & supported_bits) as i32; + }, IA32_APIC_BASE => { dbg_assert!( high == 0, @@ -1277,6 +1291,7 @@ pub unsafe fn instr_0F32() { low = tsc as i32; high = (tsc >> 32) as i32 }, + IA32_EFER => low = *efer, IA32_FEAT_CTL => {}, // linux 5.x MSR_TEST_CTRL => {}, // linux 5.x IA32_PLATFORM_ID => {}, @@ -3309,10 +3324,18 @@ pub unsafe fn instr_0FA2() { 0x80000000 => { // maximum supported extended level - eax = 5; + eax = 0x80000001u32 as i32; // other registers are reserved }, + 0x80000001 => { + let vme = 0 << 1; + edx = (if true /* have fpu */ { 1 } else { 0 }) | // fpu + vme | 1 << 3 | 1 << 4 | 1 << 5 | 1 << 6 | // vme, pse, tsc, msr, pae + 1 << 8 | 1 << 11 | 1 << 13 | 1 << 15 | 1 << 20 | // cx8, sep, pge, cmov, nx + 1 << 23 | 1 << 24 | 1 << 25 | 1 << 26; // mmx, fxsr, sse1, sse2 + }, + 0x40000000 => { // hypervisor if config::VMWARE_HYPERVISOR_PORT {