[PATCH RFC v1 13/20] KVM: x86: Add REX2 opcode tables to the instruction decoder

Chang S. Bae
2025-11-10T18:24:58Z

Extend the decoder to find REX2-prefixed opcodes by introducing dedicated
REX2 opcode tables. During initialization, clone the legacy opcode tables
and patch entries that differ under REX2.

Although most REX2-prefixed opcodes follow the legacy tables, some differ
for instructions that do not reference extended register bits or are
newly introduced under REX2. Using separate tables simplifies the
lookup logic and allows efficient patching of exceptions.

The EGPR checker will be implemented later.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
---
RFC note:
The lookup logic could be separated from the table population, but
keeping the user of the tables close to their initialization helps
clarify the purpose of the new table. If this becomes hard to follow,
splitting the lookup separately can be an option.
---
arch/x86/kvm/emulate.c | 73 +++++++++++++++++++++++++++++++++++++-
arch/x86/kvm/kvm_emulate.h | 2 ++
arch/x86/kvm/x86.c | 1 +
3 files changed, 75 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index ed3a8c0bca20..58879a31abcd 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -4475,6 +4475,19 @@ static const struct opcode opcode_map_0f_38[256] = {
N, N, X4(N), X8(N)
};

+/*
+ * REX2 opcode tables.
+ *
+ * REX2-prefixed opcodes mostly follow the legacy tables but differ slightly
+ * for instructions that do not use R/X/B register bits. Initialize the REX2
+ * tables by copying the legacy ones, then mark mismatched rows as undefined.
+ */
+static struct opcode rex2_opcode_table[256] __ro_after_init;
+static struct opcode rex2_twobyte_table[256] __ro_after_init;
+
+static const struct opcode undefined = D(Undefined);
+static const struct opcode notimpl = N;
+
#undef D
#undef N
#undef G
@@ -4761,6 +4774,11 @@ static int decode_operand(struct x86_emulate_ctxt *ctxt, struct operand *op,
return rc;
}

+static inline bool emul_egpr_enabled(struct x86_emulate_ctxt *ctxt __maybe_unused)
+{
+ return false;
+}
+
int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int emulation_type)
{
int rc = X86EMUL_CONTINUE;
@@ -4881,7 +4899,24 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
ctxt->op_bytes = 8;

/* Determine opcode byte(s): */
- if (ctxt->b == 0x0f) {
+ if (ctxt->rex_prefix == REX2_INVALID) {
+ /*
+ * A REX2 prefix was detected, but the prefix decoder
+ * found invalid byte sequence.
+ */
+ opcode = undefined;
+ } else if (ctxt->rex_prefix == REX2_PREFIX) {
+ /* REX2 prefix is only valid when EGPRs are enabled. */
+ if (!emul_egpr_enabled(ctxt)) {
+ opcode = undefined;
+ } else if (ctxt->rex.bits.m0) {
+ ctxt->opcode_len = 2;
+ opcode = rex2_twobyte_table[ctxt->b];
+ } else {
+ ctxt->opcode_len = 1;
+ opcode = rex2_opcode_table[ctxt->b];
+ }
+ } else if (ctxt->b == 0x0f) {
/* Escape byte: start two-byte opcode sequence */
ctxt->b = insn_fetch(u8, ctxt);
if (ctxt->b == 0x38) {
@@ -5526,3 +5561,39 @@ bool emulator_can_use_gpa(struct x86_emulate_ctxt *ctxt)

return true;
}
+
+static void undefine_row(struct opcode *row)
+{
+ struct opcode *ptr = row;
+ int i;
+
+ /* Clear 16 entries per row */
+ for (i = 0; i < 0x10; i++, ptr++)
+ *ptr = undefined;
+}
+
+/*
+ * Populate REX2 opcode table:
+ *
+ * REX2-prefixed opcodes mostly reuse the legacy layout, except for those that
+ * neither reference extended register bits nor are newly introduced under the
+ * REX2 prefix. Initialize both single- and two-byte tables by cloning the
+ * legacy versions, then patch the table for some exceptions.
+ */
+void __init kvm_init_rex2_opcode_table(void)
+{
+ /* Copy legacy tables: */
+ memcpy(rex2_opcode_table, opcode_table, sizeof(opcode_table));
+ memcpy(rex2_twobyte_table, twobyte_table, sizeof(twobyte_table));
+
+ /* Undefine reserved opcode ranges: */
+ undefine_row(&rex2_opcode_table[0x40]);
+ undefine_row(&rex2_opcode_table[0x70]);
+ undefine_row(&rex2_opcode_table[0xa0]);
+ undefine_row(&rex2_opcode_table[0xe0]);
+ undefine_row(&rex2_twobyte_table[0x30]);
+ undefine_row(&rex2_twobyte_table[0x80]);
+
+ /* Mark opcode not yet implemented: */
+ rex2_opcode_table[0xa1] = notimpl;
+}
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index b285299ebfa4..cc16211d61f6 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -589,4 +589,6 @@ static inline ulong *reg_rmw(struct x86_emulate_ctxt *ctxt, unsigned nr)
return reg_write(ctxt, nr);
}

+void __init kvm_init_rex2_opcode_table(void);
+
#endif /* _ASM_X86_KVM_X86_EMULATE_H */
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 338986a5a3ae..4c8c2fc3bda6 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -14354,6 +14354,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_rmp_fault);
static int __init kvm_x86_init(void)
{
kvm_init_xstate_sizes();
+ kvm_init_rex2_opcode_table();

kvm_mmu_x86_module_init();
mitigate_smt_rsb &= boot_cpu_has_bug(X86_BUG_SMT_RSB) && cpu_smt_possible();
--
2.51.0