blob: 750e908931411ff5d91ae8d924650929bffc0e91 [file] [log] [blame]
Andrew Jones5e61cba2014-01-21 17:21:59 +01001/*
2 * Test the framework itself. These tests confirm that setup works.
3 *
4 * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
5 *
6 * This work is licensed under the terms of the GNU LGPL, version 2.
7 */
Andrew Jones8cca5662014-12-10 20:59:58 +01008#include <libcflat.h>
Andrew Jonese7c68b42015-07-24 13:04:00 +02009#include <util.h>
Andrew Jones0415fee2015-02-01 19:34:46 +010010#include <devicetree.h>
Andrew Jones8cca5662014-12-10 20:59:58 +010011#include <asm/setup.h>
12#include <asm/ptrace.h>
13#include <asm/asm-offsets.h>
14#include <asm/processor.h>
Andrew Jones16936442015-02-01 19:34:35 +010015#include <asm/thread_info.h>
Andrew Jones0415fee2015-02-01 19:34:46 +010016#include <asm/psci.h>
17#include <asm/smp.h>
18#include <asm/cpumask.h>
19#include <asm/barrier.h>
Andrew Jones5e61cba2014-01-21 17:21:59 +010020
Andrew Jones5e61cba2014-01-21 17:21:59 +010021static void check_setup(int argc, char **argv)
22{
Andrew Jonese7c68b42015-07-24 13:04:00 +020023 int nr_tests = 0, len, i;
Andrew Jones5e61cba2014-01-21 17:21:59 +010024 long val;
25
26 for (i = 0; i < argc; ++i) {
27
Andrew Jonese7c68b42015-07-24 13:04:00 +020028 len = parse_keyval(argv[i], &val);
29 if (len == -1)
Andrew Jones5e61cba2014-01-21 17:21:59 +010030 continue;
31
Andrew Jonese7c68b42015-07-24 13:04:00 +020032 argv[i][len] = '\0';
33 report_prefix_push(argv[i]);
Andrew Jonesa8568122014-12-12 17:06:17 +010034
Andrew Jonese7c68b42015-07-24 13:04:00 +020035 if (strcmp(argv[i], "mem") == 0) {
Andrew Jones5e61cba2014-01-21 17:21:59 +010036
37 phys_addr_t memsize = PHYS_END - PHYS_OFFSET;
38 phys_addr_t expected = ((phys_addr_t)val)*1024*1024;
39
Andrew Jonesa8568122014-12-12 17:06:17 +010040 report("size = %d MB", memsize == expected,
41 memsize/1024/1024);
Andrew Jones5e61cba2014-01-21 17:21:59 +010042 ++nr_tests;
43
Andrew Jonese7c68b42015-07-24 13:04:00 +020044 } else if (strcmp(argv[i], "smp") == 0) {
Andrew Jones5e61cba2014-01-21 17:21:59 +010045
Andrew Jonesa8568122014-12-12 17:06:17 +010046 report("nr_cpus = %d", nr_cpus == (int)val, nr_cpus);
Andrew Jones5e61cba2014-01-21 17:21:59 +010047 ++nr_tests;
48 }
Andrew Jonesa8568122014-12-12 17:06:17 +010049
50 report_prefix_pop();
Andrew Jones5e61cba2014-01-21 17:21:59 +010051 }
52
Andrew Jonese7c68b42015-07-24 13:04:00 +020053 if (nr_tests < 2)
54 report_abort("missing input");
Andrew Jones5e61cba2014-01-21 17:21:59 +010055}
56
Andrew Jones2edfe422014-04-08 15:31:56 +020057static struct pt_regs expected_regs;
Andrew Jones7ee966e2014-12-10 21:00:02 +010058static bool und_works;
59static bool svc_works;
60#if defined(__arm__)
Andrew Jones2edfe422014-04-08 15:31:56 +020061/*
62 * Capture the current register state and execute an instruction
63 * that causes an exception. The test handler will check that its
64 * capture of the current register state matches the capture done
65 * here.
66 *
67 * NOTE: update clobber list if passed insns needs more than r0,r1
68 */
69#define test_exception(pre_insns, excptn_insn, post_insns) \
70 asm volatile( \
71 pre_insns "\n" \
72 "mov r0, %0\n" \
73 "stmia r0, { r0-lr }\n" \
74 "mrs r1, cpsr\n" \
75 "str r1, [r0, #" xstr(S_PSR) "]\n" \
76 "mov r1, #-1\n" \
77 "str r1, [r0, #" xstr(S_OLD_R0) "]\n" \
78 "add r1, pc, #8\n" \
79 "str r1, [r0, #" xstr(S_R1) "]\n" \
80 "str r1, [r0, #" xstr(S_PC) "]\n" \
81 excptn_insn "\n" \
82 post_insns "\n" \
83 :: "r" (&expected_regs) : "r0", "r1")
84
85static bool check_regs(struct pt_regs *regs)
86{
87 unsigned i;
88
89 /* exception handlers should always run in svc mode */
90 if (current_mode() != SVC_MODE)
91 return false;
92
93 for (i = 0; i < ARRAY_SIZE(regs->uregs); ++i) {
94 if (regs->uregs[i] != expected_regs.uregs[i])
95 return false;
96 }
97
98 return true;
99}
100
Andrew Jones2edfe422014-04-08 15:31:56 +0200101static void und_handler(struct pt_regs *regs)
102{
103 und_works = check_regs(regs);
104}
105
106static bool check_und(void)
107{
108 install_exception_handler(EXCPTN_UND, und_handler);
109
110 /* issue an instruction to a coprocessor we don't have */
111 test_exception("", "mcr p2, 0, r0, c0, c0", "");
112
113 install_exception_handler(EXCPTN_UND, NULL);
114
115 return und_works;
116}
117
Andrew Jones2edfe422014-04-08 15:31:56 +0200118static void svc_handler(struct pt_regs *regs)
119{
120 u32 svc = *(u32 *)(regs->ARM_pc - 4) & 0xffffff;
121
122 if (processor_mode(regs) == SVC_MODE) {
123 /*
124 * When issuing an svc from supervisor mode lr_svc will
125 * get corrupted. So before issuing the svc, callers must
126 * always push it on the stack. We pushed it to offset 4.
127 */
128 regs->ARM_lr = *(unsigned long *)(regs->ARM_sp + 4);
129 }
130
131 svc_works = check_regs(regs) && svc == 123;
132}
133
134static bool check_svc(void)
135{
136 install_exception_handler(EXCPTN_SVC, svc_handler);
137
138 if (current_mode() == SVC_MODE) {
139 /*
140 * An svc from supervisor mode will corrupt lr_svc and
141 * spsr_svc. We need to save/restore them separately.
142 */
143 test_exception(
144 "mrs r0, spsr\n"
145 "push { r0,lr }\n",
146 "svc #123\n",
147 "pop { r0,lr }\n"
148 "msr spsr_cxsf, r0\n"
149 );
150 } else {
151 test_exception("", "svc #123", "");
152 }
153
154 install_exception_handler(EXCPTN_SVC, NULL);
155
156 return svc_works;
157}
Andrew Jones7ee966e2014-12-10 21:00:02 +0100158#elif defined(__aarch64__)
Andrew Jones7ee966e2014-12-10 21:00:02 +0100159
160/*
161 * Capture the current register state and execute an instruction
162 * that causes an exception. The test handler will check that its
163 * capture of the current register state matches the capture done
164 * here.
165 *
166 * NOTE: update clobber list if passed insns needs more than x0,x1
167 */
168#define test_exception(pre_insns, excptn_insn, post_insns) \
169 asm volatile( \
170 pre_insns "\n" \
171 "mov x1, %0\n" \
172 "ldr x0, [x1, #" xstr(S_PSTATE) "]\n" \
173 "mrs x1, nzcv\n" \
174 "orr w0, w0, w1\n" \
175 "mov x1, %0\n" \
176 "str w0, [x1, #" xstr(S_PSTATE) "]\n" \
177 "mov x0, sp\n" \
178 "str x0, [x1, #" xstr(S_SP) "]\n" \
179 "adr x0, 1f\n" \
180 "str x0, [x1, #" xstr(S_PC) "]\n" \
181 "stp x2, x3, [x1, #16]\n" \
182 "stp x4, x5, [x1, #32]\n" \
183 "stp x6, x7, [x1, #48]\n" \
184 "stp x8, x9, [x1, #64]\n" \
185 "stp x10, x11, [x1, #80]\n" \
186 "stp x12, x13, [x1, #96]\n" \
187 "stp x14, x15, [x1, #112]\n" \
188 "stp x16, x17, [x1, #128]\n" \
189 "stp x18, x19, [x1, #144]\n" \
190 "stp x20, x21, [x1, #160]\n" \
191 "stp x22, x23, [x1, #176]\n" \
192 "stp x24, x25, [x1, #192]\n" \
193 "stp x26, x27, [x1, #208]\n" \
194 "stp x28, x29, [x1, #224]\n" \
195 "str x30, [x1, #" xstr(S_LR) "]\n" \
196 "stp x0, x1, [x1]\n" \
197 "1:" excptn_insn "\n" \
198 post_insns "\n" \
199 :: "r" (&expected_regs) : "x0", "x1")
200
201static bool check_regs(struct pt_regs *regs)
202{
203 unsigned i;
204
205 /* exception handlers should always run in EL1 */
206 if (current_level() != CurrentEL_EL1)
207 return false;
208
209 for (i = 0; i < ARRAY_SIZE(regs->regs); ++i) {
210 if (regs->regs[i] != expected_regs.regs[i])
211 return false;
212 }
213
214 regs->pstate &= 0xf0000000 /* NZCV */ | 0x3c0 /* DAIF */
215 | PSR_MODE_MASK;
216
217 return regs->sp == expected_regs.sp
218 && regs->pc == expected_regs.pc
219 && regs->pstate == expected_regs.pstate;
220}
221
222static enum vector check_vector_prep(void)
223{
224 unsigned long daif;
225
Andrew Jonesf6d10792015-02-01 19:34:36 +0100226 if (is_user())
Andrew Jones7ee966e2014-12-10 21:00:02 +0100227 return EL0_SYNC_64;
228
229 asm volatile("mrs %0, daif" : "=r" (daif) ::);
230 expected_regs.pstate = daif | PSR_MODE_EL1h;
231 return EL1H_SYNC;
232}
233
234static void unknown_handler(struct pt_regs *regs, unsigned int esr __unused)
235{
236 und_works = check_regs(regs);
237 regs->pc += 4;
238}
239
240static bool check_und(void)
241{
242 enum vector v = check_vector_prep();
243
244 install_exception_handler(v, ESR_EL1_EC_UNKNOWN, unknown_handler);
245
246 /* try to read an el2 sysreg from el0/1 */
247 test_exception("", "mrs x0, sctlr_el2", "");
248
249 install_exception_handler(v, ESR_EL1_EC_UNKNOWN, NULL);
250
251 return und_works;
252}
253
254static void svc_handler(struct pt_regs *regs, unsigned int esr)
255{
256 u16 svc = esr & 0xffff;
257
258 expected_regs.pc += 4;
259 svc_works = check_regs(regs) && svc == 123;
260}
261
262static bool check_svc(void)
263{
264 enum vector v = check_vector_prep();
265
266 install_exception_handler(v, ESR_EL1_EC_SVC64, svc_handler);
267
268 test_exception("", "svc #123", "");
269
270 install_exception_handler(v, ESR_EL1_EC_SVC64, NULL);
271
272 return svc_works;
273}
274#endif
Andrew Jones2edfe422014-04-08 15:31:56 +0200275
276static void check_vectors(void *arg __unused)
277{
Andrew Jonesa8568122014-12-12 17:06:17 +0100278 report("und", check_und());
279 report("svc", check_svc());
Andrew Jones2edfe422014-04-08 15:31:56 +0200280 exit(report_summary());
281}
282
Andrew Jones0415fee2015-02-01 19:34:46 +0100283static bool psci_check(void)
284{
285 const struct fdt_property *method;
286 int node, len, ver;
287
288 node = fdt_node_offset_by_compatible(dt_fdt(), -1, "arm,psci-0.2");
289 if (node < 0) {
290 printf("PSCI v0.2 compatibility required\n");
291 return false;
292 }
293
294 method = fdt_get_property(dt_fdt(), node, "method", &len);
295 if (method == NULL) {
296 printf("bad psci device tree node\n");
297 return false;
298 }
299
300 if (len < 4 || strcmp(method->data, "hvc") != 0) {
301 printf("psci method must be hvc\n");
302 return false;
303 }
304
305 ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
306 printf("PSCI version %d.%d\n", PSCI_VERSION_MAJOR(ver),
307 PSCI_VERSION_MINOR(ver));
308
309 return true;
310}
311
312static cpumask_t smp_reported;
313static void cpu_report(void)
314{
Andrew Jonesd9729022016-01-22 10:02:42 -0500315 uint64_t mpidr = get_mpidr();
Andrew Jones0415fee2015-02-01 19:34:46 +0100316 int cpu = smp_processor_id();
317
Andrew Jonesd9729022016-01-22 10:02:42 -0500318 report("CPU(%3d) mpidr=%010" PRIx64,
319 mpidr_to_cpu(mpidr) == cpu, cpu, mpidr);
Andrew Jones0415fee2015-02-01 19:34:46 +0100320 cpumask_set_cpu(cpu, &smp_reported);
321 halt();
322}
323
Andrew Jones5e61cba2014-01-21 17:21:59 +0100324int main(int argc, char **argv)
325{
Andrew Jonesa8568122014-12-12 17:06:17 +0100326 report_prefix_push("selftest");
Andrew Jonese7c68b42015-07-24 13:04:00 +0200327
Andrew Jones6ffea952016-06-12 19:29:29 +0200328 if (argc < 2)
Andrew Jonese7c68b42015-07-24 13:04:00 +0200329 report_abort("no test specified");
330
Andrew Jones6ffea952016-06-12 19:29:29 +0200331 report_prefix_push(argv[1]);
Andrew Jones5e61cba2014-01-21 17:21:59 +0100332
Andrew Jones6ffea952016-06-12 19:29:29 +0200333 if (strcmp(argv[1], "setup") == 0) {
Andrew Jones2edfe422014-04-08 15:31:56 +0200334
Andrew Jones6ffea952016-06-12 19:29:29 +0200335 check_setup(argc-2, &argv[2]);
Andrew Jones5e61cba2014-01-21 17:21:59 +0100336
Andrew Jones6ffea952016-06-12 19:29:29 +0200337 } else if (strcmp(argv[1], "vectors-kernel") == 0) {
Andrew Jones2edfe422014-04-08 15:31:56 +0200338
339 check_vectors(NULL);
340
Andrew Jones6ffea952016-06-12 19:29:29 +0200341 } else if (strcmp(argv[1], "vectors-user") == 0) {
Andrew Jones2edfe422014-04-08 15:31:56 +0200342
Andrew Jones16936442015-02-01 19:34:35 +0100343 start_usr(check_vectors, NULL,
Andrew Jones632f9352016-01-15 18:41:30 +0100344 (unsigned long)thread_stack_alloc());
Andrew Jones0415fee2015-02-01 19:34:46 +0100345
Andrew Jones6ffea952016-06-12 19:29:29 +0200346 } else if (strcmp(argv[1], "smp") == 0) {
Andrew Jones0415fee2015-02-01 19:34:46 +0100347
Andrew Jonesd9729022016-01-22 10:02:42 -0500348 uint64_t mpidr = get_mpidr();
Andrew Jones0415fee2015-02-01 19:34:46 +0100349 int cpu;
350
351 report("PSCI version", psci_check());
352
353 for_each_present_cpu(cpu) {
354 if (cpu == 0)
355 continue;
356 smp_boot_secondary(cpu, cpu_report);
357 }
358
Andrew Jonesd9729022016-01-22 10:02:42 -0500359 report("CPU(%3d) mpidr=%010" PRIx64,
360 mpidr_to_cpu(mpidr) == 0, 0, mpidr);
Andrew Jones0415fee2015-02-01 19:34:46 +0100361 cpumask_set_cpu(0, &smp_reported);
362 while (!cpumask_full(&smp_reported))
363 cpu_relax();
Christopher Covingtonb5a91882015-11-05 18:24:38 -0600364 } else {
365 printf("Unknown subtest\n");
366 abort();
Andrew Jones2edfe422014-04-08 15:31:56 +0200367 }
368
Andrew Jones5e61cba2014-01-21 17:21:59 +0100369 return report_summary();
370}