[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260112235408.168200-11-chang.seok.bae@intel.com>
Date: Mon, 12 Jan 2026 23:54:02 +0000
From: "Chang S. Bae" <chang.seok.bae@...el.com>
To: pbonzini@...hat.com,
seanjc@...gle.com
Cc: kvm@...r.kernel.org,
linux-kernel@...r.kernel.org,
chao.gao@...el.com,
chang.seok.bae@...el.com
Subject: [PATCH v2 10/16] KVM: emulate: Handle EGPR index and REX2-incompatible opcodes
Prepare the emulator for REX2 handling by introducing the NoRex2 opcode
flag and supporting extended register indices.
Add a helper to factor out common logic for calculating register indices
from a given register identifier and REX bits alone.
REX2 does not support three-byte opcodes. Instead, the REX2.M bit selects
between one- and two-byte opcode tables, which were previously
distinguished by the 0x0F escape byte.
Some legacy instructions in those tables never reference extended
registers. When prefixed with REX, such instructions are treated as if
the prefix were absent. In contrast, a REX2 prefix causes a #UD, which
should be handled explicitly.
Link: https://lore.kernel.org/1ebf3a23-5671-41c1-8daa-c83f2f105936@redhat.com
Suggested-by: Paolo Bonzini <pbonzini@...hat.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@...el.com>
---
V1 -> V2: Rename NoRex to NoRex2 (Paolo)
---
arch/x86/kvm/emulate.c | 80 +++++++++++++++++++++++---------------
arch/x86/kvm/kvm_emulate.h | 1 +
2 files changed, 50 insertions(+), 31 deletions(-)
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index c8e292e9a24d..ef0da1acab5a 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -175,6 +175,7 @@
#define TwoMemOp ((u64)1 << 55) /* Instruction has two memory operand */
#define IsBranch ((u64)1 << 56) /* Instruction is considered a branch. */
#define ShadowStack ((u64)1 << 57) /* Instruction affects Shadow Stacks. */
+#define NoRex2 ((u64)1 << 58) /* Instruction not present in REX2 maps */
#define DstXacc (DstAccLo | SrcAccHi | SrcWrite)
@@ -244,6 +245,7 @@ enum rex_bits {
REX_X = 2,
REX_R = 4,
REX_W = 8,
+ REX_M = 0x80,
};
static void writeback_registers(struct x86_emulate_ctxt *ctxt)
@@ -1078,6 +1080,15 @@ static int em_fnstsw(struct x86_emulate_ctxt *ctxt)
return X86EMUL_CONTINUE;
}
+static __always_inline int rex_get_rxb(u8 rex, u8 fld)
+{
+ BUILD_BUG_ON(!__builtin_constant_p(fld));
+ BUILD_BUG_ON(fld != REX_B && fld != REX_X && fld != REX_R);
+
+ rex >>= ffs(fld) - 1;
+ return (rex & 1 ? 8 : 0) + (rex & 0x10 ? 16 : 0);
+}
+
static void __decode_register_operand(struct x86_emulate_ctxt *ctxt,
struct operand *op, int reg)
{
@@ -1117,7 +1128,7 @@ static void decode_register_operand(struct x86_emulate_ctxt *ctxt,
if (ctxt->d & ModRM)
reg = ctxt->modrm_reg;
else
- reg = (ctxt->b & 7) | (ctxt->rex_bits & REX_B ? 8 : 0);
+ reg = (ctxt->b & 7) | rex_get_rxb(ctxt->rex_bits, REX_B);
__decode_register_operand(ctxt, op, reg);
}
@@ -1136,9 +1147,9 @@ static int decode_modrm(struct x86_emulate_ctxt *ctxt,
int rc = X86EMUL_CONTINUE;
ulong modrm_ea = 0;
- ctxt->modrm_reg = (ctxt->rex_bits & REX_R ? 8 : 0);
- index_reg = (ctxt->rex_bits & REX_X ? 8 : 0);
- base_reg = (ctxt->rex_bits & REX_B ? 8 : 0);
+ ctxt->modrm_reg = rex_get_rxb(ctxt->rex_bits, REX_R);
+ index_reg = rex_get_rxb(ctxt->rex_bits, REX_X);
+ base_reg = rex_get_rxb(ctxt->rex_bits, REX_B);
ctxt->modrm_mod = (ctxt->modrm & 0xc0) >> 6;
ctxt->modrm_reg |= (ctxt->modrm & 0x38) >> 3;
@@ -4245,7 +4256,7 @@ static const struct opcode opcode_table[256] = {
/* 0x38 - 0x3F */
I6ALU(NoWrite, em_cmp), N, N,
/* 0x40 - 0x4F */
- X8(I(DstReg, em_inc)), X8(I(DstReg, em_dec)),
+ X8(I(DstReg | NoRex2, em_inc)), X8(I(DstReg | NoRex2, em_dec)),
/* 0x50 - 0x57 */
X8(I(SrcReg | Stack, em_push)),
/* 0x58 - 0x5F */
@@ -4263,7 +4274,7 @@ static const struct opcode opcode_table[256] = {
I2bvIP(DstDI | SrcDX | Mov | String | Unaligned, em_in, ins, check_perm_in), /* insb, insw/insd */
I2bvIP(SrcSI | DstDX | String, em_out, outs, check_perm_out), /* outsb, outsw/outsd */
/* 0x70 - 0x7F */
- X16(D(SrcImmByte | NearBranch | IsBranch)),
+ X16(D(SrcImmByte | NearBranch | IsBranch | NoRex2)),
/* 0x80 - 0x87 */
G(ByteOp | DstMem | SrcImm, group1),
G(DstMem | SrcImm, group1),
@@ -4287,15 +4298,15 @@ static const struct opcode opcode_table[256] = {
II(ImplicitOps | Stack, em_popf, popf),
I(ImplicitOps, em_sahf), I(ImplicitOps, em_lahf),
/* 0xA0 - 0xA7 */
- I2bv(DstAcc | SrcMem | Mov | MemAbs, em_mov),
- I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable, em_mov),
- I2bv(SrcSI | DstDI | Mov | String | TwoMemOp, em_mov),
- I2bv(SrcSI | DstDI | String | NoWrite | TwoMemOp, em_cmp_r),
+ I2bv(DstAcc | SrcMem | Mov | MemAbs | NoRex2, em_mov),
+ I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable | NoRex2, em_mov),
+ I2bv(SrcSI | DstDI | Mov | String | TwoMemOp | NoRex2, em_mov),
+ I2bv(SrcSI | DstDI | String | NoWrite | TwoMemOp | NoRex2, em_cmp_r),
/* 0xA8 - 0xAF */
- I2bv(DstAcc | SrcImm | NoWrite, em_test),
- I2bv(SrcAcc | DstDI | Mov | String, em_mov),
- I2bv(SrcSI | DstAcc | Mov | String, em_mov),
- I2bv(SrcAcc | DstDI | String | NoWrite, em_cmp_r),
+ I2bv(DstAcc | SrcImm | NoWrite | NoRex2, em_test),
+ I2bv(SrcAcc | DstDI | Mov | String | NoRex2, em_mov),
+ I2bv(SrcSI | DstAcc | Mov | String | NoRex2, em_mov),
+ I2bv(SrcAcc | DstDI | String | NoWrite | NoRex2, em_cmp_r),
/* 0xB0 - 0xB7 */
X8(I(ByteOp | DstReg | SrcImm | Mov, em_mov)),
/* 0xB8 - 0xBF */
@@ -4325,17 +4336,17 @@ static const struct opcode opcode_table[256] = {
/* 0xD8 - 0xDF */
N, E(0, &escape_d9), N, E(0, &escape_db), N, E(0, &escape_dd), N, N,
/* 0xE0 - 0xE7 */
- X3(I(SrcImmByte | NearBranch | IsBranch, em_loop)),
- I(SrcImmByte | NearBranch | IsBranch, em_jcxz),
- I2bvIP(SrcImmUByte | DstAcc, em_in, in, check_perm_in),
- I2bvIP(SrcAcc | DstImmUByte, em_out, out, check_perm_out),
+ X3(I(SrcImmByte | NearBranch | IsBranch | NoRex2, em_loop)),
+ I(SrcImmByte | NearBranch | IsBranch | NoRex2, em_jcxz),
+ I2bvIP(SrcImmUByte | DstAcc | NoRex2, em_in, in, check_perm_in),
+ I2bvIP(SrcAcc | DstImmUByte | NoRex2, em_out, out, check_perm_out),
/* 0xE8 - 0xEF */
- I(SrcImm | NearBranch | IsBranch | ShadowStack, em_call),
- D(SrcImm | ImplicitOps | NearBranch | IsBranch),
- I(SrcImmFAddr | No64 | IsBranch, em_jmp_far),
- D(SrcImmByte | ImplicitOps | NearBranch | IsBranch),
- I2bvIP(SrcDX | DstAcc, em_in, in, check_perm_in),
- I2bvIP(SrcAcc | DstDX, em_out, out, check_perm_out),
+ I(SrcImm | NearBranch | IsBranch | ShadowStack | NoRex2, em_call),
+ D(SrcImm | ImplicitOps | NearBranch | IsBranch | NoRex2),
+ I(SrcImmFAddr | No64 | IsBranch | NoRex2, em_jmp_far),
+ D(SrcImmByte | ImplicitOps | NearBranch | IsBranch | NoRex2),
+ I2bvIP(SrcDX | DstAcc | NoRex2, em_in, in, check_perm_in),
+ I2bvIP(SrcAcc | DstDX | NoRex2, em_out, out, check_perm_out),
/* 0xF0 - 0xF7 */
N, DI(ImplicitOps, icebp), N, N,
DI(ImplicitOps | Priv, hlt), D(ImplicitOps),
@@ -4376,12 +4387,12 @@ static const struct opcode twobyte_table[256] = {
N, GP(ModRM | DstMem | SrcReg | Mov | Sse | Avx, &pfx_0f_2b),
N, N, N, N,
/* 0x30 - 0x3F */
- II(ImplicitOps | Priv, em_wrmsr, wrmsr),
- IIP(ImplicitOps, em_rdtsc, rdtsc, check_rdtsc),
- II(ImplicitOps | Priv, em_rdmsr, rdmsr),
- IIP(ImplicitOps, em_rdpmc, rdpmc, check_rdpmc),
- I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack, em_sysenter),
- I(ImplicitOps | Priv | EmulateOnUD | IsBranch | ShadowStack, em_sysexit),
+ II(ImplicitOps | Priv | NoRex2, em_wrmsr, wrmsr),
+ IIP(ImplicitOps | NoRex2, em_rdtsc, rdtsc, check_rdtsc),
+ II(ImplicitOps | Priv | NoRex2, em_rdmsr, rdmsr),
+ IIP(ImplicitOps | NoRex2, em_rdpmc, rdpmc, check_rdpmc),
+ I(ImplicitOps | EmulateOnUD | IsBranch | ShadowStack | NoRex2, em_sysenter),
+ I(ImplicitOps | Priv | EmulateOnUD | IsBranch | ShadowStack | NoRex2, em_sysexit),
N, N,
N, N, N, N, N, N, N, N,
/* 0x40 - 0x4F */
@@ -4399,7 +4410,7 @@ static const struct opcode twobyte_table[256] = {
N, N, N, N,
N, N, N, GP(SrcReg | DstMem | ModRM | Mov, &pfx_0f_6f_0f_7f),
/* 0x80 - 0x8F */
- X16(D(SrcImm | NearBranch | IsBranch)),
+ X16(D(SrcImm | NearBranch | IsBranch | NoRex2)),
/* 0x90 - 0x9F */
X16(D(ByteOp | DstMem | SrcNone | ModRM| Mov)),
/* 0xA0 - 0xA7 */
@@ -4992,6 +5003,13 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len, int
opcode = opcode_table[ctxt->b];
}
+ /*
+ * Instructions marked with NoRex2 ignore a legacy REX prefix, but
+ * #UD should be raised when prefixed with REX2.
+ */
+ if (ctxt->d & NoRex2 && ctxt->rex_prefix == REX2_PREFIX)
+ opcode.flags = Undefined;
+
if (opcode.flags & ModRM)
ctxt->modrm = insn_fetch(u8, ctxt);
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index 16b35a796a7f..dd5d1e489db6 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -325,6 +325,7 @@ typedef void (*fastop_t)(struct fastop *);
enum rex_type {
REX_NONE,
REX_PREFIX,
+ REX2_PREFIX,
};
struct x86_emulate_ctxt {
--
2.51.0
Powered by blists - more mailing lists