forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.1 KiB
227 lines
6.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) |
|
*/ |
|
|
|
#include <linux/ptrace.h> |
|
#include <linux/module.h> |
|
#include <linux/mm.h> |
|
#include <linux/fs.h> |
|
#include <linux/kdev_t.h> |
|
#include <linux/proc_fs.h> |
|
#include <linux/file.h> |
|
#include <linux/sched/mm.h> |
|
#include <linux/sched/debug.h> |
|
|
|
#include <asm/arcregs.h> |
|
#include <asm/irqflags.h> |
|
|
|
#define ARC_PATH_MAX 256 |
|
|
|
static noinline void print_regs_scratch(struct pt_regs *regs) |
|
{ |
|
pr_cont("BTA: 0x%08lx\n SP: 0x%08lx FP: 0x%08lx BLK: %pS\n", |
|
regs->bta, regs->sp, regs->fp, (void *)regs->blink); |
|
pr_cont("LPS: 0x%08lx\tLPE: 0x%08lx\tLPC: 0x%08lx\n", |
|
regs->lp_start, regs->lp_end, regs->lp_count); |
|
|
|
pr_info("r00: 0x%08lx\tr01: 0x%08lx\tr02: 0x%08lx\n" \ |
|
"r03: 0x%08lx\tr04: 0x%08lx\tr05: 0x%08lx\n" \ |
|
"r06: 0x%08lx\tr07: 0x%08lx\tr08: 0x%08lx\n" \ |
|
"r09: 0x%08lx\tr10: 0x%08lx\tr11: 0x%08lx\n" \ |
|
"r12: 0x%08lx\t", |
|
regs->r0, regs->r1, regs->r2, |
|
regs->r3, regs->r4, regs->r5, |
|
regs->r6, regs->r7, regs->r8, |
|
regs->r9, regs->r10, regs->r11, |
|
regs->r12); |
|
} |
|
|
|
static void print_regs_callee(struct callee_regs *regs) |
|
{ |
|
pr_cont("r13: 0x%08lx\tr14: 0x%08lx\n" \ |
|
"r15: 0x%08lx\tr16: 0x%08lx\tr17: 0x%08lx\n" \ |
|
"r18: 0x%08lx\tr19: 0x%08lx\tr20: 0x%08lx\n" \ |
|
"r21: 0x%08lx\tr22: 0x%08lx\tr23: 0x%08lx\n" \ |
|
"r24: 0x%08lx\tr25: 0x%08lx\n", |
|
regs->r13, regs->r14, |
|
regs->r15, regs->r16, regs->r17, |
|
regs->r18, regs->r19, regs->r20, |
|
regs->r21, regs->r22, regs->r23, |
|
regs->r24, regs->r25); |
|
} |
|
|
|
static void print_task_path_n_nm(struct task_struct *tsk) |
|
{ |
|
char *path_nm = NULL; |
|
struct mm_struct *mm; |
|
struct file *exe_file; |
|
char buf[ARC_PATH_MAX]; |
|
|
|
mm = get_task_mm(tsk); |
|
if (!mm) |
|
goto done; |
|
|
|
exe_file = get_mm_exe_file(mm); |
|
mmput(mm); |
|
|
|
if (exe_file) { |
|
path_nm = file_path(exe_file, buf, ARC_PATH_MAX-1); |
|
fput(exe_file); |
|
} |
|
|
|
done: |
|
pr_info("Path: %s\n", !IS_ERR(path_nm) ? path_nm : "?"); |
|
} |
|
|
|
static void show_faulting_vma(unsigned long address) |
|
{ |
|
struct vm_area_struct *vma; |
|
struct mm_struct *active_mm = current->active_mm; |
|
|
|
/* can't use print_vma_addr() yet as it doesn't check for |
|
* non-inclusive vma |
|
*/ |
|
mmap_read_lock(active_mm); |
|
vma = vma_lookup(active_mm, address); |
|
|
|
/* Lookup the vma at the address and report if the container VMA is not |
|
* found |
|
*/ |
|
if (vma) { |
|
char buf[ARC_PATH_MAX]; |
|
char *nm = "?"; |
|
|
|
if (vma->vm_file) { |
|
nm = file_path(vma->vm_file, buf, ARC_PATH_MAX-1); |
|
if (IS_ERR(nm)) |
|
nm = "?"; |
|
} |
|
pr_info(" @off 0x%lx in [%s] VMA: 0x%08lx to 0x%08lx\n", |
|
vma->vm_start < TASK_UNMAPPED_BASE ? |
|
address : address - vma->vm_start, |
|
nm, vma->vm_start, vma->vm_end); |
|
} else |
|
pr_info(" @No matching VMA found\n"); |
|
|
|
mmap_read_unlock(active_mm); |
|
} |
|
|
|
static void show_ecr_verbose(struct pt_regs *regs) |
|
{ |
|
unsigned int vec, cause_code; |
|
unsigned long address; |
|
|
|
/* For Data fault, this is data address not instruction addr */ |
|
address = current->thread.fault_address; |
|
|
|
vec = regs->ecr_vec; |
|
cause_code = regs->ecr_cause; |
|
|
|
/* For DTLB Miss or ProtV, display the memory involved too */ |
|
if (vec == ECR_V_DTLB_MISS) { |
|
pr_cont("Invalid %s @ 0x%08lx by insn @ %pS\n", |
|
(cause_code == 0x01) ? "Read" : |
|
((cause_code == 0x02) ? "Write" : "EX"), |
|
address, (void *)regs->ret); |
|
} else if (vec == ECR_V_ITLB_MISS) { |
|
pr_cont("Insn could not be fetched\n"); |
|
} else if (vec == ECR_V_MACH_CHK) { |
|
pr_cont("Machine Check (%s)\n", (cause_code == 0x0) ? |
|
"Double Fault" : "Other Fatal Err"); |
|
|
|
} else if (vec == ECR_V_PROTV) { |
|
if (cause_code == ECR_C_PROTV_INST_FETCH) |
|
pr_cont("Execute from Non-exec Page\n"); |
|
else if (cause_code == ECR_C_PROTV_MISALIG_DATA && |
|
IS_ENABLED(CONFIG_ISA_ARCOMPACT)) |
|
pr_cont("Misaligned r/w from 0x%08lx\n", address); |
|
else |
|
pr_cont("%s access not allowed on page\n", |
|
(cause_code == 0x01) ? "Read" : |
|
((cause_code == 0x02) ? "Write" : "EX")); |
|
} else if (vec == ECR_V_INSN_ERR) { |
|
pr_cont("Illegal Insn\n"); |
|
#ifdef CONFIG_ISA_ARCV2 |
|
} else if (vec == ECR_V_MEM_ERR) { |
|
if (cause_code == 0x00) |
|
pr_cont("Bus Error from Insn Mem\n"); |
|
else if (cause_code == 0x10) |
|
pr_cont("Bus Error from Data Mem\n"); |
|
else |
|
pr_cont("Bus Error, check PRM\n"); |
|
} else if (vec == ECR_V_MISALIGN) { |
|
pr_cont("Misaligned r/w from 0x%08lx\n", address); |
|
#endif |
|
} else if (vec == ECR_V_TRAP) { |
|
if (regs->ecr_param == 5) |
|
pr_cont("gcc generated __builtin_trap\n"); |
|
} else { |
|
pr_cont("Check Programmer's Manual\n"); |
|
} |
|
} |
|
|
|
/************************************************************************ |
|
* API called by rest of kernel |
|
***********************************************************************/ |
|
|
|
void show_regs(struct pt_regs *regs) |
|
{ |
|
struct task_struct *tsk = current; |
|
struct callee_regs *cregs = (struct callee_regs *)tsk->thread.callee_reg; |
|
|
|
/* |
|
* generic code calls us with preemption disabled, but some calls |
|
* here could sleep, so re-enable to avoid lockdep splat |
|
*/ |
|
preempt_enable(); |
|
|
|
print_task_path_n_nm(tsk); |
|
show_regs_print_info(KERN_INFO); |
|
|
|
show_ecr_verbose(regs); |
|
|
|
if (user_mode(regs)) |
|
show_faulting_vma(regs->ret); /* faulting code, not data */ |
|
|
|
pr_info("ECR: 0x%08lx EFA: 0x%08lx ERET: 0x%08lx\nSTAT: 0x%08lx", |
|
regs->event, current->thread.fault_address, regs->ret, |
|
regs->status32); |
|
|
|
#define STS_BIT(r, bit) r->status32 & STATUS_##bit##_MASK ? #bit" " : "" |
|
|
|
#ifdef CONFIG_ISA_ARCOMPACT |
|
pr_cont(" [%2s%2s%2s%2s%2s%2s%2s]", |
|
(regs->status32 & STATUS_U_MASK) ? "U " : "K ", |
|
STS_BIT(regs, DE), STS_BIT(regs, AE), |
|
STS_BIT(regs, A2), STS_BIT(regs, A1), |
|
STS_BIT(regs, E2), STS_BIT(regs, E1)); |
|
#else |
|
pr_cont(" [%2s%2s%2s%2s] ", |
|
STS_BIT(regs, IE), |
|
(regs->status32 & STATUS_U_MASK) ? "U " : "K ", |
|
STS_BIT(regs, DE), STS_BIT(regs, AE)); |
|
#endif |
|
|
|
print_regs_scratch(regs); |
|
if (cregs) |
|
print_regs_callee(cregs); |
|
|
|
preempt_disable(); |
|
} |
|
|
|
void show_kernel_fault_diag(const char *str, struct pt_regs *regs, |
|
unsigned long address) |
|
{ |
|
current->thread.fault_address = address; |
|
|
|
/* Show fault description */ |
|
pr_info("\n%s\n", str); |
|
|
|
/* Caller and Callee regs */ |
|
show_regs(regs); |
|
|
|
/* Show stack trace if this Fatality happened in kernel mode */ |
|
if (!user_mode(regs)) |
|
show_stacktrace(current, regs, KERN_DEFAULT); |
|
}
|
|
|