@@ -363,7 +363,7 @@ static const char *cpuid_6_feature_name[] = {
CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2,
CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM,
CPUID_7_0_EBX_RDSEED */
-#define TCG_7_0_ECX_FEATURES 0
+#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE)
#define TCG_APM_FEATURES 0
#define TCG_6_EAX_FEATURES CPUID_6_EAX_ARAT
@@ -157,6 +157,7 @@
#define HF_SMAP_SHIFT 23 /* CR4.SMAP */
#define HF_IOBPT_SHIFT 24 /* an io breakpoint enabled */
#define HF_OSXSAVE_SHIFT 25 /* CR4.OSXSAVE */
+#define HF_PKE_SHIFT 26 /* CR4.PKE enabled */
#define HF_CPL_MASK (3 << HF_CPL_SHIFT)
#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT)
@@ -182,6 +183,7 @@
#define HF_SMAP_MASK (1 << HF_SMAP_SHIFT)
#define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT)
#define HF_OSXSAVE_MASK (1 << HF_OSXSAVE_SHIFT)
+#define HF_PKE_MASK (1 << HF_PKE_SHIFT)
/* hflags2 */
@@ -229,6 +231,7 @@
#define CR4_OSXSAVE_MASK (1U << 18)
#define CR4_SMEP_MASK (1U << 20)
#define CR4_SMAP_MASK (1U << 21)
+#define CR4_PKE_MASK (1U << 22)
#define DR6_BD (1 << 13)
#define DR6_BS (1 << 14)
@@ -257,6 +260,7 @@
#define PG_PSE_BIT 7
#define PG_GLOBAL_BIT 8
#define PG_PSE_PAT_BIT 12
+#define PG_PKRU_BIT 59
#define PG_NX_BIT 63
#define PG_PRESENT_MASK (1 << PG_PRESENT_BIT)
@@ -272,7 +276,8 @@
#define PG_ADDRESS_MASK 0x000ffffffffff000LL
#define PG_HI_RSVD_MASK (PG_ADDRESS_MASK & ~PHYS_ADDR_MASK)
#define PG_HI_USER_MASK 0x7ff0000000000000LL
-#define PG_NX_MASK (1LL << PG_NX_BIT)
+#define PG_PKRU_MASK (15ULL << PG_PKRU_BIT)
+#define PG_NX_MASK (1ULL << PG_NX_BIT)
#define PG_ERROR_W_BIT 1
@@ -281,6 +286,7 @@
#define PG_ERROR_U_MASK 0x04
#define PG_ERROR_RSVD_MASK 0x08
#define PG_ERROR_I_D_MASK 0x10
+#define PG_ERROR_PK_MASK 0x20
#define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */
#define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */
@@ -1169,6 +1169,11 @@ static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t retaddr)
}
}
+static void do_xsave_pkru(CPUX86State *env, target_ulong ptr, uintptr_t retaddr)
+{
+ cpu_stq_data_ra(env, ptr + 0xA80, env->pkru, retaddr);
+}
+
void helper_fxsave(CPUX86State *env, target_ulong ptr)
{
/* The operand must be 16 byte aligned */
@@ -1191,9 +1196,18 @@ void helper_fxsave(CPUX86State *env, target_ulong ptr)
static uint64_t get_xinuse(CPUX86State *env)
{
- /* We don't track XINUSE. We could calculate it here, but it's
- probably less work to simply indicate all components in use. */
- return -1;
+ uint64_t inuse = -1;
+
+ /* For the most part, we don't track XINUSE. We could calculate it
+ * here for all components, but it's probably less work to simply
+ * indicate in use. That said, for state that is important
+ * enough to track in HFLAGS, we might as well use that here.
+ */
+ if ((env->hflags & HF_PKE_MASK) == 0) {
+ inuse &= ~XSTATE_PKRU;
+ }
+ return inuse;
+}
}
static void do_xsave(CPUX86State *env, target_ulong ptr,
@@ -1220,6 +1248,9 @@ static void do_xsave(CPUX86State *env, target_ulong ptr,
if (opt & XSTATE_SSE) {
do_xsave_sse(env, ptr, retaddr);
}
+ if (opt & XSTATE_PKRU) {
+ do_xsave_pkru(env, ptr, retaddr);
+ }
/* Update the XSTATE_BV field. */
old_bv = cpu_ldq_data_ra(env, ptr + 512, retaddr);
@@ -1285,6 +1317,16 @@ static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t retaddr)
}
}
+static void do_xrstor_pkru(CPUX86State *env, target_ulong ptr, uintptr_t retaddr)
+{
+ uint64_t old_pkru = env->pkru;
+ env->pkru = cpu_ldq_data_ra(env, ptr + 0xA80, retaddr);
+ if (env->pkru != old_pkru) {
+ CPUState *cs = CPU(x86_env_get_cpu(env));
+ tlb_flush(cs, 1);
+ }
+}
+
void helper_fxrstor(CPUX86State *env, target_ulong ptr)
{
/* The operand must be 16 byte aligned */
@@ -1357,6 +1399,10 @@ void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm)
memset(env->xmm_regs, 0, sizeof(env->xmm_regs));
}
}
+
+ if (rfbm & XSTATE_PKRU) {
+ do_xrstor_pkru(env, ptr, GETPC());
+ }
}
uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx)
@@ -689,6 +689,16 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4)
hflags |= HF_SMAP_MASK;
}
+ if (!(env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKU)) {
+ new_cr4 &= ~CR4_PKE_MASK;
+ }
+ env->hflags &= ~HF_PKE_MASK;
+ env->features[FEAT_7_0_ECX] &= ~CPUID_7_0_ECX_OSPKE;
+ if (new_cr4 & CR4_PKE_MASK) {
+ env->hflags |= HF_PKE_MASK;
+ env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_OSPKE;
+ }
+
env->cr[4] = new_cr4;
env->hflags = hflags;
return;
@@ -935,6 +945,24 @@ do_check_protect_pse36:
goto do_fault_protect;
}
+ if ((env->cr[4] & CR4_PKE_MASK) && (env->hflags & HF_LMA_MASK) &&
+ (ptep & PG_USER_MASK) && env->pkru) {
+ uint32_t pk = (pte & PG_PKRU_MASK) >> PG_PKRU_BIT;
+ uint32_t pkru_ad = (env->pkru >> pk * 2) & 1;
+ uint32_t pkru_wd = (env->pkru >> pk * 2) & 2;
+
+ if (pkru_ad) {
+ prot &= ~(PAGE_READ | PAGE_WRITE);
+ } else if (pkru_wd && (is_user || env->cr[0] & CR0_WP_MASK)) {
+ prot &= ~PAGE_WRITE;
+ }
+ if ((prot & (1 << is_write1)) == 0) {
+ assert(is_write1 != 2);
+ error_code |= PG_ERROR_PK_MASK;
+ goto do_fault_protect;
+ }
+ }
+
/* yes, it can! */
is_dirty = is_write && !(pte & PG_DIRTY_MASK);
if (!(pte & PG_ACCESSED_MASK) || is_dirty) {
@@ -79,6 +79,8 @@ DEF_HELPER_1(rdtscp, void, env)
DEF_HELPER_1(rdpmc, void, env)
DEF_HELPER_1(rdmsr, void, env)
DEF_HELPER_1(wrmsr, void, env)
+DEF_HELPER_1(rdpkru, void, env)
+DEF_HELPER_1(wrpkru, void, env)
DEF_HELPER_2(check_iob, void, env, i32)
DEF_HELPER_2(check_iow, void, env, i32)
@@ -600,3 +600,35 @@ void helper_debug(CPUX86State *env)
cs->exception_index = EXCP_DEBUG;
cpu_loop_exit(cs);
}
+
+void helper_rdpkru(CPUX86State *env)
+{
+ if ((env->cr[4] & CR4_PKE_MASK) == 0) {
+ raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
+ return;
+ }
+ if (env->regs[R_ECX] != 0) {
+ raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
+ return;
+ }
+
+ env->regs[R_EAX] = env->pkru;
+ env->regs[R_EDX] = 0;
+}
+
+void helper_wrpkru(CPUX86State *env)
+{
+ CPUState *cs = CPU(x86_env_get_cpu(env));
+
+ if ((env->cr[4] & CR4_PKE_MASK) == 0) {
+ raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC());
+ return;
+ }
+ if (env->regs[R_ECX] != 0) {
+ raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC());
+ return;
+ }
+
+ env->pkru = env->regs[R_EAX];
+ tlb_flush(cs, 1);
+}
@@ -7262,6 +7262,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s,
#endif
gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 1);
break;
+ case 0xee: /* rdpkru */
+ gen_update_cc_op(s);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_rdpkru(cpu_env);
+ break;
+ case 0xef: /* wrpkru */
+ gen_update_cc_op(s);
+ gen_jmp_im(pc_start - s->cs_base);
+ gen_helper_wrpkru(cpu_env);
+ break;
CASE_MEM_OP(6): /* lmsw */
if (s->cpl != 0) {
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base);
This includes setting up TLB permissions, the new RDPKRU/WRPKRU instructions, and XSAVE support. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> --- target-i386/cpu.c | 2 +- target-i386/cpu.h | 8 ++++++- target-i386/fpu_helper.c | 54 +++++++++++++++++++++++++++++++++++++++++++---- target-i386/helper.c | 28 ++++++++++++++++++++++++ target-i386/helper.h | 2 ++ target-i386/misc_helper.c | 32 ++++++++++++++++++++++++++++ target-i386/translate.c | 6 ++++++ 7 files changed, 126 insertions(+), 6 deletions(-)