| /* |
| * 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(rc == H_SUCCESS && sprg0 == 0xcafebabedeadbeefULL, |
| "sprg0 = 0xcafebabedeadbeef"); |
| |
| rc = hcall(H_SET_SPRG0, 0xaaaaaaaa55555555ULL); |
| sprg0 = mfspr(SPR_SPRG0); |
| report(rc == H_SUCCESS && sprg0 == 0xaaaaaaaa55555555ULL, |
| "sprg0 = 0xaaaaaaaa55555555"); |
| |
| rc = hcall(H_SET_SPRG0, sprg0_orig); |
| sprg0 = mfspr(SPR_SPRG0); |
| report(rc == H_SUCCESS && sprg0 == sprg0_orig, "sprg0 = %#" PRIx64, |
| 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(rc == H_SUCCESS && *(uint64_t *)dst == 0, "h_zero_page"); |
| |
| *(uint64_t*)src = 0xbeefc0dedeadcafeULL; |
| rc = hcall(H_PAGE_INIT, H_COPY_PAGE, dst, src); |
| report(rc == H_SUCCESS && *(uint64_t *)dst == 0xbeefc0dedeadcafeULL, |
| "h_copy_page"); |
| |
| *(uint64_t*)src = 0x9abcdef012345678ULL; |
| rc = hcall(H_PAGE_INIT, H_COPY_PAGE|H_ZERO_PAGE, dst, src); |
| report(rc == H_SUCCESS && *(uint64_t *)dst == 0x9abcdef012345678ULL, |
| "h_copy_page+h_zero_page"); |
| |
| rc = hcall(H_PAGE_INIT, H_ZERO_PAGE, dst + 0x123, src); |
| report(rc == H_PARAMETER, "h_zero_page unaligned dst"); |
| |
| rc = hcall(H_PAGE_INIT, H_COPY_PAGE, dst, src + 0x123); |
| report(rc == H_PARAMETER, "h_copy_page unaligned src"); |
| } |
| |
| 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) : |
| : "r0", "r5", "r6", "r7", "r8", "r9", "r10", |
| "r11", "r12", "xer", "ctr", "cc"); |
| *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]); |
| |
| /* H_RANDOM is optional - so check for sane return values first */ |
| rc = h_random(&rval); |
| if (rc == H_FUNCTION) { |
| report_skip("h-call is not available"); |
| return; |
| } |
| report(rc == H_SUCCESS, "h-call can be used successfully"); |
| |
| val0 = 0ULL; |
| val1 = ~0ULL; |
| |
| i = 100; |
| do { |
| rc = h_random(&rval); |
| if (rc != H_SUCCESS) |
| break; |
| val0 |= rval; |
| val1 &= rval; |
| } while (i-- > 0 && (val0 != ~0ULL || val1 != 0ULL)); |
| |
| report(rc == H_SUCCESS && val0 == ~0ULL && val1 == 0, "no stuck bits"); |
| } |
| |
| 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 < 2 || (argc == 2 && !strcmp(argv[1], "all"))) |
| all = 1; |
| |
| for (i = 0; hctests[i].name != NULL; i++) { |
| report_prefix_push(hctests[i].name); |
| if (all || strcmp(argv[1], hctests[i].name) == 0) { |
| hctests[i].func(argc-1, &argv[1]); |
| } |
| report_prefix_pop(); |
| } |
| |
| return report_summary(); |
| } |