Commit bc294838 authored by Sven Schnelle's avatar Sven Schnelle Committed by Helge Deller
Browse files

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: default avatarSven Schnelle <svens@stackframe.org>
Signed-off-by: default avatarHelge Deller <deller@gmx.de>
parent ecac7036
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -291,6 +291,20 @@ config SMP

	  If you don't know what to do here, say N.

config TOC
	bool "Support TOC switch"
	default y if 64BIT || !SMP
	help
	  Most PA-RISC machines have either a switch at the back of the machine
	  or a command in BMC to trigger a TOC interrupt. If you say Y here a
	  handler will be installed which will either show a backtrace on all
	  CPUs, or enter a possible configured debugger like kgdb/kdb.

	  Note that with this option enabled, the kernel will use an additional 16KB
	  per possible CPU as a special stack for the TOC handler.

	  If you don't want to debug the Kernel, say N.

config PARISC_CPU_TOPOLOGY
	bool "Support cpu topology definition"
	depends on SMP
+4 −0
Original line number Diff line number Diff line
@@ -295,6 +295,10 @@ extern int _parisc_requires_coherency;

extern int running_on_qemu;

extern void toc_handler(void);
extern unsigned int toc_handler_size;
extern unsigned int toc_handler_csum;

#endif /* __ASSEMBLY__ */

#endif /* __ASM_PARISC_PROCESSOR_H */
+4 −2
Original line number Diff line number Diff line
@@ -398,7 +398,9 @@ struct zeropage {
	/* int	(*vec_rendz)(void); */
	unsigned int vec_rendz;
	int	vec_pow_fail_flen;
	int	vec_pad[10];		
	int	vec_pad0[3];
	unsigned int vec_toc_hi;
	int	vec_pad1[6];

	/* [0x040] reserved processor dependent */
	int	pad0[112];
+1 −0
Original line number Diff line number Diff line
@@ -39,3 +39,4 @@ obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_KPROBES)			+= kprobes.o
obj-$(CONFIG_KEXEC_CORE)		+= kexec.o relocate_kernel.o
obj-$(CONFIG_KEXEC_FILE)		+= kexec_file.o
obj-$(CONFIG_TOC)			+= toc.o toc_asm.o
+111 −0
Original line number Diff line number Diff line
// 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);
Loading