17ee966e9SAndrew Jones /* 27ee966e9SAndrew Jones * processor control and status functions 37ee966e9SAndrew Jones * 47ee966e9SAndrew Jones * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com> 57ee966e9SAndrew Jones * 67ee966e9SAndrew Jones * This work is licensed under the terms of the GNU LGPL, version 2. 77ee966e9SAndrew Jones */ 87ee966e9SAndrew Jones #include <libcflat.h> 97ee966e9SAndrew Jones #include <asm/ptrace.h> 107ee966e9SAndrew Jones #include <asm/processor.h> 117ee966e9SAndrew Jones #include <asm/esr.h> 127ee966e9SAndrew Jones 13*a4049322SAndrew Jones static const char *vector_names[] = { 147ee966e9SAndrew Jones "el1t_sync", 157ee966e9SAndrew Jones "el1t_irq", 167ee966e9SAndrew Jones "el1t_fiq", 177ee966e9SAndrew Jones "el1t_error", 187ee966e9SAndrew Jones "el1h_sync", 197ee966e9SAndrew Jones "el1h_irq", 207ee966e9SAndrew Jones "el1h_fiq", 217ee966e9SAndrew Jones "el1h_error", 227ee966e9SAndrew Jones "el0_sync_64", 237ee966e9SAndrew Jones "el0_irq_64", 247ee966e9SAndrew Jones "el0_fiq_64", 257ee966e9SAndrew Jones "el0_error_64", 267ee966e9SAndrew Jones "el0_sync_32", 277ee966e9SAndrew Jones "el0_irq_32", 287ee966e9SAndrew Jones "el0_fiq_32", 297ee966e9SAndrew Jones "el0_error_32", 307ee966e9SAndrew Jones }; 317ee966e9SAndrew Jones 32*a4049322SAndrew Jones static const char *ec_names[EC_MAX] = { 337ee966e9SAndrew Jones [ESR_EL1_EC_UNKNOWN] = "UNKNOWN", 347ee966e9SAndrew Jones [ESR_EL1_EC_WFI] = "WFI", 357ee966e9SAndrew Jones [ESR_EL1_EC_CP15_32] = "CP15_32", 367ee966e9SAndrew Jones [ESR_EL1_EC_CP15_64] = "CP15_64", 377ee966e9SAndrew Jones [ESR_EL1_EC_CP14_MR] = "CP14_MR", 387ee966e9SAndrew Jones [ESR_EL1_EC_CP14_LS] = "CP14_LS", 397ee966e9SAndrew Jones [ESR_EL1_EC_FP_ASIMD] = "FP_ASMID", 407ee966e9SAndrew Jones [ESR_EL1_EC_CP10_ID] = "CP10_ID", 417ee966e9SAndrew Jones [ESR_EL1_EC_CP14_64] = "CP14_64", 427ee966e9SAndrew Jones [ESR_EL1_EC_ILL_ISS] = "ILL_ISS", 437ee966e9SAndrew Jones [ESR_EL1_EC_SVC32] = "SVC32", 447ee966e9SAndrew Jones [ESR_EL1_EC_SVC64] = "SVC64", 457ee966e9SAndrew Jones [ESR_EL1_EC_SYS64] = "SYS64", 467ee966e9SAndrew Jones [ESR_EL1_EC_IABT_EL0] = "IABT_EL0", 477ee966e9SAndrew Jones [ESR_EL1_EC_IABT_EL1] = "IABT_EL1", 487ee966e9SAndrew Jones [ESR_EL1_EC_PC_ALIGN] = "PC_ALIGN", 497ee966e9SAndrew Jones [ESR_EL1_EC_DABT_EL0] = "DABT_EL0", 507ee966e9SAndrew Jones [ESR_EL1_EC_DABT_EL1] = "DABT_EL1", 517ee966e9SAndrew Jones [ESR_EL1_EC_SP_ALIGN] = "SP_ALIGN", 527ee966e9SAndrew Jones [ESR_EL1_EC_FP_EXC32] = "FP_EXC32", 537ee966e9SAndrew Jones [ESR_EL1_EC_FP_EXC64] = "FP_EXC64", 547ee966e9SAndrew Jones [ESR_EL1_EC_SERROR] = "SERROR", 557ee966e9SAndrew Jones [ESR_EL1_EC_BREAKPT_EL0] = "BREAKPT_EL0", 567ee966e9SAndrew Jones [ESR_EL1_EC_BREAKPT_EL1] = "BREAKPT_EL1", 577ee966e9SAndrew Jones [ESR_EL1_EC_SOFTSTP_EL0] = "SOFTSTP_EL0", 587ee966e9SAndrew Jones [ESR_EL1_EC_SOFTSTP_EL1] = "SOFTSTP_EL1", 597ee966e9SAndrew Jones [ESR_EL1_EC_WATCHPT_EL0] = "WATCHPT_EL0", 607ee966e9SAndrew Jones [ESR_EL1_EC_WATCHPT_EL1] = "WATCHPT_EL1", 617ee966e9SAndrew Jones [ESR_EL1_EC_BKPT32] = "BKPT32", 627ee966e9SAndrew Jones [ESR_EL1_EC_BRK64] = "BRK64", 637ee966e9SAndrew Jones }; 647ee966e9SAndrew Jones 657ee966e9SAndrew Jones void show_regs(struct pt_regs *regs) 667ee966e9SAndrew Jones { 677ee966e9SAndrew Jones int i; 687ee966e9SAndrew Jones 697ee966e9SAndrew Jones printf("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n", 707ee966e9SAndrew Jones regs->pc, regs->regs[30], regs->pstate); 717ee966e9SAndrew Jones printf("sp : %016llx\n", regs->sp); 727ee966e9SAndrew Jones 737ee966e9SAndrew Jones for (i = 29; i >= 0; --i) { 747ee966e9SAndrew Jones printf("x%-2d: %016llx ", i, regs->regs[i]); 757ee966e9SAndrew Jones if (i % 2 == 0) 767ee966e9SAndrew Jones printf("\n"); 777ee966e9SAndrew Jones } 787ee966e9SAndrew Jones printf("\n"); 797ee966e9SAndrew Jones } 807ee966e9SAndrew Jones 817ee966e9SAndrew Jones void *get_sp(void) 827ee966e9SAndrew Jones { 837ee966e9SAndrew Jones register unsigned long sp asm("sp"); 847ee966e9SAndrew Jones return (void *)sp; 857ee966e9SAndrew Jones } 867ee966e9SAndrew Jones 87db328a24SAndrew Jones bool get_far(unsigned int esr, unsigned long *far) 88db328a24SAndrew Jones { 89db328a24SAndrew Jones unsigned int ec = esr >> ESR_EL1_EC_SHIFT; 90db328a24SAndrew Jones 91db328a24SAndrew Jones asm volatile("mrs %0, far_el1": "=r" (*far)); 92db328a24SAndrew Jones 93db328a24SAndrew Jones switch (ec) { 94db328a24SAndrew Jones case ESR_EL1_EC_IABT_EL0: 95db328a24SAndrew Jones case ESR_EL1_EC_IABT_EL1: 96db328a24SAndrew Jones case ESR_EL1_EC_PC_ALIGN: 97db328a24SAndrew Jones case ESR_EL1_EC_DABT_EL0: 98db328a24SAndrew Jones case ESR_EL1_EC_DABT_EL1: 99db328a24SAndrew Jones case ESR_EL1_EC_WATCHPT_EL0: 100db328a24SAndrew Jones case ESR_EL1_EC_WATCHPT_EL1: 101db328a24SAndrew Jones if ((esr & 0x3f /* DFSC */) != 0x10 102db328a24SAndrew Jones || !(esr & 0x400 /* FnV */)) 103db328a24SAndrew Jones return true; 104db328a24SAndrew Jones } 105db328a24SAndrew Jones return false; 106db328a24SAndrew Jones } 107db328a24SAndrew Jones 1087ee966e9SAndrew Jones static void bad_exception(enum vector v, struct pt_regs *regs, 1097ee966e9SAndrew Jones unsigned int esr, bool bad_vector) 1107ee966e9SAndrew Jones { 111db328a24SAndrew Jones unsigned long far; 112db328a24SAndrew Jones bool far_valid = get_far(esr, &far); 1137ee966e9SAndrew Jones unsigned int ec = esr >> ESR_EL1_EC_SHIFT; 1147ee966e9SAndrew Jones 1157ee966e9SAndrew Jones if (bad_vector) { 1167ee966e9SAndrew Jones if (v < VECTOR_MAX) 1177ee966e9SAndrew Jones printf("Unhandled vector %d (%s)\n", v, 1187ee966e9SAndrew Jones vector_names[v]); 1197ee966e9SAndrew Jones else 1207ee966e9SAndrew Jones printf("Got bad vector=%d\n", v); 1217ee966e9SAndrew Jones } else { 1227ee966e9SAndrew Jones if (ec_names[ec]) 1237ee966e9SAndrew Jones printf("Unhandled exception ec=0x%x (%s)\n", ec, 1247ee966e9SAndrew Jones ec_names[ec]); 1257ee966e9SAndrew Jones else 1267ee966e9SAndrew Jones printf("Got bad ec=0x%x\n", ec); 1277ee966e9SAndrew Jones } 1287ee966e9SAndrew Jones 1297ee966e9SAndrew Jones printf("Vector: %d (%s)\n", v, vector_names[v]); 130db328a24SAndrew Jones printf("ESR_EL1: %8s%08lx, ec=0x%x (%s)\n", "", esr, ec, ec_names[ec]); 131db328a24SAndrew Jones printf("FAR_EL1: %016lx (%svalid)\n", far, far_valid ? "" : "not "); 1327ee966e9SAndrew Jones printf("Exception frame registers:\n"); 1337ee966e9SAndrew Jones show_regs(regs); 1347ee966e9SAndrew Jones abort(); 1357ee966e9SAndrew Jones } 1367ee966e9SAndrew Jones 1377ee966e9SAndrew Jones static exception_fn exception_handlers[VECTOR_MAX][EC_MAX]; 1387ee966e9SAndrew Jones 1397ee966e9SAndrew Jones void install_exception_handler(enum vector v, unsigned int ec, exception_fn fn) 1407ee966e9SAndrew Jones { 1417ee966e9SAndrew Jones if (v < VECTOR_MAX && ec < EC_MAX) 1427ee966e9SAndrew Jones exception_handlers[v][ec] = fn; 1437ee966e9SAndrew Jones } 1447ee966e9SAndrew Jones 145*a4049322SAndrew Jones void default_vector_handler(enum vector v, struct pt_regs *regs, 1467ee966e9SAndrew Jones unsigned int esr) 1477ee966e9SAndrew Jones { 1487ee966e9SAndrew Jones unsigned int ec = esr >> ESR_EL1_EC_SHIFT; 1497ee966e9SAndrew Jones 1507ee966e9SAndrew Jones if (ec < EC_MAX && exception_handlers[v][ec]) 1517ee966e9SAndrew Jones exception_handlers[v][ec](regs, esr); 1527ee966e9SAndrew Jones else 1537ee966e9SAndrew Jones bad_exception(v, regs, esr, false); 1547ee966e9SAndrew Jones } 1557ee966e9SAndrew Jones 1567ee966e9SAndrew Jones static vector_fn vector_handlers[VECTOR_MAX] = { 1577ee966e9SAndrew Jones [EL1H_SYNC] = default_vector_handler, 1587ee966e9SAndrew Jones [EL1H_IRQ] = default_vector_handler, 1597ee966e9SAndrew Jones [EL0_SYNC_64] = default_vector_handler, 1607ee966e9SAndrew Jones [EL0_IRQ_64] = default_vector_handler, 1617ee966e9SAndrew Jones }; 1627ee966e9SAndrew Jones 1637ee966e9SAndrew Jones void do_handle_exception(enum vector v, struct pt_regs *regs, unsigned int esr) 1647ee966e9SAndrew Jones { 1657ee966e9SAndrew Jones if (v < VECTOR_MAX && vector_handlers[v]) 1667ee966e9SAndrew Jones vector_handlers[v](v, regs, esr); 1677ee966e9SAndrew Jones else 1687ee966e9SAndrew Jones bad_exception(v, regs, esr, true); 1697ee966e9SAndrew Jones } 1707ee966e9SAndrew Jones 1717ee966e9SAndrew Jones void install_vector_handler(enum vector v, vector_fn fn) 1727ee966e9SAndrew Jones { 1737ee966e9SAndrew Jones if (v < VECTOR_MAX) 1747ee966e9SAndrew Jones vector_handlers[v] = fn; 1757ee966e9SAndrew Jones } 1767ee966e9SAndrew Jones 1777ee966e9SAndrew Jones bool user_mode; 1787ee966e9SAndrew Jones void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr) 1797ee966e9SAndrew Jones { 1807ee966e9SAndrew Jones sp_usr &= (~15UL); /* stack ptr needs 16-byte alignment */ 1817ee966e9SAndrew Jones 1827ee966e9SAndrew Jones user_mode = true; 1837ee966e9SAndrew Jones 1847ee966e9SAndrew Jones asm volatile( 1857ee966e9SAndrew Jones "mov x0, %0\n" 1867ee966e9SAndrew Jones "msr sp_el0, %1\n" 1877ee966e9SAndrew Jones "msr elr_el1, %2\n" 1887ee966e9SAndrew Jones "mov x3, xzr\n" /* clear and "set" PSR_MODE_EL0t */ 1897ee966e9SAndrew Jones "msr spsr_el1, x3\n" 1907ee966e9SAndrew Jones "eret\n" 1917ee966e9SAndrew Jones :: "r" (arg), "r" (sp_usr), "r" (func) : "x0", "x3"); 1927ee966e9SAndrew Jones } 193