Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31cdd0c3 authored by Paul Mackerras's avatar Paul Mackerras Committed by Michael Ellerman
Browse files

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: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
parent f4782207
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -7,7 +7,7 @@ UBSAN_SANITIZE := n


ccflags-$(CONFIG_PPC64) := $(NO_MINIMAL_TOC)
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
ifdef CONFIG_XMON_DISASSEMBLY
obj-y			+= ppc-dis.o ppc-opc.o
obj-y			+= ppc-dis.o ppc-opc.o
+45 −0
Original line number Original line Diff line number Diff line
#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
+76 −60
Original line number Original line Diff line number Diff line
@@ -86,6 +86,7 @@ static char tmpstr[128];


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


/* Breakpoint stuff */
/* Breakpoint stuff */
@@ -147,7 +148,7 @@ void getstring(char *, int);
static void flush_input(void);
static void flush_input(void);
static int inchar(void);
static int inchar(void);
static void take_input(char *);
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 write_spr(int, unsigned long);
static void super_regs(void);
static void super_regs(void);
static void remove_bpts(void);
static void remove_bpts(void);
@@ -250,6 +251,9 @@ Commands:\n\
  sdi #	disassemble spu local store for spu # (in hex)\n"
  sdi #	disassemble spu local store for spu # (in hex)\n"
#endif
#endif
"  S	print special registers\n\
"  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\
  t	print backtrace\n\
  x	exit monitor and recover\n\
  x	exit monitor and recover\n\
  X	exit monitor and don't recover\n"
  X	exit monitor and don't recover\n"
@@ -442,6 +446,12 @@ static int xmon_core(struct pt_regs *regs, int fromipi)
#ifdef CONFIG_SMP
#ifdef CONFIG_SMP
	cpu = smp_processor_id();
	cpu = smp_processor_id();
	if (cpumask_test_cpu(cpu, &cpus_in_xmon)) {
	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();
		get_output_lock();
		excprint(regs);
		excprint(regs);
		printf("cpu 0x%x: Exception %lx %s in xmon, "
		printf("cpu 0x%x: Exception %lx %s in xmon, "
@@ -1635,89 +1645,87 @@ static void cacheflush(void)
	catch_memory_errors = 0;
	catch_memory_errors = 0;
}
}


static unsigned long
extern unsigned long xmon_mfspr(int spr, unsigned long default_value);
read_spr(int n)
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;
	unsigned long ret = -1UL;
#ifdef CONFIG_PPC64
	int ok = 0;
	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);


	if (setjmp(bus_error_jmp) == 0) {
	if (setjmp(bus_error_jmp) == 0) {
		catch_memory_errors = 1;
		catch_spr_faults = 1;
		sync();
		sync();


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


		sync();
		sync();
		/* wait a little while to see if we get a machine check */
		*vp = ret;
		__delay(200);
		ok = 1;
		n = size;
	}
	}
	catch_spr_faults = 0;


	return ret;
	return ok;
}
}


static void
static void
write_spr(int n, unsigned long val)
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) {
	if (setjmp(bus_error_jmp) == 0) {
		catch_memory_errors = 1;
		catch_spr_faults = 1;
		sync();
		sync();


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


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


static unsigned long regno;
static unsigned long regno;
extern char exc_prolog;
extern char exc_prolog;
extern char dec_exc;
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)
static void super_regs(void)
{
{
	int cmd;
	int cmd;
	unsigned long val;
	int spr;


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

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

		return;
		return;
	}
	}

	case 'w': {
		unsigned long val;
		scanhex(&regno);
		scanhex(&regno);
	switch (cmd) {
		val = 0;
	case 'w':
		read_spr(regno, &val);
		val = read_spr(regno);
		scanhex(&val);
		scanhex(&val);
		write_spr(regno, val);
		write_spr(regno, val);
		/* fall through */
		dump_one_spr(regno, true);
		break;
	}
	case 'r':
	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;
		break;
	}
	}

	scannl();
	scannl();
}
}