return X86EMUL_UNHANDLEABLE;
}
+#define INVPCID_ADDR 0x12345678
+#define INVPCID_PCID 0x123
+
+static int read_cr_invpcid(
+ unsigned int reg,
+ unsigned long *val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ int rc = emul_test_read_cr(reg, val, ctxt);
+
+ if ( rc == X86EMUL_OKAY && reg == 4 )
+ *val |= X86_CR4_PCIDE;
+
+ return rc;
+}
+
+static int tlb_op_invpcid(
+ enum x86emul_tlb_op op,
+ unsigned long addr,
+ unsigned long aux,
+ struct x86_emulate_ctxt *ctxt)
+{
+ static unsigned int seq;
+
+ if ( op != x86emul_invpcid || addr != INVPCID_ADDR ||
+ x86emul_invpcid_pcid(aux) != (seq < 4 ? 0 : INVPCID_PCID) ||
+ x86emul_invpcid_type(aux) != (seq++ & 3) )
+ return X86EMUL_UNHANDLEABLE;
+
+ return X86EMUL_OKAY;
+}
+
static struct x86_emulate_ops emulops = {
.read = read,
.insn_fetch = fetch,
else
printf("skipped\n");
+ printf("%-40s", "Testing invpcid 16(%ecx),%%edx...");
+ if ( stack_exec )
+ {
+ decl_insn(invpcid);
+
+ asm volatile ( put_insn(invpcid, "invpcid 16(%0), %1")
+ :: "c" (NULL), "d" (0L) );
+
+ res[4] = 0;
+ res[5] = 0;
+ res[6] = INVPCID_ADDR;
+ res[7] = 0;
+ regs.ecx = (unsigned long)res;
+ emulops.tlb_op = tlb_op_invpcid;
+
+ for ( ; ; )
+ {
+ for ( regs.edx = 0; regs.edx < 4; ++regs.edx )
+ {
+ set_insn(invpcid);
+ rc = x86_emulate(&ctxt, &emulops);
+ if ( rc != X86EMUL_OKAY || !check_eip(invpcid) )
+ goto fail;
+ }
+
+ if ( ctxt.addr_size < 64 || res[4] == INVPCID_PCID )
+ break;
+
+ emulops.read_cr = read_cr_invpcid;
+ res[4] = INVPCID_PCID;
+ }
+
+ emulops.read_cr = emul_test_read_cr;
+ emulops.tlb_op = NULL;
+
+ printf("okay\n");
+ }
+ else
+ printf("skipped\n");
+
#undef decl_insn
#undef put_insn
#undef set_insn
[0x7a ... 0x7c] = { .simd_size = simd_none, .two_op = 1 },
[0x7d ... 0x7e] = { .simd_size = simd_packed_int, .d8s = d8s_vl },
[0x7f] = { .simd_size = simd_packed_fp, .d8s = d8s_vl },
+ [0x82] = { .simd_size = simd_other },
[0x83] = { .simd_size = simd_packed_int, .d8s = d8s_vl },
[0x88] = { .simd_size = simd_packed_fp, .two_op = 1, .d8s = d8s_dq },
[0x89] = { .simd_size = simd_packed_int, .two_op = 1, .d8s = d8s_dq },
#define vcpu_has_hle() (ctxt->cpuid->feat.hle)
#define vcpu_has_avx2() (ctxt->cpuid->feat.avx2)
#define vcpu_has_bmi2() (ctxt->cpuid->feat.bmi2)
+#define vcpu_has_invpcid() (ctxt->cpuid->feat.invpcid)
#define vcpu_has_rtm() (ctxt->cpuid->feat.rtm)
#define vcpu_has_mpx() (ctxt->cpuid->feat.mpx)
#define vcpu_has_avx512f() (ctxt->cpuid->feat.avx512f)
ASSERT(!state->simd_size);
break;
+ case X86EMUL_OPC_66(0x0f38, 0x82): /* invpcid reg,m128 */
+ vcpu_must_have(invpcid);
+ generate_exception_if(ea.type != OP_MEM, EXC_UD);
+ generate_exception_if(!mode_ring0(), EXC_GP, 0);
+
+ if ( (rc = ops->read(ea.mem.seg, ea.mem.off, mmvalp, 16,
+ ctxt)) != X86EMUL_OKAY )
+ goto done;
+
+ generate_exception_if(mmvalp->xmm[0] & ~0xfff, EXC_GP, 0);
+ dst.val = mode_64bit() ? *dst.reg : (uint32_t)*dst.reg;
+
+ switch ( dst.val )
+ {
+ case X86_INVPCID_INDIV_ADDR:
+ generate_exception_if(!is_canonical_address(mmvalp->xmm[1]),
+ EXC_GP, 0);
+ /* fall through */
+ case X86_INVPCID_SINGLE_CTXT:
+ if ( !mode_64bit() || !ops->read_cr )
+ cr4 = 0;
+ else if ( (rc = ops->read_cr(4, &cr4, ctxt)) != X86EMUL_OKAY )
+ goto done;
+ generate_exception_if(!(cr4 & X86_CR4_PCIDE) && mmvalp->xmm[0],
+ EXC_GP, 0);
+ break;
+ case X86_INVPCID_ALL_INCL_GLOBAL:
+ case X86_INVPCID_ALL_NON_GLOBAL:
+ break;
+ default:
+ generate_exception(EXC_GP, 0);
+ }
+
+ fail_if(!ops->tlb_op);
+ if ( (rc = ops->tlb_op(x86emul_invpcid, truncate_ea(mmvalp->xmm[1]),
+ x86emul_invpcid_aux(mmvalp->xmm[0], dst.val),
+ ctxt)) != X86EMUL_OKAY )
+ goto done;
+
+ state->simd_size = simd_none;
+ break;
+
case X86EMUL_OPC_EVEX_66(0x0f38, 0x83): /* vpmultishiftqb [xyz]mm/mem,[xyz]mm,[xyz]mm{k} */
generate_exception_if(!evex.w, EXC_UD);
host_and_vcpu_must_have(avx512_vbmi);
enum x86emul_tlb_op {
x86emul_invlpg,
x86emul_invlpga,
+ x86emul_invpcid,
};
+static inline unsigned int x86emul_invpcid_aux(unsigned int pcid,
+ unsigned int type)
+{
+ ASSERT(!(pcid & ~0xfff));
+ return (type << 12) | pcid;
+}
+
+static inline unsigned int x86emul_invpcid_pcid(unsigned int aux)
+{
+ return aux & 0xfff;
+}
+
+static inline unsigned int x86emul_invpcid_type(unsigned int aux)
+{
+ return aux >> 12;
+}
+
struct x86_emulate_state;
/*
* @addr and @aux have @op-specific meaning:
* - INVLPG: @aux:@addr represent seg:offset
* - INVLPGA: @addr is the linear address, @aux the ASID
+ * - INVPCID: @addr is the linear address, @aux the combination of
+ * PCID and type (see x86emul_invpcid_*()).
*/
int (*tlb_op)(
enum x86emul_tlb_op op,