diff options
author | Andre Przywara <andre.przywara@amd.com> | 2009-09-19 00:30:49 +0200 |
---|---|---|
committer | Aurelien Jarno <aurelien@aurel32.net> | 2009-10-04 14:46:34 +0200 |
commit | 1b050077d2d72b15c73257c13e2e46932786f7e2 (patch) | |
tree | dc42c285ca15dc1901ef7f6ae268d583521300d5 /target-i386 | |
parent | d9f4bb27dbff2e40ec2e36eb8017c9dedce77f30 (diff) | |
download | qemu-1b050077d2d72b15c73257c13e2e46932786f7e2.zip qemu-1b050077d2d72b15c73257c13e2e46932786f7e2.tar.gz qemu-1b050077d2d72b15c73257c13e2e46932786f7e2.tar.bz2 |
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>
Diffstat (limited to 'target-i386')
-rw-r--r-- | target-i386/cpu.h | 5 | ||||
-rw-r--r-- | target-i386/helper.h | 1 | ||||
-rw-r--r-- | target-i386/machine.c | 4 | ||||
-rw-r--r-- | target-i386/op_helper.c | 12 | ||||
-rw-r--r-- | target-i386/translate.c | 57 |
5 files changed, 63 insertions, 16 deletions
diff --git a/target-i386/cpu.h b/target-i386/cpu.h index b9a6392..f318942 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 68d57b1..ca953f4 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 ab31329..e5a060f 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 33d44b0..ef0acfc 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 66aa21b..82ee3d5 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, ®_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, ®_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; |