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>
This commit is contained in:
Andre Przywara 2009-09-19 00:30:49 +02:00 committed by Aurelien Jarno
parent d9f4bb27db
commit 1b050077d2
5 changed files with 65 additions and 18 deletions

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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):

View File

@ -7206,23 +7206,10 @@ 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);
} else {
if (mod == 3) {
#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));
} else
#endif
{
goto illegal_op;
}
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);
@ -7232,6 +7219,46 @@ static target_ulong disas_insn(DisasContext *s, target_ulong pc_start)
gen_jmp_im(s->pc - s->cs_base);
gen_eob(s);
}
} else {
switch (rm) {
case 0: /* swapgs */
#ifdef TARGET_X86_64
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;
}
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);
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;
default: