Introduce a test for sPAPR hypercalls, starting with the three hypercalls H_SET_SPRG0, H_PAGE_INIT and H_RANDOM. Signed-off-by: Thomas Huth <thuth@xxxxxxxxxx> --- v2: - Rebased to the final version of Andrew's initial ppc64 support patches that got merged yesterday - Added a test for the H_RANDOM hypercall lib/powerpc/asm/hcall.h | 3 + powerpc/Makefile.common | 5 +- powerpc/spapr_hcall.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++ powerpc/unittests.cfg | 3 + 4 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 powerpc/spapr_hcall.c diff --git a/lib/powerpc/asm/hcall.h b/lib/powerpc/asm/hcall.h index 750c655..f6f9ea8 100644 --- a/lib/powerpc/asm/hcall.h +++ b/lib/powerpc/asm/hcall.h @@ -15,8 +15,11 @@ #define H_PRIVILEGE -3 #define H_PARAMETER -4 +#define H_SET_SPRG0 0x24 #define H_SET_DABR 0x28 +#define H_PAGE_INIT 0x2c #define H_PUT_TERM_CHAR 0x58 +#define H_RANDOM 0x300 #ifndef __ASSEMBLY__ /* diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common index b526668..2ce6494 100644 --- a/powerpc/Makefile.common +++ b/powerpc/Makefile.common @@ -5,7 +5,8 @@ # tests-common = \ - $(TEST_DIR)/selftest.elf + $(TEST_DIR)/selftest.elf \ + $(TEST_DIR)/spapr_hcall.elf all: $(TEST_DIR)/boot_rom.bin test_cases @@ -63,3 +64,5 @@ generated_files = $(asm-offsets) test_cases: $(generated_files) $(tests-common) $(tests) $(TEST_DIR)/selftest.elf: $(cstart.o) $(reloc.o) $(TEST_DIR)/selftest.o + +$(TEST_DIR)/spapr_hcall.elf: $(cstart.o) $(reloc.o) $(TEST_DIR)/spapr_hcall.o diff --git a/powerpc/spapr_hcall.c b/powerpc/spapr_hcall.c new file mode 100644 index 0000000..1474595 --- /dev/null +++ b/powerpc/spapr_hcall.c @@ -0,0 +1,168 @@ +/* + * Test sPAPR hypervisor calls (aka. h-calls) + * + * Copyright 2016 Thomas Huth, Red Hat Inc. + * + * This work is licensed under the terms of the GNU LGPL, version 2. + */ +#include <libcflat.h> +#include <util.h> +#include <alloc.h> +#include <asm/hcall.h> + +#define PAGE_SIZE 4096 + +#define H_ZERO_PAGE (1UL << (63-48)) +#define H_COPY_PAGE (1UL << (63-49)) + +#define mfspr(nr) ({ \ + uint64_t ret; \ + asm volatile("mfspr %0,%1" : "=r"(ret) : "i"(nr)); \ + ret; \ +}) + +#define SPR_SPRG0 0x110 + +/** + * Test the H_SET_SPRG0 h-call by setting some values and checking whether + * the SPRG0 register contains the correct values afterwards + */ +static void test_h_set_sprg0(int argc, char **argv) +{ + uint64_t sprg0, sprg0_orig; + int rc; + + if (argc > 1) + report_abort("Unsupported argument: '%s'", argv[1]); + + sprg0_orig = mfspr(SPR_SPRG0); + + rc = hcall(H_SET_SPRG0, 0xcafebabedeadbeefULL); + sprg0 = mfspr(SPR_SPRG0); + report("sprg0 = 0xcafebabedeadbeef", + rc == H_SUCCESS && sprg0 == 0xcafebabedeadbeefULL); + + rc = hcall(H_SET_SPRG0, 0xaaaaaaaa55555555ULL); + sprg0 = mfspr(SPR_SPRG0); + report("sprg0 = 0xaaaaaaaa55555555", + rc == H_SUCCESS && sprg0 == 0xaaaaaaaa55555555ULL); + + rc = hcall(H_SET_SPRG0, sprg0_orig); + sprg0 = mfspr(SPR_SPRG0); + report("sprg0 = 0x%llx", + rc == H_SUCCESS && sprg0 == sprg0_orig, sprg0_orig); +} + +/** + * Test the H_PAGE_INIT h-call by using it to clear and to copy a page, and + * by checking for the correct values in the destination page afterwards + */ +static void test_h_page_init(int argc, char **argv) +{ + u8 *dst, *src; + int rc; + + if (argc > 1) + report_abort("Unsupported argument: '%s'", argv[1]); + + dst = memalign(PAGE_SIZE, PAGE_SIZE); + src = memalign(PAGE_SIZE, PAGE_SIZE); + if (!dst || !src) + report_abort("Failed to alloc memory"); + + memset(dst, 0xaa, PAGE_SIZE); + rc = hcall(H_PAGE_INIT, H_ZERO_PAGE, dst, src); + report("h_zero_page", rc == H_SUCCESS && *(uint64_t*)dst == 0); + + *(uint64_t*)src = 0xbeefc0dedeadcafeULL; + rc = hcall(H_PAGE_INIT, H_COPY_PAGE, dst, src); + report("h_copy_page", + rc == H_SUCCESS && *(uint64_t*)dst == 0xbeefc0dedeadcafeULL); + + *(uint64_t*)src = 0x9abcdef012345678ULL; + rc = hcall(H_PAGE_INIT, H_COPY_PAGE|H_ZERO_PAGE, dst, src); + report("h_copy_page+h_zero_page", + rc == H_SUCCESS && *(uint64_t*)dst == 0x9abcdef012345678ULL); + + rc = hcall(H_PAGE_INIT, H_ZERO_PAGE, dst + 0x123, src); + report("h_zero_page unaligned dst", rc == H_PARAMETER); + + rc = hcall(H_PAGE_INIT, H_COPY_PAGE, dst, src + 0x123); + report("h_copy_page unaligned src", rc == H_PARAMETER); +} + +static int h_random(uint64_t *val) +{ + register uint64_t r3 asm("r3") = H_RANDOM; + register uint64_t r4 asm("r4"); + + asm volatile (" sc 1 " : "+r"(r3), "=r"(r4) : "r"(r3)); + *val = r4; + + return r3; +} + +/** + * Test H_RANDOM by calling it a couple of times to check whether all bit + * positions really toggle (there should be no "stuck" bits in the output) + */ +static void test_h_random(int argc, char **argv) +{ + uint64_t rval, val0, val1; + int rc, i; + + if (argc > 1) + report_abort("Unsupported argument: '%s'", argv[1]); + + val0 = 0ULL; + val1 = ~0ULL; + + /* H_RANDOM is optional - so check for sane return values first */ + rc = h_random(&rval); + if (rc != H_SUCCESS) { + return; + } + + i = 100; + do { + rc = h_random(&rval); + if (rc != H_SUCCESS) + break; + val0 |= rval; + val1 &= rval; + } while (i-- > 0 && (val0 != ~0ULL || val1 != 0ULL)); + + report("h-call available and working", + rc == H_SUCCESS && val0 == ~0ULL && val1 == 0ULL); +} + +struct { + const char *name; + void (*func)(int argc, char **argv); +} hctests[] = { + { "h_set_sprg0", test_h_set_sprg0 }, + { "h_page_init", test_h_page_init }, + { "h_random", test_h_random }, + { NULL, NULL } +}; + +int main(int argc, char **argv) +{ + int all = 0; + int i; + + report_prefix_push("hypercall"); + + if (!argc || (argc == 1 && !strcmp(argv[0], "all"))) + all = 1; + + for (i = 0; hctests[i].name != NULL; i++) { + report_prefix_push(hctests[i].name); + if (all || strcmp(argv[0], hctests[i].name) == 0) { + hctests[i].func(argc, argv); + } + report_prefix_pop(); + } + + return report_summary(); +} diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg index 60f9be8..d858436 100644 --- a/powerpc/unittests.cfg +++ b/powerpc/unittests.cfg @@ -28,3 +28,6 @@ file = selftest.elf smp = 2 extra_params = -m 256 -append 'setup smp=2 mem=256' groups = selftest + +[spapr_hcall] +file = spapr_hcall.elf -- 1.8.3.1 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html