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 13a4049322SAndrew 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 32a4049322SAndrew 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 81db328a24SAndrew Jones bool get_far(unsigned int esr, unsigned long *far) 82db328a24SAndrew Jones { 83db328a24SAndrew Jones unsigned int ec = esr >> ESR_EL1_EC_SHIFT; 84db328a24SAndrew Jones 85db328a24SAndrew Jones asm volatile("mrs %0, far_el1": "=r" (*far)); 86db328a24SAndrew Jones 87db328a24SAndrew Jones switch (ec) { 88db328a24SAndrew Jones case ESR_EL1_EC_IABT_EL0: 89db328a24SAndrew Jones case ESR_EL1_EC_IABT_EL1: 90db328a24SAndrew Jones case ESR_EL1_EC_PC_ALIGN: 91db328a24SAndrew Jones case ESR_EL1_EC_DABT_EL0: 92db328a24SAndrew Jones case ESR_EL1_EC_DABT_EL1: 93db328a24SAndrew Jones case ESR_EL1_EC_WATCHPT_EL0: 94db328a24SAndrew Jones case ESR_EL1_EC_WATCHPT_EL1: 95db328a24SAndrew Jones if ((esr & 0x3f /* DFSC */) != 0x10 96db328a24SAndrew Jones || !(esr & 0x400 /* FnV */)) 97db328a24SAndrew Jones return true; 98db328a24SAndrew Jones } 99db328a24SAndrew Jones return false; 100db328a24SAndrew Jones } 101db328a24SAndrew Jones 1027ee966e9SAndrew Jones static void bad_exception(enum vector v, struct pt_regs *regs, 1037ee966e9SAndrew Jones unsigned int esr, bool bad_vector) 1047ee966e9SAndrew Jones { 105db328a24SAndrew Jones unsigned long far; 106db328a24SAndrew Jones bool far_valid = get_far(esr, &far); 1077ee966e9SAndrew Jones unsigned int ec = esr >> ESR_EL1_EC_SHIFT; 1087ee966e9SAndrew Jones 1097ee966e9SAndrew Jones if (bad_vector) { 1107ee966e9SAndrew Jones if (v < VECTOR_MAX) 1117ee966e9SAndrew Jones printf("Unhandled vector %d (%s)\n", v, 1127ee966e9SAndrew Jones vector_names[v]); 1137ee966e9SAndrew Jones else 1147ee966e9SAndrew Jones printf("Got bad vector=%d\n", v); 1157ee966e9SAndrew Jones } else { 1167ee966e9SAndrew Jones if (ec_names[ec]) 1177ee966e9SAndrew Jones printf("Unhandled exception ec=0x%x (%s)\n", ec, 1187ee966e9SAndrew Jones ec_names[ec]); 1197ee966e9SAndrew Jones else 1207ee966e9SAndrew Jones printf("Got bad ec=0x%x\n", ec); 1217ee966e9SAndrew Jones } 1227ee966e9SAndrew Jones 1237ee966e9SAndrew Jones printf("Vector: %d (%s)\n", v, vector_names[v]); 124db328a24SAndrew Jones printf("ESR_EL1: %8s%08lx, ec=0x%x (%s)\n", "", esr, ec, ec_names[ec]); 125db328a24SAndrew Jones printf("FAR_EL1: %016lx (%svalid)\n", far, far_valid ? "" : "not "); 1267ee966e9SAndrew Jones printf("Exception frame registers:\n"); 1277ee966e9SAndrew Jones show_regs(regs); 1287ee966e9SAndrew Jones abort(); 1297ee966e9SAndrew Jones } 1307ee966e9SAndrew Jones 1317ee966e9SAndrew Jones static exception_fn exception_handlers[VECTOR_MAX][EC_MAX]; 1327ee966e9SAndrew Jones 1337ee966e9SAndrew Jones void install_exception_handler(enum vector v, unsigned int ec, exception_fn fn) 1347ee966e9SAndrew Jones { 1357ee966e9SAndrew Jones if (v < VECTOR_MAX && ec < EC_MAX) 1367ee966e9SAndrew Jones exception_handlers[v][ec] = fn; 1377ee966e9SAndrew Jones } 1387ee966e9SAndrew Jones 139a4049322SAndrew Jones void default_vector_handler(enum vector v, struct pt_regs *regs, 1407ee966e9SAndrew Jones unsigned int esr) 1417ee966e9SAndrew Jones { 1427ee966e9SAndrew Jones unsigned int ec = esr >> ESR_EL1_EC_SHIFT; 1437ee966e9SAndrew Jones 1447ee966e9SAndrew Jones if (ec < EC_MAX && exception_handlers[v][ec]) 1457ee966e9SAndrew Jones exception_handlers[v][ec](regs, esr); 1467ee966e9SAndrew Jones else 1477ee966e9SAndrew Jones bad_exception(v, regs, esr, false); 1487ee966e9SAndrew Jones } 1497ee966e9SAndrew Jones 1507ee966e9SAndrew Jones static vector_fn vector_handlers[VECTOR_MAX] = { 1517ee966e9SAndrew Jones [EL1H_SYNC] = default_vector_handler, 1527ee966e9SAndrew Jones [EL1H_IRQ] = default_vector_handler, 1537ee966e9SAndrew Jones [EL0_SYNC_64] = default_vector_handler, 1547ee966e9SAndrew Jones [EL0_IRQ_64] = default_vector_handler, 1557ee966e9SAndrew Jones }; 1567ee966e9SAndrew Jones 1577ee966e9SAndrew Jones void do_handle_exception(enum vector v, struct pt_regs *regs, unsigned int esr) 1587ee966e9SAndrew Jones { 1597ee966e9SAndrew Jones if (v < VECTOR_MAX && vector_handlers[v]) 1607ee966e9SAndrew Jones vector_handlers[v](v, regs, esr); 1617ee966e9SAndrew Jones else 1627ee966e9SAndrew Jones bad_exception(v, regs, esr, true); 1637ee966e9SAndrew Jones } 1647ee966e9SAndrew Jones 1657ee966e9SAndrew Jones void install_vector_handler(enum vector v, vector_fn fn) 1667ee966e9SAndrew Jones { 1677ee966e9SAndrew Jones if (v < VECTOR_MAX) 1687ee966e9SAndrew Jones vector_handlers[v] = fn; 1697ee966e9SAndrew Jones } 1707ee966e9SAndrew Jones 171*f6d10793SAndrew Jones void thread_info_init(struct thread_info *ti, unsigned int flags) 172*f6d10793SAndrew Jones { 173*f6d10793SAndrew Jones memset(ti, 0, sizeof(struct thread_info)); 174*f6d10793SAndrew Jones ti->cpu = mpidr_to_cpu(get_mpidr()); 175*f6d10793SAndrew Jones ti->flags = flags; 176*f6d10793SAndrew Jones } 177*f6d10793SAndrew Jones 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 182*f6d10793SAndrew Jones thread_info_init(thread_info_sp(sp_usr), TIF_USER_MODE); 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*f6d10793SAndrew Jones 194*f6d10793SAndrew Jones bool is_user(void) 195*f6d10793SAndrew Jones { 196*f6d10793SAndrew Jones return current_thread_info()->flags & TIF_USER_MODE; 197*f6d10793SAndrew Jones } 198