1 /* 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 */ 8 #include <libcflat.h> 9 #include <alloc.h> 10 #include <devicetree.h> 11 #include <asm/setup.h> 12 #include <asm/ptrace.h> 13 #include <asm/asm-offsets.h> 14 #include <asm/processor.h> 15 #include <asm/thread_info.h> 16 #include <asm/psci.h> 17 #include <asm/smp.h> 18 #include <asm/cpumask.h> 19 #include <asm/barrier.h> 20 21 static void assert_args(int num_args, int needed_args) 22 { 23 if (num_args < needed_args) { 24 printf("selftest: not enough arguments\n"); 25 abort(); 26 } 27 } 28 29 static char *split_var(char *s, long *val) 30 { 31 char *p; 32 33 p = strchr(s, '='); 34 if (!p) 35 return NULL; 36 37 *val = atol(p+1); 38 *p = '\0'; 39 40 return s; 41 } 42 43 static void check_setup(int argc, char **argv) 44 { 45 int nr_tests = 0, i; 46 char *var; 47 long val; 48 49 for (i = 0; i < argc; ++i) { 50 51 var = split_var(argv[i], &val); 52 if (!var) 53 continue; 54 55 report_prefix_push(var); 56 57 if (strcmp(var, "mem") == 0) { 58 59 phys_addr_t memsize = PHYS_END - PHYS_OFFSET; 60 phys_addr_t expected = ((phys_addr_t)val)*1024*1024; 61 62 report("size = %d MB", memsize == expected, 63 memsize/1024/1024); 64 ++nr_tests; 65 66 } else if (strcmp(var, "smp") == 0) { 67 68 report("nr_cpus = %d", nr_cpus == (int)val, nr_cpus); 69 ++nr_tests; 70 } 71 72 report_prefix_pop(); 73 } 74 75 assert_args(nr_tests, 2); 76 } 77 78 static struct pt_regs expected_regs; 79 static bool und_works; 80 static bool svc_works; 81 #if defined(__arm__) 82 /* 83 * Capture the current register state and execute an instruction 84 * that causes an exception. The test handler will check that its 85 * capture of the current register state matches the capture done 86 * here. 87 * 88 * NOTE: update clobber list if passed insns needs more than r0,r1 89 */ 90 #define test_exception(pre_insns, excptn_insn, post_insns) \ 91 asm volatile( \ 92 pre_insns "\n" \ 93 "mov r0, %0\n" \ 94 "stmia r0, { r0-lr }\n" \ 95 "mrs r1, cpsr\n" \ 96 "str r1, [r0, #" xstr(S_PSR) "]\n" \ 97 "mov r1, #-1\n" \ 98 "str r1, [r0, #" xstr(S_OLD_R0) "]\n" \ 99 "add r1, pc, #8\n" \ 100 "str r1, [r0, #" xstr(S_R1) "]\n" \ 101 "str r1, [r0, #" xstr(S_PC) "]\n" \ 102 excptn_insn "\n" \ 103 post_insns "\n" \ 104 :: "r" (&expected_regs) : "r0", "r1") 105 106 static bool check_regs(struct pt_regs *regs) 107 { 108 unsigned i; 109 110 /* exception handlers should always run in svc mode */ 111 if (current_mode() != SVC_MODE) 112 return false; 113 114 for (i = 0; i < ARRAY_SIZE(regs->uregs); ++i) { 115 if (regs->uregs[i] != expected_regs.uregs[i]) 116 return false; 117 } 118 119 return true; 120 } 121 122 static void und_handler(struct pt_regs *regs) 123 { 124 und_works = check_regs(regs); 125 } 126 127 static bool check_und(void) 128 { 129 install_exception_handler(EXCPTN_UND, und_handler); 130 131 /* issue an instruction to a coprocessor we don't have */ 132 test_exception("", "mcr p2, 0, r0, c0, c0", ""); 133 134 install_exception_handler(EXCPTN_UND, NULL); 135 136 return und_works; 137 } 138 139 static void svc_handler(struct pt_regs *regs) 140 { 141 u32 svc = *(u32 *)(regs->ARM_pc - 4) & 0xffffff; 142 143 if (processor_mode(regs) == SVC_MODE) { 144 /* 145 * When issuing an svc from supervisor mode lr_svc will 146 * get corrupted. So before issuing the svc, callers must 147 * always push it on the stack. We pushed it to offset 4. 148 */ 149 regs->ARM_lr = *(unsigned long *)(regs->ARM_sp + 4); 150 } 151 152 svc_works = check_regs(regs) && svc == 123; 153 } 154 155 static bool check_svc(void) 156 { 157 install_exception_handler(EXCPTN_SVC, svc_handler); 158 159 if (current_mode() == SVC_MODE) { 160 /* 161 * An svc from supervisor mode will corrupt lr_svc and 162 * spsr_svc. We need to save/restore them separately. 163 */ 164 test_exception( 165 "mrs r0, spsr\n" 166 "push { r0,lr }\n", 167 "svc #123\n", 168 "pop { r0,lr }\n" 169 "msr spsr_cxsf, r0\n" 170 ); 171 } else { 172 test_exception("", "svc #123", ""); 173 } 174 175 install_exception_handler(EXCPTN_SVC, NULL); 176 177 return svc_works; 178 } 179 #elif defined(__aarch64__) 180 #include <asm/esr.h> 181 182 /* 183 * Capture the current register state and execute an instruction 184 * that causes an exception. The test handler will check that its 185 * capture of the current register state matches the capture done 186 * here. 187 * 188 * NOTE: update clobber list if passed insns needs more than x0,x1 189 */ 190 #define test_exception(pre_insns, excptn_insn, post_insns) \ 191 asm volatile( \ 192 pre_insns "\n" \ 193 "mov x1, %0\n" \ 194 "ldr x0, [x1, #" xstr(S_PSTATE) "]\n" \ 195 "mrs x1, nzcv\n" \ 196 "orr w0, w0, w1\n" \ 197 "mov x1, %0\n" \ 198 "str w0, [x1, #" xstr(S_PSTATE) "]\n" \ 199 "mov x0, sp\n" \ 200 "str x0, [x1, #" xstr(S_SP) "]\n" \ 201 "adr x0, 1f\n" \ 202 "str x0, [x1, #" xstr(S_PC) "]\n" \ 203 "stp x2, x3, [x1, #16]\n" \ 204 "stp x4, x5, [x1, #32]\n" \ 205 "stp x6, x7, [x1, #48]\n" \ 206 "stp x8, x9, [x1, #64]\n" \ 207 "stp x10, x11, [x1, #80]\n" \ 208 "stp x12, x13, [x1, #96]\n" \ 209 "stp x14, x15, [x1, #112]\n" \ 210 "stp x16, x17, [x1, #128]\n" \ 211 "stp x18, x19, [x1, #144]\n" \ 212 "stp x20, x21, [x1, #160]\n" \ 213 "stp x22, x23, [x1, #176]\n" \ 214 "stp x24, x25, [x1, #192]\n" \ 215 "stp x26, x27, [x1, #208]\n" \ 216 "stp x28, x29, [x1, #224]\n" \ 217 "str x30, [x1, #" xstr(S_LR) "]\n" \ 218 "stp x0, x1, [x1]\n" \ 219 "1:" excptn_insn "\n" \ 220 post_insns "\n" \ 221 :: "r" (&expected_regs) : "x0", "x1") 222 223 static bool check_regs(struct pt_regs *regs) 224 { 225 unsigned i; 226 227 /* exception handlers should always run in EL1 */ 228 if (current_level() != CurrentEL_EL1) 229 return false; 230 231 for (i = 0; i < ARRAY_SIZE(regs->regs); ++i) { 232 if (regs->regs[i] != expected_regs.regs[i]) 233 return false; 234 } 235 236 regs->pstate &= 0xf0000000 /* NZCV */ | 0x3c0 /* DAIF */ 237 | PSR_MODE_MASK; 238 239 return regs->sp == expected_regs.sp 240 && regs->pc == expected_regs.pc 241 && regs->pstate == expected_regs.pstate; 242 } 243 244 static enum vector check_vector_prep(void) 245 { 246 unsigned long daif; 247 248 if (is_user()) 249 return EL0_SYNC_64; 250 251 asm volatile("mrs %0, daif" : "=r" (daif) ::); 252 expected_regs.pstate = daif | PSR_MODE_EL1h; 253 return EL1H_SYNC; 254 } 255 256 static void unknown_handler(struct pt_regs *regs, unsigned int esr __unused) 257 { 258 und_works = check_regs(regs); 259 regs->pc += 4; 260 } 261 262 static bool check_und(void) 263 { 264 enum vector v = check_vector_prep(); 265 266 install_exception_handler(v, ESR_EL1_EC_UNKNOWN, unknown_handler); 267 268 /* try to read an el2 sysreg from el0/1 */ 269 test_exception("", "mrs x0, sctlr_el2", ""); 270 271 install_exception_handler(v, ESR_EL1_EC_UNKNOWN, NULL); 272 273 return und_works; 274 } 275 276 static void svc_handler(struct pt_regs *regs, unsigned int esr) 277 { 278 u16 svc = esr & 0xffff; 279 280 expected_regs.pc += 4; 281 svc_works = check_regs(regs) && svc == 123; 282 } 283 284 static bool check_svc(void) 285 { 286 enum vector v = check_vector_prep(); 287 288 install_exception_handler(v, ESR_EL1_EC_SVC64, svc_handler); 289 290 test_exception("", "svc #123", ""); 291 292 install_exception_handler(v, ESR_EL1_EC_SVC64, NULL); 293 294 return svc_works; 295 } 296 #endif 297 298 static void check_vectors(void *arg __unused) 299 { 300 report("und", check_und()); 301 report("svc", check_svc()); 302 exit(report_summary()); 303 } 304 305 static bool psci_check(void) 306 { 307 const struct fdt_property *method; 308 int node, len, ver; 309 310 node = fdt_node_offset_by_compatible(dt_fdt(), -1, "arm,psci-0.2"); 311 if (node < 0) { 312 printf("PSCI v0.2 compatibility required\n"); 313 return false; 314 } 315 316 method = fdt_get_property(dt_fdt(), node, "method", &len); 317 if (method == NULL) { 318 printf("bad psci device tree node\n"); 319 return false; 320 } 321 322 if (len < 4 || strcmp(method->data, "hvc") != 0) { 323 printf("psci method must be hvc\n"); 324 return false; 325 } 326 327 ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); 328 printf("PSCI version %d.%d\n", PSCI_VERSION_MAJOR(ver), 329 PSCI_VERSION_MINOR(ver)); 330 331 return true; 332 } 333 334 static cpumask_t smp_reported; 335 static void cpu_report(void) 336 { 337 int cpu = smp_processor_id(); 338 339 report("CPU%d online", true, cpu); 340 cpumask_set_cpu(cpu, &smp_reported); 341 halt(); 342 } 343 344 int main(int argc, char **argv) 345 { 346 report_prefix_push("selftest"); 347 assert_args(argc, 1); 348 report_prefix_push(argv[0]); 349 350 if (strcmp(argv[0], "setup") == 0) { 351 352 check_setup(argc-1, &argv[1]); 353 354 } else if (strcmp(argv[0], "vectors-kernel") == 0) { 355 356 check_vectors(NULL); 357 358 } else if (strcmp(argv[0], "vectors-user") == 0) { 359 360 void *sp = memalign(THREAD_SIZE, THREAD_SIZE); 361 start_usr(check_vectors, NULL, 362 (unsigned long)sp + THREAD_START_SP); 363 364 } else if (strcmp(argv[0], "smp") == 0) { 365 366 int cpu; 367 368 report("PSCI version", psci_check()); 369 370 for_each_present_cpu(cpu) { 371 if (cpu == 0) 372 continue; 373 smp_boot_secondary(cpu, cpu_report); 374 } 375 376 cpumask_set_cpu(0, &smp_reported); 377 while (!cpumask_full(&smp_reported)) 378 cpu_relax(); 379 } else { 380 printf("Unknown subtest\n"); 381 abort(); 382 } 383 384 return report_summary(); 385 } 386