diff --git a/bochs/CHANGES b/bochs/CHANGES index 28c15f68f..310a60e4c 100644 --- a/bochs/CHANGES +++ b/bochs/CHANGES @@ -12,6 +12,7 @@ Bochs repository moved to the SVN version control ! When compiled in, AVX still has to be enabled using .bochsrc CPUID option. - Added emulation of AVX float16 convert instructions, the feature can be enabled using .bochsrc CPUID option. + - Implemented VMX preemption timer VMEXIT control (patch by Jianan Hao) - Redefined/Updated/Fixed instrumentation callbacks. - Bugfixes for CPU emulation correctness and stability. @@ -65,6 +66,7 @@ Bochs repository moved to the SVN version control ! from the "clock" setting) - SF patches applied + [3302668] VMX preemption timer by Jianan Hao [3327510] Fix wrong address translation in debugger by Jianan Hao [3323758] Ctrl-Break support for the Win32 gui by Nikolay Nikolov [3316785] Ctrl-Break support for the X11 gui by Nikolay Nikolov diff --git a/bochs/cpu/apic.cc b/bochs/cpu/apic.cc index d8f4763e0..753580e5f 100644 --- a/bochs/cpu/apic.cc +++ b/bochs/cpu/apic.cc @@ -184,8 +184,17 @@ bx_local_apic_c::bx_local_apic_c(BX_CPU_C *mycpu, unsigned id) // Register a non-active timer for use when the timer is started. timer_handle = bx_pc_system.register_timer_ticks(this, - BX_CPU(0)->lapic.periodic_smf, 0, 0, 0, "lapic"); + bx_local_apic_c::periodic_smf, 0, 0, 0, "lapic"); timer_active = 0; + +#if BX_SUPPORT_VMX >= 2 + // Register a non-active timer for VMX preemption timer. + vmx_timer_handle = bx_pc_system.register_timer_ticks(this, + bx_local_apic_c::vmx_preemption_timer_expired, 0, 0, 0, "vmx_preemtion"); + BX_DEBUG(("vmx_timer is = %d", vmx_timer_handle)); + vmx_preemption_timer_rate = VMX_MISC_PREEMPTION_TIMER_RATE; + vmx_timer_active = 0; +#endif xapic = simulate_xapic; // xAPIC or legacy APIC @@ -219,6 +228,13 @@ void bx_local_apic_c::reset(unsigned type) timer_active = 0; } +#if BX_SUPPORT_VMX >= 2 + if(vmx_timer_active) { + bx_pc_system.deactivate_timer(vmx_timer_handle); + vmx_timer_active = 0; + } +#endif + for(i=0; i= 2 +Bit32u bx_local_apic_c::read_vmx_preemption_timer(void) +{ + Bit32u diff = (bx_pc_system.time_ticks() >> vmx_preemption_timer_rate) - (vmx_preemption_timer_initial >> vmx_preemption_timer_rate); + if (vmx_preemption_timer_value < diff) + return 0; + else + return vmx_preemption_timer_value - diff; +} + +void bx_local_apic_c::set_vmx_preemption_timer(Bit32u value) +{ + vmx_preemption_timer_value = value; + vmx_preemption_timer_initial = bx_pc_system.time_ticks(); + vmx_preemption_timer_fire = ((vmx_preemption_timer_initial >> vmx_preemption_timer_rate) + value) << vmx_preemption_timer_rate; + BX_DEBUG(("VMX Preemption timer. value = %u, rate = %u, init = %u, fire = %u", value, vmx_preemption_timer_rate, vmx_preemption_timer_initial, vmx_preemption_timer_fire)); + bx_pc_system.activate_timer_ticks(vmx_timer_handle, vmx_preemption_timer_fire - vmx_preemption_timer_initial, 0); + vmx_timer_active = 1; +} + +void bx_local_apic_c::deactivate_vmx_preemption_timer(void) +{ + if (! vmx_timer_active) return; + bx_pc_system.deactivate_timer(vmx_timer_handle); + vmx_timer_active = 0; +} + +// Invoked when VMX preemption timer expired +void bx_local_apic_c::vmx_preemption_timer_expired(void *this_ptr) +{ + bx_local_apic_c *class_ptr = (bx_local_apic_c *) this_ptr; + class_ptr->cpu->pending_vmx_timer_expired = 1; + class_ptr->cpu->async_event = 1; + class_ptr->deactivate_vmx_preemption_timer(); +} +#endif + #if BX_CPU_LEVEL >= 6 // return false when x2apic is not supported/not readable bx_bool bx_local_apic_c::read_x2apic(unsigned index, Bit64u *val_64) @@ -1142,7 +1195,7 @@ void bx_local_apic_c::register_state(bx_param_c *parent) unsigned i; char name[6]; - bx_list_c *lapic = new bx_list_c(parent, "local_apic", 25); + bx_list_c *lapic = new bx_list_c(parent, "local_apic", 31); BXRS_HEX_PARAM_SIMPLE(lapic, base_addr); BXRS_HEX_PARAM_SIMPLE(lapic, apic_id); @@ -1183,6 +1236,15 @@ void bx_local_apic_c::register_state(bx_param_c *parent) BXRS_PARAM_BOOL(lapic, timer_active, timer_active); BXRS_HEX_PARAM_SIMPLE(lapic, ticksInitial); BXRS_PARAM_BOOL(lapic, INTR, INTR); + +#if BX_SUPPORT_VMX >= 2 + BXRS_DEC_PARAM_SIMPLE(lapic, vmx_timer_handle); + BXRS_HEX_PARAM_SIMPLE(lapic, vmx_preemption_timer_initial); + BXRS_HEX_PARAM_SIMPLE(lapic, vmx_preemption_timer_fire); + BXRS_HEX_PARAM_SIMPLE(lapic, vmx_preemption_timer_value); + BXRS_HEX_PARAM_SIMPLE(lapic, vmx_preemption_timer_rate); + BXRS_PARAM_BOOL(lapic, vmx_timer_active, vmx_timer_active); +#endif } #endif /* if BX_SUPPORT_APIC */ diff --git a/bochs/cpu/apic.h b/bochs/cpu/apic.h index 7fd3cd2fd..8821894ee 100644 --- a/bochs/cpu/apic.h +++ b/bochs/cpu/apic.h @@ -113,6 +113,15 @@ class BOCHSAPI bx_local_apic_c : public logfunctions #define APIC_DM_SIPI 6 #define APIC_DM_EXTINT 7 +#if BX_SUPPORT_VMX >= 2 + int vmx_timer_handle; + Bit32u vmx_preemption_timer_value; + Bit64u vmx_preemption_timer_initial; //The value of system tick when set the timer (absolute value) + Bit64u vmx_preemption_timer_fire; //The value of system tick when fire the exception (absolute value) + Bit32u vmx_preemption_timer_rate; //rate stated in MSR_VMX_MISC + bx_bool vmx_timer_active; +#endif + BX_CPU_C *cpu; public: @@ -158,6 +167,12 @@ public: void set_initial_timer_count(Bit32u value); void startup_msg(Bit8u vector); void register_state(bx_param_c *parent); +#if BX_SUPPORT_VMX >= 2 + Bit32u read_vmx_preemption_timer(void); + void set_vmx_preemption_timer(Bit32u value); + void deactivate_vmx_preemption_timer(void); + static void vmx_preemption_timer_expired(void *); +#endif }; int apic_bus_deliver_lowest_priority(Bit8u vector, apic_dest_t dest, bx_bool trig_mode, bx_bool broadcast); diff --git a/bochs/cpu/cpu.cc b/bochs/cpu/cpu.cc index fb90c49e1..f2981ef84 100644 --- a/bochs/cpu/cpu.cc +++ b/bochs/cpu/cpu.cc @@ -421,8 +421,10 @@ unsigned BX_CPU_C::handleAsyncEvent(void) // an interrupt wakes up the CPU. while (1) { - if ((BX_CPU_INTR && (BX_CPU_THIS_PTR get_IF() || - (BX_CPU_THIS_PTR activity_state == BX_ACTIVITY_STATE_MWAIT_IF))) || + if ((BX_CPU_INTR && (BX_CPU_THIS_PTR get_IF() || BX_CPU_THIS_PTR activity_state == BX_ACTIVITY_STATE_MWAIT_IF)) || +#if BX_SUPPORT_VMX >= 2 + BX_CPU_THIS_PTR pending_vmx_timer_expired || +#endif BX_CPU_THIS_PTR pending_NMI || BX_CPU_THIS_PTR pending_SMI || BX_CPU_THIS_PTR pending_INIT) { // interrupt ends the HALT condition @@ -521,6 +523,16 @@ unsigned BX_CPU_C::handleAsyncEvent(void) if (BX_CPU_THIS_PTR debug_trap) exception(BX_DB_EXCEPTION, 0); // no error, not interrupt } + + // Priority 4.5: VMX Preemption Timer Expired. FIXME: is it a kind of external interrupt? +#if BX_SUPPORT_VMX >= 2 + if (BX_CPU_THIS_PTR in_vmx_guest) { + if (BX_CPU_THIS_PTR pending_vmx_timer_expired) { + BX_CPU_THIS_PTR pending_vmx_timer_expired = 0; + VMexit_PreemptionTimerExpired(); + } + } +#endif // Priority 5: External Interrupts // NMI Interrupts @@ -637,6 +649,9 @@ unsigned BX_CPU_C::handleAsyncEvent(void) #if BX_SUPPORT_VMX || BX_CPU_THIS_PTR vmx_interrupt_window || BX_CPU_THIS_PTR inhibit_mask #endif +#if BX_SUPPORT_VMX >= 2 + || BX_CPU_THIS_PTR pending_vmx_timer_expired +#endif #if BX_X86_DEBUGGER // a debug code breakpoint is set in current page || BX_CPU_THIS_PTR codebp diff --git a/bochs/cpu/cpu.h b/bochs/cpu/cpu.h index 348f6b244..fa5aed237 100644 --- a/bochs/cpu/cpu.h +++ b/bochs/cpu/cpu.h @@ -980,6 +980,9 @@ public: // for now... bx_bool in_smm_vmx; // save in_vmx and in_vmx_guest flags when in SMM mode bx_bool in_smm_vmx_guest; bx_bool vmx_interrupt_window; +#if BX_SUPPORT_VMX >= 2 + bx_bool pending_vmx_timer_expired; +#endif Bit64u vmcsptr; bx_hostpageaddr_t vmcshostptr; Bit64u vmxonptr; @@ -3673,6 +3676,7 @@ public: // for now... BX_SMF void VMexit_RDPMC(bxInstruction_c *i) BX_CPP_AttrRegparmN(1); #if BX_SUPPORT_VMX >= 2 BX_SMF void VMexit_WBINVD(bxInstruction_c *i) BX_CPP_AttrRegparmN(1); + BX_SMF void VMexit_PreemptionTimerExpired(void); #endif BX_SMF bx_bool VMexit_CLTS(bxInstruction_c *i) BX_CPP_AttrRegparmN(1); BX_SMF void VMexit_MSR(bxInstruction_c *i, unsigned op, Bit32u msr) BX_CPP_AttrRegparmN(3); diff --git a/bochs/cpu/ctrl_xfer32.cc b/bochs/cpu/ctrl_xfer32.cc index ab2d6c30d..f5fb0d53a 100644 --- a/bochs/cpu/ctrl_xfer32.cc +++ b/bochs/cpu/ctrl_xfer32.cc @@ -30,6 +30,38 @@ #define RIP EIP #endif +#undef BX_INSTRUMENTATION +#define BX_INSTRUMENTATION 1 + +void update_branch_stats(Bit32s offset, bx_bool taken) +{ + static Bit32u counts = 0; + static Bit32u taken_pos = 0, taken_neg = 0; + static Bit32u not_taken_pos = 0, not_taken_neg = 0; + + counts++; + + if (offset < 0) { + if (taken) taken_neg++; + else not_taken_neg++; + } + else { + if (taken) taken_pos++; + else not_taken_pos++; + } + + if (counts > 10000000) { + printf("======================\n"); + printf("negative: taken %f, not taken %f\n", taken_neg / 100000.0, not_taken_neg / 100000.0); + printf("positive: taken %f, not taken %f\n", taken_pos / 100000.0, not_taken_pos / 100000.0); + counts = 0; + taken_neg = 0; + not_taken_neg = 0; + taken_pos = 0; + not_taken_pos = 0; + } +} + #if BX_CPU_LEVEL >= 3 BX_CPP_INLINE void BX_CPP_AttrRegparmN(1) BX_CPU_C::branch_near32(Bit32u new_EIP) @@ -284,10 +316,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JO_Jd(bxInstruction_c *i) if (get_OF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -298,10 +332,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNO_Jd(bxInstruction_c *i) if (! get_OF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -312,10 +348,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JB_Jd(bxInstruction_c *i) if (get_CF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -326,10 +364,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNB_Jd(bxInstruction_c *i) if (! get_CF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -340,10 +380,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JZ_Jd(bxInstruction_c *i) if (get_ZF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -354,10 +396,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNZ_Jd(bxInstruction_c *i) if (! get_ZF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -368,10 +412,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JBE_Jd(bxInstruction_c *i) if (get_CF() || get_ZF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -382,10 +428,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNBE_Jd(bxInstruction_c *i) if (! (get_CF() || get_ZF())) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -396,10 +444,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JS_Jd(bxInstruction_c *i) if (get_SF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -410,10 +460,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNS_Jd(bxInstruction_c *i) if (! get_SF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -424,10 +476,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JP_Jd(bxInstruction_c *i) if (get_PF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -438,10 +492,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNP_Jd(bxInstruction_c *i) if (! get_PF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -452,10 +508,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JL_Jd(bxInstruction_c *i) if (getB_SF() != getB_OF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -466,10 +524,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNL_Jd(bxInstruction_c *i) if (getB_SF() == getB_OF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -480,10 +540,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JLE_Jd(bxInstruction_c *i) if (get_ZF() || (getB_SF() != getB_OF())) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -494,10 +556,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JNLE_Jd(bxInstruction_c *i) if (! get_ZF() && (getB_SF() == getB_OF())) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -646,10 +710,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::JECXZ_Jb(bxInstruction_c *i) if (temp_ECX == 0) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -677,10 +743,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::LOOPNE32_Jb(bxInstruction_c *i) if (count != 0 && (get_ZF()==0)) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -694,10 +762,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::LOOPNE32_Jb(bxInstruction_c *i) if (count != 0 && (get_ZF()==0)) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -718,10 +788,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::LOOPE32_Jb(bxInstruction_c *i) if (count != 0 && get_ZF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -735,10 +807,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::LOOPE32_Jb(bxInstruction_c *i) if (count != 0 && get_ZF()) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -759,10 +833,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::LOOP32_Jb(bxInstruction_c *i) if (count != 0) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif @@ -776,10 +852,12 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::LOOP32_Jb(bxInstruction_c *i) if (count != 0) { Bit32u new_EIP = EIP + (Bit32s) i->Id(); branch_near32(new_EIP); + update_branch_stats((Bit32s) i->Id(), 1); BX_INSTR_CNEAR_BRANCH_TAKEN(BX_CPU_ID, new_EIP); } #if BX_INSTRUMENTATION else { + update_branch_stats((Bit32s) i->Id(), 0); BX_INSTR_CNEAR_BRANCH_NOT_TAKEN(BX_CPU_ID); } #endif diff --git a/bochs/cpu/init.cc b/bochs/cpu/init.cc index 207016933..04879cf08 100644 --- a/bochs/cpu/init.cc +++ b/bochs/cpu/init.cc @@ -1035,6 +1035,9 @@ void BX_CPU_C::reset(unsigned source) BX_CPU_THIS_PTR in_smm_vmx = BX_CPU_THIS_PTR in_smm_vmx_guest = 0; BX_CPU_THIS_PTR in_event = 0; BX_CPU_THIS_PTR vmx_interrupt_window = 0; +#if BX_SUPPORT_VMX >= 2 + BX_CPU_THIS_PTR pending_vmx_timer_expired = 0; +#endif BX_CPU_THIS_PTR vmcsptr = BX_CPU_THIS_PTR vmxonptr = BX_INVALID_VMCSPTR; BX_CPU_THIS_PTR vmcshostptr = 0; /* enable VMX, should be done in BIOS instead */ diff --git a/bochs/cpu/vmcs.cc b/bochs/cpu/vmcs.cc index b7e657a55..a60abef4c 100644 --- a/bochs/cpu/vmcs.cc +++ b/bochs/cpu/vmcs.cc @@ -175,6 +175,9 @@ bx_bool BX_CPU_C::vmcs_field_supported(Bit32u encoding) case VMCS_32BIT_GUEST_ACTIVITY_STATE: case VMCS_32BIT_GUEST_SMBASE: case VMCS_32BIT_GUEST_IA32_SYSENTER_CS_MSR: +#if BX_SUPPORT_VMX >= 2 + case VMCS_32BIT_GUEST_PREEMPTION_TIMER_VALUE: +#endif return 1; /* VMCS 32-bit host-state fields */ diff --git a/bochs/cpu/vmexit.cc b/bochs/cpu/vmexit.cc index 60799fdb9..8df06cca5 100644 --- a/bochs/cpu/vmexit.cc +++ b/bochs/cpu/vmexit.cc @@ -240,6 +240,18 @@ void BX_CPU_C::VMexit_ExtInterrupt(void) } } +#if BX_SUPPORT_VMX >= 2 +void BX_CPU_C::VMexit_PreemptionTimerExpired(void) +{ + if (! BX_CPU_THIS_PTR in_vmx_guest) return; + + if (PIN_VMEXIT(VMX_VM_EXEC_CTRL1_VMX_PREEMPTION_TIMER_VMEXIT)) { + BX_DEBUG(("VMEXIT: VMX Preemption Timer Expired")); + VMexit(0, VMX_VMEXIT_VMX_PREEMPTION_TIMER_EXPIRED, 0); + } +} +#endif + void BX_CPU_C::VMexit_Event(bxInstruction_c *i, unsigned type, unsigned vector, Bit16u errcode, bx_bool errcode_valid, Bit64u qualification) { if (! BX_CPU_THIS_PTR in_vmx_guest) return; diff --git a/bochs/cpu/vmx.cc b/bochs/cpu/vmx.cc index b2baf812a..fce3bb31c 100644 --- a/bochs/cpu/vmx.cc +++ b/bochs/cpu/vmx.cc @@ -222,6 +222,12 @@ void BX_CPU_C::VMabort(VMX_vmabort_code error_code) bx_phy_address pAddr = BX_CPU_THIS_PTR vmcsptr + VMCS_VMX_ABORT_FIELD_ADDR; access_write_physical(pAddr, 4, &abort); BX_DBG_PHY_MEMORY_ACCESS(BX_CPU_ID, pAddr, 4, BX_VMCS_ACCESS | BX_WRITE, (Bit8u*)(&abort)); + +#if BX_SUPPORT_VMX >= 2 + // Deactivate VMX preemtion timer + BX_CPU_THIS_PTR lapic.deactivate_vmx_preemption_timer(); +#endif + shutdown(); } @@ -470,6 +476,13 @@ VMX_error_code BX_CPU_C::VMenterLoadCheckVmControls(void) return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD; } +#if BX_SUPPORT_VMX >= 2 + if ((~vm->vmexec_ctrls1 & VMX_VM_EXEC_CTRL1_VMX_PREEMPTION_TIMER_VMEXIT) && (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER)) { + BX_ERROR(("VMFAIL: save_VMX_preemption_timer VMEXIT control is set but VMX_preemption_timer VMEXEC control is clear")); + return VMXERR_VMENTRY_INVALID_VM_CONTROL_FIELD; + } +#endif + if (vm->vmexit_msr_store_cnt > 0) { vm->vmexit_msr_store_addr = VMread64(VMCS_64BIT_CONTROL_VMEXIT_MSR_STORE_ADDR); if ((vm->vmexit_msr_store_addr & 0xf) != 0 || ! IsValidPhyAddr(vm->vmexit_msr_store_addr)) { @@ -1852,6 +1865,13 @@ void BX_CPU_C::VMexitSaveGuestState(void) VMwrite32(VMCS_32BIT_CONTROL_VMENTRY_CONTROLS, vm->vmentry_ctrls); } + + // Deactivate VMX preemtion timer + BX_CPU_THIS_PTR lapic.deactivate_vmx_preemption_timer(); + BX_CPU_THIS_PTR pending_vmx_timer_expired = 0; + // Store back to VMCS + if (vm->vmexit_ctrls & VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER) + VMwrite32(VMCS_32BIT_GUEST_PREEMPTION_TIMER_VALUE, BX_CPU_THIS_PTR lapic.read_vmx_preemption_timer()); #endif } @@ -2461,6 +2481,22 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::VMLAUNCH(bxInstruction_c *i) BX_CPU_THIS_PTR in_vmx_guest = 1; BX_CPU_THIS_PTR disable_INIT = 0; +#if BX_SUPPORT_VMX >= 2 + if (PIN_VMEXIT(VMX_VM_EXEC_CTRL1_VMX_PREEMPTION_TIMER_VMEXIT)) { + Bit32u timer_value = VMread32(VMCS_32BIT_GUEST_PREEMPTION_TIMER_VALUE); + if (timer_value == 0) { + BX_CPU_THIS_PTR pending_vmx_timer_expired = 1; + BX_CPU_THIS_PTR async_event = 1; + } + else { + // activate VMX preemption timer + BX_INFO(("VMX preemption timer active")); + BX_CPU_THIS_PTR pending_vmx_timer_expired = 0; + BX_CPU_THIS_PTR lapic.set_vmx_preemption_timer(timer_value); + } + } +#endif + /////////////////////////////////////////////////////// // STEP 7: Inject events to the guest /////////////////////////////////////////////////////// @@ -2920,7 +2956,7 @@ void BX_CPP_AttrRegparmN(1) BX_CPU_C::INVVPID(bxInstruction_c *i) void BX_CPU_C::register_vmx_state(bx_param_c *parent) { // register VMX state for save/restore param tree - bx_list_c *vmx = new bx_list_c(parent, "VMX", 8); + bx_list_c *vmx = new bx_list_c(parent, "VMX", 9); BXRS_HEX_PARAM_FIELD(vmx, vmcsptr, BX_CPU_THIS_PTR vmcsptr); BXRS_HEX_PARAM_FIELD(vmx, vmxonptr, BX_CPU_THIS_PTR vmxonptr); @@ -2929,6 +2965,9 @@ void BX_CPU_C::register_vmx_state(bx_param_c *parent) BXRS_PARAM_BOOL(vmx, in_smm_vmx, BX_CPU_THIS_PTR in_smm_vmx); BXRS_PARAM_BOOL(vmx, in_smm_vmx_guest, BX_CPU_THIS_PTR in_smm_vmx_guest); BXRS_PARAM_BOOL(vmx, vmx_interrupt_window, BX_CPU_THIS_PTR vmx_interrupt_window); +#if BX_SUPPORT_VMX >= 2 + BXRS_PARAM_BOOL(vmx, pending_vmx_timer_expired, BX_CPU_THIS_PTR pending_vmx_timer_expired); +#endif bx_list_c *vmcache = new bx_list_c(vmx, "VMCS_CACHE", 5); diff --git a/bochs/cpu/vmx.h b/bochs/cpu/vmx.h index 0cd864083..614827293 100644 --- a/bochs/cpu/vmx.h +++ b/bochs/cpu/vmx.h @@ -123,7 +123,7 @@ enum VMX_vmexit_reason { VMX_VMEXIT_EPT_MISCONFIGURATION = 49, VMX_VMEXIT_INVEPT = 50, VMX_VMEXIT_RDTSCP = 51, - VMX_VMEXIT_VMX_PREEMTION_TIMER_FIRED = 52, + VMX_VMEXIT_VMX_PREEMPTION_TIMER_EXPIRED = 52, VMX_VMEXIT_INVVPID = 53, VMX_VMEXIT_WBINVD = 54, VMX_VMEXIT_XSETBV = 55, @@ -305,6 +305,7 @@ enum VMX_vmabort_code { #define VMCS_32BIT_GUEST_ACTIVITY_STATE 0x00004826 #define VMCS_32BIT_GUEST_SMBASE 0x00004828 #define VMCS_32BIT_GUEST_IA32_SYSENTER_CS_MSR 0x0000482A +#define VMCS_32BIT_GUEST_PREEMPTION_TIMER_VALUE 0x0000482E /* VMCS 32-bit host-state fields */ /* binary 0100_11xx_xxxx_xxx0 */ @@ -368,7 +369,7 @@ enum VMX_vmabort_code { #define VMCS_HOST_RSP 0x00006C14 #define VMCS_HOST_RIP 0x00006C16 -#define VMX_HIGHEST_VMCS_ENCODING (0x2C) +#define VMX_HIGHEST_VMCS_ENCODING (0x2F) // =============================== // VMCS fields encoding/decoding @@ -504,10 +505,10 @@ typedef struct bx_VMCS // VM-Execution Control Fields // -#define VMX_VM_EXEC_CTRL1_EXTERNAL_INTERRUPT_VMEXIT (1 << 0) -#define VMX_VM_EXEC_CTRL1_NMI_VMEXIT (1 << 3) -#define VMX_VM_EXEC_CTRL1_VIRTUAL_NMI (1 << 5) -#define VMX_VM_EXEC_CTRL1_VMX_PREEMPTION_TIMER (1 << 6) +#define VMX_VM_EXEC_CTRL1_EXTERNAL_INTERRUPT_VMEXIT (1 << 0) +#define VMX_VM_EXEC_CTRL1_NMI_VMEXIT (1 << 3) +#define VMX_VM_EXEC_CTRL1_VIRTUAL_NMI (1 << 5) +#define VMX_VM_EXEC_CTRL1_VMX_PREEMPTION_TIMER_VMEXIT (1 << 6) #ifdef BX_VMX_ENABLE_ALL @@ -517,7 +518,8 @@ typedef struct bx_VMCS #define VMX_VM_EXEC_CTRL1_SUPPORTED_BITS \ (VMX_VM_EXEC_CTRL1_EXTERNAL_INTERRUPT_VMEXIT | \ - VMX_VM_EXEC_CTRL1_NMI_VMEXIT) + VMX_VM_EXEC_CTRL1_NMI_VMEXIT | \ + ((BX_SUPPORT_VMX >= 2) ? VMX_VM_EXEC_CTRL1_VMX_PREEMPTION_TIMER_VMEXIT : 0)) #endif @@ -662,7 +664,8 @@ typedef struct bx_VMCS ((BX_SUPPORT_VMX >= 2) ? VMX_VMEXIT_CTRL1_STORE_PAT_MSR : 0) | \ ((BX_SUPPORT_VMX >= 2) ? VMX_VMEXIT_CTRL1_LOAD_PAT_MSR : 0) | \ ((BX_SUPPORT_VMX >= 2) ? VMX_VMEXIT_CTRL1_STORE_EFER_MSR : 0) | \ - ((BX_SUPPORT_VMX >= 2) ? VMX_VMEXIT_CTRL1_LOAD_EFER_MSR : 0)) + ((BX_SUPPORT_VMX >= 2) ? VMX_VMEXIT_CTRL1_LOAD_EFER_MSR : 0) | \ + ((BX_SUPPORT_VMX >= 2) ? VMX_VMEXIT_CTRL1_STORE_VMX_PREEMPTION_TIMER : 0)) #endif @@ -930,8 +933,12 @@ enum VMX_Activity_State { #define VMX_MISC_STORE_LMA_TO_X86_64_GUEST_VMENTRY_CONTROL (0) #endif +//Rate to increase VMX preemtion timer +#define VMX_MISC_PREEMPTION_TIMER_RATE (0) + #define VMX_MSR_MISC ((VMX_CR3_TARGET_MAX_CNT << 16) | \ - VMX_MISC_STORE_LMA_TO_X86_64_GUEST_VMENTRY_CONTROL) + VMX_MISC_STORE_LMA_TO_X86_64_GUEST_VMENTRY_CONTROL | \ + VMX_MISC_PREEMPTION_TIMER_RATE) // // IA32_VMX_CR0_FIXED0 MSR (0x486) IA32_VMX_CR0_FIXED1 MSR (0x487)