aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */