mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
x86emul: support LKGS
Provide support for this insn, which is a prereq to FRED. CPUID-wise,
while its and FRED's enumerators were already introduced, their dependency
still needs adding.
While adding a testcase, also add a SWAPGS one. In order to not affect
the behavior of pre-existing tests, install write_{segment,msr} hooks
only transiently.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
This commit is contained in:
1 parent
d9b3aceae1
commit
a6b143e5a3
7 files changed
+137
-4
No files matched your search
@@ -326,6 +326,7 @@ static const struct {
|
||||
{ { 0x00, 0x18 }, { 2, 2 }, T, R }, /* ltr */
|
||||
{ { 0x00, 0x20 }, { 2, 2 }, T, R }, /* verr */
|
||||
{ { 0x00, 0x28 }, { 2, 2 }, T, R }, /* verw */
|
||||
{ { 0x00, 0x30 }, { 0, 2 }, T, R, pfx_f2 }, /* lkgs */
|
||||
{ { 0x01, 0x00 }, { 2, 2 }, F, W }, /* sgdt */
|
||||
{ { 0x01, 0x08 }, { 2, 2 }, F, W }, /* sidt */
|
||||
{ { 0x01, 0x10 }, { 2, 2 }, F, R }, /* lgdt */
|
||||
|
||||
@@ -673,6 +673,10 @@ static int blk(
|
||||
return x86_emul_blk((void *)offset, p_data, bytes, eflags, state, ctxt);
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
static unsigned long gs_base, gs_base_shadow;
|
||||
#endif
|
||||
|
||||
static int read_segment(
|
||||
enum x86_segment seg,
|
||||
struct segment_register *reg,
|
||||
@@ -682,9 +686,26 @@ static int read_segment(
|
||||
return X86EMUL_UNHANDLEABLE;
|
||||
memset(reg, 0, sizeof(*reg));
|
||||
reg->p = 1;
|
||||
|
||||
return X86EMUL_OKAY;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
static int write_segment(
|
||||
enum x86_segment seg,
|
||||
const struct segment_register *reg,
|
||||
struct x86_emulate_ctxt *ctxt)
|
||||
{
|
||||
if ( !is_x86_user_segment(seg) )
|
||||
return X86EMUL_UNHANDLEABLE;
|
||||
|
||||
if ( seg == x86_seg_gs )
|
||||
gs_base = reg->base;
|
||||
|
||||
return X86EMUL_OKAY;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int read_msr(
|
||||
unsigned int reg,
|
||||
uint64_t *val,
|
||||
@@ -696,6 +717,20 @@ static int read_msr(
|
||||
*val = ctxt->addr_size > 32 ? EFER_LME | EFER_LMA : 0;
|
||||
return X86EMUL_OKAY;
|
||||
|
||||
#ifdef __x86_64__
|
||||
case MSR_GS_BASE:
|
||||
if ( ctxt->addr_size < 64 )
|
||||
break;
|
||||
*val = gs_base;
|
||||
return X86EMUL_OKAY;
|
||||
|
||||
case MSR_SHADOW_GS_BASE:
|
||||
if ( ctxt->addr_size < 64 )
|
||||
break;
|
||||
*val = gs_base_shadow;
|
||||
return X86EMUL_OKAY;
|
||||
#endif
|
||||
|
||||
case MSR_TSC_AUX:
|
||||
#define TSC_AUX_VALUE 0xCACACACA
|
||||
*val = TSC_AUX_VALUE;
|
||||
@@ -705,6 +740,32 @@ static int read_msr(
|
||||
return X86EMUL_UNHANDLEABLE;
|
||||
}
|
||||
|
||||
#ifdef __x86_64__
|
||||
static int write_msr(
|
||||
unsigned int reg,
|
||||
uint64_t val,
|
||||
struct x86_emulate_ctxt *ctxt,
|
||||
bool explicit)
|
||||
{
|
||||
switch ( reg )
|
||||
{
|
||||
case MSR_GS_BASE:
|
||||
if ( ctxt->addr_size < 64 || !is_canonical_address(val) )
|
||||
break;
|
||||
gs_base = val;
|
||||
return X86EMUL_OKAY;
|
||||
|
||||
case MSR_SHADOW_GS_BASE:
|
||||
if ( ctxt->addr_size < 64 || !is_canonical_address(val) )
|
||||
break;
|
||||
gs_base_shadow = val;
|
||||
return X86EMUL_OKAY;
|
||||
}
|
||||
|
||||
return X86EMUL_UNHANDLEABLE;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define INVPCID_ADDR 0x12345678
|
||||
#define INVPCID_PCID 0x123
|
||||
|
||||
@@ -1339,6 +1400,41 @@ int main(int argc, char **argv)
|
||||
printf("%u bytes read - ", bytes_read);
|
||||
goto fail;
|
||||
}
|
||||
printf("okay\n");
|
||||
|
||||
emulops.write_segment = write_segment;
|
||||
emulops.write_msr = write_msr;
|
||||
|
||||
printf("%-40s", "Testing swapgs...");
|
||||
instr[0] = 0x0f; instr[1] = 0x01; instr[2] = 0xf8;
|
||||
regs.eip = (unsigned long)&instr[0];
|
||||
gs_base = 0xffffeeeecccc8888UL;
|
||||
gs_base_shadow = 0x0000111122224444UL;
|
||||
rc = x86_emulate(&ctxt, &emulops);
|
||||
if ( (rc != X86EMUL_OKAY) ||
|
||||
(regs.eip != (unsigned long)&instr[3]) ||
|
||||
(gs_base != 0x0000111122224444UL) ||
|
||||
(gs_base_shadow != 0xffffeeeecccc8888UL) )
|
||||
goto fail;
|
||||
printf("okay\n");
|
||||
|
||||
printf("%-40s", "Testing lkgs 2(%rdx)...");
|
||||
instr[0] = 0xf2; instr[1] = 0x0f; instr[2] = 0x00; instr[3] = 0x72; instr[4] = 0x02;
|
||||
regs.eip = (unsigned long)&instr[0];
|
||||
regs.edx = (unsigned long)res;
|
||||
res[0] = 0x00004444;
|
||||
res[1] = 0x8888cccc;
|
||||
i = cpu_policy.extd.nscb; cpu_policy.extd.nscb = true; /* for AMD */
|
||||
rc = x86_emulate(&ctxt, &emulops);
|
||||
if ( (rc != X86EMUL_OKAY) ||
|
||||
(regs.eip != (unsigned long)&instr[5]) ||
|
||||
(gs_base != 0x0000111122224444UL) ||
|
||||
gs_base_shadow )
|
||||
goto fail;
|
||||
|
||||
cpu_policy.extd.nscb = i;
|
||||
emulops.write_segment = NULL;
|
||||
emulops.write_msr = NULL;
|
||||
#endif
|
||||
printf("okay\n");
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ bool emul_test_init(void)
|
||||
cpu_policy.feat.invpcid = true;
|
||||
cpu_policy.feat.adx = true;
|
||||
cpu_policy.feat.rdpid = true;
|
||||
cpu_policy.feat.lkgs = true;
|
||||
cpu_policy.feat.wrmsrns = true;
|
||||
cpu_policy.extd.clzero = true;
|
||||
|
||||
|
||||
@@ -745,8 +745,12 @@ decode_twobyte(struct x86_emulate_state *s,
|
||||
case 0:
|
||||
s->desc |= DstMem | SrcImplicit | Mov;
|
||||
break;
|
||||
case 6:
|
||||
if ( !(s->modrm_reg & 1) && mode_64bit() )
|
||||
{
|
||||
case 2: case 4:
|
||||
s->desc |= SrcMem16;
|
||||
s->desc |= SrcMem16;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -609,6 +609,7 @@ amd_like(const struct x86_emulate_ctxt *ctxt)
|
||||
#define vcpu_has_avx_vnni() (ctxt->cpuid->feat.avx_vnni)
|
||||
#define vcpu_has_avx512_bf16() (ctxt->cpuid->feat.avx512_bf16)
|
||||
#define vcpu_has_cmpccxadd() (ctxt->cpuid->feat.cmpccxadd)
|
||||
#define vcpu_has_lkgs() (ctxt->cpuid->feat.lkgs)
|
||||
#define vcpu_has_wrmsrns() (ctxt->cpuid->feat.wrmsrns)
|
||||
#define vcpu_has_avx_ifma() (ctxt->cpuid->feat.avx_ifma)
|
||||
#define vcpu_has_avx_vnni_int8() (ctxt->cpuid->feat.avx_vnni_int8)
|
||||
|
||||
@@ -2899,8 +2899,37 @@ x86_emulate(
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
generate_exception_if(true, X86_EXC_UD);
|
||||
|
||||
case 6: /* lkgs */
|
||||
generate_exception_if((modrm_reg & 1) || vex.pfx != vex_f2,
|
||||
X86_EXC_UD);
|
||||
generate_exception_if(!mode_64bit() || !mode_ring0(), X86_EXC_UD);
|
||||
vcpu_must_have(lkgs);
|
||||
fail_if(!ops->read_msr || !ops->write_segment || !ops->write_msr);
|
||||
if ( (rc = ops->read_msr(MSR_SHADOW_GS_BASE, &msr_val,
|
||||
ctxt)) != X86EMUL_OKAY ||
|
||||
(rc = ops->read_msr(MSR_GS_BASE, &sreg.base,
|
||||
ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
dst.orig_val = sreg.base; /* Preserve current GS Base. */
|
||||
if ( (rc = protmode_load_seg(x86_seg_gs, src.val, false, &sreg,
|
||||
ctxt, ops)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
/* Write new base into SHADOW_GS, zero extended from GDT/LDT. */
|
||||
if ( (rc = ops->write_msr(MSR_SHADOW_GS_BASE, sreg.base,
|
||||
ctxt, false)) != X86EMUL_OKAY ||
|
||||
(sreg.base = dst.orig_val, /* Reinstate original GS Base. */
|
||||
(rc = ops->write_segment(x86_seg_gs, &sreg,
|
||||
ctxt)) != X86EMUL_OKAY) )
|
||||
{
|
||||
/*
|
||||
* In real hardware, access to the registers cannot fail. It
|
||||
* is an error in Xen if the writes fail.
|
||||
*/
|
||||
ASSERT_UNREACHABLE();
|
||||
x86_emul_reset_event(ctxt);
|
||||
generate_exception(X86_EXC_DF, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -282,7 +282,8 @@ def crunch_numbers(state):
|
||||
# superpages, PCID and PKU are only available in 4 level paging.
|
||||
# NO_LMSL indicates the absense of Long Mode Segment Limits, which
|
||||
# have been dropped in hardware.
|
||||
LM: [CX16, PCID, LAHF_LM, PAGE1GB, PKU, NO_LMSL, AMX_TILE, CMPCCXADD],
|
||||
LM: [CX16, PCID, LAHF_LM, PAGE1GB, PKU, NO_LMSL, AMX_TILE, CMPCCXADD,
|
||||
LKGS],
|
||||
|
||||
# AMD K6-2+ and K6-III processors shipped with 3DNow+, beyond the
|
||||
# standard 3DNow in the earlier K6 processors.
|
||||
|
||||