Skip to content

Commit

Permalink
powerpc/xmon: Fix SPR read/write commands and add command to dump SPRs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Paul Mackerras authored and Michael Ellerman committed May 11, 2016
1 parent f478220 commit 31cdd0c
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 61 deletions.
2 changes: 1 addition & 1 deletion arch/powerpc/xmon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 spr_access.o

ifdef CONFIG_XMON_DISASSEMBLY
obj-y += ppc-dis.o ppc-opc.o
Expand Down
45 changes: 45 additions & 0 deletions arch/powerpc/xmon/spr_access.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <asm/ppc_asm.h>

/* unsigned long xmon_mfspr(sprn, default_value) */
_GLOBAL(xmon_mfspr)
ld r5, .Lmfspr_table@got(r2)
b xmon_mxspr

/* void xmon_mtspr(sprn, new_value) */
_GLOBAL(xmon_mtspr)
ld r5, .Lmtspr_table@got(r2)
b xmon_mxspr

/*
* r3 = sprn
* r4 = default or new value
* r5 = table base
*/
xmon_mxspr:
/*
* To index into the table of mxsprs we need:
* i = (sprn & 0x3ff) * 8
* or using rwlinm:
* i = (sprn << 3) & (0x3ff << 3)
*/
rlwinm r3, r3, 3, 0x3ff << 3
add r5, r5, r3
mtctr r5
mr r3, r4 /* put default_value in r3 for mfspr */
bctr

.Lmfspr_table:
spr = 0
.rept 1024
mfspr r3, spr
blr
spr = spr + 1
.endr

.Lmtspr_table:
spr = 0
.rept 1024
mtspr spr, r4
blr
spr = spr + 1
.endr
136 changes: 76 additions & 60 deletions arch/powerpc/xmon/xmon.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ static char tmpstr[128];

static long bus_error_jmp[JMP_BUF_LEN];
static int catch_memory_errors;
static int catch_spr_faults;
static long *xmon_fault_jmp[NR_CPUS];

/* Breakpoint stuff */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -250,6 +251,9 @@ Commands:\n\
sdi # disassemble spu local store for spu # (in hex)\n"
#endif
" S print special registers\n\
Sa print all SPRs\n\
Sr # read SPR #\n\
Sw #v write v to SPR #\n\
t print backtrace\n\
x exit monitor and recover\n\
X exit monitor and don't recover\n"
Expand Down Expand Up @@ -442,6 +446,12 @@ 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)) {
/*
* We catch SPR read/write faults here because the 0x700, 0xf60
* etc. handlers don't call debugger_fault_handler().
*/
if (catch_spr_faults)
longjmp(bus_error_jmp, 1);
get_output_lock();
excprint(regs);
printf("cpu 0x%x: Exception %lx %s in xmon, "
Expand Down Expand Up @@ -1635,89 +1645,87 @@ static void cacheflush(void)
catch_memory_errors = 0;
}

static unsigned long
read_spr(int n)
extern unsigned long xmon_mfspr(int spr, unsigned long default_value);
extern void xmon_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;
catch_spr_faults = 1;
sync();

ret = code();
ret = xmon_mfspr(n, *vp);

sync();
/* wait a little while to see if we get a machine check */
__delay(200);
n = size;
*vp = ret;
ok = 1;
}
catch_spr_faults = 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;
catch_spr_faults = 1;
sync();

code(val);
xmon_mtspr(n, val);

sync();
/* wait a little while to see if we get a machine check */
__delay(200);
n = size;
} else {
printf("SPR 0x%03x (%4d) Faulted during write\n", n, n);
}
catch_spr_faults = 0;
}

static unsigned long regno;
extern char exc_prolog;
extern char dec_exc;

static void dump_one_spr(int spr, bool show_unimplemented)
{
unsigned long val;

val = 0xdeadbeef;
if (!read_spr(spr, &val)) {
printf("SPR 0x%03x (%4d) Faulted during read\n", spr, spr);
return;
}

if (val == 0xdeadbeef) {
/* Looks like read was a nop, confirm */
val = 0x0badcafe;
if (!read_spr(spr, &val)) {
printf("SPR 0x%03x (%4d) Faulted during read\n", spr, spr);
return;
}

if (val == 0x0badcafe) {
if (show_unimplemented)
printf("SPR 0x%03x (%4d) Unimplemented\n", spr, spr);
return;
}
}

printf("SPR 0x%03x (%4d) = 0x%lx\n", spr, spr, val);
}

static void super_regs(void)
{
int cmd;
unsigned long val;
int spr;

cmd = skipbl();
if (cmd == '\n') {

switch (cmd) {
case '\n': {
unsigned long sp, toc;
asm("mr %0,1" : "=r" (sp) :);
asm("mr %0,2" : "=r" (toc) :);
Expand All @@ -1730,21 +1738,29 @@ static void super_regs(void)
mfspr(SPRN_DEC), mfspr(SPRN_SPRG2));
printf("sp = "REG" sprg3= "REG"\n", sp, mfspr(SPRN_SPRG3));
printf("toc = "REG" dar = "REG"\n", toc, mfspr(SPRN_DAR));

return;
}

scanhex(&regno);
switch (cmd) {
case 'w':
val = read_spr(regno);
case 'w': {
unsigned long val;
scanhex(&regno);
val = 0;
read_spr(regno, &val);
scanhex(&val);
write_spr(regno, val);
/* fall through */
dump_one_spr(regno, true);
break;
}
case 'r':
printf("spr %lx = %lx\n", regno, read_spr(regno));
scanhex(&regno);
dump_one_spr(regno, true);
break;
case 'a':
/* dump ALL SPRs */
for (spr = 1; spr < 1024; ++spr)
dump_one_spr(spr, false);
break;
}

scannl();
}

Expand Down

0 comments on commit 31cdd0c

Please sign in to comment.