mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
tools/cpu-policy: Add unit tests
These will be extended with further libx86 work. Fix the sorting of the CPUID_GUEST_NR_* constants, noticed while writing the unit tests. Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
f48363c9ca
commit
919ddc3c0e
5 files changed
+289
-1
No files matched your search
@@ -5,6 +5,7 @@ CFLAGS += $(CFLAGS_libxenctrl)
|
||||
LDLIBS += $(LDLIBS_libxenctrl)
|
||||
|
||||
SUBDIRS-y :=
|
||||
SUBDIRS-$(CONFIG_X86) += cpu-policy
|
||||
SUBDIRS-$(CONFIG_X86) += mce-test
|
||||
SUBDIRS-y += mem-sharing
|
||||
ifneq ($(clang),y)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
test-cpu-policy
|
||||
@@ -0,0 +1,28 @@
|
||||
XEN_ROOT = $(CURDIR)/../../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
.PHONY: all
|
||||
all: test-cpu-policy
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) -f -- *.o .*.d .*.d2 test-cpu-policy
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
$(RM) -f -- *~
|
||||
|
||||
.PHONY: install
|
||||
install: all
|
||||
|
||||
.PHONY: uninstall
|
||||
|
||||
CFLAGS += -Werror $(CFLAGS_xeninclude) -D__XEN_TOOLS__ -O3
|
||||
CFLAGS += $(APPEND_CFLAGS)
|
||||
|
||||
vpath %.c ../../../xen/lib/x86
|
||||
|
||||
test-cpu-policy: test-cpu-policy.o msr.o cpuid.o
|
||||
$(CC) $(CFLAGS) $^ -o $@
|
||||
|
||||
-include $(DEPS_INCLUDE)
|
||||
@@ -0,0 +1,258 @@
|
||||
#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/lib/x86/cpuid.h>
|
||||
#include <xen/lib/x86/msr.h>
|
||||
#include <xen/domctl.h>
|
||||
|
||||
static unsigned int nr_failures;
|
||||
#define fail(fmt, ...) \
|
||||
({ \
|
||||
nr_failures++; \
|
||||
printf(fmt, ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 = ~0u, err_subleaf = ~0u;
|
||||
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,
|
||||
},
|
||||
};
|
||||
|
||||
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 = ~0u;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
printf("CPU Policy unit tests\n");
|
||||
|
||||
test_cpuid_serialise_success();
|
||||
test_msr_serialise_success();
|
||||
|
||||
test_cpuid_deserialise_failure();
|
||||
test_msr_deserialise_failure();
|
||||
|
||||
if ( nr_failures )
|
||||
printf("Done: %u failures\n", nr_failures);
|
||||
else
|
||||
printf("Done: all ok\n");
|
||||
|
||||
return !!nr_failures;
|
||||
}
|
||||
@@ -66,8 +66,8 @@ static inline void cpuid_count_leaf(
|
||||
#undef XCHG
|
||||
|
||||
#define CPUID_GUEST_NR_BASIC (0xdu + 1)
|
||||
#define CPUID_GUEST_NR_FEAT (0u + 1)
|
||||
#define CPUID_GUEST_NR_CACHE (5u + 1)
|
||||
#define CPUID_GUEST_NR_FEAT (0u + 1)
|
||||
#define CPUID_GUEST_NR_TOPO (1u + 1)
|
||||
#define CPUID_GUEST_NR_XSTATE (62u + 1)
|
||||
#define CPUID_GUEST_NR_EXTD_INTEL (0x8u + 1)
|
||||
|
||||