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