| #include <string.h> |
| #include <assert.h> |
| #include <kdebug.h> |
| #include <pmap.h> |
| #include <process.h> |
| #include <kmalloc.h> |
| #include <arch/uaccess.h> |
| |
| void gen_backtrace(void (*pfunc)(void *, const char *), void *opaque) |
| { |
| uintptr_t pcs[MAX_BT_DEPTH]; |
| size_t nr_pcs; |
| |
| nr_pcs = backtrace_list(get_caller_pc(), get_caller_fp(), pcs, |
| MAX_BT_DEPTH); |
| print_backtrace_list(pcs, nr_pcs, pfunc, opaque); |
| } |
| |
| static bool pc_is_asm_trampoline(uintptr_t pc) |
| { |
| extern char __asm_entry_points_start[], __asm_entry_points_end[]; |
| extern char __asm_pop_hwtf_start[], __asm_pop_hwtf_end[]; |
| extern char __asm_pop_swtf_start[], __asm_pop_swtf_end[]; |
| extern char __asm_pop_vmtf_start[], __asm_pop_vmtf_end[]; |
| |
| if (((uintptr_t)__asm_entry_points_start <= pc) && |
| (pc < (uintptr_t)__asm_entry_points_end)) |
| return TRUE; |
| if (((uintptr_t)__asm_pop_hwtf_start <= pc) && |
| (pc < (uintptr_t)__asm_pop_hwtf_end)) |
| return TRUE; |
| if (((uintptr_t)__asm_pop_swtf_start <= pc) && |
| (pc < (uintptr_t)__asm_pop_swtf_end)) |
| return TRUE; |
| if (((uintptr_t)__asm_pop_vmtf_start <= pc) && |
| (pc < (uintptr_t)__asm_pop_vmtf_end)) |
| return TRUE; |
| return FALSE; |
| } |
| |
| size_t backtrace_list(uintptr_t pc, uintptr_t fp, uintptr_t *pcs, |
| size_t nr_slots) |
| { |
| size_t nr_pcs = 0; |
| |
| while (nr_pcs < nr_slots) { |
| pcs[nr_pcs++] = pc; |
| printd("PC %p FP %p\n", pc, fp); |
| if (pc_is_asm_trampoline(pc)) |
| break; |
| if (!fp) |
| break; |
| assert(KERNBASE <= fp); |
| /* We need to check the next FP before reading PC from beyond |
| * it. FP could be 0 and be at the top of the stack, and |
| * reading PC in that case will be a wild read. */ |
| if (!*(uintptr_t*)fp) |
| break; |
| /* We used to set PC = retaddr - 1, where the -1 would put our |
| * PC back inside the function that called us. This was for |
| * obscure cases where a no-return function calls another |
| * function and has no other code after the function call. Or |
| * something. */ |
| pc = *(uintptr_t*)(fp + sizeof(uintptr_t)); |
| fp = *(uintptr_t*)fp; |
| } |
| return nr_pcs; |
| } |
| |
| size_t backtrace_user_list(uintptr_t pc, uintptr_t fp, uintptr_t *pcs, |
| size_t nr_slots) |
| { |
| int error; |
| size_t nr_pcs = 0; |
| uintptr_t frame[2]; |
| |
| while (nr_pcs < nr_slots) { |
| pcs[nr_pcs++] = pc; |
| if (!fp) |
| break; |
| error = copy_from_user(frame, (const void *) fp, 2 * |
| sizeof(uintptr_t)); |
| if (unlikely(error)) |
| break; |
| pc = frame[1]; |
| fp = frame[0]; |
| } |
| return nr_pcs; |
| } |
| |
| /* Assumes 32-bit header */ |
| void print_fpu_state(struct ancillary_state *fpu) |
| { |
| print_lock(); |
| printk("fcw: 0x%04x\n", fpu->fp_head_n64.fcw); |
| printk("fsw: 0x%04x\n", fpu->fp_head_n64.fsw); |
| printk("ftw: 0x%02x\n", fpu->fp_head_n64.ftw); |
| printk("fop: 0x%04x\n", fpu->fp_head_n64.fop); |
| printk("fpu_ip: 0x%08x\n", fpu->fp_head_n64.fpu_ip); |
| printk("cs: 0x%04x\n", fpu->fp_head_n64.cs); |
| printk("fpu_dp: 0x%08x\n", fpu->fp_head_n64.fpu_dp); |
| printk("ds: 0x%04x\n", fpu->fp_head_n64.ds); |
| printk("mxcsr: 0x%08x\n", fpu->fp_head_n64.mxcsr); |
| printk("mxcsrm: 0x%08x\n", fpu->fp_head_n64.mxcsr_mask); |
| |
| for (int i = 0; i < sizeof(struct ancillary_state); i++) { |
| if (i % 20 == 0) |
| printk("\n"); |
| printk("%02x ", *((uint8_t*)fpu + i)); |
| } |
| printk("\n"); |
| print_unlock(); |
| } |