diff options
-rw-r--r-- | src/parisc/head.S | 112 | ||||
-rw-r--r-- | src/parisc/hppa_hardware.h | 1 | ||||
-rw-r--r-- | src/parisc/parisc.c | 136 | ||||
-rw-r--r-- | src/parisc/pdc.h | 40 |
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 */ |