aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHelge Deller <deller@gmx.de>2022-01-05 23:22:10 +0100
committerHelge Deller <deller@gmx.de>2022-01-06 18:45:20 +0100
commite0584bf42c603430b8613e4c17405ede700af61f (patch)
tree04fc927f653e3054efef86147db6a5633f3220b8
parent187db62f09584263f25961f01aa9a0aecf0da83f (diff)
downloadseabios-hppa-e0584bf42c603430b8613e4c17405ede700af61f.zip
seabios-hppa-e0584bf42c603430b8613e4c17405ede700af61f.tar.gz
seabios-hppa-e0584bf42c603430b8613e4c17405ede700af61f.tar.bz2
parisc: Add TOC button support
Add support for an emulated TOC/NMI button. Almost all PA-RISC machines have either a button that is labeled with 'TOC' or a BMC/GSP 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 in Linux. This patch adds support for such an emulated TOC button. This patch writes the CPU registers into PIM (processor internal memmory) for later analysis. It uses the CPU "shadow registers", the IAOQ-back and IASQ-back values are provided by qemu in r24/r25. This patch uses the new qemu aritificial opcode "getshadowregs" (0xfffdead2) which restores the original values of the shadow registers. To trigger a TOC, switch to the "qemu monitor" with Ctrl-A C, and type in the command "nmi". After the TOC started the OS-debugger, exit the qemu monitor with Ctrl-A C. Signed-off-by: Helge Deller <deller@gmx.de>
-rw-r--r--src/parisc/head.S112
-rw-r--r--src/parisc/hppa_hardware.h1
-rw-r--r--src/parisc/parisc.c136
-rw-r--r--src/parisc/pdc.h40
4 files changed, 279 insertions, 10 deletions
diff --git a/src/parisc/head.S b/src/parisc/head.S
index 4869946..9d476fe 100644
--- a/src/parisc/head.S
+++ b/src/parisc/head.S
@@ -46,10 +46,11 @@ name:
#define SHLREG shld
#define ANDCM andcm,*
#define COND(x) * ## x
-#define RP_OFFSET 16
#define FRAME_SIZE 128
#define CALLEE_REG_FRAME_SIZE 144
#define ASM_ULONG_INSN .dword
+#define WORD_LEN 8
+#define INT_LEN 4
#else /* CONFIG_64BIT */
#define LDREG ldw
#define STREG stw
@@ -60,10 +61,11 @@ name:
#define SHLREG shlw
#define ANDCM andcm
#define COND(x) x
-#define RP_OFFSET 20
#define FRAME_SIZE 64
#define CALLEE_REG_FRAME_SIZE 128
#define ASM_ULONG_INSN .word
+#define WORD_LEN 4
+#define INT_LEN 4
#endif
.import $global$
@@ -72,12 +74,13 @@ name:
/* On HPMC, the CPUs will start here at 0xf0000000 */
hpmc_entry:
- b,n . /* TODO! */
+ b,n toc_asm_entry /* TOC and HPMC */
reset_entry:
/* at reset, the CPU begins fetching instructions from address 0xf0000004. */
b,n startup
+marker:
/* file identification */
.stringz "PA-RISC/HPPA PDC Firmware (SeaBIOS fork)"
.stringz "https://github.com/hdeller/seabios-hppa"
@@ -186,6 +189,108 @@ END(startup)
/*******************************************************
+ TOC handler
+ Write all GRs, CRs, SRs and the iaoq_back and iasq_back registers (in
+ r24/r25) into PIM area (when it's not filled yet). This is done by trashing the
+ shadow registers.
+ In a second step call the arificial getshadowregs asm instruction to restore
+ the shadow registers to their real values and store them in PIM as well. Then
+ call the C-code.
+ *******************************************************/
+
+/* uses the shadow registers: 1,8,9,16,17,24,25 */
+#define PIM_PTR %r1
+#define TEMP %r8
+#define TEMP2 %r9
+#define PIM_SAVE %r16
+#define IASQ_BACK %r24 /* provided by qemu */
+#define IAOQ_BACK %r25 /* provided by qemu */
+
+ .import pim_toc_data, data
+ENTRY(toc_asm_entry)
+ /* serialize CPUs on entry */
+ load32 BOOTADDR(toc_lock),TEMP
+0: ldcw,co 0(TEMP),TEMP2
+ cmpib,= 0,TEMP2,0b
+ nop
+
+ mfctl CPU_HPA_CR_REG, TEMP2 /* get CPU HPA from cr7 */
+ extru TEMP2,31-12,4, TEMP /* extract cpu id */
+
+ load32 BOOTADDR(pim_toc_data), PIM_PTR
+
+1: comib,= 0,TEMP,2f
+ ldo -1(TEMP),TEMP
+ ldo (PIM_STORAGE_SIZE)(PIM_PTR), PIM_PTR /* find PIM entry */
+ b 1b
+
+2: copy PIM_PTR, PIM_SAVE
+ mtsp %r0,%sr0
+
+ /* save registers into PIM only if cpu_state field is empty */
+ ldw ((32+32+8+2)*WORD_LEN + 1*INT_LEN)(PIM_SAVE), TEMP
+ comib,<>,n 0, TEMP, 5f
+
+ /* save all general registers */
+ .set loop,0
+ .rept 32
+ STREGM loop, WORD_LEN(PIM_PTR)
+ .set loop,loop+1
+ .endr
+
+ /* save all control registers */
+ .set loop,0
+ .rept 32
+ mfctl loop,TEMP
+ STREGM TEMP, WORD_LEN(PIM_PTR)
+ .set loop,loop+1
+ .endr
+
+ /* save all space registers */
+ .set loop,0
+ .rept 8
+ mfsp loop,TEMP
+ STREGM TEMP, WORD_LEN(PIM_PTR)
+ .set loop,loop+1
+ .endr
+
+ /* save IASQ_back and IAOQ_back as provided by qemu */
+ STREG IASQ_BACK, ((32+32+8+0)*WORD_LEN)(PIM_SAVE)
+ STREG IAOQ_BACK, ((32+32+8+1)*WORD_LEN)(PIM_SAVE)
+
+ /* restore shadow registers, can not use PIM_SAVE reg for this */
+ copy PIM_SAVE, %r26
+ .word 0xfffdead2 /* qemu artificial getshadowregs asm instruction */
+ STREG %r1, (1*WORD_LEN)(%r26)
+ STREG %r8, (8*WORD_LEN)(%r26)
+ STREG %r9, (9*WORD_LEN)(%r26)
+ STREG %r16, (16*WORD_LEN)(%r26)
+ STREG %r17, (17*WORD_LEN)(%r26)
+ STREG %r24, (24*WORD_LEN)(%r26)
+ STREG %r25, (25*WORD_LEN)(%r26)
+
+#ifdef CONFIG_64BIT
+ /* cr11 (sar) is a funny one. 5 bits on PA1.1 and 6 bit on PA2.0
+ * For PA2.0 mtsar or mtctl always write 6 bits, but mfctl only
+ * reads 5 bits. Use mfctl,w to read all six bits. Otherwise
+ * we lose the 6th bit on a save/restore. */
+ mfctl,w %cr11, TEMP
+ STREG TEMP, ((32+11)*WORD_LEN)(%r26)
+#endif
+
+5: /* call the "C" toc_handler in SeaBIOS */
+ loadgp
+ load32 BOOTADDR(parisc_stack), %sp
+ b,l toc_handler, %r2
+ ldo FRAME_SIZE(%sp),%sp
+
+ /* call OS handler, in case it returns reset the system */
+ load32 BOOTADDR(reset), %rp
+ bv,n 0(%ret0)
+END(toc_asm_entry)
+
+
+/*******************************************************
SMP Interrupt vector table (IVT)
*******************************************************/
@@ -277,7 +382,6 @@ ENTRY(boot_args)
.word 0 /* r19: fw_cfg port */
END(boot_args)
-
/****************************************************************
* Rom Header for VGA / STI
****************************************************************/
diff --git a/src/parisc/hppa_hardware.h b/src/parisc/hppa_hardware.h
index 099a492..658757c 100644
--- a/src/parisc/hppa_hardware.h
+++ b/src/parisc/hppa_hardware.h
@@ -45,5 +45,6 @@
#define CPU_CLOCK_MHZ 250 /* emulate a 250 MHz CPU */
#define CPU_HPA_CR_REG 7 /* store CPU HPA in cr7 (SeaBIOS internal) */
+#define PIM_STORAGE_SIZE 600 /* storage size of pdc_pim_toc_struct (64bit) */
#endif
diff --git a/src/parisc/parisc.c b/src/parisc/parisc.c
index 56e68cf..faedd28 100644
--- a/src/parisc/parisc.c
+++ b/src/parisc/parisc.c
@@ -42,6 +42,15 @@ u8 ExtraStack[BUILD_EXTRA_STACK_SIZE+1] __aligned(8);
u8 *StackPos;
u8 __VISIBLE parisc_stack[32*1024] __aligned(64);
+volatile int num_online_cpus;
+int __VISIBLE toc_lock = 1;
+union {
+ struct pdc_toc_pim_11 pim11;
+ struct pdc_toc_pim_20 pim20;
+} pim_toc_data[HPPA_MAX_CPUS] __VISIBLE __aligned(8);
+
+#define is_64bit() 0 /* we only support 32-bit PDC for now. */
+
u8 BiosChecksum;
char zonefseg_start, zonefseg_end; // SYMBOLS
@@ -159,6 +168,7 @@ void __VISIBLE __noreturn hlt(void)
if (pdc_debug)
printf("HALT initiated from %p\n", __builtin_return_address(0));
printf("SeaBIOS wants SYSTEM HALT.\n\n");
+ /* call qemu artificial "halt" asm instruction */
asm volatile("\t.word 0xfffdead0": : :"memory");
while (1);
}
@@ -172,7 +182,7 @@ static void check_powersw_button(void)
}
}
-void __noreturn reset(void)
+void __VISIBLE __noreturn reset(void)
{
if (pdc_debug)
printf("RESET initiated from %p\n", __builtin_return_address(0));
@@ -180,6 +190,7 @@ void __noreturn reset(void)
"***************************\n");
check_powersw_button();
PAGE0->imm_soft_boot = 1;
+ /* call qemu artificial "reset" asm instruction */
asm volatile("\t.word 0xfffdead1": : :"memory");
while (1);
}
@@ -822,21 +833,49 @@ static int pdc_pim(unsigned int *arg)
{
unsigned long option = ARG1;
unsigned long *result = (unsigned long *)ARG2;
+ unsigned long hpa;
+ int i;
+ unsigned int count, default_size;
+
+ if (is_64bit())
+ default_size = sizeof(struct pdc_toc_pim_20);
+ else
+ default_size = sizeof(struct pdc_toc_pim_11);
switch (option) {
case PDC_PIM_HPMC:
break;
case PDC_PIM_RETURN_SIZE:
- *result = sizeof(struct pdc_hpmc_pim_11); // FIXME 64bit!
+ *result = default_size;
// B160 returns only "2". Why?
return PDC_OK;
case PDC_PIM_LPMC:
case PDC_PIM_SOFT_BOOT:
- return PDC_BAD_OPTION;
- case PDC_PIM_TOC:
break;
+ case PDC_PIM_TOC:
+ hpa = mfctl(CPU_HPA_CR_REG); /* get CPU HPA from cr7 */
+ i = index_of_CPU_HPA(hpa);
+ if (i < 0 || i >= HPPA_MAX_CPUS) {
+ *result = PDC_INVALID_ARG;
+ return PDC_OK;
+ }
+ if (( is_64bit() && pim_toc_data[i].pim20.cpu_state.val == 0) ||
+ (!is_64bit() && pim_toc_data[i].pim11.cpu_state.val == 0)) {
+ /* PIM contents invalid */
+ *result = PDC_NE_MOD;
+ return PDC_OK;
+ }
+ count = (default_size < ARG4) ? default_size : ARG4;
+ memcpy((void *)ARG3, &pim_toc_data[i], count);
+ *result = count;
+ /* clear PIM contents */
+ if (is_64bit())
+ pim_toc_data[i].pim20.cpu_state.val = 0;
+ else
+ pim_toc_data[i].pim11.cpu_state.val = 0;
+ return PDC_OK;
}
- return PDC_BAD_PROC;
+ return PDC_BAD_OPTION;
}
static struct pdc_model model = { PARISC_PDC_MODEL };
@@ -1559,6 +1598,91 @@ int __VISIBLE parisc_pdc_entry(unsigned int *arg FUNC_MANY_ARGS)
return PDC_BAD_PROC;
}
+/********************************************************
+ * TOC HANDLER
+ ********************************************************/
+
+unsigned long __VISIBLE toc_handler(struct pdc_toc_pim_11 *pim)
+{
+ unsigned long hpa, os_toc_handler;
+ int cpu, y;
+ unsigned long *p;
+ struct pdc_toc_pim_11 *pim11;
+ struct pdc_toc_pim_20 *pim20;
+ struct pim_cpu_state_cf state = { .iqv=1, .iqf=1, .ipv=1, .grv=1, .crv=1, .srv=1, .trv=1, .td=1 };
+
+ hpa = mfctl(CPU_HPA_CR_REG); /* get CPU HPA from cr7 */
+ cpu = index_of_CPU_HPA(hpa);
+
+ pim11 = &pim_toc_data[cpu].pim11;
+ pim20 = &pim_toc_data[cpu].pim20;
+ if (is_64bit())
+ pim20->cpu_state = state;
+ else
+ pim11->cpu_state = state;
+
+ /* check that we use the same PIM entries as assembly code */
+ BUG_ON(pim11 != pim);
+
+ printf("\n");
+ printf("##### CPU %d HPA %lx: SeaBIOS TOC register dump #####\n", cpu, hpa);
+ for (y = 0; y < 32; y += 8) {
+ if (is_64bit())
+ p = (unsigned long *)&pim20->gr[y];
+ else
+ p = (unsigned long *)&pim11->gr[y];
+ printf("GR%02d: %lx %lx %lx %lx", y, p[0], p[1], p[2], p[3]);
+ printf( " %lx %lx %lx %lx\n", p[4], p[5], p[6], p[7]);
+ }
+ printf("\n");
+ for (y = 0; y < 32; y += 8) {
+ if (is_64bit())
+ p = (unsigned long *)&pim20->cr[y];
+ else
+ p = (unsigned long *)&pim11->cr[y];
+ printf("CR%02d: %lx %lx %lx %lx", y, p[0], p[1], p[2], p[3]);
+ printf( " %lx %lx %lx %lx\n", p[4], p[5], p[6], p[7]);
+ }
+ printf("\n");
+ if (is_64bit())
+ p = (unsigned long *)&pim20->sr[0];
+ else
+ p = (unsigned long *)&pim11->sr[0];
+ printf("SR0: %lx %lx %lx %lx %lx %lx %lx %lx\n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ if (is_64bit()) {
+ printf("IAQ: %lx.%lx %lx.%lx\n",
+ (unsigned long)pim20->cr[17], (unsigned long)pim20->cr[18],
+ (unsigned long)pim20->iasq_back, (unsigned long)pim20->iaoq_back);
+ printf("RP(r2): %lx\n", (unsigned long)pim20->gr[2]);
+ } else {
+ printf("IAQ: %x.%x %x.%x\n", pim11->cr[17], pim11->cr[18],
+ pim11->iasq_back, pim11->iaoq_back);
+ printf("RP(r2): %x\n", pim11->gr[2]);
+ }
+
+ os_toc_handler = PAGE0->vec_toc;
+ if (is_64bit())
+ os_toc_handler |= ((unsigned long long) PAGE0->vec_toc_hi << 32);
+
+ /* release lock - let other CPUs join now. */
+ toc_lock = 1;
+
+ num_online_cpus--;
+
+ if (os_toc_handler) {
+ /* will call OS handler, after all CPUs are here */
+ while (num_online_cpus)
+ ; /* wait */
+ return os_toc_handler; /* let asm code call os handler */
+ }
+
+ /* No OS handler installed. Wait for all CPUs, then last CPU will reset. */
+ if (num_online_cpus)
+ while (1) /* this CPU will wait endless. */;
+
+ printf("SeaBIOS: Resetting machine after TOC.\n");
+ reset();
+}
/********************************************************
* BOOT MENU
@@ -1775,6 +1899,7 @@ void __VISIBLE start_parisc_firmware(void)
if (smp_cpus > HPPA_MAX_CPUS)
smp_cpus = HPPA_MAX_CPUS;
+ num_online_cpus = smp_cpus;
if (ram_size >= FIRMWARE_START)
ram_size = FIRMWARE_START;
@@ -1829,6 +1954,7 @@ void __VISIBLE start_parisc_firmware(void)
BUG_ON(PAGE0->mem_free <= MEM_PDC_ENTRY);
BUG_ON(smp_cpus < 1 || smp_cpus > HPPA_MAX_CPUS);
+ BUG_ON(sizeof(pim_toc_data[0]) != PIM_STORAGE_SIZE);
/* Put QEMU/SeaBIOS marker in PAGE0.
* The Linux kernel will search for it. */
diff --git a/src/parisc/pdc.h b/src/parisc/pdc.h
index 2d03189..e794e14 100644
--- a/src/parisc/pdc.h
+++ b/src/parisc/pdc.h
@@ -398,7 +398,9 @@ struct zeropage {
/* int (*vec_rendz)(void); */
unsigned int vec_rendz;
int vec_pow_fail_flen;
- int vec_pad[10];
+ int vec_pad0[3];
+ unsigned int vec_toc_hi;
+ int vec_pad1[6];
/* [0x040] reserved processor dependent */
int pad0[112]; /* in QEMU pad0[0] holds "SeaBIOS\0" */
@@ -689,6 +691,42 @@ struct pdc_hpmc_pim_20 { /* PDC_PIM */
unsigned long long fr[32];
};
+struct pim_cpu_state_cf {
+ union {
+ unsigned int
+ iqv : 1, /* IIA queue Valid */
+ iqf : 1, /* IIA queue Failure */
+ ipv : 1, /* IPRs Valid */
+ grv : 1, /* GRs Valid */
+ crv : 1, /* CRs Valid */
+ srv : 1, /* SRs Valid */
+ trv : 1, /* CR24 through CR31 valid */
+ pad : 24, /* reserved */
+ td : 1; /* TOC did not cause any damage to the system state */
+ unsigned int val;
+ };
+};
+
+struct pdc_toc_pim_11 {
+ unsigned int gr[32];
+ unsigned int cr[32];
+ unsigned int sr[8];
+ unsigned int iasq_back;
+ unsigned int iaoq_back;
+ unsigned int check_type;
+ struct pim_cpu_state_cf cpu_state;
+};
+
+struct pdc_toc_pim_20 {
+ unsigned long long gr[32];
+ unsigned long long cr[32];
+ unsigned long long sr[8];
+ unsigned long long iasq_back;
+ unsigned long long iaoq_back;
+ unsigned int check_type;
+ struct pim_cpu_state_cf cpu_state;
+};
+
#endif /* !defined(__ASSEMBLY__) */
#endif /* _UAPI_PARISC_PDC_H */