mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-28 11:18:45 +07:00
9cc5c206d9
git commit dc7ee00d47
"s390: lowcore stack pointer offsets"
introduced a regression in regard to show_stack(). The stack pointer
for the asynchronous and the panic stack in the lowcore now have an
additional offset applied to them. This offset needs to be taken into
account in the calculation for the low and high address for the stacks.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
217 lines
6.1 KiB
C
217 lines
6.1 KiB
C
/*
|
|
* Stack dumping functions
|
|
*
|
|
* Copyright IBM Corp. 1999, 2013
|
|
*/
|
|
|
|
#include <linux/kallsyms.h>
|
|
#include <linux/hardirq.h>
|
|
#include <linux/kprobes.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/export.h>
|
|
#include <linux/kdebug.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/debug.h>
|
|
#include <asm/ipl.h>
|
|
|
|
#ifndef CONFIG_64BIT
|
|
#define LONG "%08lx "
|
|
#define FOURLONG "%08lx %08lx %08lx %08lx\n"
|
|
static int kstack_depth_to_print = 12;
|
|
#else /* CONFIG_64BIT */
|
|
#define LONG "%016lx "
|
|
#define FOURLONG "%016lx %016lx %016lx %016lx\n"
|
|
static int kstack_depth_to_print = 20;
|
|
#endif /* CONFIG_64BIT */
|
|
|
|
/*
|
|
* For show_trace we have tree different stack to consider:
|
|
* - the panic stack which is used if the kernel stack has overflown
|
|
* - the asynchronous interrupt stack (cpu related)
|
|
* - the synchronous kernel stack (process related)
|
|
* The stack trace can start at any of the three stack and can potentially
|
|
* touch all of them. The order is: panic stack, async stack, sync stack.
|
|
*/
|
|
static unsigned long
|
|
__show_trace(unsigned long sp, unsigned long low, unsigned long high)
|
|
{
|
|
struct stack_frame *sf;
|
|
struct pt_regs *regs;
|
|
|
|
while (1) {
|
|
sp = sp & PSW_ADDR_INSN;
|
|
if (sp < low || sp > high - sizeof(*sf))
|
|
return sp;
|
|
sf = (struct stack_frame *) sp;
|
|
printk("([<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN);
|
|
print_symbol("%s)\n", sf->gprs[8] & PSW_ADDR_INSN);
|
|
/* Follow the backchain. */
|
|
while (1) {
|
|
low = sp;
|
|
sp = sf->back_chain & PSW_ADDR_INSN;
|
|
if (!sp)
|
|
break;
|
|
if (sp <= low || sp > high - sizeof(*sf))
|
|
return sp;
|
|
sf = (struct stack_frame *) sp;
|
|
printk(" [<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN);
|
|
print_symbol("%s\n", sf->gprs[8] & PSW_ADDR_INSN);
|
|
}
|
|
/* Zero backchain detected, check for interrupt frame. */
|
|
sp = (unsigned long) (sf + 1);
|
|
if (sp <= low || sp > high - sizeof(*regs))
|
|
return sp;
|
|
regs = (struct pt_regs *) sp;
|
|
printk(" [<%016lx>] ", regs->psw.addr & PSW_ADDR_INSN);
|
|
print_symbol("%s\n", regs->psw.addr & PSW_ADDR_INSN);
|
|
low = sp;
|
|
sp = regs->gprs[15];
|
|
}
|
|
}
|
|
|
|
static void show_trace(struct task_struct *task, unsigned long *stack)
|
|
{
|
|
const unsigned long frame_size =
|
|
STACK_FRAME_OVERHEAD + sizeof(struct pt_regs);
|
|
register unsigned long __r15 asm ("15");
|
|
unsigned long sp;
|
|
|
|
sp = (unsigned long) stack;
|
|
if (!sp)
|
|
sp = task ? task->thread.ksp : __r15;
|
|
printk("Call Trace:\n");
|
|
#ifdef CONFIG_CHECK_STACK
|
|
sp = __show_trace(sp,
|
|
S390_lowcore.panic_stack + frame_size - 4096,
|
|
S390_lowcore.panic_stack + frame_size);
|
|
#endif
|
|
sp = __show_trace(sp,
|
|
S390_lowcore.async_stack + frame_size - ASYNC_SIZE,
|
|
S390_lowcore.async_stack + frame_size);
|
|
if (task)
|
|
__show_trace(sp, (unsigned long) task_stack_page(task),
|
|
(unsigned long) task_stack_page(task) + THREAD_SIZE);
|
|
else
|
|
__show_trace(sp, S390_lowcore.thread_info,
|
|
S390_lowcore.thread_info + THREAD_SIZE);
|
|
if (!task)
|
|
task = current;
|
|
debug_show_held_locks(task);
|
|
}
|
|
|
|
void show_stack(struct task_struct *task, unsigned long *sp)
|
|
{
|
|
register unsigned long *__r15 asm ("15");
|
|
unsigned long *stack;
|
|
int i;
|
|
|
|
if (!sp)
|
|
stack = task ? (unsigned long *) task->thread.ksp : __r15;
|
|
else
|
|
stack = sp;
|
|
|
|
for (i = 0; i < kstack_depth_to_print; i++) {
|
|
if (((addr_t) stack & (THREAD_SIZE-1)) == 0)
|
|
break;
|
|
if ((i * sizeof(long) % 32) == 0)
|
|
printk("%s ", i == 0 ? "" : "\n");
|
|
printk(LONG, *stack++);
|
|
}
|
|
printk("\n");
|
|
show_trace(task, sp);
|
|
}
|
|
|
|
static void show_last_breaking_event(struct pt_regs *regs)
|
|
{
|
|
#ifdef CONFIG_64BIT
|
|
printk("Last Breaking-Event-Address:\n");
|
|
printk(" [<%016lx>] ", regs->args[0] & PSW_ADDR_INSN);
|
|
print_symbol("%s\n", regs->args[0] & PSW_ADDR_INSN);
|
|
#endif
|
|
}
|
|
|
|
static inline int mask_bits(struct pt_regs *regs, unsigned long bits)
|
|
{
|
|
return (regs->psw.mask & bits) / ((~bits + 1) & bits);
|
|
}
|
|
|
|
void show_registers(struct pt_regs *regs)
|
|
{
|
|
char *mode;
|
|
|
|
mode = user_mode(regs) ? "User" : "Krnl";
|
|
printk("%s PSW : %p %p",
|
|
mode, (void *) regs->psw.mask,
|
|
(void *) regs->psw.addr);
|
|
print_symbol(" (%s)\n", regs->psw.addr & PSW_ADDR_INSN);
|
|
printk(" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x "
|
|
"P:%x AS:%x CC:%x PM:%x", mask_bits(regs, PSW_MASK_PER),
|
|
mask_bits(regs, PSW_MASK_DAT), mask_bits(regs, PSW_MASK_IO),
|
|
mask_bits(regs, PSW_MASK_EXT), mask_bits(regs, PSW_MASK_KEY),
|
|
mask_bits(regs, PSW_MASK_MCHECK), mask_bits(regs, PSW_MASK_WAIT),
|
|
mask_bits(regs, PSW_MASK_PSTATE), mask_bits(regs, PSW_MASK_ASC),
|
|
mask_bits(regs, PSW_MASK_CC), mask_bits(regs, PSW_MASK_PM));
|
|
#ifdef CONFIG_64BIT
|
|
printk(" EA:%x", mask_bits(regs, PSW_MASK_EA | PSW_MASK_BA));
|
|
#endif
|
|
printk("\n%s GPRS: " FOURLONG, mode,
|
|
regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
|
|
printk(" " FOURLONG,
|
|
regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
|
|
printk(" " FOURLONG,
|
|
regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]);
|
|
printk(" " FOURLONG,
|
|
regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);
|
|
show_code(regs);
|
|
}
|
|
|
|
void show_regs(struct pt_regs *regs)
|
|
{
|
|
show_regs_print_info(KERN_DEFAULT);
|
|
show_registers(regs);
|
|
/* Show stack backtrace if pt_regs is from kernel mode */
|
|
if (!user_mode(regs))
|
|
show_trace(NULL, (unsigned long *) regs->gprs[15]);
|
|
show_last_breaking_event(regs);
|
|
}
|
|
|
|
static DEFINE_SPINLOCK(die_lock);
|
|
|
|
void die(struct pt_regs *regs, const char *str)
|
|
{
|
|
static int die_counter;
|
|
|
|
oops_enter();
|
|
lgr_info_log();
|
|
debug_stop_all();
|
|
console_verbose();
|
|
spin_lock_irq(&die_lock);
|
|
bust_spinlocks(1);
|
|
printk("%s: %04x [#%d] ", str, regs->int_code & 0xffff, ++die_counter);
|
|
#ifdef CONFIG_PREEMPT
|
|
printk("PREEMPT ");
|
|
#endif
|
|
#ifdef CONFIG_SMP
|
|
printk("SMP ");
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_PAGEALLOC
|
|
printk("DEBUG_PAGEALLOC");
|
|
#endif
|
|
printk("\n");
|
|
notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV);
|
|
print_modules();
|
|
show_regs(regs);
|
|
bust_spinlocks(0);
|
|
add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
|
|
spin_unlock_irq(&die_lock);
|
|
if (in_interrupt())
|
|
panic("Fatal exception in interrupt");
|
|
if (panic_on_oops)
|
|
panic("Fatal exception: panic_on_oops");
|
|
oops_exit();
|
|
do_exit(SIGSEGV);
|
|
}
|