linux-stable/arch/parisc/kernel/toc.c
Sven Schnelle bc294838cc parisc: add support for TOC (transfer of control)
Almost all PA-RISC machines have either a button that
is labeled with 'TOC' or a BMC function to trigger a TOC.
TOC is a non-maskable interrupt that is sent to the processor.
This can be used for diagnostic purposes like obtaining a
stack trace/register dump or to enter KDB/KGDB.

As an example, on my c8000, TOC can be used with:

CONFIG_KGDB=y
CONFIG_KGDB_KDB=y

and the 'kgdboc=ttyS0,115200' appended to the command line.

Press ^[( on serial console, which will enter the BMC command line,
and enter 'TOC s':

root@(none):/# (
cli>TOC s
Sending TOC/INIT.
<Cpu3> 2800035d03e00000  0000000040c21ac8  CC_ERR_CHECK_TOC
<Cpu0> 2800035d00e00000  0000000040c21ad0  CC_ERR_CHECK_TOC
<Cpu2> 2800035d02e00000  0000000040c21ac8  CC_ERR_CHECK_TOC
<Cpu1> 2800035d01e00000  0000000040c21ad0  CC_ERR_CHECK_TOC
<Cpu3> 37000f7303e00000  2000000000000000  CC_ERR_CPU_CHECK_SUMMARY
<Cpu0> 37000f7300e00000  2000000000000000  CC_ERR_CPU_CHECK_SUMMARY
<Cpu2> 37000f7302e00000  2000000000000000  CC_ERR_CPU_CHECK_SUMMARY
<Cpu1> 37000f7301e00000  2000000000000000  CC_ERR_CPU_CHECK_SUMMARY
<Cpu3> 4300100803e00000  c0000000001d26cc  CC_MC_BR_TO_OS_TOC
<Cpu0> 4300100800e00000  c0000000001d26cc  CC_MC_BR_TO_OS_TOC
<Cpu2> 4300100802e00000  c0000000001d26cc  CC_MC_BR_TO_OS_TOC
<Cpu1> 4300100801e00000  c0000000001d26cc  CC_MC_BR_TO_OS_TOC

Entering kdb (current=0x00000000411cef80, pid 0) on processor 0 due to NonMaskable Interrupt @ 0x40c21ad0
[0]kdb>

Signed-off-by: Sven Schnelle <svens@stackframe.org>
Signed-off-by: Helge Deller <deller@gmx.de>
2021-11-01 07:35:59 +01:00

111 lines
2.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/kgdb.h>
#include <linux/printk.h>
#include <linux/sched/debug.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <asm/pdc.h>
#include <asm/pdc_chassis.h>
unsigned int __aligned(16) toc_lock = 1;
static void toc20_to_pt_regs(struct pt_regs *regs, struct pdc_toc_pim_20 *toc)
{
int i;
regs->gr[0] = (unsigned long)toc->cr[22];
for (i = 1; i < 32; i++)
regs->gr[i] = (unsigned long)toc->gr[i];
for (i = 0; i < 8; i++)
regs->sr[i] = (unsigned long)toc->sr[i];
regs->iasq[0] = (unsigned long)toc->cr[17];
regs->iasq[1] = (unsigned long)toc->iasq_back;
regs->iaoq[0] = (unsigned long)toc->cr[18];
regs->iaoq[1] = (unsigned long)toc->iaoq_back;
regs->sar = (unsigned long)toc->cr[11];
regs->iir = (unsigned long)toc->cr[19];
regs->isr = (unsigned long)toc->cr[20];
regs->ior = (unsigned long)toc->cr[21];
}
static void toc11_to_pt_regs(struct pt_regs *regs, struct pdc_toc_pim_11 *toc)
{
int i;
regs->gr[0] = toc->cr[22];
for (i = 1; i < 32; i++)
regs->gr[i] = toc->gr[i];
for (i = 0; i < 8; i++)
regs->sr[i] = toc->sr[i];
regs->iasq[0] = toc->cr[17];
regs->iasq[1] = toc->iasq_back;
regs->iaoq[0] = toc->cr[18];
regs->iaoq[1] = toc->iaoq_back;
regs->sar = toc->cr[11];
regs->iir = toc->cr[19];
regs->isr = toc->cr[20];
regs->ior = toc->cr[21];
}
void notrace __noreturn __cold toc_intr(struct pt_regs *regs)
{
struct pdc_toc_pim_20 pim_data20;
struct pdc_toc_pim_11 pim_data11;
nmi_enter();
if (boot_cpu_data.cpu_type >= pcxu) {
if (pdc_pim_toc20(&pim_data20))
panic("Failed to get PIM data");
toc20_to_pt_regs(regs, &pim_data20);
} else {
if (pdc_pim_toc11(&pim_data11))
panic("Failed to get PIM data");
toc11_to_pt_regs(regs, &pim_data11);
}
#ifdef CONFIG_KGDB
if (atomic_read(&kgdb_active) != -1)
kgdb_nmicallback(raw_smp_processor_id(), regs);
kgdb_handle_exception(9, SIGTRAP, 0, regs);
#endif
show_regs(regs);
/* give other CPUs time to show their backtrace */
mdelay(2000);
machine_restart("TOC");
/* should never reach this */
panic("TOC");
}
static __init int setup_toc(void)
{
unsigned int csum = 0;
unsigned long toc_code = (unsigned long)dereference_function_descriptor(toc_handler);
int i;
PAGE0->vec_toc = __pa(toc_code) & 0xffffffff;
#ifdef CONFIG_64BIT
PAGE0->vec_toc_hi = __pa(toc_code) >> 32;
#endif
PAGE0->vec_toclen = toc_handler_size;
for (i = 0; i < toc_handler_size/4; i++)
csum += ((u32 *)toc_code)[i];
toc_handler_csum = -csum;
pr_info("TOC handler registered\n");
return 0;
}
early_initcall(setup_toc);