aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@amd.com>2009-09-19 00:30:49 +0200
committerAurelien Jarno <aurelien@aurel32.net>2009-10-04 14:46:34 +0200
commit1b050077d2d72b15c73257c13e2e46932786f7e2 (patch)
treedc42c285ca15dc1901ef7f6ae268d583521300d5
parentd9f4bb27dbff2e40ec2e36eb8017c9dedce77f30 (diff)
target-i386: add RDTSCP support
RDTSCP reads the time stamp counter and atomically also the content of a 32-bit MSR, which can be freely set by the OS. This allows CPU local data to be queried by userspace. Linux uses this to allow a fast implementation of the getcpu() syscall, which uses the vsyscall page to avoid a context switch. AMD CPUs since K8RevF and Intel CPUs since Nehalem support this instruction. RDTSCP is guarded by the RDTSCP CPUID bit (Fn8000_0001:EDX[27]). Signed-off-by: Andre Przywara <andre.przywara@amd.com> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
-rw-r--r--target-i386/cpu.h5
-rw-r--r--target-i386/helper.h1
-rw-r--r--target-i386/machine.c4
-rw-r--r--target-i386/op_helper.c12
-rw-r--r--target-i386/translate.c57
5 files changed, 63 insertions, 16 deletions
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index b9a6392cf1..f318942eec 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -322,6 +322,7 @@
#define MSR_FSBASE 0xc0000100
#define MSR_GSBASE 0xc0000101
#define MSR_KERNELGSBASE 0xc0000102
+#define MSR_TSC_AUX 0xc0000103
#define MSR_VM_HSAVE_PA 0xc0010117
@@ -694,6 +695,8 @@ typedef struct CPUX86State {
uint64 mcg_status;
uint64 mcg_ctl;
uint64 *mce_banks;
+
+ uint64_t tsc_aux;
} CPUX86State;
CPUX86State *cpu_x86_init(const char *cpu_model);
@@ -854,7 +857,7 @@ uint64_t cpu_get_tsc(CPUX86State *env);
#define cpu_signal_handler cpu_x86_signal_handler
#define cpu_list x86_cpu_list
-#define CPU_SAVE_VERSION 10
+#define CPU_SAVE_VERSION 11
/* MMU modes definitions */
#define MMU_MODE0_SUFFIX _kernel
diff --git a/target-i386/helper.h b/target-i386/helper.h
index 68d57b130a..ca953f47d9 100644
--- a/target-i386/helper.h
+++ b/target-i386/helper.h
@@ -80,6 +80,7 @@ DEF_HELPER_1(cmpxchg16b, void, tl)
DEF_HELPER_0(single_step, void)
DEF_HELPER_0(cpuid, void)
DEF_HELPER_0(rdtsc, void)
+DEF_HELPER_0(rdtscp, void)
DEF_HELPER_0(rdpmc, void)
DEF_HELPER_0(rdmsr, void)
DEF_HELPER_0(wrmsr, void)
diff --git a/target-i386/machine.c b/target-i386/machine.c
index ab31329024..e5a060fcc2 100644
--- a/target-i386/machine.c
+++ b/target-i386/machine.c
@@ -171,6 +171,7 @@ void cpu_save(QEMUFile *f, void *opaque)
qemu_put_be64s(f, &env->mce_banks[4*i + 3]);
}
}
+ qemu_put_be64s(f, &env->tsc_aux);
}
#ifdef USE_X86LDOUBLE
@@ -377,6 +378,9 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
}
}
+ if (version_id >= 11) {
+ qemu_get_be64s(f, &env->tsc_aux);
+ }
/* XXX: ensure compatiblity for halted bit ? */
/* XXX: compute redundant hflags bits */
env->hflags = hflags;
diff --git a/target-i386/op_helper.c b/target-i386/op_helper.c
index 33d44b0037..ef0acfcf0b 100644
--- a/target-i386/op_helper.c
+++ b/target-i386/op_helper.c
@@ -2969,6 +2969,12 @@ void helper_rdtsc(void)
EDX = (uint32_t)(val >> 32);
}
+void helper_rdtscp(void)
+{
+ helper_rdtsc();
+ ECX = (uint32_t)(env->tsc_aux);
+}
+
void helper_rdpmc(void)
{
if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) {
@@ -3107,6 +3113,9 @@ void helper_wrmsr(void)
&& (val == 0 || val == ~(uint64_t)0))
env->mcg_ctl = val;
break;
+ case MSR_TSC_AUX:
+ env->tsc_aux = val;
+ break;
default:
if ((uint32_t)ECX >= MSR_MC0_CTL
&& (uint32_t)ECX < MSR_MC0_CTL + (4 * env->mcg_cap & 0xff)) {
@@ -3177,6 +3186,9 @@ void helper_rdmsr(void)
case MSR_KERNELGSBASE:
val = env->kernelgsbase;
break;
+ case MSR_TSC_AUX:
+ val = env->tsc_aux;
+ break;
#endif
case MSR_MTRRphysBase(0):
case MSR_MTRRphysBase(1):
diff --git a/target-i386/translate.c b/target-i386/translate.c
index 66aa21bb65..82ee3d50f1 100644
--- a/target-i386/translate.c
+++ b/target-i386/translate.c
@@ -7206,31 +7206,58 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_eob(s);
}
break;
- case 7: /* invlpg */
- if (s->cpl != 0) {
- gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ case 7:
+ if (mod != 3) { /* invlpg */
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ if (s->cc_op != CC_OP_DYNAMIC)
+ gen_op_set_cc_op(s->cc_op);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
+ gen_helper_invlpg(cpu_A0);
+ gen_jmp_im(s->pc - s->cs_base);
+ gen_eob(s);
+ }
} else {
- if (mod == 3) {
+ switch (rm) {
+ case 0: /* swapgs */
#ifdef TARGET_X86_64
- if (CODE64(s) && rm == 0) {
- /* swapgs */
- tcg_gen_ld_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,segs[R_GS].base));
- tcg_gen_ld_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,kernelgsbase));
- tcg_gen_st_tl(cpu_T[1], cpu_env, offsetof(CPUX86State,segs[R_GS].base));
- tcg_gen_st_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,kernelgsbase));
+ if (CODE64(s)) {
+ if (s->cpl != 0) {
+ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
+ } else {
+ tcg_gen_ld_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,segs[R_GS].base));
+ tcg_gen_ld_tl(cpu_T[1], cpu_env,
+ offsetof(CPUX86State,kernelgsbase));
+ tcg_gen_st_tl(cpu_T[1], cpu_env,
+ offsetof(CPUX86State,segs[R_GS].base));
+ tcg_gen_st_tl(cpu_T[0], cpu_env,
+ offsetof(CPUX86State,kernelgsbase));
+ }
} else
#endif
{
goto illegal_op;
}
- } else {
+ break;
+ case 1: /* rdtscp */
+ if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP))
+ goto illegal_op;
if (s->cc_op != CC_OP_DYNAMIC)
gen_op_set_cc_op(s->cc_op);
gen_jmp_im(pc_start - s->cs_base);
- gen_lea_modrm(s, modrm, &reg_addr, &offset_addr);
- gen_helper_invlpg(cpu_A0);
- gen_jmp_im(s->pc - s->cs_base);
- gen_eob(s);
+ if (use_icount)
+ gen_io_start();
+ gen_helper_rdtscp();
+ if (use_icount) {
+ gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
+ }
+ break;
+ default:
+ goto illegal_op;
}
}
break;