Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Sven Schnelle and committed by
Helge Deller
bc294838 ecac7036

+222 -2
+14
arch/parisc/Kconfig
··· 291 291 292 292 If you don't know what to do here, say N. 293 293 294 + config TOC 295 + bool "Support TOC switch" 296 + default y if 64BIT || !SMP 297 + help 298 + Most PA-RISC machines have either a switch at the back of the machine 299 + or a command in BMC to trigger a TOC interrupt. If you say Y here a 300 + handler will be installed which will either show a backtrace on all 301 + CPUs, or enter a possible configured debugger like kgdb/kdb. 302 + 303 + Note that with this option enabled, the kernel will use an additional 16KB 304 + per possible CPU as a special stack for the TOC handler. 305 + 306 + If you don't want to debug the Kernel, say N. 307 + 294 308 config PARISC_CPU_TOPOLOGY 295 309 bool "Support cpu topology definition" 296 310 depends on SMP
+4
arch/parisc/include/asm/processor.h
··· 295 295 296 296 extern int running_on_qemu; 297 297 298 + extern void toc_handler(void); 299 + extern unsigned int toc_handler_size; 300 + extern unsigned int toc_handler_csum; 301 + 298 302 #endif /* __ASSEMBLY__ */ 299 303 300 304 #endif /* __ASM_PARISC_PROCESSOR_H */
+4 -2
arch/parisc/include/uapi/asm/pdc.h
··· 398 398 /* int (*vec_rendz)(void); */ 399 399 unsigned int vec_rendz; 400 400 int vec_pow_fail_flen; 401 - int vec_pad[10]; 402 - 401 + int vec_pad0[3]; 402 + unsigned int vec_toc_hi; 403 + int vec_pad1[6]; 404 + 403 405 /* [0x040] reserved processor dependent */ 404 406 int pad0[112]; 405 407
+1
arch/parisc/kernel/Makefile
··· 39 39 obj-$(CONFIG_KPROBES) += kprobes.o 40 40 obj-$(CONFIG_KEXEC_CORE) += kexec.o relocate_kernel.o 41 41 obj-$(CONFIG_KEXEC_FILE) += kexec_file.o 42 + obj-$(CONFIG_TOC) += toc.o toc_asm.o
+111
arch/parisc/kernel/toc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/kernel.h> 4 + #include <linux/kgdb.h> 5 + #include <linux/printk.h> 6 + #include <linux/sched/debug.h> 7 + #include <linux/delay.h> 8 + #include <linux/reboot.h> 9 + 10 + #include <asm/pdc.h> 11 + #include <asm/pdc_chassis.h> 12 + 13 + unsigned int __aligned(16) toc_lock = 1; 14 + 15 + static void toc20_to_pt_regs(struct pt_regs *regs, struct pdc_toc_pim_20 *toc) 16 + { 17 + int i; 18 + 19 + regs->gr[0] = (unsigned long)toc->cr[22]; 20 + 21 + for (i = 1; i < 32; i++) 22 + regs->gr[i] = (unsigned long)toc->gr[i]; 23 + 24 + for (i = 0; i < 8; i++) 25 + regs->sr[i] = (unsigned long)toc->sr[i]; 26 + 27 + regs->iasq[0] = (unsigned long)toc->cr[17]; 28 + regs->iasq[1] = (unsigned long)toc->iasq_back; 29 + regs->iaoq[0] = (unsigned long)toc->cr[18]; 30 + regs->iaoq[1] = (unsigned long)toc->iaoq_back; 31 + 32 + regs->sar = (unsigned long)toc->cr[11]; 33 + regs->iir = (unsigned long)toc->cr[19]; 34 + regs->isr = (unsigned long)toc->cr[20]; 35 + regs->ior = (unsigned long)toc->cr[21]; 36 + } 37 + 38 + static void toc11_to_pt_regs(struct pt_regs *regs, struct pdc_toc_pim_11 *toc) 39 + { 40 + int i; 41 + 42 + regs->gr[0] = toc->cr[22]; 43 + 44 + for (i = 1; i < 32; i++) 45 + regs->gr[i] = toc->gr[i]; 46 + 47 + for (i = 0; i < 8; i++) 48 + regs->sr[i] = toc->sr[i]; 49 + 50 + regs->iasq[0] = toc->cr[17]; 51 + regs->iasq[1] = toc->iasq_back; 52 + regs->iaoq[0] = toc->cr[18]; 53 + regs->iaoq[1] = toc->iaoq_back; 54 + 55 + regs->sar = toc->cr[11]; 56 + regs->iir = toc->cr[19]; 57 + regs->isr = toc->cr[20]; 58 + regs->ior = toc->cr[21]; 59 + } 60 + 61 + void notrace __noreturn __cold toc_intr(struct pt_regs *regs) 62 + { 63 + struct pdc_toc_pim_20 pim_data20; 64 + struct pdc_toc_pim_11 pim_data11; 65 + 66 + nmi_enter(); 67 + 68 + if (boot_cpu_data.cpu_type >= pcxu) { 69 + if (pdc_pim_toc20(&pim_data20)) 70 + panic("Failed to get PIM data"); 71 + toc20_to_pt_regs(regs, &pim_data20); 72 + } else { 73 + if (pdc_pim_toc11(&pim_data11)) 74 + panic("Failed to get PIM data"); 75 + toc11_to_pt_regs(regs, &pim_data11); 76 + } 77 + 78 + #ifdef CONFIG_KGDB 79 + if (atomic_read(&kgdb_active) != -1) 80 + kgdb_nmicallback(raw_smp_processor_id(), regs); 81 + kgdb_handle_exception(9, SIGTRAP, 0, regs); 82 + #endif 83 + show_regs(regs); 84 + 85 + /* give other CPUs time to show their backtrace */ 86 + mdelay(2000); 87 + machine_restart("TOC"); 88 + 89 + /* should never reach this */ 90 + panic("TOC"); 91 + } 92 + 93 + static __init int setup_toc(void) 94 + { 95 + unsigned int csum = 0; 96 + unsigned long toc_code = (unsigned long)dereference_function_descriptor(toc_handler); 97 + int i; 98 + 99 + PAGE0->vec_toc = __pa(toc_code) & 0xffffffff; 100 + #ifdef CONFIG_64BIT 101 + PAGE0->vec_toc_hi = __pa(toc_code) >> 32; 102 + #endif 103 + PAGE0->vec_toclen = toc_handler_size; 104 + 105 + for (i = 0; i < toc_handler_size/4; i++) 106 + csum += ((u32 *)toc_code)[i]; 107 + toc_handler_csum = -csum; 108 + pr_info("TOC handler registered\n"); 109 + return 0; 110 + } 111 + early_initcall(setup_toc);
+88
arch/parisc/kernel/toc_asm.S
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + /* TOC (Transfer of Control) handler. */ 4 + 5 + .level 1.1 6 + 7 + #include <asm/assembly.h> 8 + #include <asm/psw.h> 9 + #include <linux/threads.h> 10 + #include <linux/linkage.h> 11 + 12 + .text 13 + .import toc_intr,code 14 + .import toc_lock,data 15 + .align 16 16 + ENTRY_CFI(toc_handler) 17 + /* 18 + * synchronize CPUs and obtain offset 19 + * for stack setup. 20 + */ 21 + load32 PA(toc_lock),%r1 22 + 0: ldcw,co 0(%r1),%r2 23 + cmpib,= 0,%r2,0b 24 + nop 25 + addi 1,%r2,%r4 26 + stw %r4,0(%r1) 27 + addi -1,%r2,%r4 28 + 29 + load32 PA(toc_stack),%sp 30 + /* 31 + * deposit CPU number into stack address, 32 + * so every CPU will have its own stack. 33 + */ 34 + SHLREG %r4,14,%r4 35 + add %r4,%sp,%sp 36 + 37 + /* 38 + * setup pt_regs on stack and save the 39 + * floating point registers. PIM_TOC doesn't 40 + * save fp registers, so we're doing it here. 41 + */ 42 + copy %sp,%arg0 43 + ldo PT_SZ_ALGN(%sp), %sp 44 + 45 + /* clear pt_regs */ 46 + copy %arg0,%r1 47 + 0: cmpb,<<,n %r1,%sp,0b 48 + stw,ma %r0,4(%r1) 49 + 50 + ldo PT_FR0(%arg0),%r25 51 + save_fp %r25 52 + 53 + /* go virtual */ 54 + load32 PA(swapper_pg_dir),%r4 55 + mtctl %r4,%cr24 56 + mtctl %r4,%cr25 57 + 58 + /* Clear sr4-sr7 */ 59 + mtsp %r0, %sr4 60 + mtsp %r0, %sr5 61 + mtsp %r0, %sr6 62 + mtsp %r0, %sr7 63 + 64 + tovirt_r1 %sp 65 + tovirt_r1 %arg0 66 + virt_map 67 + 68 + loadgp 69 + 70 + #ifdef CONFIG_64BIT 71 + ldo -16(%sp),%r29 72 + #endif 73 + load32 toc_intr,%r1 74 + be 0(%sr7,%r1) 75 + nop 76 + ENDPROC_CFI(toc_handler) 77 + 78 + /* 79 + * keep this checksum here, as it is part of the toc_handler 80 + * spanned by toc_handler_size (all words in toc_handler are 81 + * added in PDC and the sum must equal to zero. 82 + */ 83 + SYM_DATA(toc_handler_csum, .long 0) 84 + SYM_DATA(toc_handler_size, .long . - toc_handler) 85 + 86 + __PAGE_ALIGNED_BSS 87 + .align 64 88 + SYM_DATA(toc_stack, .block 16384*NR_CPUS)