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