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

powerpc/xmon: Fix SPR read/write commands and add command to dump SPRs

xmon has commands for reading and writing SPRs, but they don't work
currently for several reasons. They attempt to synthesize a small
function containing an mfspr or mtspr instruction and call it. However,
the instructions are on the stack, which is usually not executable.
Also, for 64-bit we set up a procedure descriptor, which is fine for the
big-endian ABIv1, but not correct for ABIv2. Finally, the code uses the
infrastructure for catching memory errors, but that only catches data
storage interrupts and machine check interrupts, but a failed
mfspr/mtspr can generate a program interrupt or a hypervisor emulation
assist interrupt, or be a no-op.

Instead of trying to synthesize a function on the fly, this adds two new
functions, xmon_mfspr() and xmon_mtspr(), which take an SPR number as an
argument and read or write the SPR. Because there is no Power ISA
instruction which takes an SPR number in a register, we have to generate
one of each possible mfspr and mtspr instruction, for all 1024 possible
SPRs. Thus we get just over 8k bytes of code for each of xmon_mfspr()
and xmon_mtspr(). However, this 16kB of code pales in comparison to the
> 130kB of PPC opcode tables used by the xmon disassembler.

To catch interrupts caused by the mfspr/mtspr instructions, we add a new
'catch_spr_faults' flag. If an interrupt occurs while it is set, we come
back into xmon() via program_check_interrupt(), _exception() and die(),
see that catch_spr_faults is set and do a longjmp to bus_error_jmp, back
into read_spr() or write_spr().

This adds a couple of other nice features: first, a "Sa" command that
attempts to read and print out the value of all 1024 SPRs. If any mfspr
instruction acts as a no-op, then the SPR is not implemented and not
printed.

Secondly, the Sr and Sw commands detect when an SPR is not
implemented (i.e. mfspr is a no-op) and print a message to that effect
rather than printing a bogus value.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Paul Mackerras and committed by
Michael Ellerman
31cdd0c3 f4782207

+123 -62
+1 -1
arch/powerpc/xmon/Makefile
··· 7 7 8 8 ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC) 9 9 10 - obj-y += xmon.o nonstdio.o 10 + obj-y += xmon.o nonstdio.o spr_access.o 11 11 12 12 ifdef CONFIG_XMON_DISASSEMBLY 13 13 obj-y += ppc-dis.o ppc-opc.o
+45
arch/powerpc/xmon/spr_access.S
··· 1 + #include <asm/ppc_asm.h> 2 + 3 + /* unsigned long xmon_mfspr(sprn, default_value) */ 4 + _GLOBAL(xmon_mfspr) 5 + ld r5, .Lmfspr_table@got(r2) 6 + b xmon_mxspr 7 + 8 + /* void xmon_mtspr(sprn, new_value) */ 9 + _GLOBAL(xmon_mtspr) 10 + ld r5, .Lmtspr_table@got(r2) 11 + b xmon_mxspr 12 + 13 + /* 14 + * r3 = sprn 15 + * r4 = default or new value 16 + * r5 = table base 17 + */ 18 + xmon_mxspr: 19 + /* 20 + * To index into the table of mxsprs we need: 21 + * i = (sprn & 0x3ff) * 8 22 + * or using rwlinm: 23 + * i = (sprn << 3) & (0x3ff << 3) 24 + */ 25 + rlwinm r3, r3, 3, 0x3ff << 3 26 + add r5, r5, r3 27 + mtctr r5 28 + mr r3, r4 /* put default_value in r3 for mfspr */ 29 + bctr 30 + 31 + .Lmfspr_table: 32 + spr = 0 33 + .rept 1024 34 + mfspr r3, spr 35 + blr 36 + spr = spr + 1 37 + .endr 38 + 39 + .Lmtspr_table: 40 + spr = 0 41 + .rept 1024 42 + mtspr spr, r4 43 + blr 44 + spr = spr + 1 45 + .endr
+77 -61
arch/powerpc/xmon/xmon.c
··· 86 86 87 87 static long bus_error_jmp[JMP_BUF_LEN]; 88 88 static int catch_memory_errors; 89 + static int catch_spr_faults; 89 90 static long *xmon_fault_jmp[NR_CPUS]; 90 91 91 92 /* Breakpoint stuff */ ··· 148 147 static void flush_input(void); 149 148 static int inchar(void); 150 149 static void take_input(char *); 151 - static unsigned long read_spr(int); 150 + static int read_spr(int, unsigned long *); 152 151 static void write_spr(int, unsigned long); 153 152 static void super_regs(void); 154 153 static void remove_bpts(void); ··· 251 250 sdi # disassemble spu local store for spu # (in hex)\n" 252 251 #endif 253 252 " S print special registers\n\ 253 + Sa print all SPRs\n\ 254 + Sr # read SPR #\n\ 255 + Sw #v write v to SPR #\n\ 254 256 t print backtrace\n\ 255 257 x exit monitor and recover\n\ 256 258 X exit monitor and don't recover\n" ··· 446 442 #ifdef CONFIG_SMP 447 443 cpu = smp_processor_id(); 448 444 if (cpumask_test_cpu(cpu, &cpus_in_xmon)) { 445 + /* 446 + * We catch SPR read/write faults here because the 0x700, 0xf60 447 + * etc. handlers don't call debugger_fault_handler(). 448 + */ 449 + if (catch_spr_faults) 450 + longjmp(bus_error_jmp, 1); 449 451 get_output_lock(); 450 452 excprint(regs); 451 453 printf("cpu 0x%x: Exception %lx %s in xmon, " ··· 1645 1635 catch_memory_errors = 0; 1646 1636 } 1647 1637 1648 - static unsigned long 1649 - read_spr(int n) 1638 + extern unsigned long xmon_mfspr(int spr, unsigned long default_value); 1639 + extern void xmon_mtspr(int spr, unsigned long value); 1640 + 1641 + static int 1642 + read_spr(int n, unsigned long *vp) 1650 1643 { 1651 - unsigned int instrs[2]; 1652 - unsigned long (*code)(void); 1653 1644 unsigned long ret = -1UL; 1654 - #ifdef CONFIG_PPC64 1655 - unsigned long opd[3]; 1656 - 1657 - opd[0] = (unsigned long)instrs; 1658 - opd[1] = 0; 1659 - opd[2] = 0; 1660 - code = (unsigned long (*)(void)) opd; 1661 - #else 1662 - code = (unsigned long (*)(void)) instrs; 1663 - #endif 1664 - 1665 - /* mfspr r3,n; blr */ 1666 - instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); 1667 - instrs[1] = 0x4e800020; 1668 - store_inst(instrs); 1669 - store_inst(instrs+1); 1645 + int ok = 0; 1670 1646 1671 1647 if (setjmp(bus_error_jmp) == 0) { 1672 - catch_memory_errors = 1; 1648 + catch_spr_faults = 1; 1673 1649 sync(); 1674 1650 1675 - ret = code(); 1651 + ret = xmon_mfspr(n, *vp); 1676 1652 1677 1653 sync(); 1678 - /* wait a little while to see if we get a machine check */ 1679 - __delay(200); 1680 - n = size; 1654 + *vp = ret; 1655 + ok = 1; 1681 1656 } 1657 + catch_spr_faults = 0; 1682 1658 1683 - return ret; 1659 + return ok; 1684 1660 } 1685 1661 1686 1662 static void 1687 1663 write_spr(int n, unsigned long val) 1688 1664 { 1689 - unsigned int instrs[2]; 1690 - unsigned long (*code)(unsigned long); 1691 - #ifdef CONFIG_PPC64 1692 - unsigned long opd[3]; 1693 - 1694 - opd[0] = (unsigned long)instrs; 1695 - opd[1] = 0; 1696 - opd[2] = 0; 1697 - code = (unsigned long (*)(unsigned long)) opd; 1698 - #else 1699 - code = (unsigned long (*)(unsigned long)) instrs; 1700 - #endif 1701 - 1702 - instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6); 1703 - instrs[1] = 0x4e800020; 1704 - store_inst(instrs); 1705 - store_inst(instrs+1); 1706 - 1707 1665 if (setjmp(bus_error_jmp) == 0) { 1708 - catch_memory_errors = 1; 1666 + catch_spr_faults = 1; 1709 1667 sync(); 1710 1668 1711 - code(val); 1669 + xmon_mtspr(n, val); 1712 1670 1713 1671 sync(); 1714 - /* wait a little while to see if we get a machine check */ 1715 - __delay(200); 1716 - n = size; 1672 + } else { 1673 + printf("SPR 0x%03x (%4d) Faulted during write\n", n, n); 1717 1674 } 1675 + catch_spr_faults = 0; 1718 1676 } 1719 1677 1720 1678 static unsigned long regno; 1721 1679 extern char exc_prolog; 1722 1680 extern char dec_exc; 1723 1681 1682 + static void dump_one_spr(int spr, bool show_unimplemented) 1683 + { 1684 + unsigned long val; 1685 + 1686 + val = 0xdeadbeef; 1687 + if (!read_spr(spr, &val)) { 1688 + printf("SPR 0x%03x (%4d) Faulted during read\n", spr, spr); 1689 + return; 1690 + } 1691 + 1692 + if (val == 0xdeadbeef) { 1693 + /* Looks like read was a nop, confirm */ 1694 + val = 0x0badcafe; 1695 + if (!read_spr(spr, &val)) { 1696 + printf("SPR 0x%03x (%4d) Faulted during read\n", spr, spr); 1697 + return; 1698 + } 1699 + 1700 + if (val == 0x0badcafe) { 1701 + if (show_unimplemented) 1702 + printf("SPR 0x%03x (%4d) Unimplemented\n", spr, spr); 1703 + return; 1704 + } 1705 + } 1706 + 1707 + printf("SPR 0x%03x (%4d) = 0x%lx\n", spr, spr, val); 1708 + } 1709 + 1724 1710 static void super_regs(void) 1725 1711 { 1726 1712 int cmd; 1727 - unsigned long val; 1713 + int spr; 1728 1714 1729 1715 cmd = skipbl(); 1730 - if (cmd == '\n') { 1716 + 1717 + switch (cmd) { 1718 + case '\n': { 1731 1719 unsigned long sp, toc; 1732 1720 asm("mr %0,1" : "=r" (sp) :); 1733 1721 asm("mr %0,2" : "=r" (toc) :); ··· 1738 1730 mfspr(SPRN_DEC), mfspr(SPRN_SPRG2)); 1739 1731 printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3)); 1740 1732 printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR)); 1741 - 1742 1733 return; 1743 1734 } 1744 - 1745 - scanhex(&regno); 1746 - switch (cmd) { 1747 - case 'w': 1748 - val = read_spr(regno); 1735 + case 'w': { 1736 + unsigned long val; 1737 + scanhex(&regno); 1738 + val = 0; 1739 + read_spr(regno, &val); 1749 1740 scanhex(&val); 1750 1741 write_spr(regno, val); 1751 - /* fall through */ 1752 - case 'r': 1753 - printf("spr %lx = %lx\n", regno, read_spr(regno)); 1742 + dump_one_spr(regno, true); 1754 1743 break; 1755 1744 } 1745 + case 'r': 1746 + scanhex(&regno); 1747 + dump_one_spr(regno, true); 1748 + break; 1749 + case 'a': 1750 + /* dump ALL SPRs */ 1751 + for (spr = 1; spr < 1024; ++spr) 1752 + dump_one_spr(spr, false); 1753 + break; 1754 + } 1755 + 1756 1756 scannl(); 1757 1757 } 1758 1758