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, do_mfspr and do_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 do_mfspr and do_mtspr.  However, this 16kB of code pales in
comparison to the > 130kB of PPC opcode tables used by the
dissasembler.

To catch interrupts caused by the mfspr/mtspr instructions, we add a
new 'doing_spr_access' flag.  If an interrupt occurs while it is set,
we come back into xmon() via program_check_interrupt(), _exception()
and die(), see that doing_spr_access 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, excluding
SPRs 4, 5 and 6 (which never exist on any PPC processor later than
the 601).  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 <pau...@samba.org>
---
 arch/powerpc/xmon/Makefile    |   2 +-
 arch/powerpc/xmon/spraccess.S |  40 +++++++++++++++
 arch/powerpc/xmon/xmon.c      | 115 +++++++++++++++++++++++-------------------
 3 files changed, 104 insertions(+), 53 deletions(-)
 create mode 100644 arch/powerpc/xmon/spraccess.S

diff --git a/arch/powerpc/xmon/Makefile b/arch/powerpc/xmon/Makefile
index 436062d..8a971dc 100644
--- a/arch/powerpc/xmon/Makefile
+++ b/arch/powerpc/xmon/Makefile
@@ -7,7 +7,7 @@ UBSAN_SANITIZE := n
 
 ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
 
-obj-y                  += xmon.o nonstdio.o
+obj-y                  += xmon.o nonstdio.o spraccess.o
 
 ifdef CONFIG_XMON_DISASSEMBLY
 obj-y                  += ppc-dis.o ppc-opc.o
diff --git a/arch/powerpc/xmon/spraccess.S b/arch/powerpc/xmon/spraccess.S
new file mode 100644
index 0000000..6e0f9ca
--- /dev/null
+++ b/arch/powerpc/xmon/spraccess.S
@@ -0,0 +1,40 @@
+#include <asm/ppc_asm.h>
+
+/* do_mfspr(sprn, default_value) */
+_GLOBAL(do_mfspr)
+       mflr    r0
+       bcl     20, 31, 1f              /* get address of next instr into LR */
+1:     rlwinm  r3, r3, 3, 0x3ff * 8    /* sprn = (sprn & 0x3ff) << 3 */
+       mflr    r5
+       mtlr    r0
+       add     r5, r5, r3
+       addi    r5, r5, 2f - 1b
+       mtctr   r5
+       mr      r3, r4
+       bctr
+2:
+       spr = 0
+       .rept   1024
+       mfspr   r3, spr
+       blr
+       spr = spr + 1
+       .endr
+
+/* do_mtspr(sprn, new_value) */
+_GLOBAL(do_mtspr)
+       mflr    r0
+       bcl     20, 31, 1f              /* get address of next instr into LR */
+1:     rlwinm  r3, r3, 3, 0x3ff * 8    /* sprn = (sprn & 0x3ff) << 3 */
+       mflr    r5
+       mtlr    r0
+       add     r5, r5, r3
+       addi    r5, r5, 2f - 1b
+       mtctr   r5
+       bctr
+2:
+       spr = 0
+       .rept   1024
+       mtspr   spr, r4
+       blr
+       spr = spr + 1
+       .endr
diff --git a/arch/powerpc/xmon/xmon.c b/arch/powerpc/xmon/xmon.c
index 942796f..90374f1 100644
--- a/arch/powerpc/xmon/xmon.c
+++ b/arch/powerpc/xmon/xmon.c
@@ -86,6 +86,7 @@ static char tmpstr[128];
 
 static long bus_error_jmp[JMP_BUF_LEN];
 static int catch_memory_errors;
+static int doing_spr_access;
 static long *xmon_fault_jmp[NR_CPUS];
 
 /* Breakpoint stuff */
@@ -147,7 +148,7 @@ void getstring(char *, int);
 static void flush_input(void);
 static int inchar(void);
 static void take_input(char *);
-static unsigned long read_spr(int);
+static int  read_spr(int, unsigned long *);
 static void write_spr(int, unsigned long);
 static void super_regs(void);
 static void remove_bpts(void);
@@ -442,6 +443,8 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
 #ifdef CONFIG_SMP
        cpu = smp_processor_id();
        if (cpumask_test_cpu(cpu, &cpus_in_xmon)) {
+               if (doing_spr_access)
+                       longjmp(bus_error_jmp, 1);
                get_output_lock();
                excprint(regs);
                printf("cpu 0x%x: Exception %lx %s in xmon, "
@@ -1635,76 +1638,42 @@ static void cacheflush(void)
        catch_memory_errors = 0;
 }
 
-static unsigned long
-read_spr(int n)
+extern unsigned long do_mfspr(int spr, unsigned long default_value);
+extern unsigned long do_mtspr(int spr, unsigned long value);
+
+static int
+read_spr(int n, unsigned long *vp)
 {
-       unsigned int instrs[2];
-       unsigned long (*code)(void);
        unsigned long ret = -1UL;
-#ifdef CONFIG_PPC64
-       unsigned long opd[3];
-
-       opd[0] = (unsigned long)instrs;
-       opd[1] = 0;
-       opd[2] = 0;
-       code = (unsigned long (*)(void)) opd;
-#else
-       code = (unsigned long (*)(void)) instrs;
-#endif
-
-       /* mfspr r3,n; blr */
-       instrs[0] = 0x7c6002a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
-       instrs[1] = 0x4e800020;
-       store_inst(instrs);
-       store_inst(instrs+1);
+       int ok = 0;
 
        if (setjmp(bus_error_jmp) == 0) {
-               catch_memory_errors = 1;
+               doing_spr_access = 1;
                sync();
 
-               ret = code();
+               ret = do_mfspr(n, *vp);
 
                sync();
-               /* wait a little while to see if we get a machine check */
-               __delay(200);
-               n = size;
+               *vp = ret;
+               ok = 1;
        }
+       doing_spr_access = 0;
 
-       return ret;
+       return ok;
 }
 
 static void
 write_spr(int n, unsigned long val)
 {
-       unsigned int instrs[2];
-       unsigned long (*code)(unsigned long);
-#ifdef CONFIG_PPC64
-       unsigned long opd[3];
-
-       opd[0] = (unsigned long)instrs;
-       opd[1] = 0;
-       opd[2] = 0;
-       code = (unsigned long (*)(unsigned long)) opd;
-#else
-       code = (unsigned long (*)(unsigned long)) instrs;
-#endif
-
-       instrs[0] = 0x7c6003a6 + ((n & 0x1F) << 16) + ((n & 0x3e0) << 6);
-       instrs[1] = 0x4e800020;
-       store_inst(instrs);
-       store_inst(instrs+1);
-
        if (setjmp(bus_error_jmp) == 0) {
-               catch_memory_errors = 1;
+               doing_spr_access = 1;
                sync();
 
-               code(val);
+               do_mtspr(n, val);
 
                sync();
-               /* wait a little while to see if we get a machine check */
-               __delay(200);
-               n = size;
        }
+       doing_spr_access = 0;
 }
 
 static unsigned long regno;
@@ -1714,6 +1683,7 @@ extern char dec_exc;
 static void super_regs(void)
 {
        int cmd;
+       int spr;
        unsigned long val;
 
        cmd = skipbl();
@@ -1734,15 +1704,56 @@ static void super_regs(void)
                return;
        }
 
+       if (cmd == 'A') {
+               /* dump ALL SPRs */
+               for (spr = 1; spr < 1024; ++spr) {
+                       if (spr >= 4 && spr <= 6)
+                               continue;
+                       val = 0xdeadbeef;
+                       if (read_spr(spr, &val)) {
+                               if (val == 0xdeadbeef) {
+                                       val = 0x0badcafe;
+                                       if (read_spr(spr, &val) &&
+                                           val == 0x0badcafe) {
+                                               /* unimplemented, skip it */
+                                               continue;
+                                       }
+                               }
+                               printf("SPR 0x%3x (%4d) = %lx\n", spr, spr,
+                                      val);
+                       } else {
+                               printf("SPR 0x%3x (%4d): ERROR\n", spr, spr);
+                       }
+               }
+               return;
+       }
+
        scanhex(&regno);
        switch (cmd) {
        case 'w':
-               val = read_spr(regno);
+               val = 0;
+               read_spr(regno, &val);
                scanhex(&val);
                write_spr(regno, val);
                /* fall through */
        case 'r':
-               printf("spr %lx = %lx\n", regno, read_spr(regno));
+               val = 0xdeadbeef;
+               if (!read_spr(regno, &val)) {
+                       printf("Error reading SPR 0x%lx\n", regno);
+                       break;
+               }
+               if (val == 0xdeadbeef) {
+                       val = 0x0badcafe;
+                       if (!read_spr(regno, &val)) {
+                               printf("Error rereading SPR 0x%lx\n", regno);
+                               break;
+                       }
+                       if (val == 0x0badcafe) {
+                               printf("spr 0x%lx is not implemented\n", regno);
+                               break;
+                       }
+               }
+               printf("spr 0x%lx = %lx\n", regno, val);
                break;
        }
        scannl();
-- 
2.6.4

_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to