xref: /kvm-unit-tests/powerpc/spapr_hcall.c (revision 2c96b77ec9d3b1fcec7525174e23a6240ee05949)
1 /*
2  * Test sPAPR hypervisor calls (aka. h-calls)
3  *
4  * Copyright 2016  Thomas Huth, Red Hat Inc.
5  *
6  * This work is licensed under the terms of the GNU LGPL, version 2.
7  */
8 #include <libcflat.h>
9 #include <util.h>
10 #include <alloc.h>
11 #include <asm/hcall.h>
12 
13 #define PAGE_SIZE 4096
14 
15 #define H_ZERO_PAGE	(1UL << (63-48))
16 #define H_COPY_PAGE	(1UL << (63-49))
17 
18 #define mfspr(nr) ({ \
19 	uint64_t ret; \
20 	asm volatile("mfspr %0,%1" : "=r"(ret) : "i"(nr)); \
21 	ret; \
22 })
23 
24 #define SPR_SPRG0	0x110
25 
26 /**
27  * Test the H_SET_SPRG0 h-call by setting some values and checking whether
28  * the SPRG0 register contains the correct values afterwards
29  */
30 static void test_h_set_sprg0(int argc, char **argv)
31 {
32 	uint64_t sprg0, sprg0_orig;
33 	int rc;
34 
35 	if (argc > 1)
36 		report_abort("Unsupported argument: '%s'", argv[1]);
37 
38 	sprg0_orig = mfspr(SPR_SPRG0);
39 
40 	rc = hcall(H_SET_SPRG0, 0xcafebabedeadbeefULL);
41 	sprg0 = mfspr(SPR_SPRG0);
42 	report(rc == H_SUCCESS && sprg0 == 0xcafebabedeadbeefULL,
43 	       "sprg0 = 0xcafebabedeadbeef");
44 
45 	rc = hcall(H_SET_SPRG0, 0xaaaaaaaa55555555ULL);
46 	sprg0 = mfspr(SPR_SPRG0);
47 	report(rc == H_SUCCESS && sprg0 == 0xaaaaaaaa55555555ULL,
48 	       "sprg0 = 0xaaaaaaaa55555555");
49 
50 	rc = hcall(H_SET_SPRG0, sprg0_orig);
51 	sprg0 = mfspr(SPR_SPRG0);
52 	report(rc == H_SUCCESS && sprg0 == sprg0_orig, "sprg0 = %#" PRIx64,
53 	       sprg0_orig);
54 }
55 
56 /**
57  * Test the H_PAGE_INIT h-call by using it to clear and to copy a page, and
58  * by checking for the correct values in the destination page afterwards
59  */
60 static void test_h_page_init(int argc, char **argv)
61 {
62 	u8 *dst, *src;
63 	int rc;
64 
65 	if (argc > 1)
66 		report_abort("Unsupported argument: '%s'", argv[1]);
67 
68 	dst = memalign(PAGE_SIZE, PAGE_SIZE);
69 	src = memalign(PAGE_SIZE, PAGE_SIZE);
70 	if (!dst || !src)
71 		report_abort("Failed to alloc memory");
72 
73 	memset(dst, 0xaa, PAGE_SIZE);
74 	rc = hcall(H_PAGE_INIT, H_ZERO_PAGE, dst, src);
75 	report(rc == H_SUCCESS && *(uint64_t *)dst == 0, "h_zero_page");
76 
77 	*(uint64_t*)src = 0xbeefc0dedeadcafeULL;
78 	rc = hcall(H_PAGE_INIT, H_COPY_PAGE, dst, src);
79 	report(rc == H_SUCCESS && *(uint64_t *)dst == 0xbeefc0dedeadcafeULL,
80 	       "h_copy_page");
81 
82 	*(uint64_t*)src = 0x9abcdef012345678ULL;
83 	rc = hcall(H_PAGE_INIT, H_COPY_PAGE|H_ZERO_PAGE, dst, src);
84 	report(rc == H_SUCCESS && *(uint64_t *)dst == 0x9abcdef012345678ULL,
85 	       "h_copy_page+h_zero_page");
86 
87 	rc = hcall(H_PAGE_INIT, H_ZERO_PAGE, dst + 0x123, src);
88 	report(rc == H_PARAMETER, "h_zero_page unaligned dst");
89 
90 	rc = hcall(H_PAGE_INIT, H_COPY_PAGE, dst, src + 0x123);
91 	report(rc == H_PARAMETER, "h_copy_page unaligned src");
92 }
93 
94 static int h_random(uint64_t *val)
95 {
96 	register uint64_t r3 asm("r3") = H_RANDOM;
97 	register uint64_t r4 asm("r4");
98 
99 	asm volatile (" sc 1 "	: "+r"(r3), "=r"(r4) :
100 				: "r0", "r5", "r6", "r7", "r8", "r9", "r10",
101 				  "r11", "r12", "xer", "ctr", "cc");
102 	*val = r4;
103 
104 	return r3;
105 }
106 
107 /**
108  * Test H_RANDOM by calling it a couple of times to check whether all bit
109  * positions really toggle (there should be no "stuck" bits in the output)
110  */
111 static void test_h_random(int argc, char **argv)
112 {
113 	uint64_t rval, val0, val1;
114 	int rc, i;
115 
116 	if (argc > 1)
117 		report_abort("Unsupported argument: '%s'", argv[1]);
118 
119 	/* H_RANDOM is optional - so check for sane return values first */
120 	rc = h_random(&rval);
121 	if (rc == H_FUNCTION) {
122 		report_skip("h-call is not available");
123 		return;
124 	}
125 	report(rc == H_SUCCESS, "h-call can be used successfully");
126 
127 	val0 = 0ULL;
128 	val1 = ~0ULL;
129 
130 	i = 100;
131 	do {
132 		rc = h_random(&rval);
133 		if (rc != H_SUCCESS)
134 			break;
135 		val0 |= rval;
136 		val1 &= rval;
137 	} while (i-- > 0 && (val0 != ~0ULL || val1 != 0ULL));
138 
139 	report(rc == H_SUCCESS && val0 == ~0ULL && val1 == 0, "no stuck bits");
140 }
141 
142 struct {
143 	const char *name;
144 	void (*func)(int argc, char **argv);
145 } hctests[] = {
146 	{ "h_set_sprg0", test_h_set_sprg0 },
147 	{ "h_page_init", test_h_page_init },
148 	{ "h_random", test_h_random },
149 	{ NULL, NULL }
150 };
151 
152 int main(int argc, char **argv)
153 {
154 	int all = 0;
155 	int i;
156 
157 	report_prefix_push("hypercall");
158 
159 	if (argc < 2 || (argc == 2 && !strcmp(argv[1], "all")))
160 		all = 1;
161 
162 	for (i = 0; hctests[i].name != NULL; i++) {
163 		report_prefix_push(hctests[i].name);
164 		if (all || strcmp(argv[1], hctests[i].name) == 0) {
165 			hctests[i].func(argc-1, &argv[1]);
166 		}
167 		report_prefix_pop();
168 	}
169 
170 	return report_summary();
171 }
172