|  | #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(); | 
|  | } |