mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
Further changes will rely on MSR entries being sorted, so add a test to assert it. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Acked-by: Andrew Cooper <andrew.cooper3@citrix.com>
803 lines
22 KiB
C
803 lines
22 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <err.h>
|
|
|
|
#include <xen-tools/libs.h>
|
|
#include <xen/asm/x86-defns.h>
|
|
#include <xen/asm/x86-vendors.h>
|
|
#include <xen/lib/x86/cpu-policy.h>
|
|
#include <xen/domctl.h>
|
|
|
|
#define XSTATE_FP_SSE (X86_XCR0_FP | X86_XCR0_SSE)
|
|
|
|
static unsigned int nr_failures;
|
|
#define fail(fmt, ...) \
|
|
({ \
|
|
nr_failures++; \
|
|
(void)printf(fmt, ##__VA_ARGS__); \
|
|
})
|
|
|
|
#define memdup(ptr) \
|
|
({ \
|
|
typeof(*(ptr)) *p_ = (ptr); \
|
|
void *n_ = malloc(sizeof(*p_)); \
|
|
\
|
|
if ( !n_ ) \
|
|
err(1, "%s malloc failure", __func__); \
|
|
\
|
|
memcpy(n_, p_, sizeof(*p_)); \
|
|
})
|
|
|
|
static void test_vendor_identification(void)
|
|
{
|
|
static const struct test {
|
|
union {
|
|
char ident[12];
|
|
struct {
|
|
uint32_t b, d, c;
|
|
};
|
|
};
|
|
unsigned int vendor;
|
|
} tests[] = {
|
|
/* The 1st entry should remain here to work around gcc bug 91667. */
|
|
{ { "" }, X86_VENDOR_UNKNOWN },
|
|
{ { " " }, X86_VENDOR_UNKNOWN },
|
|
{ { "xxxxxxxxxxxx" }, X86_VENDOR_UNKNOWN },
|
|
|
|
{ { "GenuineIntel" }, X86_VENDOR_INTEL },
|
|
{ { "AuthenticAMD" }, X86_VENDOR_AMD },
|
|
{ { "CentaurHauls" }, X86_VENDOR_CENTAUR },
|
|
{ { " Shanghai " }, X86_VENDOR_SHANGHAI },
|
|
{ { "HygonGenuine" }, X86_VENDOR_HYGON },
|
|
};
|
|
|
|
printf("Testing CPU vendor identification:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
const struct test *t = &tests[i];
|
|
unsigned int vendor = x86_cpuid_lookup_vendor(t->b, t->c, t->d);
|
|
|
|
if ( vendor != t->vendor )
|
|
fail(" Test '%.12s', expected vendor %u, got %u\n",
|
|
t->ident, t->vendor, vendor);
|
|
}
|
|
}
|
|
|
|
static bool leaves_are_sorted(const xen_cpuid_leaf_t *leaves, unsigned int nr)
|
|
{
|
|
for ( unsigned int i = 1; i < nr; ++i )
|
|
{
|
|
/* leaf index went backwards => not sorted. */
|
|
if ( leaves[i - 1].leaf > leaves[i].leaf )
|
|
return false;
|
|
|
|
/* leaf index went forwards => ok */
|
|
if ( leaves[i - 1].leaf < leaves[i].leaf )
|
|
continue;
|
|
|
|
/* leave index the same, subleaf didn't increase => not sorted. */
|
|
if ( leaves[i - 1].subleaf >= leaves[i].subleaf )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool msrs_are_sorted(const xen_msr_entry_t *entries, unsigned int nr)
|
|
{
|
|
for ( unsigned int i = 1; i < nr; ++i )
|
|
/* MSR index went backwards => not sorted. */
|
|
if ( entries[i - 1].idx > entries[i].idx )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void test_cpuid_current(void)
|
|
{
|
|
struct cpuid_policy p;
|
|
xen_cpuid_leaf_t leaves[CPUID_MAX_SERIALISED_LEAVES];
|
|
unsigned int nr = ARRAY_SIZE(leaves);
|
|
int rc;
|
|
|
|
printf("Testing CPUID on current CPU\n");
|
|
|
|
x86_cpuid_policy_fill_native(&p);
|
|
|
|
rc = x86_cpuid_copy_to_buffer(&p, leaves, &nr);
|
|
if ( rc != 0 )
|
|
return fail(" Serialise, expected rc 0, got %d\n", rc);
|
|
|
|
if ( !leaves_are_sorted(leaves, nr) )
|
|
return fail(" Leaves not sorted\n");
|
|
}
|
|
|
|
static void test_cpuid_serialise_success(void)
|
|
{
|
|
static const struct test {
|
|
struct cpuid_policy p;
|
|
const char *name;
|
|
unsigned int nr_leaves;
|
|
} tests[] = {
|
|
{
|
|
.name = "empty policy",
|
|
.nr_leaves = 4,
|
|
},
|
|
|
|
/* Leaf 4 serialisation stops at the first subleaf with type 0. */
|
|
{
|
|
.name = "empty leaf 4",
|
|
.p = {
|
|
.basic.max_leaf = 4,
|
|
},
|
|
.nr_leaves = 4 + 4,
|
|
},
|
|
{
|
|
.name = "partial leaf 4",
|
|
.p = {
|
|
.basic.max_leaf = 4,
|
|
.cache.subleaf[0].type = 1,
|
|
},
|
|
.nr_leaves = 4 + 4 + 1,
|
|
},
|
|
|
|
/* Leaf 7 serialisation stops at max_subleaf. */
|
|
{
|
|
.name = "empty leaf 7",
|
|
.p = {
|
|
.basic.max_leaf = 7,
|
|
},
|
|
.nr_leaves = 4 + 7,
|
|
},
|
|
{
|
|
.name = "partial leaf 7",
|
|
.p = {
|
|
.basic.max_leaf = 7,
|
|
.feat.max_subleaf = 1,
|
|
},
|
|
.nr_leaves = 4 + 7 + 1,
|
|
},
|
|
|
|
/* Leaf 0xb serialisation stops at the first subleaf with type 0. */
|
|
{
|
|
.name = "empty leaf 0xb",
|
|
.p = {
|
|
.basic.max_leaf = 0xb,
|
|
},
|
|
.nr_leaves = 4 + 0xb,
|
|
},
|
|
{
|
|
.name = "partial leaf 0xb",
|
|
.p = {
|
|
.basic.max_leaf = 0xb,
|
|
.topo.subleaf[0].type = 1,
|
|
},
|
|
.nr_leaves = 4 + 0xb + 1,
|
|
},
|
|
|
|
/*
|
|
* Leaf 0xd serialisation automatically has two leaves, and stops the
|
|
* highest bit set in {xcr0,xss}_{high,low}.
|
|
*/
|
|
{
|
|
.name = "empty leaf 0xd",
|
|
.p = {
|
|
.basic.max_leaf = 0xd,
|
|
},
|
|
.nr_leaves = 4 + 0xd + 1,
|
|
},
|
|
{
|
|
.name = "partial 0xd",
|
|
.p = {
|
|
.basic.max_leaf = 0xd,
|
|
.xstate.xcr0_low = 7,
|
|
},
|
|
.nr_leaves = 4 + 0xd + 1 + 1,
|
|
},
|
|
};
|
|
|
|
printf("Testing CPUID serialise success:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
const struct test *t = &tests[i];
|
|
unsigned int nr = t->nr_leaves;
|
|
xen_cpuid_leaf_t *leaves = malloc(nr * sizeof(*leaves));
|
|
int rc;
|
|
|
|
if ( !leaves )
|
|
err(1, "%s() malloc failure", __func__);
|
|
|
|
rc = x86_cpuid_copy_to_buffer(&t->p, leaves, &nr);
|
|
|
|
if ( rc != 0 )
|
|
{
|
|
fail(" Test %s, expected rc 0, got %d\n",
|
|
t->name, rc);
|
|
goto test_done;
|
|
}
|
|
|
|
if ( nr != t->nr_leaves )
|
|
{
|
|
fail(" Test %s, expected %u leaves, got %u\n",
|
|
t->name, t->nr_leaves, nr);
|
|
goto test_done;
|
|
}
|
|
|
|
if ( !leaves_are_sorted(leaves, nr) )
|
|
{
|
|
fail(" Test %s, leaves not sorted\n",
|
|
t->name);
|
|
goto test_done;
|
|
}
|
|
|
|
test_done:
|
|
free(leaves);
|
|
}
|
|
}
|
|
|
|
static void test_msr_serialise_success(void)
|
|
{
|
|
static const struct test {
|
|
struct msr_policy p;
|
|
const char *name;
|
|
unsigned int nr_msrs;
|
|
} tests[] = {
|
|
{
|
|
.name = "empty policy",
|
|
.nr_msrs = MSR_MAX_SERIALISED_ENTRIES,
|
|
},
|
|
};
|
|
|
|
printf("Testing MSR serialise success:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
const struct test *t = &tests[i];
|
|
unsigned int nr = t->nr_msrs;
|
|
xen_msr_entry_t *msrs = malloc(nr * sizeof(*msrs));
|
|
int rc;
|
|
|
|
if ( !msrs )
|
|
err(1, "%s() malloc failure", __func__);
|
|
|
|
rc = x86_msr_copy_to_buffer(&t->p, msrs, &nr);
|
|
|
|
if ( rc != 0 )
|
|
{
|
|
fail(" Test %s, expected rc 0, got %d\n",
|
|
t->name, rc);
|
|
goto test_done;
|
|
}
|
|
|
|
if ( nr != t->nr_msrs )
|
|
{
|
|
fail(" Test %s, expected %u msrs, got %u\n",
|
|
t->name, t->nr_msrs, nr);
|
|
goto test_done;
|
|
}
|
|
|
|
if ( !msrs_are_sorted(msrs, nr) )
|
|
{
|
|
fail(" Test %s, MSR entries not sorted\n",
|
|
t->name);
|
|
goto test_done;
|
|
}
|
|
|
|
test_done:
|
|
free(msrs);
|
|
}
|
|
}
|
|
|
|
static void test_cpuid_deserialise_failure(void)
|
|
{
|
|
static const struct test {
|
|
const char *name;
|
|
xen_cpuid_leaf_t leaf;
|
|
} tests[] = {
|
|
{
|
|
.name = "incorrect basic subleaf",
|
|
.leaf = { .leaf = 0, .subleaf = 0 },
|
|
},
|
|
{
|
|
.name = "incorrect hv1 subleaf",
|
|
.leaf = { .leaf = 0x40000000, .subleaf = 0 },
|
|
},
|
|
{
|
|
.name = "incorrect hv2 subleaf",
|
|
.leaf = { .leaf = 0x40000100, .subleaf = 0 },
|
|
},
|
|
{
|
|
.name = "incorrect extd subleaf",
|
|
.leaf = { .leaf = 0x80000000, .subleaf = 0 },
|
|
},
|
|
{
|
|
.name = "OoB basic leaf",
|
|
.leaf = { .leaf = CPUID_GUEST_NR_BASIC },
|
|
},
|
|
{
|
|
.name = "OoB cache leaf",
|
|
.leaf = { .leaf = 0x4, .subleaf = CPUID_GUEST_NR_CACHE },
|
|
},
|
|
{
|
|
.name = "OoB feat leaf",
|
|
.leaf = { .leaf = 0x7, .subleaf = CPUID_GUEST_NR_FEAT },
|
|
},
|
|
{
|
|
.name = "OoB topo leaf",
|
|
.leaf = { .leaf = 0xb, .subleaf = CPUID_GUEST_NR_TOPO },
|
|
},
|
|
{
|
|
.name = "OoB xstate leaf",
|
|
.leaf = { .leaf = 0xd, .subleaf = CPUID_GUEST_NR_XSTATE },
|
|
},
|
|
{
|
|
.name = "OoB extd leaf",
|
|
.leaf = { .leaf = 0x80000000 | CPUID_GUEST_NR_EXTD },
|
|
},
|
|
};
|
|
|
|
printf("Testing CPUID deserialise failure:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
const struct test *t = &tests[i];
|
|
uint32_t err_leaf, err_subleaf;
|
|
int rc;
|
|
|
|
/* No writes should occur. Use NULL to catch errors. */
|
|
rc = x86_cpuid_copy_from_buffer(NULL, &t->leaf, 1,
|
|
&err_leaf, &err_subleaf);
|
|
|
|
if ( rc != -ERANGE )
|
|
{
|
|
fail(" Test %s, expected rc %d, got %d\n",
|
|
t->name, -ERANGE, rc);
|
|
continue;
|
|
}
|
|
|
|
if ( err_leaf != t->leaf.leaf || err_subleaf != t->leaf.subleaf )
|
|
{
|
|
fail(" Test %s, expected err %08x:%08x, got %08x:%08x\n",
|
|
t->name, t->leaf.leaf, t->leaf.subleaf,
|
|
err_leaf, err_subleaf);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void test_msr_deserialise_failure(void)
|
|
{
|
|
static const struct test {
|
|
const char *name;
|
|
xen_msr_entry_t msr;
|
|
int rc;
|
|
} tests[] = {
|
|
{
|
|
.name = "bad msr index",
|
|
.msr = { .idx = 0xdeadc0de },
|
|
.rc = -ERANGE,
|
|
},
|
|
{
|
|
.name = "nonzero flags",
|
|
.msr = { .idx = 0xce, .flags = 1 },
|
|
.rc = -EINVAL,
|
|
},
|
|
{
|
|
.name = "truncated val",
|
|
.msr = { .idx = 0xce, .val = ~0ull },
|
|
.rc = -EOVERFLOW,
|
|
},
|
|
{
|
|
.name = "truncated val",
|
|
.msr = { .idx = 0x10a, .val = ~0ull },
|
|
.rc = -EOVERFLOW,
|
|
},
|
|
};
|
|
|
|
printf("Testing MSR deserialise failure:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
const struct test *t = &tests[i];
|
|
uint32_t err_msr;
|
|
int rc;
|
|
|
|
/* No writes should occur. Use NULL to catch errors. */
|
|
rc = x86_msr_copy_from_buffer(NULL, &t->msr, 1, &err_msr);
|
|
|
|
if ( rc != t->rc )
|
|
{
|
|
fail(" Test %s, expected rc %d, got %d\n",
|
|
t->name, t->rc, rc);
|
|
continue;
|
|
}
|
|
|
|
if ( err_msr != t->msr.idx )
|
|
{
|
|
fail(" Test %s, expected err_msr %#x, got %#x\n",
|
|
t->name, t->msr.idx, err_msr);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void test_cpuid_out_of_range_clearing(void)
|
|
{
|
|
static const struct test {
|
|
const char *name;
|
|
unsigned int nr_markers;
|
|
struct cpuid_policy p;
|
|
} tests[] = {
|
|
{
|
|
.name = "basic",
|
|
.nr_markers = 1,
|
|
.p = {
|
|
/* Retains marker in leaf 0. Clears others. */
|
|
.basic.max_leaf = 0,
|
|
.basic.vendor_ebx = 0xc2,
|
|
|
|
.basic.raw_fms = 0xc2,
|
|
.cache.raw[0].a = 0xc2,
|
|
.feat.raw[0].a = 0xc2,
|
|
.topo.raw[0].a = 0xc2,
|
|
.xstate.raw[0].a = 0xc2,
|
|
.xstate.raw[1].a = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "cache",
|
|
.nr_markers = 1,
|
|
.p = {
|
|
/* Retains marker in subleaf 0. Clears others. */
|
|
.basic.max_leaf = 4,
|
|
.cache.raw[0] = { .a = 1, .b = 0xc2 },
|
|
|
|
.cache.raw[1].b = 0xc2,
|
|
.feat.raw[0].a = 0xc2,
|
|
.topo.raw[0].a = 0xc2,
|
|
.xstate.raw[0].a = 0xc2,
|
|
.xstate.raw[1].a = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "feat",
|
|
.nr_markers = 1,
|
|
.p = {
|
|
/* Retains marker in subleaf 0. Clears others. */
|
|
.basic.max_leaf = 7,
|
|
.feat.raw[0].b = 0xc2,
|
|
|
|
.feat.raw[1].b = 0xc2,
|
|
.topo.raw[0].a = 0xc2,
|
|
.xstate.raw[0].a = 0xc2,
|
|
.xstate.raw[1].a = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "topo",
|
|
.nr_markers = 1,
|
|
.p = {
|
|
/* Retains marker in subleaf 0. Clears others. */
|
|
.basic.max_leaf = 0xb,
|
|
.topo.raw[0] = { .b = 0xc2, .c = 0x0100 },
|
|
|
|
.topo.raw[1].b = 0xc2,
|
|
.xstate.raw[0].a = 0xc2,
|
|
.xstate.raw[1].a = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "xstate x87",
|
|
.nr_markers = 2,
|
|
.p = {
|
|
/* First two subleaves always valid. Others cleared. */
|
|
.basic.max_leaf = 0xd,
|
|
.xstate.raw[0].a = 1,
|
|
.xstate.raw[0].b = 0xc2,
|
|
.xstate.raw[1].b = 0xc2,
|
|
|
|
.xstate.raw[2].b = 0xc2,
|
|
.xstate.raw[3].b = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "xstate sse",
|
|
.nr_markers = 2,
|
|
.p = {
|
|
/* First two subleaves always valid. Others cleared. */
|
|
.basic.max_leaf = 0xd,
|
|
.xstate.raw[0].a = 2,
|
|
.xstate.raw[0].b = 0xc2,
|
|
.xstate.raw[1].b = 0xc2,
|
|
|
|
.xstate.raw[2].b = 0xc2,
|
|
.xstate.raw[3].b = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "xstate avx",
|
|
.nr_markers = 3,
|
|
.p = {
|
|
/* Third subleaf also valid. Others cleared. */
|
|
.basic.max_leaf = 0xd,
|
|
.xstate.raw[0].a = 7,
|
|
.xstate.raw[0].b = 0xc2,
|
|
.xstate.raw[1].b = 0xc2,
|
|
.xstate.raw[2].b = 0xc2,
|
|
|
|
.xstate.raw[3].b = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "extd",
|
|
.nr_markers = 1,
|
|
.p = {
|
|
/* Retains marker in leaf 0. Clears others. */
|
|
.extd.max_leaf = 0,
|
|
.extd.vendor_ebx = 0xc2,
|
|
|
|
.extd.raw_fms = 0xc2,
|
|
},
|
|
},
|
|
};
|
|
|
|
printf("Testing CPUID out-of-range clearing:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
const struct test *t = &tests[i];
|
|
struct cpuid_policy *p = memdup(&t->p);
|
|
void *ptr;
|
|
unsigned int nr_markers;
|
|
|
|
x86_cpuid_policy_clear_out_of_range_leaves(p);
|
|
|
|
/* Count the number of 0xc2's still remaining. */
|
|
for ( ptr = p, nr_markers = 0;
|
|
(ptr = memchr(ptr, 0xc2, (void *)p + sizeof(*p) - ptr));
|
|
ptr++, nr_markers++ )
|
|
;
|
|
|
|
if ( nr_markers != t->nr_markers )
|
|
fail(" Test %s fail - expected %u markers, got %u\n",
|
|
t->name, t->nr_markers, nr_markers);
|
|
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
static void test_cpuid_maximum_leaf_shrinking(void)
|
|
{
|
|
static const struct test {
|
|
const char *name;
|
|
struct cpuid_policy p;
|
|
} tests[] = {
|
|
{
|
|
.name = "basic",
|
|
.p = {
|
|
/* Very basic information only. */
|
|
.basic.max_leaf = 1,
|
|
.basic.raw_fms = 0xc2,
|
|
},
|
|
},
|
|
{
|
|
.name = "cache",
|
|
.p = {
|
|
/* Cache subleaves present. */
|
|
.basic.max_leaf = 4,
|
|
.cache.subleaf[0].type = 1,
|
|
},
|
|
},
|
|
{
|
|
.name = "feat#0",
|
|
.p = {
|
|
/* Subleaf 0 only with some valid bit. */
|
|
.basic.max_leaf = 7,
|
|
.feat.max_subleaf = 0,
|
|
.feat.fsgsbase = 1,
|
|
},
|
|
},
|
|
{
|
|
.name = "feat#1",
|
|
.p = {
|
|
/* Subleaf 1 only with some valid bit. */
|
|
.basic.max_leaf = 7,
|
|
.feat.max_subleaf = 1,
|
|
.feat.avx_vnni = 1,
|
|
},
|
|
},
|
|
{
|
|
.name = "topo",
|
|
.p = {
|
|
/* Topology subleaves present. */
|
|
.basic.max_leaf = 0xb,
|
|
.topo.subleaf[0].type = 1,
|
|
},
|
|
},
|
|
{
|
|
.name = "xstate",
|
|
.p = {
|
|
/* First subleaf always valid (and then non-zero). */
|
|
.basic.max_leaf = 0xd,
|
|
.xstate.xcr0_low = XSTATE_FP_SSE,
|
|
},
|
|
},
|
|
{
|
|
.name = "extd",
|
|
.p = {
|
|
/* Commonly available information only. */
|
|
.extd.max_leaf = 0x80000008,
|
|
.extd.maxphysaddr = 0x28,
|
|
.extd.maxlinaddr = 0x30,
|
|
},
|
|
},
|
|
};
|
|
|
|
printf("Testing CPUID maximum leaf shrinking:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
const struct test *t = &tests[i];
|
|
struct cpuid_policy *p = memdup(&t->p);
|
|
|
|
p->basic.max_leaf = ARRAY_SIZE(p->basic.raw) - 1;
|
|
p->feat.max_subleaf = ARRAY_SIZE(p->feat.raw) - 1;
|
|
p->extd.max_leaf = 0x80000000 | (ARRAY_SIZE(p->extd.raw) - 1);
|
|
|
|
x86_cpuid_policy_shrink_max_leaves(p);
|
|
|
|
/* Check the the resulting max (sub)leaf values against expecations. */
|
|
if ( p->basic.max_leaf != t->p.basic.max_leaf )
|
|
fail(" Test %s basic fail - expected %#x, got %#x\n",
|
|
t->name, t->p.basic.max_leaf, p->basic.max_leaf);
|
|
|
|
if ( p->extd.max_leaf != t->p.extd.max_leaf )
|
|
fail(" Test %s extd fail - expected %#x, got %#x\n",
|
|
t->name, t->p.extd.max_leaf, p->extd.max_leaf);
|
|
|
|
if ( p->feat.max_subleaf != t->p.feat.max_subleaf )
|
|
fail(" Test %s feat fail - expected %#x, got %#x\n",
|
|
t->name, t->p.feat.max_subleaf, p->feat.max_subleaf);
|
|
|
|
free(p);
|
|
}
|
|
}
|
|
|
|
static void test_is_compatible_success(void)
|
|
{
|
|
static struct test {
|
|
const char *name;
|
|
struct cpuid_policy host_cpuid;
|
|
struct cpuid_policy guest_cpuid;
|
|
struct msr_policy host_msr;
|
|
struct msr_policy guest_msr;
|
|
} tests[] = {
|
|
{
|
|
.name = "Host CPUID faulting, Guest not",
|
|
.host_msr = {
|
|
.platform_info.cpuid_faulting = true,
|
|
},
|
|
},
|
|
{
|
|
.name = "Host CPUID faulting, Guest wanted",
|
|
.host_msr = {
|
|
.platform_info.cpuid_faulting = true,
|
|
},
|
|
.guest_msr = {
|
|
.platform_info.cpuid_faulting = true,
|
|
},
|
|
},
|
|
};
|
|
struct cpu_policy_errors no_errors = INIT_CPU_POLICY_ERRORS;
|
|
|
|
printf("Testing policy compatibility success:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
struct test *t = &tests[i];
|
|
struct cpu_policy sys = {
|
|
&t->host_cpuid,
|
|
&t->host_msr,
|
|
}, new = {
|
|
&t->guest_cpuid,
|
|
&t->guest_msr,
|
|
};
|
|
struct cpu_policy_errors e;
|
|
int res = x86_cpu_policies_are_compatible(&sys, &new, &e);
|
|
|
|
/* Check the expected error output. */
|
|
if ( res != 0 || memcmp(&no_errors, &e, sizeof(no_errors)) )
|
|
fail(" Test '%s' expected no errors\n"
|
|
" got res %d { leaf %08x, subleaf %08x, msr %08x }\n",
|
|
t->name, res, e.leaf, e.subleaf, e.msr);
|
|
}
|
|
}
|
|
|
|
static void test_is_compatible_failure(void)
|
|
{
|
|
static struct test {
|
|
const char *name;
|
|
struct cpuid_policy host_cpuid;
|
|
struct cpuid_policy guest_cpuid;
|
|
struct msr_policy host_msr;
|
|
struct msr_policy guest_msr;
|
|
struct cpu_policy_errors e;
|
|
} tests[] = {
|
|
{
|
|
.name = "Host basic.max_leaf out of range",
|
|
.guest_cpuid.basic.max_leaf = 1,
|
|
.e = { 0, -1, -1 },
|
|
},
|
|
{
|
|
.name = "Host extd.max_leaf out of range",
|
|
.guest_cpuid.extd.max_leaf = 1,
|
|
.e = { 0x80000000, -1, -1 },
|
|
},
|
|
{
|
|
.name = "Host no CPUID faulting, Guest wanted",
|
|
.guest_msr = {
|
|
.platform_info.cpuid_faulting = true,
|
|
},
|
|
.e = { -1, -1, 0xce },
|
|
},
|
|
};
|
|
|
|
printf("Testing policy compatibility failure:\n");
|
|
|
|
for ( size_t i = 0; i < ARRAY_SIZE(tests); ++i )
|
|
{
|
|
struct test *t = &tests[i];
|
|
struct cpu_policy sys = {
|
|
&t->host_cpuid,
|
|
&t->host_msr,
|
|
}, new = {
|
|
&t->guest_cpuid,
|
|
&t->guest_msr,
|
|
};
|
|
struct cpu_policy_errors e;
|
|
int res = x86_cpu_policies_are_compatible(&sys, &new, &e);
|
|
|
|
/* Check the expected error output. */
|
|
if ( res == 0 || memcmp(&t->e, &e, sizeof(t->e)) )
|
|
fail(" Test '%s' res %d\n"
|
|
" expected { leaf %08x, subleaf %08x, msr %08x }\n"
|
|
" got { leaf %08x, subleaf %08x, msr %08x }\n",
|
|
t->name, res,
|
|
t->e.leaf, t->e.subleaf, t->e.msr,
|
|
e.leaf, e.subleaf, e.msr);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
printf("CPU Policy unit tests\n");
|
|
|
|
test_vendor_identification();
|
|
|
|
test_cpuid_current();
|
|
test_cpuid_serialise_success();
|
|
test_cpuid_deserialise_failure();
|
|
test_cpuid_out_of_range_clearing();
|
|
test_cpuid_maximum_leaf_shrinking();
|
|
|
|
test_msr_serialise_success();
|
|
test_msr_deserialise_failure();
|
|
|
|
test_is_compatible_success();
|
|
test_is_compatible_failure();
|
|
|
|
if ( nr_failures )
|
|
printf("Done: %u failures\n", nr_failures);
|
|
else
|
|
printf("Done: all ok\n");
|
|
|
|
return !!nr_failures;
|
|
}
|