implemented TSC-Deadline APIC timer mode

This commit is contained in:
Stanislav Shwartsman 2011-11-21 12:51:50 +00:00
parent 76b742bd93
commit c74f590077
7 changed files with 155 additions and 45 deletions

View File

@ -8,7 +8,7 @@ Brief summary :
- 10% (ST) to 50% (SMP) CPU emulation speedup !
- Implemented support for new x86 ISA extensions, Bochs is aligned with
latest published Intel Architecture Manual (rev 040, AVX rev 011):
- XSAVEOPT, AVX/AVX2/FMA/F16C, BMI1/BMI2, SMEP, INVPCID
- XSAVEOPT, AVX/AVX2/FMA/F16C, BMI1/BMI2, SMEP, INVPCID, TSC-Deadline
- VMX: VMX Preemption Timer, Pause Loop Exiting and VM Functions
- Implemented support for AMD SSE4A/XOP/FMA4/TBM instruction sets
- Networking: introduced new networking module 'slirp'
@ -64,6 +64,7 @@ Detailed change log :
- Implemented Pause-Loop Exiting Secondary VMEXIT control.
- Implemented VM Functions support and EPTP-Switching VM Function.
- Added INVPCID instruction emulation support.
- Added APIC timer TSC-Deadline mode emulation support.
- Now you could disable x86-64 from .bochsrc so it become possible to
emulate 32-bit CPUs using Bochs binary compiled with x86-64 support.
- Updated/fixed instrumentation callbacks.

View File

@ -222,6 +222,7 @@ void bx_local_apic_c::reset(unsigned type)
timer_divide_factor = 1;
timer_initial = 0;
timer_current = 0;
ticksInitial = 0;
if(timer_active) {
bx_pc_system.deactivate_timer(timer_handle);
@ -408,20 +409,8 @@ void bx_local_apic_c::write_aligned(bx_phy_address addr, Bit32u value)
case BX_LAPIC_LVT_LINT0: // LVT LINT0 Reg
case BX_LAPIC_LVT_LINT1: // LVT LINT1 Reg
case BX_LAPIC_LVT_ERROR: // LVT Error Reg
{
static Bit32u lvt_mask[] = {
0x000310ff, /* TIMER */
0x000117ff, /* THERMAL */
0x000117ff, /* PERFMON */
0x0001f7ff, /* LINT0 */
0x0001f7ff, /* LINT1 */
0x000110ff /* ERROR */
};
unsigned lvt_entry = (apic_reg - BX_LAPIC_LVT_TIMER) >> 4;
lvt[lvt_entry] = value & lvt_mask[lvt_entry];
if(! software_enabled) lvt[lvt_entry] |= 0x10000;
set_lvt_entry(apic_reg, value);
break;
}
case BX_LAPIC_TIMER_INITIAL_COUNT:
set_initial_timer_count(value);
break;
@ -462,6 +451,41 @@ void bx_local_apic_c::write_aligned(bx_phy_address addr, Bit32u value)
}
}
void bx_local_apic_c::set_lvt_entry(unsigned apic_reg, Bit32u value)
{
static Bit32u lvt_mask[] = {
0x000710ff, /* TIMER */
0x000117ff, /* THERMAL */
0x000117ff, /* PERFMON */
0x0001f7ff, /* LINT0 */
0x0001f7ff, /* LINT1 */
0x000110ff /* ERROR */
};
unsigned lvt_entry = (apic_reg - BX_LAPIC_LVT_TIMER) >> 4;
#if BX_CPU_LEVEL >= 6
if (apic_reg == BX_LAPIC_LVT_TIMER) {
if (! cpu->bx_cpuid_support_tsc_deadline()) {
value &= ~0x40000; // cannot enable TSC-Deadline when not supported
}
else {
if ((value ^ lvt[lvt_entry]) & 0x40000) {
// Transition between TSC-Deadline and other timer modes disarm the timer
if(timer_active) {
bx_pc_system.deactivate_timer(timer_handle);
timer_active = 0;
}
}
}
}
#endif
lvt[lvt_entry] = value & lvt_mask[lvt_entry];
if(! software_enabled) {
lvt[lvt_entry] |= 0x10000;
}
}
void bx_local_apic_c::send_ipi(apic_dest_t dest, Bit32u lo_cmd)
{
int dest_shorthand = (lo_cmd >> 18) & 3;
@ -654,16 +678,7 @@ Bit32u bx_local_apic_c::read_aligned(bx_phy_address addr)
data = timer_initial;
break;
case BX_LAPIC_TIMER_CURRENT_COUNT: // current count for timer
if(timer_active==0) {
data = timer_current;
} else {
Bit64u delta64 = (bx_pc_system.time_ticks() - ticksInitial) / timer_divide_factor;
Bit32u delta32 = (Bit32u) delta64;
if(delta32 > timer_initial)
BX_PANIC(("APIC: R(curr timer count): delta < initial"));
timer_current = timer_initial - delta32;
data = timer_current;
}
data = get_current_timer_count();
break;
case BX_LAPIC_TIMER_DIVIDE_CFG: // timer divide configuration
data = timer_divconf;
@ -924,10 +939,8 @@ void bx_local_apic_c::periodic(void)
return;
}
// timer reached zero since the last call to periodic.
Bit32u timervec = lvt[APIC_LVT_TIMER];
if(timervec & 0x20000) {
// Periodic mode.
// If timer is not masked, trigger interrupt.
if((timervec & 0x10000)==0) {
trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
@ -935,21 +948,17 @@ void bx_local_apic_c::periodic(void)
else {
BX_DEBUG(("local apic timer LVT masked"));
}
// Reload timer values.
// timer reached zero since the last call to periodic.
if(timervec & 0x20000) {
// Periodic mode - reload timer values
timer_current = timer_initial;
ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
ticksInitial = bx_pc_system.time_ticks(); // timer value when it started to count
BX_DEBUG(("local apic timer(periodic) triggered int, reset counter to 0x%08x", timer_current));
}
else {
// one-shot mode
timer_current = 0;
// If timer is not masked, trigger interrupt.
if((timervec & 0x10000)==0) {
trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
}
else {
BX_DEBUG(("local apic timer LVT masked"));
}
timer_active = 0;
BX_DEBUG(("local apic timer(one-shot) triggered int"));
bx_pc_system.deactivate_timer(timer_handle);
@ -968,6 +977,13 @@ void bx_local_apic_c::set_divide_configuration(Bit32u value)
void bx_local_apic_c::set_initial_timer_count(Bit32u value)
{
Bit32u timervec = lvt[APIC_LVT_TIMER];
#if BX_CPU_LEVEL >= 6
// in TSC-deadline mode writes to initial time count are ignored
if (timervec & 0x40000) return;
#endif
// If active before, deactivate the current timer before changing it.
if(timer_active) {
bx_pc_system.deactivate_timer(timer_handle);
@ -984,14 +1000,70 @@ void bx_local_apic_c::set_initial_timer_count(Bit32u value)
BX_DEBUG(("APIC: Initial Timer Count Register = %u", value));
timer_current = timer_initial;
timer_active = 1;
Bit32u timervec = lvt[APIC_LVT_TIMER];
bx_bool continuous = (timervec & 0x20000) > 0;
ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
ticksInitial = bx_pc_system.time_ticks(); // timer value when it started to count
bx_pc_system.activate_timer_ticks(timer_handle,
Bit64u(timer_initial) * Bit64u(timer_divide_factor), continuous);
}
}
Bit32u bx_local_apic_c::get_current_timer_count(void)
{
#if BX_CPU_LEVEL >= 6
Bit32u timervec = lvt[APIC_LVT_TIMER];
// in TSC-deadline mode current timer count always reads 0
if (timervec & 0x40000) return 0;
#endif
if(timer_active==0) {
return timer_current;
} else {
Bit64u delta64 = (bx_pc_system.time_ticks() - ticksInitial) / timer_divide_factor;
Bit32u delta32 = (Bit32u) delta64;
if(delta32 > timer_initial)
BX_PANIC(("APIC: R(curr timer count): delta < initial"));
timer_current = timer_initial - delta32;
return timer_current;
}
}
#if BX_CPU_LEVEL >= 6
void bx_local_apic_c::set_tsc_deadline(Bit64u deadline)
{
Bit32u timervec = lvt[APIC_LVT_TIMER];
if ((timervec & 0x40000) == 0) {
BX_ERROR(("APIC: TSC-Deadline timer is disabled"));
return;
}
// If active before, deactivate the current timer before changing it.
if(timer_active) {
bx_pc_system.deactivate_timer(timer_handle);
timer_active = 0;
}
ticksInitial = deadline;
if (deadline != 0) {
BX_INFO(("APIC: TSC-Deadline is set to " FMT_LL "d", deadline));
Bit64u currtime = bx_pc_system.time_ticks();
timer_active = 1;
bx_pc_system.activate_timer_ticks(timer_handle, (deadline > currtime) ? (deadline - currtime) : 1 , 0);
}
}
Bit64u bx_local_apic_c::get_tsc_deadline(void)
{
Bit32u timervec = lvt[APIC_LVT_TIMER];
// read as zero if TSC-deadline timer is disabled
if ((timervec & 0x40000) == 0) return 0;
return ticksInitial; /* also holds TSC-deadline value */
}
#endif
#if BX_SUPPORT_VMX >= 2
Bit32u bx_local_apic_c::read_vmx_preemption_timer(void)
{

View File

@ -92,8 +92,9 @@ class BOCHSAPI bx_local_apic_c : public logfunctions
#define APIC_LVT_LINT1 4
#define APIC_LVT_ERROR 5
Bit32u timer_initial; // Initial timer count
Bit32u timer_current; // current timer count
Bit32u timer_initial; // Initial timer count (in order to reload periodic timer)
Bit32u timer_current; // Current timer count
Bit64u ticksInitial; // Timer value when it started to count, also holds TSC-Deadline value
Bit32u timer_divconf; // Timer divide configuration register
Bit32u timer_divide_factor;
@ -101,7 +102,6 @@ class BOCHSAPI bx_local_apic_c : public logfunctions
// Internal timer state, not accessible from bus
bx_bool timer_active;
int timer_handle;
Bit64u ticksInitial;
/* APIC delivery modes */
#define APIC_DM_FIXED 0
@ -161,10 +161,19 @@ public:
Bit8u get_ppr(void);
Bit8u get_apr(void);
bx_bool is_focus(Bit8u vector);
void set_lvt_entry(unsigned apic_reg, Bit32u val);
static void periodic_smf(void *);
void periodic(void);
void set_divide_configuration(Bit32u value);
void set_initial_timer_count(Bit32u value);
Bit32u get_current_timer_count(void);
#if BX_CPU_LEVEL >= 6
Bit64u get_tsc_deadline(void);
void set_tsc_deadline(Bit64u value);
#endif
void startup_msg(Bit8u vector);
void register_state(bx_param_c *parent);
#if BX_SUPPORT_VMX >= 2

View File

@ -379,6 +379,8 @@ struct BxExceptionInfo {
#define BX_MSR_MTRR_DEFTYPE 0x2ff
#endif
#define BX_MSR_TSC_DEADLINE 0x6E0
#define BX_MSR_MAX_INDEX 0x1000
enum {
@ -4025,6 +4027,7 @@ public: // for now...
BX_SMF BX_CPP_INLINE int bx_cpuid_support_smx(void);
BX_SMF BX_CPP_INLINE int bx_cpuid_support_vmx(void);
BX_SMF BX_CPP_INLINE int bx_cpuid_support_rdtscp(void);
BX_SMF BX_CPP_INLINE int bx_cpuid_support_tsc_deadline(void);
BX_SMF BX_CPP_INLINE unsigned which_cpu(void) { return BX_CPU_THIS_PTR bx_cpuid; }
#if BX_DEBUGGER || BX_SUPPORT_HANDLERS_CHAINING_SPEEDUPS || BX_SUPPORT_SMP
@ -4585,6 +4588,11 @@ BX_CPP_INLINE int BX_CPU_C::bx_cpuid_support_rdtscp(void)
#endif
}
BX_CPP_INLINE int BX_CPU_C::bx_cpuid_support_tsc_deadline(void)
{
return (BX_CPU_THIS_PTR cpu_extensions_bitmask & BX_CPU_TSC_DEADLINE);
}
IMPLEMENT_EFLAG_ACCESSOR (ID, 21)
IMPLEMENT_EFLAG_ACCESSOR (VIP, 20)
IMPLEMENT_EFLAG_ACCESSOR (VIF, 19)

View File

@ -44,8 +44,6 @@ corei7_sandy_bridge_2600k_t::corei7_sandy_bridge_2600k_t(BX_CPU_C *cpu): bx_cpui
if (! BX_SUPPORT_AVX)
BX_PANIC(("You must enable AVX for Intel Core i7 Sandy Bridge configuration"));
BX_INFO(("WARNING: TSC deadline is not implemented yet"));
if (BX_SUPPORT_VMX == 1)
BX_INFO(("You must compile with --enable-vmx=2 for Intel Core i7 Sandy Bridge VMX configuration"));
}
@ -166,7 +164,8 @@ Bit32u corei7_sandy_bridge_2600k_t::get_cpu_extensions_bitmask(void) const
/* BX_CPU_X2APIC | */
BX_CPU_LONG_MODE |
BX_CPU_NX |
BX_CPU_PCID;
BX_CPU_PCID |
BX_CPU_TSC_DEADLINE;
}
#if BX_SUPPORT_VMX >= 2
@ -322,7 +321,7 @@ void corei7_sandy_bridge_2600k_t::get_std_cpuid_leaf_1(cpuid_function_t *leaf) c
BX_CPUID_EXT_SSE4_2 |
/* BX_CPUID_EXT_X2APIC | */
BX_CPUID_EXT_POPCNT |
// BX_CPUID_EXT_TSC_DEADLINE | // not implemented yet
BX_CPUID_EXT_TSC_DEADLINE |
BX_CPUID_EXT_AES |
BX_CPUID_EXT_XSAVE |
BX_CPUID_EXT_AVX;

View File

@ -125,6 +125,7 @@ typedef bx_cpuid_t* (*bx_create_cpuid_method)(BX_CPU_C *cpu);
#define BX_CPU_SMEP (1 << 14) /* SMEP support */
#define BX_CPU_FFXSR (1 << 15) /* EFER.FFXSR support */
#define BX_CPU_ALT_MOV_CR8 (1 << 16) /* LOCK CR0 access CR8 */
#define BX_CPU_TSC_DEADLINE (1 << 17) /* TSC-Deadline */
// cpuid VMX features
#define BX_VMX_TPR_SHADOW (1 << 0) /* TPR shadow */

View File

@ -136,6 +136,16 @@ bx_bool BX_CPP_AttrRegparmN(2) BX_CPU_C::rdmsr(Bit32u index, Bit64u *msr)
break;
#endif
#if BX_CPU_LEVEL >= 6
case BX_MSR_TSC_DEADLINE:
if (! bx_cpuid_support_tsc_deadline()) {
BX_ERROR(("RDMSR BX_MSR_TSC_DEADLINE: TSC-Deadline not enabled !"));
return handle_unknown_rdmsr(index, msr);
}
val64 = BX_CPU_THIS_PTR lapic.get_tsc_deadline();
break;
#endif
#if BX_SUPPORT_VMX
/*
case BX_MSR_IA32_SMM_MONITOR_CTL:
@ -584,6 +594,16 @@ bx_bool BX_CPP_AttrRegparmN(2) BX_CPU_C::wrmsr(Bit32u index, Bit64u val_64)
return relocate_apic(val_64);
#endif
#if BX_CPU_LEVEL >= 6
case BX_MSR_TSC_DEADLINE:
if (! bx_cpuid_support_tsc_deadline()) {
BX_ERROR(("WRMSR BX_MSR_TSC_DEADLINE: TSC-Deadline not enabled !"));
return handle_unknown_wrmsr(index, val_64);
}
BX_CPU_THIS_PTR lapic.set_tsc_deadline(val_64);
break;
#endif
#if BX_SUPPORT_VMX
// Support only two bits: lock bit (bit 0) and VMX enable (bit 2)
case BX_MSR_IA32_FEATURE_CONTROL: