x86/cpuid: Introduce guest_cpuid() and struct cpuid_leaf

Longterm, pv_cpuid() and hvm_cpuid() will be merged into a single
guest_cpuid(), which is also capable of working outside of current context.

To aid this transtion, introduce guest_cpuid() with the intended API, which
simply defers back to pv_cpuid() or hvm_cpuid() as appropriate.

Introduce struct cpuid_leaf which is used to represent the results of a CPUID
query in a more efficient mannor than passing four pointers through the
calltree.

Update all codepaths which should use the new guest_cpuid() API.  These are
the codepaths which have variable inputs, and (other than some specific
x86_emulate() cases) all pertain to servicing a CPUID instruction from a
guest.

The other codepaths using {pv,hvm}_cpuid() with fixed inputs will later be
adjusted to read their data straight from the policy block.

No intended functional change.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Reviewed-by: Kevin Tian <kevint.tian@intel.com>
This commit is contained in:
Andrew Cooper committed 2017-01-11 11:59:02 +00:00
1 parent ffc103c223
commit e94ce53418
12 files changed
+136 -129

No files matched your search

+7 -8
View File
@@ -41,22 +41,21 @@ bool emul_test_make_stack_executable(void)
}
int emul_test_cpuid(
unsigned int *eax,
unsigned int *ebx,
unsigned int *ecx,
unsigned int *edx,
uint32_t leaf,
uint32_t subleaf,
struct cpuid_leaf *res,
struct x86_emulate_ctxt *ctxt)
{
unsigned int leaf = *eax;
asm ("cpuid" : "+a" (*eax), "+c" (*ecx), "=d" (*edx), "=b" (*ebx));
asm ("cpuid"
: "=a" (res->a), "=b" (res->b), "=c" (res->c), "=d" (res->d)
: "a" (leaf), "c" (subleaf));
/*
* The emulator doesn't itself use MOVBE, so we can always run the
* respective tests.
*/
if ( leaf == 1 )
*ecx |= 1U << 22;
res->c |= 1U << 22;
return X86EMUL_OKAY;
}
+29 -31
View File
@@ -58,61 +58,59 @@ static inline uint64_t xgetbv(uint32_t xcr)
}
#define cache_line_size() ({ \
unsigned int eax = 1, ebx, ecx = 0, edx; \
emul_test_cpuid(&eax, &ebx, &ecx, &edx, NULL); \
edx & (1U << 19) ? (ebx >> 5) & 0x7f8 : 0; \
struct cpuid_leaf res; \
emul_test_cpuid(1, 0, &res, NULL); \
res.d & (1U << 19) ? (res.b >> 5) & 0x7f8 : 0; \
})
#define cpu_has_mmx ({ \
unsigned int eax = 1, ecx = 0, edx; \
emul_test_cpuid(&eax, &ecx, &ecx, &edx, NULL); \
(edx & (1U << 23)) != 0; \
struct cpuid_leaf res; \
emul_test_cpuid(1, 0, &res, NULL); \
(res.d & (1U << 23)) != 0; \
})
#define cpu_has_sse ({ \
unsigned int eax = 1, ecx = 0, edx; \
emul_test_cpuid(&eax, &ecx, &ecx, &edx, NULL); \
(edx & (1U << 25)) != 0; \
struct cpuid_leaf res; \
emul_test_cpuid(1, 0, &res, NULL); \
(res.d & (1U << 25)) != 0; \
})
#define cpu_has_sse2 ({ \
unsigned int eax = 1, ecx = 0, edx; \
emul_test_cpuid(&eax, &ecx, &ecx, &edx, NULL); \
(edx & (1U << 26)) != 0; \
struct cpuid_leaf res; \
emul_test_cpuid(1, 0, &res, NULL); \
(res.d & (1U << 26)) != 0; \
})
#define cpu_has_xsave ({ \
unsigned int eax = 1, ecx = 0; \
emul_test_cpuid(&eax, &eax, &ecx, &eax, NULL); \
struct cpuid_leaf res; \
emul_test_cpuid(1, 0, &res, NULL); \
/* Intentionally checking OSXSAVE here. */ \
(ecx & (1U << 27)) != 0; \
(res.c & (1U << 27)) != 0; \
})
#define cpu_has_avx ({ \
unsigned int eax = 1, ecx = 0; \
emul_test_cpuid(&eax, &eax, &ecx, &eax, NULL); \
if ( !(ecx & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
ecx = 0; \
(ecx & (1U << 28)) != 0; \
struct cpuid_leaf res; \
emul_test_cpuid(1, 0, &res, NULL); \
if ( !(res.c & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
res.c = 0; \
(res.c & (1U << 28)) != 0; \
})
#define cpu_has_avx2 ({ \
unsigned int eax = 1, ebx, ecx = 0; \
emul_test_cpuid(&eax, &ebx, &ecx, &eax, NULL); \
if ( !(ecx & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
ebx = 0; \
struct cpuid_leaf res; \
emul_test_cpuid(1, 0, &res, NULL); \
if ( !(res.c & (1U << 27)) || ((xgetbv(0) & 6) != 6) ) \
res.b = 0; \
else { \
eax = 7, ecx = 0; \
emul_test_cpuid(&eax, &ebx, &ecx, &eax, NULL); \
emul_test_cpuid(7, 0, &res, NULL); \
} \
(ebx & (1U << 5)) != 0; \
(res.b & (1U << 5)) != 0; \
})
int emul_test_cpuid(
unsigned int *eax,
unsigned int *ebx,
unsigned int *ecx,
unsigned int *edx,
uint32_t leaf,
uint32_t subleaf,
struct cpuid_leaf *res,
struct x86_emulate_ctxt *ctxt);
int emul_test_read_cr(
+35
View File
@@ -1,5 +1,6 @@
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/sched.h>
#include <asm/cpuid.h>
#include <asm/hvm/hvm.h>
#include <asm/hvm/vmx/vmcs.h>
@@ -17,6 +18,8 @@ uint32_t __read_mostly raw_featureset[FSCAPINTS];
uint32_t __read_mostly pv_featureset[FSCAPINTS];
uint32_t __read_mostly hvm_featureset[FSCAPINTS];
#define EMPTY_LEAF ((struct cpuid_leaf){})
static void __init sanitise_featureset(uint32_t *fs)
{
/* for_each_set_bit() uses unsigned longs. Extend with zeroes. */
@@ -215,6 +218,38 @@ const uint32_t * __init lookup_deep_deps(uint32_t feature)
return NULL;
}
void guest_cpuid(const struct vcpu *v, uint32_t leaf,
uint32_t subleaf, struct cpuid_leaf *res)
{
const struct domain *d = v->domain;
*res = EMPTY_LEAF;
/* {hvm,pv}_cpuid() have this expectation. */
ASSERT(v == current);
if ( is_hvm_domain(d) )
{
res->c = subleaf;
hvm_cpuid(leaf, &res->a, &res->b, &res->c, &res->d);
}
else
{
struct cpu_user_regs regs = *guest_cpu_user_regs();
regs._eax = leaf;
regs._ecx = subleaf;
pv_cpuid(&regs);
res->a = regs._eax;
res->b = regs._ebx;
res->c = regs._ecx;
res->d = regs._edx;
}
}
static void __init __maybe_unused build_assertions(void)
{
BUILD_BUG_ON(ARRAY_SIZE(known_features) != FSCAPINTS);
+3 -7
View File
@@ -1552,12 +1552,8 @@ static int hvmemul_wbinvd(
return X86EMUL_OKAY;
}
int hvmemul_cpuid(
unsigned int *eax,
unsigned int *ebx,
unsigned int *ecx,
unsigned int *edx,
struct x86_emulate_ctxt *ctxt)
int hvmemul_cpuid(uint32_t leaf, uint32_t subleaf,
struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt)
{
/*
* x86_emulate uses this function to query CPU features for its own internal
@@ -1568,7 +1564,7 @@ int hvmemul_cpuid(
hvm_check_cpuid_faulting(current) )
return X86EMUL_EXCEPTION;
hvm_cpuid(*eax, eax, ebx, ecx, edx);
guest_cpuid(current, leaf, subleaf, res);
return X86EMUL_OKAY;
}
+10 -13
View File
@@ -1572,23 +1572,20 @@ static void svm_fpu_dirty_intercept(void)
static void svm_vmexit_do_cpuid(struct cpu_user_regs *regs)
{
unsigned int eax, ebx, ecx, edx, inst_len;
struct vcpu *curr = current;
unsigned int inst_len;
struct cpuid_leaf res;
if ( (inst_len = __get_instruction_length(current, INSTR_CPUID)) == 0 )
if ( (inst_len = __get_instruction_length(curr, INSTR_CPUID)) == 0 )
return;
eax = regs->_eax;
ebx = regs->_ebx;
ecx = regs->_ecx;
edx = regs->_edx;
guest_cpuid(curr, regs->_eax, regs->_ecx, &res);
HVMTRACE_5D(CPUID, regs->_eax, res.a, res.b, res.c, res.d);
hvm_cpuid(regs->_eax, &eax, &ebx, &ecx, &edx);
HVMTRACE_5D(CPUID, regs->_eax, eax, ebx, ecx, edx);
regs->rax = eax;
regs->rbx = ebx;
regs->rcx = ecx;
regs->rdx = edx;
regs->rax = res.a;
regs->rbx = res.b;
regs->rcx = res.c;
regs->rdx = res.d;
__update_guest_eip(regs, inst_len);
}
+10 -25
View File
@@ -2361,8 +2361,9 @@ static void vmx_fpu_dirty_intercept(void)
static int vmx_do_cpuid(struct cpu_user_regs *regs)
{
unsigned int eax, ebx, ecx, edx;
unsigned int leaf, subleaf;
struct vcpu *curr = current;
uint32_t leaf = regs->_eax, subleaf = regs->_ecx;
struct cpuid_leaf res;
if ( hvm_check_cpuid_faulting(current) )
{
@@ -2370,21 +2371,13 @@ static int vmx_do_cpuid(struct cpu_user_regs *regs)
return 1; /* Don't advance the guest IP! */
}
eax = regs->_eax;
ebx = regs->_ebx;
ecx = regs->_ecx;
edx = regs->_edx;
guest_cpuid(curr, leaf, subleaf, &res);
HVMTRACE_5D(CPUID, leaf, res.a, res.b, res.c, res.d);
leaf = regs->_eax;
subleaf = regs->_ecx;
hvm_cpuid(leaf, &eax, &ebx, &ecx, &edx);
HVMTRACE_5D(CPUID, leaf, eax, ebx, ecx, edx);
regs->rax = eax;
regs->rbx = ebx;
regs->rcx = ecx;
regs->rdx = edx;
regs->rax = res.a;
regs->rbx = res.b;
regs->rcx = res.c;
regs->rdx = res.d;
return hvm_monitor_cpuid(get_instruction_length(), leaf, subleaf);
}
@@ -3559,15 +3552,7 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
}
case EXIT_REASON_CPUID:
{
int rc;
if ( is_pvh_vcpu(v) )
{
pv_cpuid(regs);
rc = 0;
}
else
rc = vmx_do_cpuid(regs);
int rc = vmx_do_cpuid(regs);
/*
* rc < 0 error in monitor/vm_event, crash
+11 -14
View File
@@ -1412,6 +1412,7 @@ static int emulate_forced_invalid_op(struct cpu_user_regs *regs)
{
char sig[5], instr[2];
unsigned long eip, rc;
struct cpuid_leaf res;
eip = regs->rip;
@@ -1444,7 +1445,12 @@ static int emulate_forced_invalid_op(struct cpu_user_regs *regs)
eip += sizeof(instr);
pv_cpuid(regs);
guest_cpuid(current, regs->_eax, regs->_ecx, &res);
regs->rax = res.a;
regs->rbx = res.b;
regs->rcx = res.c;
regs->rdx = res.d;
instruction_done(regs, eip);
@@ -3254,10 +3260,10 @@ static int priv_op_wbinvd(struct x86_emulate_ctxt *ctxt)
return X86EMUL_OKAY;
}
int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
unsigned int *edx, struct x86_emulate_ctxt *ctxt)
int pv_emul_cpuid(uint32_t leaf, uint32_t subleaf,
struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt)
{
struct cpu_user_regs regs = *ctxt->regs;
const struct vcpu *curr = current;
/*
* x86_emulate uses this function to query CPU features for its own
@@ -3266,7 +3272,6 @@ int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
*/
if ( ctxt->opcode == X86EMUL_OPC(0x0f, 0xa2) )
{
const struct vcpu *curr = current;
/* If cpuid faulting is enabled and CPL>0 leave the #GP untouched. */
if ( curr->arch.cpuid_faulting &&
@@ -3274,15 +3279,7 @@ int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
return X86EMUL_EXCEPTION;
}
regs._eax = *eax;
regs._ecx = *ecx;
pv_cpuid(&regs);
*eax = regs._eax;
*ebx = regs._ebx;
*ecx = regs._ecx;
*edx = regs._edx;
guest_cpuid(curr, leaf, subleaf, res);
return X86EMUL_OKAY;
}
+15 -19
View File
@@ -1265,19 +1265,19 @@ static bool vcpu_has(
struct x86_emulate_ctxt *ctxt,
const struct x86_emulate_ops *ops)
{
unsigned int ebx = 0, ecx = 0, edx = 0;
struct cpuid_leaf res;
int rc = X86EMUL_OKAY;
fail_if(!ops->cpuid);
rc = ops->cpuid(&eax, &ebx, &ecx, &edx, ctxt);
rc = ops->cpuid(eax, 0, &res, ctxt);
if ( rc == X86EMUL_OKAY )
{
switch ( reg )
{
case EAX: reg = eax; break;
case EBX: reg = ebx; break;
case ECX: reg = ecx; break;
case EDX: reg = edx; break;
case EAX: reg = res.a; break;
case EBX: reg = res.b; break;
case ECX: reg = res.c; break;
case EDX: reg = res.d; break;
default: BUG();
}
if ( !(reg & (1U << bit)) )
@@ -2722,6 +2722,7 @@ x86_emulate(
{
enum x86_segment seg;
struct segment_register cs, sreg;
struct cpuid_leaf cpuid_leaf;
case 0x00 ... 0x05: add: /* add */
emulate_2op_SrcV("add", src, dst, _regs._eflags);
@@ -4525,15 +4526,14 @@ x86_emulate(
case 0xfc: /* clzero */
{
unsigned int eax = 1, ebx = 0, dummy = 0;
unsigned long zero = 0;
base = ad_bytes == 8 ? _regs.r(ax) :
ad_bytes == 4 ? _regs._eax : _regs.ax;
limit = 0;
if ( vcpu_has_clflush() &&
ops->cpuid(&eax, &ebx, &dummy, &dummy, ctxt) == X86EMUL_OKAY )
limit = ((ebx >> 8) & 0xff) * 8;
ops->cpuid(1, 0, &cpuid_leaf, ctxt) == X86EMUL_OKAY )
limit = ((cpuid_leaf.b >> 8) & 0xff) * 8;
generate_exception_if(limit < sizeof(long) ||
(limit & (limit - 1)), EXC_UD);
base &= ~(limit - 1);
@@ -5265,22 +5265,18 @@ x86_emulate(
dst.val = test_cc(b, _regs._eflags);
break;
case X86EMUL_OPC(0x0f, 0xa2): /* cpuid */ {
unsigned int eax = _regs._eax, ebx = _regs._ebx;
unsigned int ecx = _regs._ecx, edx = _regs._edx;
case X86EMUL_OPC(0x0f, 0xa2): /* cpuid */
fail_if(ops->cpuid == NULL);
rc = ops->cpuid(&eax, &ebx, &ecx, &edx, ctxt);
rc = ops->cpuid(_regs._eax, _regs._ecx, &cpuid_leaf, ctxt);
generate_exception_if(rc == X86EMUL_EXCEPTION,
EXC_GP, 0); /* CPUID Faulting? */
if ( rc != X86EMUL_OKAY )
goto done;
_regs.r(ax) = eax;
_regs.r(bx) = ebx;
_regs.r(cx) = ecx;
_regs.r(dx) = edx;
_regs.r(ax) = cpuid_leaf.a;
_regs.r(bx) = cpuid_leaf.b;
_regs.r(cx) = cpuid_leaf.c;
_regs.r(dx) = cpuid_leaf.d;
break;
}
case X86EMUL_OPC(0x0f, 0xa3): bt: /* bt */
emulate_2op_SrcV_nobyte("bt", src, dst, _regs._eflags);
+8 -4
View File
@@ -164,6 +164,11 @@ enum x86_emulate_fpu_type {
X86EMUL_FPU_ymm /* AVX/XOP instruction set (%ymm0-%ymm7/15) */
};
struct cpuid_leaf
{
uint32_t a, b, c, d;
};
struct x86_emulate_state;
/*
@@ -415,10 +420,9 @@ struct x86_emulate_ops
* #GP[0]. Used to implement CPUID faulting.
*/
int (*cpuid)(
unsigned int *eax,
unsigned int *ebx,
unsigned int *ecx,
unsigned int *edx,
uint32_t leaf,
uint32_t subleaf,
struct cpuid_leaf *res,
struct x86_emulate_ctxt *ctxt);
/*
+4
View File
@@ -17,6 +17,7 @@
#ifndef __ASSEMBLY__
#include <xen/types.h>
#include <asm/x86_emulate.h>
#include <public/sysctl.h>
extern const uint32_t known_features[FSCAPINTS];
@@ -64,6 +65,9 @@ extern struct cpuidmasks cpuidmask_defaults;
/* Whether or not cpuid faulting is available for the current domain. */
DECLARE_PER_CPU(bool, cpuid_faulting_enabled);
void guest_cpuid(const struct vcpu *v, uint32_t leaf,
uint32_t subleaf, struct cpuid_leaf *res);
#endif /* __ASSEMBLY__ */
#endif /* !__X86_CPUID_H__ */
+2 -6
View File
@@ -57,12 +57,8 @@ void hvm_emulate_init_per_insn(
unsigned int insn_bytes);
void hvm_emulate_writeback(
struct hvm_emulate_ctxt *hvmemul_ctxt);
int hvmemul_cpuid(
unsigned int *eax,
unsigned int *ebx,
unsigned int *ecx,
unsigned int *edx,
struct x86_emulate_ctxt *ctxt);
int hvmemul_cpuid(uint32_t leaf, uint32_t subleaf,
struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt);
struct segment_register *hvmemul_get_seg_reg(
enum x86_segment seg,
struct hvm_emulate_ctxt *hvmemul_ctxt);
+2 -2
View File
@@ -504,8 +504,8 @@ extern int mmcfg_intercept_write(enum x86_segment seg,
void *p_data,
unsigned int bytes,
struct x86_emulate_ctxt *ctxt);
int pv_emul_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx,
unsigned int *edx, struct x86_emulate_ctxt *ctxt);
int pv_emul_cpuid(uint32_t leaf, uint32_t subleaf,
struct cpuid_leaf *res, struct x86_emulate_ctxt *ctxt);
int ptwr_do_page_fault(struct vcpu *, unsigned long,
struct cpu_user_regs *);
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.