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> 11ad14f089SAndrew Jones #include <asm/thread_info.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 6960eb32bfSAndrew Jones printf("pc : [<%016lx>] lr : [<%016lx>] pstate: %08lx\n", 707ee966e9SAndrew Jones regs->pc, regs->regs[30], regs->pstate); 7160eb32bfSAndrew Jones printf("sp : %016lx\n", regs->sp); 727ee966e9SAndrew Jones 737ee966e9SAndrew Jones for (i = 29; i >= 0; --i) { 7460eb32bfSAndrew Jones printf("x%-2d: %016lx ", 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, 1039ae19a63SAndrew Jones unsigned int esr, bool esr_valid, 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); 1159ae19a63SAndrew Jones } else if (esr_valid) { 1167ee966e9SAndrew Jones if (ec_names[ec]) 117fd6aada0SRadim Krčmář printf("Unhandled exception ec=%#x (%s)\n", ec, 1187ee966e9SAndrew Jones ec_names[ec]); 1197ee966e9SAndrew Jones else 120fd6aada0SRadim Krčmář printf("Got bad ec=%#x\n", ec); 1217ee966e9SAndrew Jones } 1227ee966e9SAndrew Jones 1237ee966e9SAndrew Jones printf("Vector: %d (%s)\n", v, vector_names[v]); 124fd6aada0SRadim Krčmář printf("ESR_EL1: %8s%08x, ec=%#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 void install_exception_handler(enum vector v, unsigned int ec, exception_fn fn) 1327ee966e9SAndrew Jones { 133ad14f089SAndrew Jones struct thread_info *ti = current_thread_info(); 134ad14f089SAndrew Jones 1357ee966e9SAndrew Jones if (v < VECTOR_MAX && ec < EC_MAX) 136ad14f089SAndrew Jones ti->exception_handlers[v][ec] = fn; 1377ee966e9SAndrew Jones } 1387ee966e9SAndrew Jones 1399ae19a63SAndrew Jones void install_irq_handler(enum vector v, irq_handler_fn fn) 1409ae19a63SAndrew Jones { 1419ae19a63SAndrew Jones struct thread_info *ti = current_thread_info(); 1429ae19a63SAndrew Jones 1439ae19a63SAndrew Jones if (v < VECTOR_MAX) 1449ae19a63SAndrew Jones ti->exception_handlers[v][0] = (exception_fn)fn; 1459ae19a63SAndrew Jones } 1469ae19a63SAndrew Jones 1479ae19a63SAndrew Jones void default_vector_sync_handler(enum vector v, struct pt_regs *regs, 1487ee966e9SAndrew Jones unsigned int esr) 1497ee966e9SAndrew Jones { 150ad14f089SAndrew Jones struct thread_info *ti = thread_info_sp(regs->sp); 1517ee966e9SAndrew Jones unsigned int ec = esr >> ESR_EL1_EC_SHIFT; 1527ee966e9SAndrew Jones 153ad14f089SAndrew Jones if (ti->flags & TIF_USER_MODE) { 154ad14f089SAndrew Jones if (ec < EC_MAX && ti->exception_handlers[v][ec]) { 155ad14f089SAndrew Jones ti->exception_handlers[v][ec](regs, esr); 156ad14f089SAndrew Jones return; 157ad14f089SAndrew Jones } 158ad14f089SAndrew Jones ti = current_thread_info(); 159ad14f089SAndrew Jones } 160ad14f089SAndrew Jones 161ad14f089SAndrew Jones if (ec < EC_MAX && ti->exception_handlers[v][ec]) 162ad14f089SAndrew Jones ti->exception_handlers[v][ec](regs, esr); 1637ee966e9SAndrew Jones else 1649ae19a63SAndrew Jones bad_exception(v, regs, esr, true, false); 1659ae19a63SAndrew Jones } 1669ae19a63SAndrew Jones 1679ae19a63SAndrew Jones void default_vector_irq_handler(enum vector v, struct pt_regs *regs, 1689ae19a63SAndrew Jones unsigned int esr) 1699ae19a63SAndrew Jones { 1709ae19a63SAndrew Jones struct thread_info *ti = thread_info_sp(regs->sp); 1719ae19a63SAndrew Jones irq_handler_fn irq_handler = 1729ae19a63SAndrew Jones (irq_handler_fn)ti->exception_handlers[v][0]; 1739ae19a63SAndrew Jones 1749ae19a63SAndrew Jones if (ti->flags & TIF_USER_MODE) { 1759ae19a63SAndrew Jones if (irq_handler) { 1769ae19a63SAndrew Jones irq_handler(regs); 1779ae19a63SAndrew Jones return; 1789ae19a63SAndrew Jones } 1799ae19a63SAndrew Jones ti = current_thread_info(); 1809ae19a63SAndrew Jones irq_handler = (irq_handler_fn)ti->exception_handlers[v][0]; 1819ae19a63SAndrew Jones } 1829ae19a63SAndrew Jones 1839ae19a63SAndrew Jones if (irq_handler) 1849ae19a63SAndrew Jones irq_handler(regs); 1859ae19a63SAndrew Jones else 1869ae19a63SAndrew Jones bad_exception(v, regs, esr, false, false); 1877ee966e9SAndrew Jones } 1887ee966e9SAndrew Jones 189ad14f089SAndrew Jones void vector_handlers_default_init(vector_fn *handlers) 190ad14f089SAndrew Jones { 1919ae19a63SAndrew Jones handlers[EL1H_SYNC] = default_vector_sync_handler; 1929ae19a63SAndrew Jones handlers[EL1H_IRQ] = default_vector_irq_handler; 1939ae19a63SAndrew Jones handlers[EL0_SYNC_64] = default_vector_sync_handler; 1949ae19a63SAndrew Jones handlers[EL0_IRQ_64] = default_vector_irq_handler; 195ad14f089SAndrew Jones } 1967ee966e9SAndrew Jones 1977ee966e9SAndrew Jones void do_handle_exception(enum vector v, struct pt_regs *regs, unsigned int esr) 1987ee966e9SAndrew Jones { 199ad14f089SAndrew Jones struct thread_info *ti = thread_info_sp(regs->sp); 200ad14f089SAndrew Jones 201ad14f089SAndrew Jones if (ti->flags & TIF_USER_MODE) { 202ad14f089SAndrew Jones if (v < VECTOR_MAX && ti->vector_handlers[v]) { 203ad14f089SAndrew Jones ti->vector_handlers[v](v, regs, esr); 204ad14f089SAndrew Jones return; 205ad14f089SAndrew Jones } 206ad14f089SAndrew Jones ti = current_thread_info(); 207ad14f089SAndrew Jones } 208ad14f089SAndrew Jones 209ad14f089SAndrew Jones if (v < VECTOR_MAX && ti->vector_handlers[v]) 210ad14f089SAndrew Jones ti->vector_handlers[v](v, regs, esr); 2117ee966e9SAndrew Jones else 2129ae19a63SAndrew Jones bad_exception(v, regs, esr, true, true); 2137ee966e9SAndrew Jones } 2147ee966e9SAndrew Jones 2157ee966e9SAndrew Jones void install_vector_handler(enum vector v, vector_fn fn) 2167ee966e9SAndrew Jones { 217ad14f089SAndrew Jones struct thread_info *ti = current_thread_info(); 218ad14f089SAndrew Jones 2197ee966e9SAndrew Jones if (v < VECTOR_MAX) 220ad14f089SAndrew Jones ti->vector_handlers[v] = fn; 2217ee966e9SAndrew Jones } 2227ee966e9SAndrew Jones 223f1a7b2b2SAndrew Jones static void __thread_info_init(struct thread_info *ti, unsigned int flags) 224f6d10793SAndrew Jones { 225f6d10793SAndrew Jones memset(ti, 0, sizeof(struct thread_info)); 226f6d10793SAndrew Jones ti->cpu = mpidr_to_cpu(get_mpidr()); 227f6d10793SAndrew Jones ti->flags = flags; 228f1a7b2b2SAndrew Jones } 229f1a7b2b2SAndrew Jones 230f1a7b2b2SAndrew Jones void thread_info_init(struct thread_info *ti, unsigned int flags) 231f1a7b2b2SAndrew Jones { 232f1a7b2b2SAndrew Jones __thread_info_init(ti, flags); 233ad14f089SAndrew Jones vector_handlers_default_init(ti->vector_handlers); 234f6d10793SAndrew Jones } 235f6d10793SAndrew Jones 2367ee966e9SAndrew Jones void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr) 2377ee966e9SAndrew Jones { 2387ee966e9SAndrew Jones sp_usr &= (~15UL); /* stack ptr needs 16-byte alignment */ 2397ee966e9SAndrew Jones 240f1a7b2b2SAndrew Jones __thread_info_init(thread_info_sp(sp_usr), TIF_USER_MODE); 241*36b50de9SAndrew Jones thread_info_sp(sp_usr)->pgtable = current_thread_info()->pgtable; 2427ee966e9SAndrew Jones 2437ee966e9SAndrew Jones asm volatile( 2447ee966e9SAndrew Jones "mov x0, %0\n" 2457ee966e9SAndrew Jones "msr sp_el0, %1\n" 2467ee966e9SAndrew Jones "msr elr_el1, %2\n" 2477ee966e9SAndrew Jones "mov x3, xzr\n" /* clear and "set" PSR_MODE_EL0t */ 2487ee966e9SAndrew Jones "msr spsr_el1, x3\n" 2497ee966e9SAndrew Jones "eret\n" 2507ee966e9SAndrew Jones :: "r" (arg), "r" (sp_usr), "r" (func) : "x0", "x3"); 2517ee966e9SAndrew Jones } 252f6d10793SAndrew Jones 253f6d10793SAndrew Jones bool is_user(void) 254f6d10793SAndrew Jones { 255f6d10793SAndrew Jones return current_thread_info()->flags & TIF_USER_MODE; 256f6d10793SAndrew Jones } 257