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 #ifdef __arm__ 12 #include <asm/ptrace.h> 13 #include <asm/asm-offsets.h> 14 #include <asm/processor.h> 15 #include <asm/page.h> 16 #endif 17 18 static void assert_args(int num_args, int needed_args) 19 { 20 if (num_args < needed_args) { 21 printf("selftest: not enough arguments\n"); 22 abort(); 23 } 24 } 25 26 static char *split_var(char *s, long *val) 27 { 28 char *p; 29 30 p = strchr(s, '='); 31 if (!p) 32 return NULL; 33 34 *val = atol(p+1); 35 *p = '\0'; 36 37 return s; 38 } 39 40 static void check_setup(int argc, char **argv) 41 { 42 int nr_tests = 0, i; 43 char *var; 44 long val; 45 46 for (i = 0; i < argc; ++i) { 47 48 var = split_var(argv[i], &val); 49 if (!var) 50 continue; 51 52 report_prefix_push(var); 53 54 if (strcmp(var, "mem") == 0) { 55 56 phys_addr_t memsize = PHYS_END - PHYS_OFFSET; 57 phys_addr_t expected = ((phys_addr_t)val)*1024*1024; 58 59 report("size = %d MB", memsize == expected, 60 memsize/1024/1024); 61 ++nr_tests; 62 63 } else if (strcmp(var, "smp") == 0) { 64 65 report("nr_cpus = %d", nr_cpus == (int)val, nr_cpus); 66 ++nr_tests; 67 } 68 69 report_prefix_pop(); 70 } 71 72 assert_args(nr_tests, 2); 73 } 74 75 #ifdef __arm__ 76 static struct pt_regs expected_regs; 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 bool und_works; 118 static void und_handler(struct pt_regs *regs) 119 { 120 und_works = check_regs(regs); 121 } 122 123 static bool check_und(void) 124 { 125 install_exception_handler(EXCPTN_UND, und_handler); 126 127 /* issue an instruction to a coprocessor we don't have */ 128 test_exception("", "mcr p2, 0, r0, c0, c0", ""); 129 130 install_exception_handler(EXCPTN_UND, NULL); 131 132 return und_works; 133 } 134 135 static bool svc_works; 136 static void svc_handler(struct pt_regs *regs) 137 { 138 u32 svc = *(u32 *)(regs->ARM_pc - 4) & 0xffffff; 139 140 if (processor_mode(regs) == SVC_MODE) { 141 /* 142 * When issuing an svc from supervisor mode lr_svc will 143 * get corrupted. So before issuing the svc, callers must 144 * always push it on the stack. We pushed it to offset 4. 145 */ 146 regs->ARM_lr = *(unsigned long *)(regs->ARM_sp + 4); 147 } 148 149 svc_works = check_regs(regs) && svc == 123; 150 } 151 152 static bool check_svc(void) 153 { 154 install_exception_handler(EXCPTN_SVC, svc_handler); 155 156 if (current_mode() == SVC_MODE) { 157 /* 158 * An svc from supervisor mode will corrupt lr_svc and 159 * spsr_svc. We need to save/restore them separately. 160 */ 161 test_exception( 162 "mrs r0, spsr\n" 163 "push { r0,lr }\n", 164 "svc #123\n", 165 "pop { r0,lr }\n" 166 "msr spsr_cxsf, r0\n" 167 ); 168 } else { 169 test_exception("", "svc #123", ""); 170 } 171 172 install_exception_handler(EXCPTN_SVC, NULL); 173 174 return svc_works; 175 } 176 177 static void check_vectors(void *arg __unused) 178 { 179 report("und", check_und()); 180 report("svc", check_svc()); 181 exit(report_summary()); 182 } 183 #endif 184 185 int main(int argc, char **argv) 186 { 187 report_prefix_push("selftest"); 188 assert_args(argc, 1); 189 report_prefix_push(argv[0]); 190 191 if (strcmp(argv[0], "setup") == 0) { 192 193 check_setup(argc-1, &argv[1]); 194 195 #ifdef __arm__ 196 } else if (strcmp(argv[0], "vectors-kernel") == 0) { 197 198 check_vectors(NULL); 199 200 } else if (strcmp(argv[0], "vectors-user") == 0) { 201 202 void *sp = memalign(PAGE_SIZE, PAGE_SIZE); 203 memset(sp, 0, PAGE_SIZE); 204 start_usr(check_vectors, NULL, (unsigned long)sp + PAGE_SIZE); 205 #endif 206 } 207 208 return report_summary(); 209 } 210