target/i386: implement RDPID in TCG

RDPID corresponds to a RDMSR(TSC_AUX); however, it is unprivileged
so for user-mode emulation we must provide the value that the kernel
places in the MSR.  For Linux, it is a combination of the current CPU
and the current NUMA node, both of which can be retrieved with getcpu(2).
Also try sched_getcpu(), which might be there on the BSDs.  If there is
no portable way to retrieve the current CPU id from userspace, return 0.

RDTSCP is reimplemented as RDTSC + RDPID ECX; the differences in terms
of serializability are not relevant to QEMU.

Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Paolo Bonzini 2023-06-21 00:47:31 +02:00
parent 53b9b4cc9f
commit 6750485bf4
5 changed files with 46 additions and 13 deletions

View File

@ -2232,6 +2232,8 @@ config_host_data.set('CONFIG_CLOCK_ADJTIME', cc.has_function('clock_adjtime'))
config_host_data.set('CONFIG_DUP3', cc.has_function('dup3'))
config_host_data.set('CONFIG_FALLOCATE', cc.has_function('fallocate'))
config_host_data.set('CONFIG_POSIX_FALLOCATE', cc.has_function('posix_fallocate'))
config_host_data.set('CONFIG_GETCPU', cc.has_function('getcpu', prefix: gnu_source_prefix))
config_host_data.set('CONFIG_SCHED_GETCPU', cc.has_function('sched_getcpu', prefix: '#include <sched.h>'))
# Note that we need to specify prefix: here to avoid incorrectly
# thinking that Windows has posix_memalign()
config_host_data.set('CONFIG_POSIX_MEMALIGN', cc.has_function('posix_memalign', prefix: '#include <stdlib.h>'))

View File

@ -661,9 +661,17 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
/* missing:
CPUID_7_0_EBX_HLE
CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM */
#if defined CONFIG_SOFTMMU || defined CONFIG_LINUX
#define TCG_7_0_ECX_RDPID CPUID_7_0_ECX_RDPID
#else
#define TCG_7_0_ECX_RDPID 0
#endif
#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | \
/* CPUID_7_0_ECX_OSPKE is dynamic */ \
CPUID_7_0_ECX_LA57 | CPUID_7_0_ECX_PKS | CPUID_7_0_ECX_VAES)
CPUID_7_0_ECX_LA57 | CPUID_7_0_ECX_PKS | CPUID_7_0_ECX_VAES | \
TCG_7_0_ECX_RDPID)
#define TCG_7_0_EDX_FEATURES CPUID_7_0_EDX_FSRM
#define TCG_7_1_EAX_FEATURES (CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | \
CPUID_7_1_EAX_FSRC)

View File

@ -69,8 +69,8 @@ DEF_HELPER_2(into, void, env, int)
DEF_HELPER_FLAGS_1(single_step, TCG_CALL_NO_WG, noreturn, env)
DEF_HELPER_1(rechecking_single_step, void, env)
DEF_HELPER_1(cpuid, void, env)
DEF_HELPER_FLAGS_1(rdpid, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_1(rdtsc, void, env)
DEF_HELPER_1(rdtscp, void, env)
DEF_HELPER_FLAGS_1(rdpmc, TCG_CALL_NO_WG, noreturn, env)
#ifndef CONFIG_USER_ONLY

View File

@ -75,12 +75,6 @@ void helper_rdtsc(CPUX86State *env)
env->regs[R_EDX] = (uint32_t)(val >> 32);
}
void helper_rdtscp(CPUX86State *env)
{
helper_rdtsc(env);
env->regs[R_ECX] = (uint32_t)(env->tsc_aux);
}
G_NORETURN void helper_rdpmc(CPUX86State *env)
{
if (((env->cr[4] & CR4_PCE_MASK) == 0 ) &&
@ -137,3 +131,18 @@ void helper_wrpkru(CPUX86State *env, uint32_t ecx, uint64_t val)
env->pkru = val;
tlb_flush(cs);
}
target_ulong HELPER(rdpid)(CPUX86State *env)
{
#if defined CONFIG_SOFTMMU
return env->tsc_aux;
#elif defined CONFIG_LINUX && defined CONFIG_GETCPU
unsigned cpu, node;
getcpu(&cpu, &node);
return (node << 12) | (cpu & 0xfff);
#elif defined CONFIG_SCHED_GETCPU
return sched_getcpu();
#else
return 0;
#endif
}

View File

@ -3924,13 +3924,25 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
gen_cmpxchg8b(s, env, modrm);
break;
case 7: /* RDSEED */
case 7: /* RDSEED, RDPID with f3 prefix */
if (mod != 3 ||
(s->prefix & (PREFIX_LOCK | PREFIX_REPZ | PREFIX_REPNZ)) ||
!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_RDSEED)) {
(s->prefix & (PREFIX_LOCK | PREFIX_REPNZ))) {
goto illegal_op;
}
if (s->prefix & PREFIX_REPZ) {
if (!(s->cpuid_ext_features & CPUID_7_0_ECX_RDPID)) {
goto illegal_op;
}
gen_helper_rdpid(s->T0, cpu_env);
rm = (modrm & 7) | REX_B(s);
gen_op_mov_reg_v(s, dflag, rm, s->T0);
break;
} else {
if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_RDSEED)) {
goto illegal_op;
}
goto do_rdrand;
}
case 6: /* RDRAND */
if (mod != 3 ||
@ -6125,7 +6137,9 @@ static bool disas_insn(DisasContext *s, CPUState *cpu)
gen_update_cc_op(s);
gen_update_eip_cur(s);
translator_io_start(&s->base);
gen_helper_rdtscp(cpu_env);
gen_helper_rdtsc(cpu_env);
gen_helper_rdpid(s->T0, cpu_env);
gen_op_mov_reg_v(s, dflag, R_ECX, s->T0);
break;
default: