Path: blob/main/sys/powerpc/pseries/platform_chrp.c
39507 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2008 Marcel Moolenaar4* Copyright (c) 2009 Nathan Whitehorn5* All rights reserved.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10*11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR18* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES19* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.20* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,21* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT22* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*/2829#include <sys/cdefs.h>30#include <sys/endian.h>31#include <sys/param.h>32#include <sys/systm.h>33#include <sys/kernel.h>34#include <sys/bus.h>35#include <sys/pcpu.h>36#include <sys/proc.h>37#include <sys/sched.h>38#include <sys/smp.h>39#include <vm/vm.h>40#include <vm/pmap.h>4142#include <machine/bus.h>43#include <machine/cpu.h>44#include <machine/hid.h>45#include <machine/platformvar.h>46#include <machine/rtas.h>47#include <machine/smp.h>48#include <machine/spr.h>49#include <machine/trap.h>5051#include <dev/ofw/openfirm.h>52#include <machine/ofw_machdep.h>5354#include "platform_if.h"5556#ifdef SMP57extern void *ap_pcpu;58#endif5960#ifdef __powerpc64__61static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */62#endif6364static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;6566static int chrp_probe(platform_t);67static int chrp_attach(platform_t);68void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,69struct mem_region *avail, int *availsz);70static vm_offset_t chrp_real_maxaddr(platform_t);71static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);72static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);73static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);74static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);75static void chrp_smp_ap_init(platform_t);76static int chrp_cpuref_init(void);77#ifdef SMP78static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);79static void chrp_smp_probe_threads(platform_t plat);80static struct cpu_group *chrp_smp_topo(platform_t plat);81#endif82static void chrp_reset(platform_t);83#ifdef __powerpc64__84#include "phyp-hvcall.h"85static void phyp_cpu_idle(sbintime_t sbt);86#endif8788static struct cpuref platform_cpuref[MAXCPU];89static int platform_cpuref_cnt;90static int platform_cpuref_valid;9192static platform_method_t chrp_methods[] = {93PLATFORMMETHOD(platform_probe, chrp_probe),94PLATFORMMETHOD(platform_attach, chrp_attach),95PLATFORMMETHOD(platform_mem_regions, chrp_mem_regions),96PLATFORMMETHOD(platform_real_maxaddr, chrp_real_maxaddr),97PLATFORMMETHOD(platform_timebase_freq, chrp_timebase_freq),9899PLATFORMMETHOD(platform_smp_ap_init, chrp_smp_ap_init),100PLATFORMMETHOD(platform_smp_first_cpu, chrp_smp_first_cpu),101PLATFORMMETHOD(platform_smp_next_cpu, chrp_smp_next_cpu),102PLATFORMMETHOD(platform_smp_get_bsp, chrp_smp_get_bsp),103#ifdef SMP104PLATFORMMETHOD(platform_smp_start_cpu, chrp_smp_start_cpu),105PLATFORMMETHOD(platform_smp_probe_threads, chrp_smp_probe_threads),106PLATFORMMETHOD(platform_smp_topo, chrp_smp_topo),107#endif108109PLATFORMMETHOD(platform_reset, chrp_reset),110{ 0, 0 }111};112113static platform_def_t chrp_platform = {114"chrp",115chrp_methods,1160117};118119PLATFORM_DEF(chrp_platform);120121static int122chrp_probe(platform_t plat)123{124if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)125return (BUS_PROBE_GENERIC);126127return (ENXIO);128}129130static int131chrp_attach(platform_t plat)132{133int quiesce;134#ifdef __powerpc64__135int i;136#if BYTE_ORDER == LITTLE_ENDIAN137int result;138#endif139140/* XXX: check for /rtas/ibm,hypertas-functions? */141if (!(mfmsr() & PSL_HV)) {142struct mem_region *phys, *avail;143int nphys, navail;144vm_offset_t off;145146mem_regions(&phys, &nphys, &avail, &navail);147148realmaxaddr = 0;149for (i = 0; i < nphys; i++) {150off = phys[i].mr_start + phys[i].mr_size;151realmaxaddr = MAX(off, realmaxaddr);152}153154if (!radix_mmu)155pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);156cpu_idle_hook = phyp_cpu_idle;157158/* Set up important VPA fields */159for (i = 0; i < MAXCPU; i++) {160/* First two: VPA size */161splpar_vpa[i][4] =162(uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);163splpar_vpa[i][5] =164(uint8_t)(sizeof(splpar_vpa[i]) & 0xff);165splpar_vpa[i][0xba] = 1; /* Maintain FPRs */166splpar_vpa[i][0xbb] = 1; /* Maintain PMCs */167splpar_vpa[i][0xfc] = 0xff; /* Maintain full SLB */168splpar_vpa[i][0xfd] = 0xff;169splpar_vpa[i][0xff] = 1; /* Maintain Altivec */170}171mb();172173/* Set up hypervisor CPU stuff */174chrp_smp_ap_init(plat);175176#if BYTE_ORDER == LITTLE_ENDIAN177/*178* Ask the hypervisor to update the LPAR ILE bit.179*180* This involves all processors reentering the hypervisor181* so the change appears simultaneously in all processors.182* This can take a long time.183*/184for(;;) {185result = phyp_hcall(H_SET_MODE, 1UL,186H_SET_MODE_RSRC_ILE, 0, 0);187if (result == H_SUCCESS)188break;189DELAY(1000);190}191#endif192193}194#endif195chrp_cpuref_init();196197/* Some systems (e.g. QEMU) need Open Firmware to stand down */198quiesce = 1;199TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);200if (quiesce)201ofw_quiesce();202203return (0);204}205206static int207parse_drconf_memory(struct mem_region *ofmem, int *msz,208struct mem_region *ofavail, int *asz)209{210phandle_t phandle;211vm_offset_t base;212int i, idx, len, lasz, lmsz, res;213uint32_t flags, lmb_size[2];214uint32_t *dmem;215216lmsz = *msz;217lasz = *asz;218219phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");220if (phandle == -1)221/* No drconf node, return. */222return (0);223224res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,225sizeof(lmb_size));226if (res == -1)227return (0);228printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);229230/* Parse the /ibm,dynamic-memory.231The first position gives the # of entries. The next two words232reflect the address of the memory block. The next four words are233the DRC index, reserved, list index and flags.234(see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)235236#el Addr DRC-idx res list-idx flags237-------------------------------------------------238| 4 | 8 | 4 | 4 | 4 | 4 |....239-------------------------------------------------240*/241242len = OF_getproplen(phandle, "ibm,dynamic-memory");243if (len > 0) {244/* We have to use a variable length array on the stack245since we have very limited stack space.246*/247cell_t arr[len/sizeof(cell_t)];248249res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,250sizeof(arr));251if (res == -1)252return (0);253254/* Number of elements */255idx = arr[0];256257/* First address, in arr[1], arr[2]*/258dmem = &arr[1];259260for (i = 0; i < idx; i++) {261base = ((uint64_t)dmem[0] << 32) + dmem[1];262dmem += 4;263flags = dmem[1];264/* Use region only if available and not reserved. */265if ((flags & 0x8) && !(flags & 0x80)) {266ofmem[lmsz].mr_start = base;267ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];268ofavail[lasz].mr_start = base;269ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];270lmsz++;271lasz++;272}273dmem += 2;274}275}276277*msz = lmsz;278*asz = lasz;279280return (1);281}282283void284chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,285struct mem_region *avail, int *availsz)286{287vm_offset_t maxphysaddr;288int i;289290ofw_mem_regions(phys, physsz, avail, availsz);291parse_drconf_memory(phys, physsz, avail, availsz);292293/*294* On some firmwares (SLOF), some memory may be marked available that295* doesn't actually exist. This manifests as an extension of the last296* available segment past the end of physical memory, so truncate that297* one.298*/299maxphysaddr = 0;300for (i = 0; i < *physsz; i++)301if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)302maxphysaddr = phys[i].mr_start + phys[i].mr_size;303304for (i = 0; i < *availsz; i++)305if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)306avail[i].mr_size = maxphysaddr - avail[i].mr_start;307}308309static vm_offset_t310chrp_real_maxaddr(platform_t plat)311{312return (realmaxaddr);313}314315static u_long316chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)317{318phandle_t cpus, cpunode;319int32_t ticks = -1;320int res;321char buf[8];322323cpus = OF_finddevice("/cpus");324if (cpus == -1)325panic("CPU tree not found on Open Firmware\n");326327for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {328res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));329if (res > 0 && strcmp(buf, "cpu") == 0)330break;331}332if (cpunode <= 0)333panic("CPU node not found on Open Firmware\n");334335OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));336337if (ticks <= 0)338panic("Unable to determine timebase frequency!");339340return (ticks);341}342343static int344chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)345{346347if (platform_cpuref_valid == 0)348return (EINVAL);349350cpuref->cr_cpuid = 0;351cpuref->cr_hwref = platform_cpuref[0].cr_hwref;352353return (0);354}355356static int357chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)358{359int id;360361if (platform_cpuref_valid == 0)362return (EINVAL);363364id = cpuref->cr_cpuid + 1;365if (id >= platform_cpuref_cnt)366return (ENOENT);367368cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;369cpuref->cr_hwref = platform_cpuref[id].cr_hwref;370371return (0);372}373374static int375chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)376{377378cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;379cpuref->cr_hwref = platform_cpuref[0].cr_hwref;380return (0);381}382383static void384get_cpu_reg(phandle_t cpu, cell_t *reg)385{386int res;387388res = OF_getproplen(cpu, "reg");389if (res != sizeof(cell_t))390panic("Unexpected length for CPU property reg on Open Firmware\n");391OF_getencprop(cpu, "reg", reg, res);392}393394static int395chrp_cpuref_init(void)396{397phandle_t cpu, dev, chosen, pbsp;398ihandle_t ibsp;399char buf[32];400int a, bsp, res, res2, tmp_cpuref_cnt;401static struct cpuref tmp_cpuref[MAXCPU];402cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;403404if (platform_cpuref_valid)405return (0);406407dev = OF_peer(0);408dev = OF_child(dev);409while (dev != 0) {410res = OF_getprop(dev, "name", buf, sizeof(buf));411if (res > 0 && strcmp(buf, "cpus") == 0)412break;413dev = OF_peer(dev);414}415416/* Make sure that cpus reg property have 1 address cell and 0 size cells */417res = OF_getproplen(dev, "#address-cells");418res2 = OF_getproplen(dev, "#size-cells");419if (res != res2 || res != sizeof(cell_t))420panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");421OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));422OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));423if (addr_cells != 1 || size_cells != 0)424panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");425426/* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */427428chosen = OF_finddevice("/chosen");429if (chosen == -1)430panic("Device /chosen not found on Open Firmware\n");431432bsp_reg = -1;433434/* /chosen/cpu */435if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {436OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));437pbsp = OF_instance_to_package(be32toh(ibsp));438if (pbsp != -1)439get_cpu_reg(pbsp, &bsp_reg);440}441442/* /chosen/fdtbootcpu */443if (bsp_reg == -1) {444if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))445OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));446}447448if (bsp_reg == -1)449panic("Boot CPU not found on Open Firmware\n");450451bsp = -1;452tmp_cpuref_cnt = 0;453for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {454res = OF_getprop(cpu, "device_type", buf, sizeof(buf));455if (res > 0 && strcmp(buf, "cpu") == 0) {456res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");457if (res > 0) {458OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",459interrupt_servers, res);460461get_cpu_reg(cpu, ®);462if (reg == bsp_reg)463bsp = tmp_cpuref_cnt;464465for (a = 0; a < res/sizeof(cell_t); a++) {466tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];467tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;468tmp_cpuref_cnt++;469}470}471}472}473474if (bsp == -1)475panic("Boot CPU not found\n");476477/* Map IDs, so BSP has CPUID 0 regardless of hwref */478for (a = bsp; a < tmp_cpuref_cnt; a++) {479platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;480platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;481platform_cpuref_cnt++;482}483for (a = 0; a < bsp; a++) {484platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;485platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;486platform_cpuref_cnt++;487}488489platform_cpuref_valid = 1;490491return (0);492}493494#ifdef SMP495static int496chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)497{498cell_t start_cpu;499int result, err, timeout;500501if (!rtas_exists()) {502printf("RTAS uninitialized: unable to start AP %d\n",503pc->pc_cpuid);504return (ENXIO);505}506507start_cpu = rtas_token_lookup("start-cpu");508if (start_cpu == -1) {509printf("RTAS unknown method: unable to start AP %d\n",510pc->pc_cpuid);511return (ENXIO);512}513514ap_pcpu = pc;515powerpc_sync();516517result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,518&err);519if (result < 0 || err != 0) {520printf("RTAS error (%d/%d): unable to start AP %d\n",521result, err, pc->pc_cpuid);522return (ENXIO);523}524525timeout = 10000;526while (!pc->pc_awake && timeout--)527DELAY(100);528529return ((pc->pc_awake) ? 0 : EBUSY);530}531532static void533chrp_smp_probe_threads(platform_t plat)534{535struct pcpu *pc, *last_pc;536int i, ncores;537538ncores = 0;539last_pc = NULL;540for (i = 0; i <= mp_maxid; i++) {541pc = pcpu_find(i);542if (pc == NULL)543continue;544if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)545ncores++;546last_pc = pc;547}548549mp_ncores = ncores;550if (mp_ncpus % ncores == 0)551smp_threads_per_core = mp_ncpus / ncores;552}553554static struct cpu_group *555chrp_smp_topo(platform_t plat)556{557558if (mp_ncpus % mp_ncores != 0) {559printf("WARNING: Irregular SMP topology. Performance may be "560"suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);561return (smp_topo_none());562}563564/* Don't do anything fancier for non-threaded SMP */565if (mp_ncpus == mp_ncores)566return (smp_topo_none());567568return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,569CG_FLAG_SMT));570}571#endif572573static void574chrp_reset(platform_t platform)575{576OF_reboot();577}578579#ifdef __powerpc64__580static void581phyp_cpu_idle(sbintime_t sbt)582{583register_t msr;584585msr = mfmsr();586587mtmsr(msr & ~PSL_EE);588if (sched_runnable()) {589mtmsr(msr);590return;591}592593phyp_hcall(H_CEDE); /* Re-enables interrupts internally */594mtmsr(msr);595}596597static void598chrp_smp_ap_init(platform_t platform)599{600if (!(mfmsr() & PSL_HV)) {601/* Register VPA */602phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),603splpar_vpa[PCPU_GET(hwref)]);604605/* Set interrupt priority */606phyp_hcall(H_CPPR, 0xff);607}608}609#else610static void611chrp_smp_ap_init(platform_t platform)612{613}614#endif615616617