| /* |
| * processor control and status functions |
| * |
| * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com> |
| * |
| * This work is licensed under the terms of the GNU LGPL, version 2. |
| */ |
| #include <libcflat.h> |
| #include <asm/ptrace.h> |
| #include <asm/processor.h> |
| #include <asm/thread_info.h> |
| |
| static const char *processor_modes[] = { |
| "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , |
| "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" , |
| "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", |
| "UK12_26", "UK13_26", "UK14_26", "UK15_26", |
| "USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , |
| "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" , |
| "UK8_32" , "UK9_32" , "UK10_32", "UND_32" , |
| "UK12_32", "UK13_32", "UK14_32", "SYS_32" |
| }; |
| |
| static const char *vector_names[] = { |
| "rst", "und", "svc", "pabt", "dabt", "addrexcptn", "irq", "fiq" |
| }; |
| |
| void show_regs(struct pt_regs *regs) |
| { |
| unsigned long flags; |
| char buf[64]; |
| |
| printf("pc : [<%08lx>] lr : [<%08lx>] psr: %08lx\n" |
| "sp : %08lx ip : %08lx fp : %08lx\n", |
| regs->ARM_pc, regs->ARM_lr, regs->ARM_cpsr, |
| regs->ARM_sp, regs->ARM_ip, regs->ARM_fp); |
| printf("r10: %08lx r9 : %08lx r8 : %08lx\n", |
| regs->ARM_r10, regs->ARM_r9, regs->ARM_r8); |
| printf("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", |
| regs->ARM_r7, regs->ARM_r6, regs->ARM_r5, regs->ARM_r4); |
| printf("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", |
| regs->ARM_r3, regs->ARM_r2, regs->ARM_r1, regs->ARM_r0); |
| |
| flags = regs->ARM_cpsr; |
| buf[0] = flags & PSR_N_BIT ? 'N' : 'n'; |
| buf[1] = flags & PSR_Z_BIT ? 'Z' : 'z'; |
| buf[2] = flags & PSR_C_BIT ? 'C' : 'c'; |
| buf[3] = flags & PSR_V_BIT ? 'V' : 'v'; |
| buf[4] = '\0'; |
| |
| printf("Flags: %s IRQs o%s FIQs o%s Mode %s\n", |
| buf, interrupts_enabled(regs) ? "n" : "ff", |
| fast_interrupts_enabled(regs) ? "n" : "ff", |
| processor_modes[processor_mode(regs)]); |
| |
| if (!user_mode(regs)) { |
| unsigned int ctrl, transbase, dac; |
| asm volatile( |
| "mrc p15, 0, %0, c1, c0\n" |
| "mrc p15, 0, %1, c2, c0\n" |
| "mrc p15, 0, %2, c3, c0\n" |
| : "=r" (ctrl), "=r" (transbase), "=r" (dac)); |
| printf("Control: %08x Table: %08x DAC: %08x\n", |
| ctrl, transbase, dac); |
| } |
| } |
| |
| void install_exception_handler(enum vector v, exception_fn fn) |
| { |
| struct thread_info *ti = current_thread_info(); |
| |
| if (v < EXCPTN_MAX) |
| ti->exception_handlers[v] = fn; |
| } |
| |
| /* Needed to compile with -Wmissing-prototypes */ |
| void do_handle_exception(enum vector v, struct pt_regs *regs); |
| |
| void do_handle_exception(enum vector v, struct pt_regs *regs) |
| { |
| struct thread_info *ti = thread_info_sp(regs->ARM_sp); |
| |
| if (ti->flags & TIF_USER_MODE) { |
| if (v < EXCPTN_MAX && ti->exception_handlers[v]) { |
| ti->exception_handlers[v](regs); |
| return; |
| } |
| ti = current_thread_info(); |
| } |
| |
| if (v < EXCPTN_MAX && ti->exception_handlers[v]) { |
| ti->exception_handlers[v](regs); |
| return; |
| } |
| |
| if (v < EXCPTN_MAX) |
| printf("Unhandled exception %d (%s)\n", v, vector_names[v]); |
| else |
| printf("%s called with vector=%d\n", __func__, v); |
| |
| printf("Exception frame registers:\n"); |
| show_regs(regs); |
| if (v == EXCPTN_DABT) { |
| unsigned long far, fsr; |
| asm volatile("mrc p15, 0, %0, c6, c0, 0": "=r" (far)); |
| asm volatile("mrc p15, 0, %0, c5, c0, 0": "=r" (fsr)); |
| printf("DFAR: %08lx DFSR: %08lx\n", far, fsr); |
| } else if (v == EXCPTN_PABT) { |
| unsigned long far, fsr; |
| asm volatile("mrc p15, 0, %0, c6, c0, 2": "=r" (far)); |
| asm volatile("mrc p15, 0, %0, c5, c0, 1": "=r" (fsr)); |
| printf("IFAR: %08lx IFSR: %08lx\n", far, fsr); |
| } |
| dump_frame_stack((void *)regs->ARM_pc, (void *)regs->ARM_fp); |
| abort(); |
| } |
| |
| void thread_info_init(struct thread_info *ti, unsigned int flags) |
| { |
| memset(ti, 0, sizeof(struct thread_info)); |
| ti->cpu = mpidr_to_cpu(get_mpidr()); |
| ti->flags = flags; |
| } |
| |
| void start_usr(void (*func)(void *arg), void *arg, unsigned long sp_usr) |
| { |
| sp_usr &= (~7UL); /* stack ptr needs 8-byte alignment */ |
| |
| thread_info_init(thread_info_sp(sp_usr), TIF_USER_MODE); |
| thread_info_sp(sp_usr)->pgtable = current_thread_info()->pgtable; |
| |
| asm volatile( |
| "mrs r0, cpsr\n" |
| "bic r0, #" xstr(MODE_MASK) "\n" |
| "orr r0, #" xstr(USR_MODE) "\n" |
| "msr cpsr_c, r0\n" |
| "isb\n" |
| "mov r0, %0\n" |
| "mov sp, %1\n" |
| "mov pc, %2\n" |
| :: "r" (arg), "r" (sp_usr), "r" (func) : "r0"); |
| } |
| |
| bool is_user(void) |
| { |
| return current_thread_info()->flags & TIF_USER_MODE; |
| } |
| |
| bool __mmu_enabled(void) |
| { |
| return read_sysreg(SCTRL) & CR_M; |
| } |