mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-14 18:46:45 +07:00
8ca4d19932
Currently, the log-level of show_stack() depends on a platform realization. It creates situations where the headers are printed with lower log level or higher than the stacktrace (depending on a platform or user). Furthermore, it forces the logic decision from user to an architecture side. In result, some users as sysrq/kdb/etc are doing tricks with temporary rising console_loglevel while printing their messages. And in result it not only may print unwanted messages from other CPUs, but also omit printing at all in the unlucky case where the printk() was deferred. Introducing log-level parameter and KERN_UNSUPPRESSED [1] seems an easier approach than introducing more printk buffers. Also, it will consolidate printings with headers. Introduce show_stack_loglvl(), that eventually will substitute show_stack(). As a good side-effect header "Stack Trace:" is now printed with the same log level as the rest of backtrace. [1]: https://lore.kernel.org/lkml/20190528002412.1625-1-dima@arista.com/T/#u Signed-off-by: Dmitry Safonov <dima@arista.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Vineet Gupta <vgupta@synopsys.com> Link: http://lkml.kernel.org/r/20200418201944.482088-4-dima@arista.com Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
245 lines
6.4 KiB
C
245 lines
6.4 KiB
C
// 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
|
|
|
|
/*
|
|
* Common routine to print scratch regs (r0-r12) or callee regs (r13-r25)
|
|
* -Prints 3 regs per line and a CR.
|
|
* -To continue, callee regs right after scratch, special handling of CR
|
|
*/
|
|
static noinline void print_reg_file(long *reg_rev, int start_num)
|
|
{
|
|
unsigned int i;
|
|
char buf[512];
|
|
int n = 0, len = sizeof(buf);
|
|
|
|
for (i = start_num; i < start_num + 13; i++) {
|
|
n += scnprintf(buf + n, len - n, "r%02u: 0x%08lx\t",
|
|
i, (unsigned long)*reg_rev);
|
|
|
|
if (((i + 1) % 3) == 0)
|
|
n += scnprintf(buf + n, len - n, "\n");
|
|
|
|
/* because pt_regs has regs reversed: r12..r0, r25..r13 */
|
|
if (is_isa_arcv2() && start_num == 0)
|
|
reg_rev++;
|
|
else
|
|
reg_rev--;
|
|
}
|
|
|
|
if (start_num != 0)
|
|
n += scnprintf(buf + n, len - n, "\n\n");
|
|
|
|
/* To continue printing callee regs on same line as scratch regs */
|
|
if (start_num == 0)
|
|
pr_info("%s", buf);
|
|
else
|
|
pr_cont("%s\n", buf);
|
|
}
|
|
|
|
static void show_callee_regs(struct callee_regs *cregs)
|
|
{
|
|
print_reg_file(&(cregs->r13), 13);
|
|
}
|
|
|
|
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
|
|
*/
|
|
down_read(&active_mm->mmap_sem);
|
|
vma = find_vma(active_mm, address);
|
|
|
|
/* check against the find_vma( ) behaviour which returns the next VMA
|
|
* if the container VMA is not found
|
|
*/
|
|
if (vma && (vma->vm_start <= address)) {
|
|
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");
|
|
|
|
up_read(&active_mm->mmap_sem);
|
|
}
|
|
|
|
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;
|
|
|
|
/*
|
|
* 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
|
|
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_info("LPS: 0x%08lx\tLPE: 0x%08lx\tLPC: 0x%08lx\n",
|
|
regs->lp_start, regs->lp_end, regs->lp_count);
|
|
|
|
/* print regs->r0 thru regs->r12
|
|
* Sequential printing was generating horrible code
|
|
*/
|
|
print_reg_file(&(regs->r0), 0);
|
|
|
|
/* If Callee regs were saved, display them too */
|
|
cregs = (struct callee_regs *)current->thread.callee_reg;
|
|
if (cregs)
|
|
show_callee_regs(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);
|
|
}
|