15e61cba0SAndrew Jones /* 25e61cba0SAndrew Jones * Test the framework itself. These tests confirm that setup works. 35e61cba0SAndrew Jones * 45e61cba0SAndrew Jones * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com> 55e61cba0SAndrew Jones * 65e61cba0SAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 75e61cba0SAndrew Jones */ 8*8cca5668SAndrew Jones #include <libcflat.h> 9*8cca5668SAndrew Jones #include <alloc.h> 10*8cca5668SAndrew Jones #include <asm/setup.h> 11*8cca5668SAndrew Jones #include <asm/ptrace.h> 12*8cca5668SAndrew Jones #include <asm/asm-offsets.h> 13*8cca5668SAndrew Jones #include <asm/processor.h> 14*8cca5668SAndrew Jones #include <asm/page.h> 155e61cba0SAndrew Jones 165e61cba0SAndrew Jones static void assert_args(int num_args, int needed_args) 175e61cba0SAndrew Jones { 185e61cba0SAndrew Jones if (num_args < needed_args) { 19a8568128SAndrew Jones printf("selftest: not enough arguments\n"); 205e61cba0SAndrew Jones abort(); 215e61cba0SAndrew Jones } 225e61cba0SAndrew Jones } 235e61cba0SAndrew Jones 245e61cba0SAndrew Jones static char *split_var(char *s, long *val) 255e61cba0SAndrew Jones { 265e61cba0SAndrew Jones char *p; 275e61cba0SAndrew Jones 285e61cba0SAndrew Jones p = strchr(s, '='); 295e61cba0SAndrew Jones if (!p) 305e61cba0SAndrew Jones return NULL; 315e61cba0SAndrew Jones 325e61cba0SAndrew Jones *val = atol(p+1); 335e61cba0SAndrew Jones *p = '\0'; 345e61cba0SAndrew Jones 355e61cba0SAndrew Jones return s; 365e61cba0SAndrew Jones } 375e61cba0SAndrew Jones 385e61cba0SAndrew Jones static void check_setup(int argc, char **argv) 395e61cba0SAndrew Jones { 405e61cba0SAndrew Jones int nr_tests = 0, i; 415e61cba0SAndrew Jones char *var; 425e61cba0SAndrew Jones long val; 435e61cba0SAndrew Jones 445e61cba0SAndrew Jones for (i = 0; i < argc; ++i) { 455e61cba0SAndrew Jones 465e61cba0SAndrew Jones var = split_var(argv[i], &val); 475e61cba0SAndrew Jones if (!var) 485e61cba0SAndrew Jones continue; 495e61cba0SAndrew Jones 50a8568128SAndrew Jones report_prefix_push(var); 51a8568128SAndrew Jones 525e61cba0SAndrew Jones if (strcmp(var, "mem") == 0) { 535e61cba0SAndrew Jones 545e61cba0SAndrew Jones phys_addr_t memsize = PHYS_END - PHYS_OFFSET; 555e61cba0SAndrew Jones phys_addr_t expected = ((phys_addr_t)val)*1024*1024; 565e61cba0SAndrew Jones 57a8568128SAndrew Jones report("size = %d MB", memsize == expected, 58a8568128SAndrew Jones memsize/1024/1024); 595e61cba0SAndrew Jones ++nr_tests; 605e61cba0SAndrew Jones 615e61cba0SAndrew Jones } else if (strcmp(var, "smp") == 0) { 625e61cba0SAndrew Jones 63a8568128SAndrew Jones report("nr_cpus = %d", nr_cpus == (int)val, nr_cpus); 645e61cba0SAndrew Jones ++nr_tests; 655e61cba0SAndrew Jones } 66a8568128SAndrew Jones 67a8568128SAndrew Jones report_prefix_pop(); 685e61cba0SAndrew Jones } 695e61cba0SAndrew Jones 705e61cba0SAndrew Jones assert_args(nr_tests, 2); 715e61cba0SAndrew Jones } 725e61cba0SAndrew Jones 732edfe428SAndrew Jones static struct pt_regs expected_regs; 742edfe428SAndrew Jones /* 752edfe428SAndrew Jones * Capture the current register state and execute an instruction 762edfe428SAndrew Jones * that causes an exception. The test handler will check that its 772edfe428SAndrew Jones * capture of the current register state matches the capture done 782edfe428SAndrew Jones * here. 792edfe428SAndrew Jones * 802edfe428SAndrew Jones * NOTE: update clobber list if passed insns needs more than r0,r1 812edfe428SAndrew Jones */ 822edfe428SAndrew Jones #define test_exception(pre_insns, excptn_insn, post_insns) \ 832edfe428SAndrew Jones asm volatile( \ 842edfe428SAndrew Jones pre_insns "\n" \ 852edfe428SAndrew Jones "mov r0, %0\n" \ 862edfe428SAndrew Jones "stmia r0, { r0-lr }\n" \ 872edfe428SAndrew Jones "mrs r1, cpsr\n" \ 882edfe428SAndrew Jones "str r1, [r0, #" xstr(S_PSR) "]\n" \ 892edfe428SAndrew Jones "mov r1, #-1\n" \ 902edfe428SAndrew Jones "str r1, [r0, #" xstr(S_OLD_R0) "]\n" \ 912edfe428SAndrew Jones "add r1, pc, #8\n" \ 922edfe428SAndrew Jones "str r1, [r0, #" xstr(S_R1) "]\n" \ 932edfe428SAndrew Jones "str r1, [r0, #" xstr(S_PC) "]\n" \ 942edfe428SAndrew Jones excptn_insn "\n" \ 952edfe428SAndrew Jones post_insns "\n" \ 962edfe428SAndrew Jones :: "r" (&expected_regs) : "r0", "r1") 972edfe428SAndrew Jones 982edfe428SAndrew Jones static bool check_regs(struct pt_regs *regs) 992edfe428SAndrew Jones { 1002edfe428SAndrew Jones unsigned i; 1012edfe428SAndrew Jones 1022edfe428SAndrew Jones /* exception handlers should always run in svc mode */ 1032edfe428SAndrew Jones if (current_mode() != SVC_MODE) 1042edfe428SAndrew Jones return false; 1052edfe428SAndrew Jones 1062edfe428SAndrew Jones for (i = 0; i < ARRAY_SIZE(regs->uregs); ++i) { 1072edfe428SAndrew Jones if (regs->uregs[i] != expected_regs.uregs[i]) 1082edfe428SAndrew Jones return false; 1092edfe428SAndrew Jones } 1102edfe428SAndrew Jones 1112edfe428SAndrew Jones return true; 1122edfe428SAndrew Jones } 1132edfe428SAndrew Jones 1142edfe428SAndrew Jones static bool und_works; 1152edfe428SAndrew Jones static void und_handler(struct pt_regs *regs) 1162edfe428SAndrew Jones { 1172edfe428SAndrew Jones und_works = check_regs(regs); 1182edfe428SAndrew Jones } 1192edfe428SAndrew Jones 1202edfe428SAndrew Jones static bool check_und(void) 1212edfe428SAndrew Jones { 1222edfe428SAndrew Jones install_exception_handler(EXCPTN_UND, und_handler); 1232edfe428SAndrew Jones 1242edfe428SAndrew Jones /* issue an instruction to a coprocessor we don't have */ 1252edfe428SAndrew Jones test_exception("", "mcr p2, 0, r0, c0, c0", ""); 1262edfe428SAndrew Jones 1272edfe428SAndrew Jones install_exception_handler(EXCPTN_UND, NULL); 1282edfe428SAndrew Jones 1292edfe428SAndrew Jones return und_works; 1302edfe428SAndrew Jones } 1312edfe428SAndrew Jones 1322edfe428SAndrew Jones static bool svc_works; 1332edfe428SAndrew Jones static void svc_handler(struct pt_regs *regs) 1342edfe428SAndrew Jones { 1352edfe428SAndrew Jones u32 svc = *(u32 *)(regs->ARM_pc - 4) & 0xffffff; 1362edfe428SAndrew Jones 1372edfe428SAndrew Jones if (processor_mode(regs) == SVC_MODE) { 1382edfe428SAndrew Jones /* 1392edfe428SAndrew Jones * When issuing an svc from supervisor mode lr_svc will 1402edfe428SAndrew Jones * get corrupted. So before issuing the svc, callers must 1412edfe428SAndrew Jones * always push it on the stack. We pushed it to offset 4. 1422edfe428SAndrew Jones */ 1432edfe428SAndrew Jones regs->ARM_lr = *(unsigned long *)(regs->ARM_sp + 4); 1442edfe428SAndrew Jones } 1452edfe428SAndrew Jones 1462edfe428SAndrew Jones svc_works = check_regs(regs) && svc == 123; 1472edfe428SAndrew Jones } 1482edfe428SAndrew Jones 1492edfe428SAndrew Jones static bool check_svc(void) 1502edfe428SAndrew Jones { 1512edfe428SAndrew Jones install_exception_handler(EXCPTN_SVC, svc_handler); 1522edfe428SAndrew Jones 1532edfe428SAndrew Jones if (current_mode() == SVC_MODE) { 1542edfe428SAndrew Jones /* 1552edfe428SAndrew Jones * An svc from supervisor mode will corrupt lr_svc and 1562edfe428SAndrew Jones * spsr_svc. We need to save/restore them separately. 1572edfe428SAndrew Jones */ 1582edfe428SAndrew Jones test_exception( 1592edfe428SAndrew Jones "mrs r0, spsr\n" 1602edfe428SAndrew Jones "push { r0,lr }\n", 1612edfe428SAndrew Jones "svc #123\n", 1622edfe428SAndrew Jones "pop { r0,lr }\n" 1632edfe428SAndrew Jones "msr spsr_cxsf, r0\n" 1642edfe428SAndrew Jones ); 1652edfe428SAndrew Jones } else { 1662edfe428SAndrew Jones test_exception("", "svc #123", ""); 1672edfe428SAndrew Jones } 1682edfe428SAndrew Jones 1692edfe428SAndrew Jones install_exception_handler(EXCPTN_SVC, NULL); 1702edfe428SAndrew Jones 1712edfe428SAndrew Jones return svc_works; 1722edfe428SAndrew Jones } 1732edfe428SAndrew Jones 1742edfe428SAndrew Jones static void check_vectors(void *arg __unused) 1752edfe428SAndrew Jones { 176a8568128SAndrew Jones report("und", check_und()); 177a8568128SAndrew Jones report("svc", check_svc()); 1782edfe428SAndrew Jones exit(report_summary()); 1792edfe428SAndrew Jones } 1802edfe428SAndrew Jones 1815e61cba0SAndrew Jones int main(int argc, char **argv) 1825e61cba0SAndrew Jones { 183a8568128SAndrew Jones report_prefix_push("selftest"); 1845e61cba0SAndrew Jones assert_args(argc, 1); 185a8568128SAndrew Jones report_prefix_push(argv[0]); 1865e61cba0SAndrew Jones 1872edfe428SAndrew Jones if (strcmp(argv[0], "setup") == 0) { 1882edfe428SAndrew Jones 1895e61cba0SAndrew Jones check_setup(argc-1, &argv[1]); 1905e61cba0SAndrew Jones 191f052fea1SAndrew Jones } else if (strcmp(argv[0], "vectors-kernel") == 0) { 1922edfe428SAndrew Jones 1932edfe428SAndrew Jones check_vectors(NULL); 1942edfe428SAndrew Jones 195f052fea1SAndrew Jones } else if (strcmp(argv[0], "vectors-user") == 0) { 1962edfe428SAndrew Jones 1972edfe428SAndrew Jones void *sp = memalign(PAGE_SIZE, PAGE_SIZE); 1982edfe428SAndrew Jones memset(sp, 0, PAGE_SIZE); 1992edfe428SAndrew Jones start_usr(check_vectors, NULL, (unsigned long)sp + PAGE_SIZE); 2002edfe428SAndrew Jones } 2012edfe428SAndrew Jones 2025e61cba0SAndrew Jones return report_summary(); 2035e61cba0SAndrew Jones } 204