Index: head/sys/powerpc/aim/aim_machdep.c =================================================================== --- head/sys/powerpc/aim/aim_machdep.c (revision 366032) +++ head/sys/powerpc/aim/aim_machdep.c (revision 366033) @@ -1,780 +1,786 @@ /*- * Copyright (C) 1995, 1996 Wolfgang Solfrank. * Copyright (C) 1995, 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /*- * Copyright (C) 2001 Benno Rice * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * $NetBSD: machdep.c,v 1.74.2.1 2000/11/01 16:13:48 tv Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_ddb.h" #include "opt_kstack_pages.h" #include "opt_platform.h" +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __powerpc64__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __powerpc64__ #include "mmu_oea64.h" #endif #ifndef __powerpc64__ struct bat battable[16]; #endif int radix_mmu = 0; #ifndef __powerpc64__ /* Bits for running on 64-bit systems in 32-bit mode. */ extern void *testppc64, *testppc64size; extern void *restorebridge, *restorebridgesize; extern void *rfid_patch, *rfi_patch1, *rfi_patch2; extern void *trapcode64; extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; #endif extern void *rstcode, *rstcodeend; extern void *trapcode, *trapcodeend; extern void *hypertrapcode, *hypertrapcodeend; extern void *generictrap, *generictrap64; extern void *alitrap, *aliend; extern void *dsitrap, *dsiend; extern void *decrint, *decrsize; extern void *extint, *extsize; extern void *dblow, *dbend; extern void *imisstrap, *imisssize; extern void *dlmisstrap, *dlmisssize; extern void *dsmisstrap, *dsmisssize; extern void *ap_pcpu; extern void __restartkernel(vm_offset_t, vm_offset_t, vm_offset_t, void *, uint32_t, register_t offset, register_t msr); extern void __restartkernel_virtual(vm_offset_t, vm_offset_t, vm_offset_t, void *, uint32_t, register_t offset, register_t msr); void aim_early_init(vm_offset_t fdt, vm_offset_t toc, vm_offset_t ofentry, void *mdp, uint32_t mdp_cookie); void aim_cpu_init(vm_offset_t toc); void aim_early_init(vm_offset_t fdt, vm_offset_t toc, vm_offset_t ofentry, void *mdp, uint32_t mdp_cookie) { register_t scratch; /* * If running from an FDT, make sure we are in real mode to avoid * tromping on firmware page tables. Everything in the kernel assumes * 1:1 mappings out of firmware, so this won't break anything not * already broken. This doesn't work if there is live OF, since OF * may internally use non-1:1 mappings. */ if (ofentry == 0) mtmsr(mfmsr() & ~(PSL_IR | PSL_DR)); #ifdef __powerpc64__ /* * Relocate to high memory so that the kernel * can execute from the direct map. * * If we are in virtual mode already, use a special entry point * that sets up a temporary DMAP to execute from until we can * properly set up the MMU. */ if ((vm_offset_t)&aim_early_init < DMAP_BASE_ADDRESS) { if (mfmsr() & PSL_DR) { __restartkernel_virtual(fdt, 0, ofentry, mdp, mdp_cookie, DMAP_BASE_ADDRESS, mfmsr()); } else { __restartkernel(fdt, 0, ofentry, mdp, mdp_cookie, DMAP_BASE_ADDRESS, mfmsr()); } } #endif /* Various very early CPU fix ups */ switch (mfpvr() >> 16) { /* * PowerPC 970 CPUs have a misfeature requested by Apple that * makes them pretend they have a 32-byte cacheline. Turn this * off before we measure the cacheline size. */ case IBM970: case IBM970FX: case IBM970MP: case IBM970GX: scratch = mfspr(SPR_HID5); scratch &= ~HID5_970_DCBZ_SIZE_HI; mtspr(SPR_HID5, scratch); break; #ifdef __powerpc64__ case IBMPOWER7: case IBMPOWER7PLUS: case IBMPOWER8: case IBMPOWER8E: case IBMPOWER8NVL: case IBMPOWER9: /* XXX: get from ibm,slb-size in device tree */ n_slbs = 32; break; #endif } } void aim_cpu_init(vm_offset_t toc) { size_t trap_offset, trapsize; vm_offset_t trap; register_t msr; uint8_t *cache_check; int cacheline_warn; #ifndef __powerpc64__ register_t scratch; int ppc64; #endif trap_offset = 0; cacheline_warn = 0; /* General setup for AIM CPUs */ psl_kernset = PSL_EE | PSL_ME | PSL_IR | PSL_DR | PSL_RI; #ifdef __powerpc64__ psl_kernset |= PSL_SF; if (mfmsr() & PSL_HV) psl_kernset |= PSL_HV; + +#if BYTE_ORDER == LITTLE_ENDIAN + psl_kernset |= PSL_LE; +#endif + #endif psl_userset = psl_kernset | PSL_PR; #ifdef __powerpc64__ psl_userset32 = psl_userset & ~PSL_SF; #endif /* * Zeroed bits in this variable signify that the value of the bit * in its position is allowed to vary between userspace contexts. * * All other bits are required to be identical for every userspace * context. The actual *value* of the bit is determined by * psl_userset and/or psl_userset32, and is not allowed to change. * * Remember to update this set when implementing support for * *conditionally* enabling a processor facility. Failing to do * this will cause swapcontext() in userspace to break when a * process uses a conditionally-enabled facility. * * When *unconditionally* implementing support for a processor * facility, update psl_userset / psl_userset32 instead. * * See the access control check in set_mcontext(). */ psl_userstatic = ~(PSL_VSX | PSL_VEC | PSL_FP | PSL_FE0 | PSL_FE1); /* * Mask bits from the SRR1 that aren't really the MSR: * Bits 1-4, 10-15 (ppc32), 33-36, 42-47 (ppc64) */ psl_userstatic &= ~0x783f0000UL; /* * Initialize the interrupt tables and figure out our cache line * size and whether or not we need the 64-bit bridge code. */ /* * Disable translation in case the vector area hasn't been * mapped (G5). Note that no OFW calls can be made until * translation is re-enabled. */ msr = mfmsr(); mtmsr((msr & ~(PSL_IR | PSL_DR)) | PSL_RI); /* * Measure the cacheline size using dcbz * * Use EXC_PGM as a playground. We are about to overwrite it * anyway, we know it exists, and we know it is cache-aligned. */ cache_check = (void *)EXC_PGM; for (cacheline_size = 0; cacheline_size < 0x100; cacheline_size++) cache_check[cacheline_size] = 0xff; __asm __volatile("dcbz 0,%0":: "r" (cache_check) : "memory"); /* Find the first byte dcbz did not zero to get the cache line size */ for (cacheline_size = 0; cacheline_size < 0x100 && cache_check[cacheline_size] == 0; cacheline_size++); /* Work around psim bug */ if (cacheline_size == 0) { cacheline_warn = 1; cacheline_size = 32; } #ifndef __powerpc64__ /* * Figure out whether we need to use the 64 bit PMAP. This works by * executing an instruction that is only legal on 64-bit PPC (mtmsrd), * and setting ppc64 = 0 if that causes a trap. */ ppc64 = 1; bcopy(&testppc64, (void *)EXC_PGM, (size_t)&testppc64size); __syncicache((void *)EXC_PGM, (size_t)&testppc64size); __asm __volatile("\ mfmsr %0; \ mtsprg2 %1; \ \ mtmsrd %0; \ mfsprg2 %1;" : "=r"(scratch), "=r"(ppc64)); if (ppc64) cpu_features |= PPC_FEATURE_64; /* * Now copy restorebridge into all the handlers, if necessary, * and set up the trap tables. */ if (cpu_features & PPC_FEATURE_64) { /* Patch the two instances of rfi -> rfid */ bcopy(&rfid_patch,&rfi_patch1,4); #ifdef KDB /* rfi_patch2 is at the end of dbleave */ bcopy(&rfid_patch,&rfi_patch2,4); #endif } #else /* powerpc64 */ cpu_features |= PPC_FEATURE_64; #endif trapsize = (size_t)&trapcodeend - (size_t)&trapcode; /* * Copy generic handler into every possible trap. Special cases will get * different ones in a minute. */ for (trap = EXC_RST; trap < EXC_LAST; trap += 0x20) bcopy(&trapcode, (void *)trap, trapsize); #ifndef __powerpc64__ if (cpu_features & PPC_FEATURE_64) { /* * Copy a code snippet to restore 32-bit bridge mode * to the top of every non-generic trap handler */ trap_offset += (size_t)&restorebridgesize; bcopy(&restorebridge, (void *)EXC_RST, trap_offset); bcopy(&restorebridge, (void *)EXC_DSI, trap_offset); bcopy(&restorebridge, (void *)EXC_ALI, trap_offset); bcopy(&restorebridge, (void *)EXC_PGM, trap_offset); bcopy(&restorebridge, (void *)EXC_MCHK, trap_offset); bcopy(&restorebridge, (void *)EXC_TRC, trap_offset); bcopy(&restorebridge, (void *)EXC_BPT, trap_offset); } else { /* * Use an IBAT and a DBAT to map the bottom 256M segment. * * It is very important to do it *now* to avoid taking a * fault in .text / .data before the MMU is bootstrapped, * because until then, the translation data has not been * copied over from OpenFirmware, so our DSI/ISI will fail * to find a match. */ battable[0x0].batl = BATL(0x00000000, BAT_M, BAT_PP_RW); battable[0x0].batu = BATU(0x00000000, BAT_BL_256M, BAT_Vs); __asm (".balign 32; \n" "mtibatu 0,%0; mtibatl 0,%1; isync; \n" "mtdbatu 0,%0; mtdbatl 0,%1; isync" :: "r"(battable[0].batu), "r"(battable[0].batl)); } #else trapsize = (size_t)&hypertrapcodeend - (size_t)&hypertrapcode; bcopy(&hypertrapcode, (void *)(EXC_HEA + trap_offset), trapsize); bcopy(&hypertrapcode, (void *)(EXC_HMI + trap_offset), trapsize); bcopy(&hypertrapcode, (void *)(EXC_HVI + trap_offset), trapsize); bcopy(&hypertrapcode, (void *)(EXC_SOFT_PATCH + trap_offset), trapsize); #endif bcopy(&rstcode, (void *)(EXC_RST + trap_offset), (size_t)&rstcodeend - (size_t)&rstcode); #ifdef KDB bcopy(&dblow, (void *)(EXC_MCHK + trap_offset), (size_t)&dbend - (size_t)&dblow); bcopy(&dblow, (void *)(EXC_PGM + trap_offset), (size_t)&dbend - (size_t)&dblow); bcopy(&dblow, (void *)(EXC_TRC + trap_offset), (size_t)&dbend - (size_t)&dblow); bcopy(&dblow, (void *)(EXC_BPT + trap_offset), (size_t)&dbend - (size_t)&dblow); #endif bcopy(&alitrap, (void *)(EXC_ALI + trap_offset), (size_t)&aliend - (size_t)&alitrap); bcopy(&dsitrap, (void *)(EXC_DSI + trap_offset), (size_t)&dsiend - (size_t)&dsitrap); /* Set address of generictrap for self-reloc calculations */ *((void **)TRAP_GENTRAP) = &generictrap; #ifdef __powerpc64__ /* Set TOC base so that the interrupt code can get at it */ *((void **)TRAP_ENTRY) = &generictrap; *((register_t *)TRAP_TOCBASE) = toc; #else /* Set branch address for trap code */ if (cpu_features & PPC_FEATURE_64) *((void **)TRAP_ENTRY) = &generictrap64; else *((void **)TRAP_ENTRY) = &generictrap; *((void **)TRAP_TOCBASE) = _GLOBAL_OFFSET_TABLE_; /* G2-specific TLB miss helper handlers */ bcopy(&imisstrap, (void *)EXC_IMISS, (size_t)&imisssize); bcopy(&dlmisstrap, (void *)EXC_DLMISS, (size_t)&dlmisssize); bcopy(&dsmisstrap, (void *)EXC_DSMISS, (size_t)&dsmisssize); #endif __syncicache(EXC_RSVD, EXC_LAST - EXC_RSVD); /* * Restore MSR */ mtmsr(msr); /* Warn if cachline size was not determined */ if (cacheline_warn == 1) { printf("WARNING: cacheline size undetermined, setting to 32\n"); } /* * Initialise virtual memory. Use BUS_PROBE_GENERIC priority * in case the platform module had a better idea of what we * should do. */ if (cpu_features2 & PPC_FEATURE2_ARCH_3_00) { radix_mmu = 0; TUNABLE_INT_FETCH("radix_mmu", &radix_mmu); if (radix_mmu) pmap_mmu_install(MMU_TYPE_RADIX, BUS_PROBE_GENERIC); else pmap_mmu_install(MMU_TYPE_G5, BUS_PROBE_GENERIC); } else if (cpu_features & PPC_FEATURE_64) pmap_mmu_install(MMU_TYPE_G5, BUS_PROBE_GENERIC); else pmap_mmu_install(MMU_TYPE_OEA, BUS_PROBE_GENERIC); } /* * Shutdown the CPU as much as possible. */ void cpu_halt(void) { OF_exit(); } int ptrace_single_step(struct thread *td) { struct trapframe *tf; tf = td->td_frame; tf->srr1 |= PSL_SE; return (0); } int ptrace_clear_single_step(struct thread *td) { struct trapframe *tf; tf = td->td_frame; tf->srr1 &= ~PSL_SE; return (0); } void kdb_cpu_clear_singlestep(void) { kdb_frame->srr1 &= ~PSL_SE; } void kdb_cpu_set_singlestep(void) { kdb_frame->srr1 |= PSL_SE; } /* * Initialise a struct pcpu. */ void cpu_pcpu_init(struct pcpu *pcpu, int cpuid, size_t sz) { #ifdef __powerpc64__ /* Copy the SLB contents from the current CPU */ memcpy(pcpu->pc_aim.slb, PCPU_GET(aim.slb), sizeof(pcpu->pc_aim.slb)); #endif } /* Return 0 on handled success, otherwise signal number. */ int cpu_machine_check(struct thread *td, struct trapframe *frame, int *ucode) { #ifdef __powerpc64__ /* * This block is 64-bit CPU specific currently. Punt running in 32-bit * mode on 64-bit CPUs. */ /* Check if the important information is in DSISR */ if ((frame->srr1 & SRR1_MCHK_DATA) != 0) { printf("Machine check, DSISR: %016lx\n", frame->cpu.aim.dsisr); /* SLB multi-hit is recoverable. */ if ((frame->cpu.aim.dsisr & DSISR_MC_SLB_MULTIHIT) != 0) return (0); if ((frame->cpu.aim.dsisr & DSISR_MC_DERAT_MULTIHIT) != 0) { pmap_tlbie_all(); return (0); } /* TODO: Add other machine check recovery procedures. */ } else { if ((frame->srr1 & SRR1_MCHK_IFETCH_M) == SRR1_MCHK_IFETCH_SLBMH) return (0); } #endif *ucode = BUS_OBJERR; return (SIGBUS); } #ifndef __powerpc64__ uint64_t va_to_vsid(pmap_t pm, vm_offset_t va) { return ((pm->pm_sr[(uintptr_t)va >> ADDR_SR_SHFT]) & SR_VSID_MASK); } #endif /* * These functions need to provide addresses that both (a) work in real mode * (or whatever mode/circumstances the kernel is in in early boot (now)) and * (b) can still, in principle, work once the kernel is going. Because these * rely on existing mappings/real mode, unmap is a no-op. */ vm_offset_t pmap_early_io_map(vm_paddr_t pa, vm_size_t size) { KASSERT(!pmap_bootstrapped, ("Not available after PMAP started!")); /* * If we have the MMU up in early boot, assume it is 1:1. Otherwise, * try to get the address in a memory region compatible with the * direct map for efficiency later. */ if (mfmsr() & PSL_DR) return (pa); else return (DMAP_BASE_ADDRESS + pa); } void pmap_early_io_unmap(vm_offset_t va, vm_size_t size) { KASSERT(!pmap_bootstrapped, ("Not available after PMAP started!")); } /* From p3-53 of the MPC7450 RISC Microprocessor Family Reference Manual */ void flush_disable_caches(void) { register_t msr; register_t msscr0; register_t cache_reg; volatile uint32_t *memp; uint32_t temp; int i; int x; msr = mfmsr(); powerpc_sync(); mtmsr(msr & ~(PSL_EE | PSL_DR)); msscr0 = mfspr(SPR_MSSCR0); msscr0 &= ~MSSCR0_L2PFE; mtspr(SPR_MSSCR0, msscr0); powerpc_sync(); isync(); /* 7e00066c: dssall */ __asm__ __volatile__(".long 0x7e00066c; sync"); powerpc_sync(); isync(); __asm__ __volatile__("dcbf 0,%0" :: "r"(0)); __asm__ __volatile__("dcbf 0,%0" :: "r"(0)); __asm__ __volatile__("dcbf 0,%0" :: "r"(0)); /* Lock the L1 Data cache. */ mtspr(SPR_LDSTCR, mfspr(SPR_LDSTCR) | 0xFF); powerpc_sync(); isync(); mtspr(SPR_LDSTCR, 0); /* * Perform this in two stages: Flush the cache starting in RAM, then do it * from ROM. */ memp = (volatile uint32_t *)0x00000000; for (i = 0; i < 128 * 1024; i++) { temp = *memp; __asm__ __volatile__("dcbf 0,%0" :: "r"(memp)); memp += 32/sizeof(*memp); } memp = (volatile uint32_t *)0xfff00000; x = 0xfe; for (; x != 0xff;) { mtspr(SPR_LDSTCR, x); for (i = 0; i < 128; i++) { temp = *memp; __asm__ __volatile__("dcbf 0,%0" :: "r"(memp)); memp += 32/sizeof(*memp); } x = ((x << 1) | 1) & 0xff; } mtspr(SPR_LDSTCR, 0); cache_reg = mfspr(SPR_L2CR); if (cache_reg & L2CR_L2E) { cache_reg &= ~(L2CR_L2IO_7450 | L2CR_L2DO_7450); mtspr(SPR_L2CR, cache_reg); powerpc_sync(); mtspr(SPR_L2CR, cache_reg | L2CR_L2HWF); while (mfspr(SPR_L2CR) & L2CR_L2HWF) ; /* Busy wait for cache to flush */ powerpc_sync(); cache_reg &= ~L2CR_L2E; mtspr(SPR_L2CR, cache_reg); powerpc_sync(); mtspr(SPR_L2CR, cache_reg | L2CR_L2I); powerpc_sync(); while (mfspr(SPR_L2CR) & L2CR_L2I) ; /* Busy wait for L2 cache invalidate */ powerpc_sync(); } cache_reg = mfspr(SPR_L3CR); if (cache_reg & L3CR_L3E) { cache_reg &= ~(L3CR_L3IO | L3CR_L3DO); mtspr(SPR_L3CR, cache_reg); powerpc_sync(); mtspr(SPR_L3CR, cache_reg | L3CR_L3HWF); while (mfspr(SPR_L3CR) & L3CR_L3HWF) ; /* Busy wait for cache to flush */ powerpc_sync(); cache_reg &= ~L3CR_L3E; mtspr(SPR_L3CR, cache_reg); powerpc_sync(); mtspr(SPR_L3CR, cache_reg | L3CR_L3I); powerpc_sync(); while (mfspr(SPR_L3CR) & L3CR_L3I) ; /* Busy wait for L3 cache invalidate */ powerpc_sync(); } mtspr(SPR_HID0, mfspr(SPR_HID0) & ~HID0_DCE); powerpc_sync(); isync(); mtmsr(msr); } void cpu_sleep() { static u_quad_t timebase = 0; static register_t sprgs[4]; static register_t srrs[2]; jmp_buf resetjb; struct thread *fputd; struct thread *vectd; register_t hid0; register_t msr; register_t saved_msr; ap_pcpu = pcpup; PCPU_SET(restore, &resetjb); saved_msr = mfmsr(); fputd = PCPU_GET(fputhread); vectd = PCPU_GET(vecthread); if (fputd != NULL) save_fpu(fputd); if (vectd != NULL) save_vec(vectd); if (setjmp(resetjb) == 0) { sprgs[0] = mfspr(SPR_SPRG0); sprgs[1] = mfspr(SPR_SPRG1); sprgs[2] = mfspr(SPR_SPRG2); sprgs[3] = mfspr(SPR_SPRG3); srrs[0] = mfspr(SPR_SRR0); srrs[1] = mfspr(SPR_SRR1); timebase = mftb(); powerpc_sync(); flush_disable_caches(); hid0 = mfspr(SPR_HID0); hid0 = (hid0 & ~(HID0_DOZE | HID0_NAP)) | HID0_SLEEP; powerpc_sync(); isync(); msr = mfmsr() | PSL_POW; mtspr(SPR_HID0, hid0); powerpc_sync(); while (1) mtmsr(msr); } platform_smp_timebase_sync(timebase, 0); PCPU_SET(curthread, curthread); PCPU_SET(curpcb, curthread->td_pcb); pmap_activate(curthread); powerpc_sync(); mtspr(SPR_SPRG0, sprgs[0]); mtspr(SPR_SPRG1, sprgs[1]); mtspr(SPR_SPRG2, sprgs[2]); mtspr(SPR_SPRG3, sprgs[3]); mtspr(SPR_SRR0, srrs[0]); mtspr(SPR_SRR1, srrs[1]); mtmsr(saved_msr); if (fputd == curthread) enable_fpu(curthread); if (vectd == curthread) enable_vec(curthread); powerpc_sync(); } Index: head/sys/powerpc/aim/locore64.S =================================================================== --- head/sys/powerpc/aim/locore64.S (revision 366032) +++ head/sys/powerpc/aim/locore64.S (revision 366033) @@ -1,270 +1,274 @@ /* $FreeBSD$ */ /*- * Copyright (C) 2010-2016 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include "assym.inc" #include #include #include #include #include #include #ifdef _CALL_ELF .abiversion _CALL_ELF #endif /* Glue for linker script */ .globl kernbase .set kernbase, KERNBASE /* * Globals */ .data .align 3 GLOBAL(__startkernel) .llong begin GLOBAL(__endkernel) .llong end GLOBAL(can_wakeup) .llong 0x0 .align 4 #define TMPSTKSZ 16384 /* 16K temporary stack */ GLOBAL(tmpstk) .space TMPSTKSZ TOC_ENTRY(tmpstk) TOC_ENTRY(can_wakeup) #ifdef KDB #define TRAPSTKSZ 8192 /* 8k trap stack */ GLOBAL(trapstk) .space TRAPSTKSZ TOC_ENTRY(trapstk) #endif /* * Entry point for bootloaders that do not fully implement ELF and start * at the beginning of the image (kexec, notably). In its own section so * that it ends up before any linker-generated call stubs and actually at * the beginning of the image. kexec on some systems also enters at * (start of image) + 0x60, so put a spin loop there. */ .section ".text.kboot", "x", @progbits kbootentry: b __start . = kbootentry + 0x40 /* Magic address used in platform layer */ .global smp_spin_sem ap_kexec_spin_sem: .long -1 . = kbootentry + 0x60 /* Entry point for kexec APs */ ap_kexec_start: /* At 0x60 past start, copied to 0x60 by kexec */ /* r3 set to CPU ID by kexec */ /* Invalidate icache for low-memory copy and jump there */ li %r0,0x80 dcbst 0,%r0 sync icbi 0,%r0 isync ba 0x80 /* Absolute branch to next inst */ . = kbootentry + 0x80 /* Aligned to cache line */ 1: or 31,31,31 /* yield */ sync lwz %r1,0x40(0) /* Spin on ap_kexec_spin_sem */ cmpw %r1,%r3 /* Until it equals our CPU ID */ bne 1b /* Released */ or 2,2,2 /* unyield */ /* Make sure that it will be software reset. Clear SRR1 */ li %r1,0 mtsrr1 %r1 ba EXC_RST /* * Now start the real text section */ .text .globl btext btext: /* * Main kernel entry point. * * Calling convention: * r3: Flattened Device Tree pointer (or zero) * r4: ignored * r5: OF client interface pointer (or zero) * r6: Loader metadata pointer (or zero) * r7: Magic cookie (0xfb5d104d) to indicate that r6 has loader metadata */ .text -ASENTRY_NOPROF(__start) +_NAKED_ENTRY(__start) + +#ifdef __LITTLE_ENDIAN__ + RETURN_TO_NATIVE_ENDIAN +#endif /* Set 64-bit mode if not yet set before branching to C */ mfmsr %r20 li %r21,1 insrdi %r20,%r21,1,0 mtmsrd %r20 isync nop /* Make this block a multiple of 8 bytes */ /* Set up the TOC pointer */ b 0f .align 3 0: nop bl 1f .llong __tocbase + 0x8000 - . 1: mflr %r2 ld %r1,0(%r2) add %r2,%r1,%r2 /* Get load offset */ ld %r31,-0x8000(%r2) /* First TOC entry is TOC base */ subf %r31,%r31,%r2 /* Subtract from real TOC base to get base */ /* Set up the stack pointer */ bl 1f .llong tmpstk + TMPSTKSZ - 96 - . 1: mflr %r30 ld %r1,0(%r30) add %r1,%r1,%r30 nop /* Relocate kernel */ std %r3,48(%r1) std %r4,56(%r1) std %r5,64(%r1) std %r6,72(%r1) std %r7,80(%r1) bl 1f .llong _DYNAMIC-. 1: mflr %r3 ld %r4,0(%r3) add %r3,%r4,%r3 mr %r4,%r31 bl elf_reloc_self nop ld %r3,48(%r1) ld %r4,56(%r1) ld %r5,64(%r1) ld %r6,72(%r1) ld %r7,80(%r1) /* Begin CPU init */ mr %r4,%r2 /* Replace ignored r4 with tocbase for trap handlers */ bl powerpc_init nop /* Set stack pointer to new value and branch to mi_startup */ mr %r1, %r3 li %r3, 0 std %r3, 0(%r1) bl mi_startup nop /* Unreachable */ b . ASENTRY_NOPROF(__restartkernel_virtual) /* * When coming in via this entry point, we need to alter the SLB to * shadow the segment register emulation entries in DMAP space. * We need to do this dance because we are running with virtual-mode * OpenFirmware and have not yet taken over the MMU. * * Assumptions: * 1) The kernel is currently identity-mapped. * 2) We are currently executing at an address compatible with * real mode. * 3) The first 16 SLB entries are emulating SRs. * 4) The rest of the SLB is not in use. * 5) OpenFirmware is not manipulating the SLB at runtime. * 6) We are running on 64-bit AIM. * * Tested on a G5. */ mfmsr %r14 /* Switch to real mode because we are about to mess with the SLB. */ andi. %r14, %r14, ~(PSL_DR|PSL_IR|PSL_ME|PSL_RI)@l mtmsr %r14 isync /* Prepare variables for later use. */ li %r14, 0 li %r18, 0 oris %r18, %r18, 0xc000 sldi %r18, %r18, 32 /* r18: 0xc000000000000000 */ 1: /* * Loop over the first 16 SLB entries. * Offset the SLBE into the DMAP, add 16 to the index, and write * it back to the SLB. */ /* XXX add more safety checks */ slbmfev %r15, %r14 slbmfee %r16, %r14 or %r16, %r16, %r14 /* index is 0-15 */ ori %r16, %r16, 0x10 /* add 16 to index. */ or %r16, %r16, %r18 /* SLBE DMAP offset */ rldicr %r17, %r16, 0, 37 /* Invalidation SLBE */ isync slbie %r17 /* isync */ slbmte %r15, %r16 isync addi %r14, %r14, 1 cmpdi %r14, 16 blt 1b ASENTRY_NOPROF(__restartkernel) /* * r3-r7: arguments to go to __start * r8: offset from current kernel address to apply * r9: MSR to set when (atomically) jumping to __start + r8 */ mtsrr1 %r9 bl 1f 1: mflr %r25 add %r25,%r8,%r25 addi %r25,%r25,2f-1b mtsrr0 %r25 rfid 2: bl __start nop #include Index: head/sys/powerpc/ofw/ofw_machdep.c =================================================================== --- head/sys/powerpc/ofw/ofw_machdep.c (revision 366032) +++ head/sys/powerpc/ofw/ofw_machdep.c (revision 366033) @@ -1,871 +1,875 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1996 Wolfgang Solfrank. * Copyright (C) 1996 TooLs GmbH. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $ */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef POWERNV #include #endif static void *fdt; int ofw_real_mode; #ifdef AIM extern register_t ofmsr[5]; extern void *openfirmware_entry; char save_trap_init[0x2f00]; /* EXC_LAST */ char save_trap_of[0x2f00]; /* EXC_LAST */ int ofwcall(void *); static int openfirmware(void *args); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfortify-source" __inline void ofw_save_trap_vec(char *save_trap_vec) { if (!ofw_real_mode || !hw_direct_map) return; bcopy((void *)PHYS_TO_DMAP(EXC_RST), save_trap_vec, EXC_LAST - EXC_RST); } static __inline void ofw_restore_trap_vec(char *restore_trap_vec) { if (!ofw_real_mode || !hw_direct_map) return; bcopy(restore_trap_vec, (void *)PHYS_TO_DMAP(EXC_RST), EXC_LAST - EXC_RST); __syncicache((void *)PHYS_TO_DMAP(EXC_RSVD), EXC_LAST - EXC_RSVD); } #pragma clang diagnostic pop /* * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback. */ register_t ofw_sprg0_save; static __inline void ofw_sprg_prepare(void) { if (ofw_real_mode) return; /* * Assume that interrupt are disabled at this point, or * SPRG1-3 could be trashed */ #ifdef __powerpc64__ __asm __volatile("mtsprg1 %0\n\t" "mtsprg2 %1\n\t" "mtsprg3 %2\n\t" : : "r"(ofmsr[2]), "r"(ofmsr[3]), "r"(ofmsr[4])); #else __asm __volatile("mfsprg0 %0\n\t" "mtsprg0 %1\n\t" "mtsprg1 %2\n\t" "mtsprg2 %3\n\t" "mtsprg3 %4\n\t" : "=&r"(ofw_sprg0_save) : "r"(ofmsr[1]), "r"(ofmsr[2]), "r"(ofmsr[3]), "r"(ofmsr[4])); #endif } static __inline void ofw_sprg_restore(void) { if (ofw_real_mode) return; /* * Note that SPRG1-3 contents are irrelevant. They are scratch * registers used in the early portion of trap handling when * interrupts are disabled. * * PCPU data cannot be used until this routine is called ! */ #ifndef __powerpc64__ __asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save)); #endif } #endif static int parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output) { cell_t address_cells, size_cells; cell_t OFmem[4 * PHYS_AVAIL_SZ]; int sz, i, j; phandle_t phandle; sz = 0; /* * Get #address-cells from root node, defaulting to 1 if it cannot * be found. */ phandle = OF_finddevice("/"); if (OF_getencprop(phandle, "#address-cells", &address_cells, sizeof(address_cells)) < (ssize_t)sizeof(address_cells)) address_cells = 1; if (OF_getencprop(phandle, "#size-cells", &size_cells, sizeof(size_cells)) < (ssize_t)sizeof(size_cells)) size_cells = 1; /* * Get memory. */ if (node == -1 || (sz = OF_getencprop(node, prop, OFmem, sizeof(OFmem))) <= 0) panic("Physical memory map not found"); i = 0; j = 0; while (i < sz/sizeof(cell_t)) { output[j].mr_start = OFmem[i++]; if (address_cells == 2) { output[j].mr_start <<= 32; output[j].mr_start += OFmem[i++]; } output[j].mr_size = OFmem[i++]; if (size_cells == 2) { output[j].mr_size <<= 32; output[j].mr_size += OFmem[i++]; } if (output[j].mr_start > BUS_SPACE_MAXADDR) continue; /* * Constrain memory to that which we can access. * 32-bit AIM can only reference 32 bits of address currently, * but Book-E can access 36 bits. */ if (((uint64_t)output[j].mr_start + (uint64_t)output[j].mr_size - 1) > BUS_SPACE_MAXADDR) { output[j].mr_size = BUS_SPACE_MAXADDR - output[j].mr_start + 1; } j++; } return (j); } static int parse_numa_ofw_memory(phandle_t node, const char *prop, struct numa_mem_region *output) { cell_t address_cells, size_cells; cell_t OFmem[4 * PHYS_AVAIL_SZ]; int sz, i, j; phandle_t phandle; sz = 0; /* * Get #address-cells from root node, defaulting to 1 if it cannot * be found. */ phandle = OF_finddevice("/"); if (OF_getencprop(phandle, "#address-cells", &address_cells, sizeof(address_cells)) < (ssize_t)sizeof(address_cells)) address_cells = 1; if (OF_getencprop(phandle, "#size-cells", &size_cells, sizeof(size_cells)) < (ssize_t)sizeof(size_cells)) size_cells = 1; /* * Get memory. */ if (node == -1 || (sz = OF_getencprop(node, prop, OFmem, sizeof(OFmem))) <= 0) panic("Physical memory map not found"); i = 0; j = 0; while (i < sz/sizeof(cell_t)) { output[j].mr_start = OFmem[i++]; if (address_cells == 2) { output[j].mr_start <<= 32; output[j].mr_start += OFmem[i++]; } output[j].mr_size = OFmem[i++]; if (size_cells == 2) { output[j].mr_size <<= 32; output[j].mr_size += OFmem[i++]; } j++; } return (j); } #ifdef FDT static int excise_reserved_regions(struct mem_region *avail, int asz, struct mem_region *exclude, int esz) { int i, j, k; for (i = 0; i < asz; i++) { for (j = 0; j < esz; j++) { /* * Case 1: Exclusion region encloses complete * available entry. Drop it and move on. */ if (exclude[j].mr_start <= avail[i].mr_start && exclude[j].mr_start + exclude[j].mr_size >= avail[i].mr_start + avail[i].mr_size) { for (k = i+1; k < asz; k++) avail[k-1] = avail[k]; asz--; i--; /* Repeat some entries */ continue; } /* * Case 2: Exclusion region starts in available entry. * Trim it to where the entry begins and append * a new available entry with the region after * the excluded region, if any. */ if (exclude[j].mr_start >= avail[i].mr_start && exclude[j].mr_start < avail[i].mr_start + avail[i].mr_size) { if (exclude[j].mr_start + exclude[j].mr_size < avail[i].mr_start + avail[i].mr_size) { avail[asz].mr_start = exclude[j].mr_start + exclude[j].mr_size; avail[asz].mr_size = avail[i].mr_start + avail[i].mr_size - avail[asz].mr_start; asz++; } avail[i].mr_size = exclude[j].mr_start - avail[i].mr_start; } /* * Case 3: Exclusion region ends in available entry. * Move start point to where the exclusion zone ends. * The case of a contained exclusion zone has already * been caught in case 2. */ if (exclude[j].mr_start + exclude[j].mr_size >= avail[i].mr_start && exclude[j].mr_start + exclude[j].mr_size < avail[i].mr_start + avail[i].mr_size) { avail[i].mr_size += avail[i].mr_start; avail[i].mr_start = exclude[j].mr_start + exclude[j].mr_size; avail[i].mr_size -= avail[i].mr_start; } } } return (asz); } static int excise_initrd_region(struct mem_region *avail, int asz) { phandle_t chosen; uint64_t start, end; ssize_t size; struct mem_region initrdmap[1]; pcell_t cell[2]; chosen = OF_finddevice("/chosen"); size = OF_getencprop(chosen, "linux,initrd-start", cell, sizeof(cell)); if (size < 0) return (asz); else if (size == 4) start = cell[0]; else if (size == 8) start = (uint64_t)cell[0] << 32 | cell[1]; else { /* Invalid value length */ printf("WARNING: linux,initrd-start must be either 4 or 8 bytes long\n"); return (asz); } size = OF_getencprop(chosen, "linux,initrd-end", cell, sizeof(cell)); if (size < 0) return (asz); else if (size == 4) end = cell[0]; else if (size == 8) end = (uint64_t)cell[0] << 32 | cell[1]; else { /* Invalid value length */ printf("WARNING: linux,initrd-end must be either 4 or 8 bytes long\n"); return (asz); } if (end <= start) return (asz); initrdmap[0].mr_start = start; initrdmap[0].mr_size = end - start; asz = excise_reserved_regions(avail, asz, initrdmap, 1); return (asz); } #ifdef POWERNV static int excise_msi_region(struct mem_region *avail, int asz) { uint64_t start, end; struct mem_region initrdmap[1]; /* * This range of physical addresses is used to implement optimized * 32 bit MSI interrupts on POWER9. Exclude it to avoid accidentally * using it for DMA, as this will cause an immediate PHB fence. * While we could theoretically turn off this behavior in the ETU, * doing so would break 32-bit MSI, so just reserve the range in * the physical map instead. * See section 4.4.2.8 of the PHB4 specification. */ start = 0x00000000ffff0000ul; end = 0x00000000fffffffful; initrdmap[0].mr_start = start; initrdmap[0].mr_size = end - start; asz = excise_reserved_regions(avail, asz, initrdmap, 1); return (asz); } #endif static int excise_fdt_reserved(struct mem_region *avail, int asz) { struct mem_region fdtmap[32]; ssize_t fdtmapsize; phandle_t chosen; int j, fdtentries; chosen = OF_finddevice("/chosen"); fdtmapsize = OF_getprop(chosen, "fdtmemreserv", fdtmap, sizeof(fdtmap)); for (j = 0; j < fdtmapsize/sizeof(fdtmap[0]); j++) { fdtmap[j].mr_start = be64toh(fdtmap[j].mr_start) & ~PAGE_MASK; fdtmap[j].mr_size = round_page(be64toh(fdtmap[j].mr_size)); } KASSERT(j*sizeof(fdtmap[0]) < sizeof(fdtmap), ("Exceeded number of FDT reservations")); /* Add a virtual entry for the FDT itself */ if (fdt != NULL) { fdtmap[j].mr_start = (vm_offset_t)fdt & ~PAGE_MASK; fdtmap[j].mr_size = round_page(fdt_totalsize(fdt)); fdtmapsize += sizeof(fdtmap[0]); } fdtentries = fdtmapsize/sizeof(fdtmap[0]); asz = excise_reserved_regions(avail, asz, fdtmap, fdtentries); return (asz); } #endif /* * This is called during powerpc_init, before the system is really initialized. * It shall provide the total and the available regions of RAM. * The available regions need not take the kernel into account. */ void ofw_numa_mem_regions(struct numa_mem_region *memp, int *memsz) { phandle_t phandle; int count, msz; char name[31]; struct numa_mem_region *curmemp; msz = 0; /* * Get memory from all the /memory nodes. */ for (phandle = OF_child(OF_peer(0)); phandle != 0; phandle = OF_peer(phandle)) { if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0) continue; if (strncmp(name, "memory@", strlen("memory@")) != 0) continue; count = parse_numa_ofw_memory(phandle, "reg", &memp[msz]); if (count == 0) continue; curmemp = &memp[msz]; MPASS(count == 1); curmemp->mr_domain = platform_node_numa_domain(phandle); if (bootverbose) printf("%s %#jx-%#jx domain(%ju)\n", name, (uintmax_t)curmemp->mr_start, (uintmax_t)curmemp->mr_start + curmemp->mr_size, (uintmax_t)curmemp->mr_domain); msz += count; } *memsz = msz; } /* * This is called during powerpc_init, before the system is really initialized. * It shall provide the total and the available regions of RAM. * The available regions need not take the kernel into account. */ void ofw_mem_regions(struct mem_region *memp, int *memsz, struct mem_region *availp, int *availsz) { phandle_t phandle; int asz, msz; int res; char name[31]; asz = msz = 0; /* * Get memory from all the /memory nodes. */ for (phandle = OF_child(OF_peer(0)); phandle != 0; phandle = OF_peer(phandle)) { if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0) continue; if (strncmp(name, "memory", sizeof(name)) != 0 && strncmp(name, "memory@", strlen("memory@")) != 0) continue; res = parse_ofw_memory(phandle, "reg", &memp[msz]); msz += res; /* * On POWER9 Systems we might have both linux,usable-memory and * reg properties. 'reg' denotes all available memory, but we * must use 'linux,usable-memory', a subset, as some memory * regions are reserved for NVLink. */ if (OF_getproplen(phandle, "linux,usable-memory") >= 0) res = parse_ofw_memory(phandle, "linux,usable-memory", &availp[asz]); else if (OF_getproplen(phandle, "available") >= 0) res = parse_ofw_memory(phandle, "available", &availp[asz]); else res = parse_ofw_memory(phandle, "reg", &availp[asz]); asz += res; } #ifdef FDT phandle = OF_finddevice("/chosen"); if (OF_hasprop(phandle, "fdtmemreserv")) asz = excise_fdt_reserved(availp, asz); /* If the kernel is being loaded through kexec, initrd region is listed * in /chosen but the region is not marked as reserved, so, we might exclude * it here. */ if (OF_hasprop(phandle, "linux,initrd-start")) asz = excise_initrd_region(availp, asz); #endif #ifdef POWERNV if (opal_check() == 0) asz = excise_msi_region(availp, asz); #endif *memsz = msz; *availsz = asz; } void OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *)) { #ifdef AIM ofmsr[0] = mfmsr(); #ifdef __powerpc64__ ofmsr[0] &= ~PSL_SF; + #ifdef __LITTLE_ENDIAN__ + /* Assume OFW is BE. */ + ofmsr[0] &= ~PSL_LE; + #endif #else __asm __volatile("mfsprg0 %0" : "=&r"(ofmsr[1])); #endif __asm __volatile("mfsprg1 %0" : "=&r"(ofmsr[2])); __asm __volatile("mfsprg2 %0" : "=&r"(ofmsr[3])); __asm __volatile("mfsprg3 %0" : "=&r"(ofmsr[4])); openfirmware_entry = openfirm; if (ofmsr[0] & PSL_DR) ofw_real_mode = 0; else ofw_real_mode = 1; ofw_save_trap_vec(save_trap_init); #else ofw_real_mode = 1; #endif fdt = fdt_ptr; } boolean_t OF_bootstrap() { boolean_t status = FALSE; int err = 0; #ifdef AIM if (openfirmware_entry != NULL) { if (ofw_real_mode) { status = OF_install(OFW_STD_REAL, 0); } else { #ifdef __powerpc64__ status = OF_install(OFW_STD_32BIT, 0); #else status = OF_install(OFW_STD_DIRECT, 0); #endif } if (status != TRUE) return status; err = OF_init(openfirmware); } else #endif if (fdt != NULL) { #ifdef FDT #ifdef AIM bus_space_tag_t fdt_bt; vm_offset_t tmp_fdt_ptr; vm_size_t fdt_size; uintptr_t fdt_va; #endif status = OF_install(OFW_FDT, 0); if (status != TRUE) return status; #ifdef AIM /* AIM-only for now -- Book-E does this remapping in early init */ /* Get the FDT size for mapping if we can */ tmp_fdt_ptr = pmap_early_io_map((vm_paddr_t)fdt, PAGE_SIZE); if (fdt_check_header((void *)tmp_fdt_ptr) != 0) { pmap_early_io_unmap(tmp_fdt_ptr, PAGE_SIZE); return FALSE; } fdt_size = fdt_totalsize((void *)tmp_fdt_ptr); pmap_early_io_unmap(tmp_fdt_ptr, PAGE_SIZE); /* * Map this for real. Use bus_space_map() to take advantage * of its auto-remapping function once the kernel is loaded. * This is a dirty hack, but what we have. */ -#ifdef _LITTLE_ENDIAN +#ifdef __LITTLE_ENDIAN__ fdt_bt = &bs_le_tag; #else fdt_bt = &bs_be_tag; #endif bus_space_map(fdt_bt, (vm_paddr_t)fdt, fdt_size, 0, &fdt_va); err = OF_init((void *)fdt_va); #else err = OF_init(fdt); #endif #endif } #ifdef FDT_DTB_STATIC /* * Check for a statically included blob already in the kernel and * needing no mapping. */ else { status = OF_install(OFW_FDT, 0); if (status != TRUE) return status; err = OF_init(&fdt_static_dtb); } #endif if (err != 0) { OF_install(NULL, 0); status = FALSE; } return (status); } #ifdef AIM void ofw_quiesce(void) { struct { cell_t name; cell_t nargs; cell_t nreturns; } args; KASSERT(!pmap_bootstrapped, ("Cannot call ofw_quiesce after VM is up")); args.name = (cell_t)(uintptr_t)"quiesce"; args.nargs = 0; args.nreturns = 0; openfirmware(&args); } static int openfirmware_core(void *args) { int result; register_t oldmsr; if (openfirmware_entry == NULL) return (-1); /* * Turn off exceptions - we really don't want to end up * anywhere unexpected with PCPU set to something strange * or the stack pointer wrong. */ oldmsr = intr_disable(); ofw_sprg_prepare(); /* Save trap vectors */ ofw_save_trap_vec(save_trap_of); /* Restore initially saved trap vectors */ ofw_restore_trap_vec(save_trap_init); #ifndef __powerpc64__ /* * Clear battable[] translations */ if (!(cpu_features & PPC_FEATURE_64)) __asm __volatile("mtdbatu 2, %0\n" "mtdbatu 3, %0" : : "r" (0)); isync(); #endif result = ofwcall(args); /* Restore trap vecotrs */ ofw_restore_trap_vec(save_trap_of); ofw_sprg_restore(); intr_restore(oldmsr); return (result); } #ifdef SMP struct ofw_rv_args { void *args; int retval; volatile int in_progress; }; static void ofw_rendezvous_dispatch(void *xargs) { struct ofw_rv_args *rv_args = xargs; /* NOTE: Interrupts are disabled here */ if (PCPU_GET(cpuid) == 0) { /* * Execute all OF calls on CPU 0 */ rv_args->retval = openfirmware_core(rv_args->args); rv_args->in_progress = 0; } else { /* * Spin with interrupts off on other CPUs while OF has * control of the machine. */ while (rv_args->in_progress) cpu_spinwait(); } } #endif static int openfirmware(void *args) { int result; #ifdef SMP struct ofw_rv_args rv_args; #endif if (openfirmware_entry == NULL) return (-1); #ifdef SMP if (cold) { result = openfirmware_core(args); } else { rv_args.args = args; rv_args.in_progress = 1; smp_rendezvous(smp_no_rendezvous_barrier, ofw_rendezvous_dispatch, smp_no_rendezvous_barrier, &rv_args); result = rv_args.retval; } #else result = openfirmware_core(args); #endif return (result); } void OF_reboot() { struct { cell_t name; cell_t nargs; cell_t nreturns; cell_t arg; } args; args.name = (cell_t)(uintptr_t)"interpret"; args.nargs = 1; args.nreturns = 0; args.arg = (cell_t)(uintptr_t)"reset-all"; openfirmware_core(&args); /* Don't do rendezvous! */ for (;;); /* just in case */ } #endif /* AIM */ void OF_getetheraddr(device_t dev, u_char *addr) { phandle_t node; node = ofw_bus_get_node(dev); OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN); } /* * Return a bus handle and bus tag that corresponds to the register * numbered regno for the device referenced by the package handle * dev. This function is intended to be used by console drivers in * early boot only. It works by mapping the address of the device's * register in the address space of its parent and recursively walk * the device tree upward this way. */ int OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag, bus_space_handle_t *handle, bus_size_t *sz) { bus_addr_t addr; bus_size_t size; pcell_t pci_hi; int flags, res; res = ofw_reg_to_paddr(dev, regno, &addr, &size, &pci_hi); if (res < 0) return (res); if (pci_hi == OFW_PADDR_NOT_PCI) { *tag = &bs_be_tag; flags = 0; } else { *tag = &bs_le_tag; flags = (pci_hi & OFW_PCI_PHYS_HI_PREFETCHABLE) ? BUS_SPACE_MAP_PREFETCHABLE: 0; } if (sz != NULL) *sz = size; return (bus_space_map(*tag, addr, size, flags, handle)); } Index: head/sys/powerpc/ofw/ofwcall64.S =================================================================== --- head/sys/powerpc/ofw/ofwcall64.S (revision 366032) +++ head/sys/powerpc/ofw/ofwcall64.S (revision 366033) @@ -1,333 +1,353 @@ /*- * Copyright (C) 2009-2011 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #define OFWSTKSZ 4096 /* 4K Open Firmware stack */ /* * Globals */ .data .align 4 ofwstk: .space OFWSTKSZ rtas_regsave: .space 32 /* 4 * sizeof(register_t) */ GLOBAL(ofmsr) .llong 0, 0, 0, 0, 0 /* msr/sprg0-3 used in Open Firmware */ GLOBAL(rtasmsr) .llong 0 GLOBAL(openfirmware_entry) .llong 0 /* Open Firmware entry point */ GLOBAL(rtas_entry) .llong 0 /* RTAS entry point */ TOC_ENTRY(ofmsr) TOC_ENTRY(ofwstk) TOC_ENTRY(rtasmsr) TOC_ENTRY(openfirmware_entry) TOC_ENTRY(rtas_entry) TOC_ENTRY(rtas_regsave) /* * Open Firmware Real-mode Entry Point. This is a huge pain. */ ASENTRY_NOPROF(ofwcall) mflr %r8 std %r8,16(%r1) stdu %r1,-208(%r1) /* * We need to save the following, because OF's register save/ * restore code assumes that the contents of registers are * at most 32 bits wide: lr, cr, r2, r13-r31, the old MSR. These * get placed in that order in the stack. */ mfcr %r4 std %r4,48(%r1) std %r13,56(%r1) std %r14,64(%r1) std %r15,72(%r1) std %r16,80(%r1) std %r17,88(%r1) std %r18,96(%r1) std %r19,104(%r1) std %r20,112(%r1) std %r21,120(%r1) std %r22,128(%r1) std %r23,136(%r1) std %r24,144(%r1) std %r25,152(%r1) std %r26,160(%r1) std %r27,168(%r1) std %r28,176(%r1) std %r29,184(%r1) std %r30,192(%r1) std %r31,200(%r1) /* Record the old MSR */ mfmsr %r6 /* read client interface handler */ addis %r4,%r2,TOC_REF(openfirmware_entry)@ha ld %r4,TOC_REF(openfirmware_entry)@l(%r4) ld %r4,0(%r4) /* Get OF stack pointer */ addis %r7,%r2,TOC_REF(ofwstk)@ha ld %r7,TOC_REF(ofwstk)@l(%r7) addi %r7,%r7,OFWSTKSZ-40 /* * Set the MSR to the OF value. This has the side effect of disabling * exceptions, which is important for the next few steps. + * This does NOT, however, cause us to switch endianness. */ addis %r5,%r2,TOC_REF(ofmsr)@ha ld %r5,TOC_REF(ofmsr)@l(%r5) ld %r5,0(%r5) mtmsrd %r5 isync /* * Set up OF stack. This needs to be accessible in real mode and * use the 32-bit ABI stack frame format. The pointer to the current * kernel stack is placed at the very top of the stack along with * the old MSR so we can get them back later. */ mr %r5,%r1 mr %r1,%r7 std %r5,8(%r1) /* Save real stack pointer */ std %r2,16(%r1) /* Save old TOC */ std %r6,24(%r1) /* Save old MSR */ std %r8,32(%r1) /* Save high 32-bits of the kernel's PC */ li %r5,0 stw %r5,4(%r1) stw %r5,0(%r1) +#ifdef __LITTLE_ENDIAN__ + /* Atomic context switch w/ endian change */ + mtmsrd %r5, 1 /* Clear PSL_EE|PSL_RI */ + addis %r5,%r2,TOC_REF(ofmsr)@ha + ld %r5,TOC_REF(ofmsr)@l(%r5) + ld %r5,0(%r5) + mtsrr0 %r4 + mtsrr1 %r5 + LOAD_LR_NIA +1: + mflr %r5 + addi %r5, %r5, (2f-1b) + mtlr %r5 + li %r5, 0 + rfid +2: + RETURN_TO_NATIVE_ENDIAN +#else /* Finally, branch to OF */ mtctr %r4 bctrl +#endif /* Reload stack pointer, MSR, and reference PC from the OFW stack */ ld %r7,32(%r1) ld %r6,24(%r1) ld %r2,16(%r1) ld %r1,8(%r1) /* Get back to the MSR/PC we want, using the cached high bits of PC */ mtsrr1 %r6 clrrdi %r7,%r7,32 bl 1f 1: mflr %r8 or %r8,%r8,%r7 addi %r8,%r8,2f-1b mtsrr0 %r8 rfid /* Turn on MMU, exceptions, and 64-bit mode */ 2: /* Sign-extend the return value from OF */ extsw %r3,%r3 /* Restore all the non-volatile registers */ ld %r5,48(%r1) mtcr %r5 ld %r13,56(%r1) ld %r14,64(%r1) ld %r15,72(%r1) ld %r16,80(%r1) ld %r17,88(%r1) ld %r18,96(%r1) ld %r19,104(%r1) ld %r20,112(%r1) ld %r21,120(%r1) ld %r22,128(%r1) ld %r23,136(%r1) ld %r24,144(%r1) ld %r25,152(%r1) ld %r26,160(%r1) ld %r27,168(%r1) ld %r28,176(%r1) ld %r29,184(%r1) ld %r30,192(%r1) ld %r31,200(%r1) /* Restore the stack and link register */ ld %r1,0(%r1) ld %r0,16(%r1) mtlr %r0 blr /* * RTAS 32-bit Entry Point. Similar to the OF one, but simpler (no separate * stack) * * C prototype: int rtascall(void *callbuffer, void *rtas_privdat); */ ASENTRY_NOPROF(rtascall) mflr %r9 std %r9,16(%r1) stdu %r1,-208(%r1) /* * We need to save the following, because RTAS's register save/ * restore code assumes that the contents of registers are * at most 32 bits wide: lr, cr, r2, r13-r31, the old MSR. These * get placed in that order in the stack. */ mfcr %r5 std %r5,48(%r1) std %r13,56(%r1) std %r14,64(%r1) std %r15,72(%r1) std %r16,80(%r1) std %r17,88(%r1) std %r18,96(%r1) std %r19,104(%r1) std %r20,112(%r1) std %r21,120(%r1) std %r22,128(%r1) std %r23,136(%r1) std %r24,144(%r1) std %r25,152(%r1) std %r26,160(%r1) std %r27,168(%r1) std %r28,176(%r1) std %r29,184(%r1) std %r30,192(%r1) std %r31,200(%r1) /* Record the old MSR */ mfmsr %r6 /* Read RTAS entry and reg save area pointers */ addis %r5,%r2,TOC_REF(rtas_entry)@ha ld %r5,TOC_REF(rtas_entry)@l(%r5) ld %r5,0(%r5) addis %r8,%r2,TOC_REF(rtas_regsave)@ha ld %r8,TOC_REF(rtas_regsave)@l(%r8) /* * Set the MSR to the RTAS value. This has the side effect of disabling * exceptions, which is important for the next few steps. */ addis %r7,%r2,TOC_REF(rtasmsr)@ha ld %r7,TOC_REF(rtasmsr)@l(%r7) ld %r7,0(%r7) mtmsrd %r7 isync /* * Set up RTAS register save area, so that we can get back all of * our 64-bit pointers. Save our stack pointer, the TOC, and the MSR. * Put this in r1, since RTAS is obliged to save it. Kernel globals * are below 4 GB, so this is safe. */ mr %r7,%r1 mr %r1,%r8 std %r7,0(%r1) /* Save 64-bit stack pointer */ std %r2,8(%r1) /* Save TOC */ std %r6,16(%r1) /* Save MSR */ std %r9,24(%r1) /* Save reference PC for high 32 bits */ /* Finally, branch to RTAS */ mtctr %r5 bctrl /* * Reload stack pointer, MSR, reg PC from the reg save area in r1. We * are running in 32-bit mode at this point, so it doesn't matter if r1 * has become sign-extended. */ ld %r7,24(%r1) ld %r6,16(%r1) ld %r2,8(%r1) ld %r1,0(%r1) /* * Get back to the right PC. We need to atomically re-enable * exceptions, 64-bit mode, and the MMU. One thing that has likely * happened is that, if we were running in the high-memory direct * map, we no longer are as a result of LR truncation in RTAS. * Fix this by copying the high-order bits of the LR at function * entry onto the current PC and then jumping there while flipping * all the MSR bits. */ mtsrr1 %r6 clrrdi %r7,%r7,32 bl 1f 1: mflr %r8 or %r8,%r8,%r7 addi %r8,%r8,2f-1b mtsrr0 %r8 rfid /* Turn on MMU, exceptions, and 64-bit mode */ 2: /* Sign-extend the return value from RTAS */ extsw %r3,%r3 /* Restore all the non-volatile registers */ ld %r5,48(%r1) mtcr %r5 ld %r13,56(%r1) ld %r14,64(%r1) ld %r15,72(%r1) ld %r16,80(%r1) ld %r17,88(%r1) ld %r18,96(%r1) ld %r19,104(%r1) ld %r20,112(%r1) ld %r21,120(%r1) ld %r22,128(%r1) ld %r23,136(%r1) ld %r24,144(%r1) ld %r25,152(%r1) ld %r26,160(%r1) ld %r27,168(%r1) ld %r28,176(%r1) ld %r29,184(%r1) ld %r30,192(%r1) ld %r31,200(%r1) /* Restore the stack and link register */ ld %r1,0(%r1) ld %r0,16(%r1) mtlr %r0 blr Index: head/sys/powerpc/pseries/platform_chrp.c =================================================================== --- head/sys/powerpc/pseries/platform_chrp.c (revision 366032) +++ head/sys/powerpc/pseries/platform_chrp.c (revision 366033) @@ -1,594 +1,595 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2008 Marcel Moolenaar * Copyright (c) 2009 Nathan Whitehorn * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" #ifdef SMP extern void *ap_pcpu; #endif #ifdef __powerpc64__ static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */ #endif static vm_offset_t realmaxaddr = VM_MAX_ADDRESS; static int chrp_probe(platform_t); static int chrp_attach(platform_t); void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static vm_offset_t chrp_real_maxaddr(platform_t); static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref); static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref); static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref); static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref); static void chrp_smp_ap_init(platform_t); static int chrp_cpuref_init(void); #ifdef SMP static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu); static void chrp_smp_probe_threads(platform_t plat); static struct cpu_group *chrp_smp_topo(platform_t plat); #endif static void chrp_reset(platform_t); #ifdef __powerpc64__ #include "phyp-hvcall.h" static void phyp_cpu_idle(sbintime_t sbt); #endif static struct cpuref platform_cpuref[MAXCPU]; static int platform_cpuref_cnt; static int platform_cpuref_valid; static platform_method_t chrp_methods[] = { PLATFORMMETHOD(platform_probe, chrp_probe), PLATFORMMETHOD(platform_attach, chrp_attach), PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions), PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr), PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq), PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init), PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp), #ifdef SMP PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu), PLATFORMMETHOD(platform_smp_probe_threads, chrp_smp_probe_threads), PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo), #endif PLATFORMMETHOD(platform_reset, chrp_reset), { 0, 0 } }; static platform_def_t chrp_platform = { "chrp", chrp_methods, 0 }; PLATFORM_DEF(chrp_platform); static int chrp_probe(platform_t plat) { if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) return (BUS_PROBE_GENERIC); return (ENXIO); } static int chrp_attach(platform_t plat) { int quiesce; #ifdef __powerpc64__ int i; /* XXX: check for /rtas/ibm,hypertas-functions? */ if (!(mfmsr() & PSL_HV)) { struct mem_region *phys, *avail; int nphys, navail; vm_offset_t off; mem_regions(&phys, &nphys, &avail, &navail); realmaxaddr = 0; for (i = 0; i < nphys; i++) { off = phys[i].mr_start + phys[i].mr_size; realmaxaddr = MAX(off, realmaxaddr); } pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC); cpu_idle_hook = phyp_cpu_idle; /* Set up important VPA fields */ for (i = 0; i < MAXCPU; i++) { /* First two: VPA size */ splpar_vpa[i][4] = (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff); splpar_vpa[i][5] = (uint8_t)(sizeof(splpar_vpa[i]) & 0xff); splpar_vpa[i][0xba] = 1; /* Maintain FPRs */ splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */ splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */ splpar_vpa[i][0xfd] = 0xff; splpar_vpa[i][0xff] = 1; /* Maintain Altivec */ } mb(); /* Set up hypervisor CPU stuff */ chrp_smp_ap_init(plat); } #endif chrp_cpuref_init(); /* Some systems (e.g. QEMU) need Open Firmware to stand down */ quiesce = 1; TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce); if (quiesce) ofw_quiesce(); return (0); } static int parse_drconf_memory(struct mem_region *ofmem, int *msz, struct mem_region *ofavail, int *asz) { phandle_t phandle; vm_offset_t base; int i, idx, len, lasz, lmsz, res; uint32_t flags, lmb_size[2]; uint32_t *dmem; lmsz = *msz; lasz = *asz; phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory"); if (phandle == -1) /* No drconf node, return. */ return (0); res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size)); if (res == -1) return (0); printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20); /* Parse the /ibm,dynamic-memory. The first position gives the # of entries. The next two words reflect the address of the memory block. The next four words are the DRC index, reserved, list index and flags. (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory) #el Addr DRC-idx res list-idx flags ------------------------------------------------- | 4 | 8 | 4 | 4 | 4 | 4 |.... ------------------------------------------------- */ len = OF_getproplen(phandle, "ibm,dynamic-memory"); if (len > 0) { /* We have to use a variable length array on the stack since we have very limited stack space. */ cell_t arr[len/sizeof(cell_t)]; res = OF_getencprop(phandle, "ibm,dynamic-memory", arr, sizeof(arr)); if (res == -1) return (0); /* Number of elements */ idx = arr[0]; /* First address, in arr[1], arr[2]*/ dmem = &arr[1]; for (i = 0; i < idx; i++) { base = ((uint64_t)dmem[0] << 32) + dmem[1]; dmem += 4; flags = dmem[1]; /* Use region only if available and not reserved. */ if ((flags & 0x8) && !(flags & 0x80)) { ofmem[lmsz].mr_start = base; ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1]; ofavail[lasz].mr_start = base; ofavail[lasz].mr_size = (vm_size_t)lmb_size[1]; lmsz++; lasz++; } dmem += 2; } } *msz = lmsz; *asz = lasz; return (1); } void chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz) { vm_offset_t maxphysaddr; int i; ofw_mem_regions(phys, physsz, avail, availsz); parse_drconf_memory(phys, physsz, avail, availsz); /* * On some firmwares (SLOF), some memory may be marked available that * doesn't actually exist. This manifests as an extension of the last * available segment past the end of physical memory, so truncate that * one. */ maxphysaddr = 0; for (i = 0; i < *physsz; i++) if (phys[i].mr_start + phys[i].mr_size > maxphysaddr) maxphysaddr = phys[i].mr_start + phys[i].mr_size; for (i = 0; i < *availsz; i++) if (avail[i].mr_start + avail[i].mr_size > maxphysaddr) avail[i].mr_size = maxphysaddr - avail[i].mr_start; } static vm_offset_t chrp_real_maxaddr(platform_t plat) { return (realmaxaddr); } static u_long chrp_timebase_freq(platform_t plat, struct cpuref *cpuref) { phandle_t cpus, cpunode; int32_t ticks = -1; int res; char buf[8]; cpus = OF_finddevice("/cpus"); if (cpus == -1) panic("CPU tree not found on Open Firmware\n"); for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) { res = OF_getprop(cpunode, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) break; } if (cpunode <= 0) panic("CPU node not found on Open Firmware\n"); OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks)); if (ticks <= 0) panic("Unable to determine timebase frequency!"); return (ticks); } static int chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { if (platform_cpuref_valid == 0) return (EINVAL); cpuref->cr_cpuid = 0; cpuref->cr_hwref = platform_cpuref[0].cr_hwref; return (0); } static int chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { int id; if (platform_cpuref_valid == 0) return (EINVAL); id = cpuref->cr_cpuid + 1; if (id >= platform_cpuref_cnt) return (ENOENT); cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid; cpuref->cr_hwref = platform_cpuref[id].cr_hwref; return (0); } static int chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid; cpuref->cr_hwref = platform_cpuref[0].cr_hwref; return (0); } static void get_cpu_reg(phandle_t cpu, cell_t *reg) { int res; res = OF_getproplen(cpu, "reg"); if (res != sizeof(cell_t)) panic("Unexpected length for CPU property reg on Open Firmware\n"); OF_getencprop(cpu, "reg", reg, res); } static int chrp_cpuref_init(void) { phandle_t cpu, dev, chosen, pbsp; ihandle_t ibsp; char buf[32]; int a, bsp, res, res2, tmp_cpuref_cnt; static struct cpuref tmp_cpuref[MAXCPU]; cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg; if (platform_cpuref_valid) return (0); dev = OF_peer(0); dev = OF_child(dev); while (dev != 0) { res = OF_getprop(dev, "name", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpus") == 0) break; dev = OF_peer(dev); } /* Make sure that cpus reg property have 1 address cell and 0 size cells */ res = OF_getproplen(dev, "#address-cells"); res2 = OF_getproplen(dev, "#size-cells"); if (res != res2 || res != sizeof(cell_t)) panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n"); OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells)); OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells)); if (addr_cells != 1 || size_cells != 0) panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n"); /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */ chosen = OF_finddevice("/chosen"); if (chosen == -1) panic("Device /chosen not found on Open Firmware\n"); bsp_reg = -1; /* /chosen/cpu */ if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) { OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp)); - pbsp = OF_instance_to_package(ibsp); + pbsp = OF_instance_to_package(be32toh(ibsp)); if (pbsp != -1) get_cpu_reg(pbsp, &bsp_reg); } /* /chosen/fdtbootcpu */ if (bsp_reg == -1) { if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t)) OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg)); } if (bsp_reg == -1) panic("Boot CPU not found on Open Firmware\n"); bsp = -1; tmp_cpuref_cnt = 0; for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) { res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); if (res > 0 && strcmp(buf, "cpu") == 0) { res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s"); if (res > 0) { OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", interrupt_servers, res); get_cpu_reg(cpu, ®); if (reg == bsp_reg) bsp = tmp_cpuref_cnt; for (a = 0; a < res/sizeof(cell_t); a++) { tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a]; tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt; tmp_cpuref_cnt++; } } } } if (bsp == -1) panic("Boot CPU not found\n"); /* Map IDs, so BSP has CPUID 0 regardless of hwref */ for (a = bsp; a < tmp_cpuref_cnt; a++) { platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; platform_cpuref_cnt++; } for (a = 0; a < bsp; a++) { platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref; platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt; platform_cpuref_cnt++; } platform_cpuref_valid = 1; return (0); } #ifdef SMP static int chrp_smp_start_cpu(platform_t plat, struct pcpu *pc) { cell_t start_cpu; int result, err, timeout; if (!rtas_exists()) { printf("RTAS uninitialized: unable to start AP %d\n", pc->pc_cpuid); return (ENXIO); } start_cpu = rtas_token_lookup("start-cpu"); if (start_cpu == -1) { printf("RTAS unknown method: unable to start AP %d\n", pc->pc_cpuid); return (ENXIO); } ap_pcpu = pc; powerpc_sync(); result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc, &err); if (result < 0 || err != 0) { printf("RTAS error (%d/%d): unable to start AP %d\n", result, err, pc->pc_cpuid); return (ENXIO); } timeout = 10000; while (!pc->pc_awake && timeout--) DELAY(100); return ((pc->pc_awake) ? 0 : EBUSY); } static void chrp_smp_probe_threads(platform_t plat) { struct pcpu *pc, *last_pc; int i, ncores; ncores = 0; last_pc = NULL; for (i = 0; i <= mp_maxid; i++) { pc = pcpu_find(i); if (pc == NULL) continue; if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) ncores++; last_pc = pc; } mp_ncores = ncores; if (mp_ncpus % ncores == 0) smp_threads_per_core = mp_ncpus / ncores; } static struct cpu_group * chrp_smp_topo(platform_t plat) { if (mp_ncpus % mp_ncores != 0) { printf("WARNING: Irregular SMP topology. Performance may be " "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores); return (smp_topo_none()); } /* Don't do anything fancier for non-threaded SMP */ if (mp_ncpus == mp_ncores) return (smp_topo_none()); return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core, CG_FLAG_SMT)); } #endif static void chrp_reset(platform_t platform) { OF_reboot(); } #ifdef __powerpc64__ static void phyp_cpu_idle(sbintime_t sbt) { register_t msr; msr = mfmsr(); mtmsr(msr & ~PSL_EE); if (sched_runnable()) { mtmsr(msr); return; } phyp_hcall(H_CEDE); /* Re-enables interrupts internally */ mtmsr(msr); } static void chrp_smp_ap_init(platform_t platform) { if (!(mfmsr() & PSL_HV)) { /* Register VPA */ phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref), splpar_vpa[PCPU_GET(hwref)]); /* Set interrupt priority */ phyp_hcall(H_CPPR, 0xff); } } #else static void chrp_smp_ap_init(platform_t platform) { } #endif