11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51708Sstevel * Common Development and Distribution License (the "License").
61708Sstevel * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
211708Sstevel
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel
271708Sstevel /*
281708Sstevel * CPU management for serengeti DR
291708Sstevel *
301708Sstevel * There are three states a CPU can be in:
311708Sstevel *
321708Sstevel * disconnected: In reset
331708Sstevel * connect,unconfigured: Idling in OBP's idle loop
341708Sstevel * configured: Running Solaris
351708Sstevel *
361708Sstevel * State transitions:
371708Sstevel *
381708Sstevel * connect configure
391708Sstevel * ------------> ------------>
401708Sstevel * disconnected connected configured
411708Sstevel * unconfigured
421708Sstevel * <----------- <-------------
431708Sstevel * disconnect unconfigure
441708Sstevel *
451708Sstevel * Firmware involvements
461708Sstevel *
471708Sstevel * start_cpu(SC)
481708Sstevel * prom_serengeti_wakeupcpu(OBP)
491708Sstevel * ------------> ------------------------->
501708Sstevel * disconnected connected configured
511708Sstevel * unconfigured
521708Sstevel * <----------- <-------------------------
531708Sstevel * prom_serengeti_cpu_off(OBP) prom_serengeti_cpu_off(OBP)
541708Sstevel * stop_cpu(SC) prom_serengeti_wakeupcpu(OBP)
551708Sstevel *
561708Sstevel * SIR (Software Initiated Reset) is used to unconfigure a CPU.
571708Sstevel * After the CPU has completed flushing the caches, it issues an
581708Sstevel * sir instruction to put itself through POST. POST detects that
591708Sstevel * it is an SIR, and re-enters OBP as a slave. When the operation
601708Sstevel * completes successfully, the CPU will be idling in OBP.
611708Sstevel */
621708Sstevel
631708Sstevel #include <sys/obpdefs.h>
641708Sstevel #include <sys/types.h>
651708Sstevel #include <sys/cmn_err.h>
661708Sstevel #include <sys/cpuvar.h>
671708Sstevel #include <sys/membar.h>
681708Sstevel #include <sys/x_call.h>
691708Sstevel #include <sys/machsystm.h>
701708Sstevel #include <sys/cpu_sgnblk_defs.h>
711708Sstevel #include <sys/pte.h>
721708Sstevel #include <vm/hat_sfmmu.h>
731708Sstevel #include <sys/promif.h>
741708Sstevel #include <sys/note.h>
751708Sstevel #include <sys/vmsystm.h>
761708Sstevel #include <vm/seg_kmem.h>
771708Sstevel
781708Sstevel #include <sys/sbd_ioctl.h>
791708Sstevel #include <sys/sbd.h>
801708Sstevel #include <sys/sbdp_priv.h>
811708Sstevel #include <sys/sbdp_mem.h>
821708Sstevel #include <sys/sbdp_error.h>
831708Sstevel #include <sys/sgsbbc_iosram.h>
841708Sstevel #include <sys/prom_plat.h>
851708Sstevel #include <sys/cheetahregs.h>
861708Sstevel
871708Sstevel uint64_t *sbdp_valp;
881708Sstevel extern uint64_t va_to_pa(void *);
891708Sstevel static int sbdp_cpu_ntries = 50000;
901708Sstevel static int sbdp_cpu_delay = 100;
911708Sstevel void sbdp_get_cpu_sram_addr(uint64_t, uint64_t);
921708Sstevel static int cpusram_map(caddr_t *, pgcnt_t *);
931708Sstevel static void cpusram_unmap(caddr_t *, pgcnt_t);
941708Sstevel extern int prom_serengeti_wakeupcpu(pnode_t);
951708Sstevel extern int prom_serengeti_cpu_off(pnode_t);
961708Sstevel extern sbdp_wnode_t *sbdp_get_wnodep(int);
971708Sstevel extern caddr_t sbdp_shutdown_va;
981708Sstevel static int sbdp_prom_get_cpu(void *arg, int changed);
991708Sstevel
1001708Sstevel
1011708Sstevel int
sbdp_disconnect_cpu(sbdp_handle_t * hp,dev_info_t * dip,processorid_t cpuid)1021708Sstevel sbdp_disconnect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
1031708Sstevel {
1041708Sstevel pnode_t nodeid;
1051708Sstevel int bd, wnode;
1061708Sstevel sbdp_wnode_t *wnodep;
1071708Sstevel sbdp_bd_t *bdp = NULL;
1081708Sstevel int rv = 0;
1091708Sstevel processorid_t cpu = cpuid;
1101708Sstevel processorid_t portid;
1111708Sstevel static fn_t f = "sbdp_disconnect_cpu";
1121708Sstevel
1131708Sstevel SBDP_DBG_FUNC("%s\n", f);
1141708Sstevel
1151708Sstevel nodeid = ddi_get_nodeid(dip);
1161708Sstevel
1171708Sstevel /*
1181708Sstevel * Get board number and node number
1191708Sstevel * The check for determining if nodeid is valid is done inside
1201708Sstevel * sbdp_get_bd_and_wnode_num.
1211708Sstevel */
1221708Sstevel if (SBDP_INJECT_ERROR(f, 0) ||
1231708Sstevel sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
1241708Sstevel
1251708Sstevel rv = -1;
1261708Sstevel goto out;
1271708Sstevel }
1281708Sstevel
1291708Sstevel /*
1301708Sstevel * Grab the lock to prevent status threads from accessing
1311708Sstevel * registers on the CPU when it is being put into reset.
1321708Sstevel */
1331708Sstevel wnodep = sbdp_get_wnodep(wnode);
1341708Sstevel bdp = &wnodep->bds[bd];
1351708Sstevel ASSERT(bdp);
1361708Sstevel mutex_enter(&bdp->bd_mutex);
1371708Sstevel
1381708Sstevel /*
1391708Sstevel * Mark the CPU in reset. This should be done before calling
1401708Sstevel * the SC because we won't know at which stage it failed if
1411708Sstevel * the SC call returns failure.
1421708Sstevel */
1431708Sstevel sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 1);
1441708Sstevel
1451708Sstevel /*
1461708Sstevel * Ask OBP to mark the CPU as in POST
1471708Sstevel */
1481708Sstevel if (SBDP_INJECT_ERROR(f, 1) || prom_serengeti_cpu_off(nodeid) != 0) {
1491708Sstevel
1501708Sstevel rv = -1;
1511708Sstevel goto out;
1521708Sstevel }
1531708Sstevel
1541708Sstevel /*
1551708Sstevel * Ask the SC to put the CPU into reset. If the first
1561708Sstevel * core is not present, the stop CPU interface needs
1571708Sstevel * to be called with the portid rather than the cpuid.
1581708Sstevel */
1591708Sstevel portid = SG_CPUID_TO_PORTID(cpuid);
1601708Sstevel if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
1611708Sstevel cpu = portid;
1621708Sstevel }
1631708Sstevel
1641708Sstevel if (SBDP_INJECT_ERROR(f, 2) || sbdp_stop_cpu(cpu) != 0) {
1651708Sstevel
1661708Sstevel rv = -1;
1671708Sstevel goto out;
1681708Sstevel }
1691708Sstevel
1701708Sstevel out:
1711708Sstevel if (bdp != NULL) {
1721708Sstevel mutex_exit(&bdp->bd_mutex);
1731708Sstevel }
1741708Sstevel
1751708Sstevel if (rv != 0) {
1761708Sstevel sbdp_set_err(hp->h_err, ESGT_STOPCPU, NULL);
1771708Sstevel }
1781708Sstevel
1791708Sstevel return (rv);
1801708Sstevel }
1811708Sstevel
1821708Sstevel int
sbdp_connect_cpu(sbdp_handle_t * hp,dev_info_t * dip,processorid_t cpuid)1831708Sstevel sbdp_connect_cpu(sbdp_handle_t *hp, dev_info_t *dip, processorid_t cpuid)
1841708Sstevel {
1851708Sstevel pnode_t nodeid;
1861708Sstevel sbd_error_t *sep;
1871708Sstevel int i;
1881708Sstevel int bd, wnode;
1891708Sstevel int rv = 0;
1901708Sstevel static fn_t f = "sbdp_connect_cpu";
1911708Sstevel
1921708Sstevel SBDP_DBG_FUNC("%s\n", f);
1931708Sstevel
1941708Sstevel sep = hp->h_err;
1951708Sstevel
1961708Sstevel nodeid = ddi_get_nodeid(dip);
1971708Sstevel
1981708Sstevel /*
1991708Sstevel * The check for determining if nodeid is valid is done inside
2001708Sstevel * sbdp_get_bd_and_wnode_num.
2011708Sstevel */
2021708Sstevel if (SBDP_INJECT_ERROR(f, 0) ||
2031708Sstevel sbdp_get_bd_and_wnode_num(nodeid, &bd, &wnode) != 0) {
2041708Sstevel
2051708Sstevel rv = -1;
2061708Sstevel goto out;
2071708Sstevel }
2081708Sstevel
2091708Sstevel /*
2101708Sstevel * Ask the SC to bring the CPU out of reset.
2111708Sstevel * At this point, the sb_dev_present bit is not set for the CPU.
2121708Sstevel * From sbd point of view the CPU is not present yet. No
2131708Sstevel * status threads will try to read registers off the CPU.
2141708Sstevel * Since we are already holding sb_mutex, it is not necessary
2151708Sstevel * to grab the board mutex when checking and setting the
2161708Sstevel * cpus_in_reset bit.
2171708Sstevel */
2181708Sstevel if (sbdp_is_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid))) {
2191708Sstevel
2201708Sstevel sbdp_wnode_t *wnodep;
2211708Sstevel sbdp_bd_t *bdp = NULL;
2221708Sstevel processorid_t cpu = cpuid;
2231708Sstevel processorid_t portid;
2241708Sstevel
2251708Sstevel wnodep = sbdp_get_wnodep(wnode);
2261708Sstevel bdp = &wnodep->bds[bd];
2271708Sstevel ASSERT(bdp);
2281708Sstevel
2291708Sstevel /*
2301708Sstevel * If the first core is not present, the start CPU
2311708Sstevel * interface needs to be called with the portid rather
2321708Sstevel * than the cpuid.
2331708Sstevel */
2341708Sstevel portid = SG_CPUID_TO_PORTID(cpuid);
2351708Sstevel if (!SBDP_IS_CPU_PRESENT(bdp, SG_CPUID_TO_CPU_UNIT(portid))) {
2361708Sstevel cpu = portid;
2371708Sstevel }
2381708Sstevel
2391708Sstevel if (SBDP_INJECT_ERROR(f, 1) || sbdp_start_cpu(cpu) != 0) {
2401708Sstevel
2411708Sstevel rv = -1;
2421708Sstevel goto out;
2431708Sstevel }
2441708Sstevel
2451708Sstevel if (SBDP_INJECT_ERROR(f, 2) ||
2461708Sstevel prom_serengeti_wakeupcpu(nodeid) != 0) {
2471708Sstevel
2481708Sstevel rv = -1;
2491708Sstevel goto out;
2501708Sstevel }
2511708Sstevel }
2521708Sstevel
2531708Sstevel /*
2541708Sstevel * Mark the CPU out of reset.
2551708Sstevel */
2561708Sstevel sbdp_cpu_in_reset(wnode, bd, SG_CPUID_TO_CPU_UNIT(cpuid), 0);
2571708Sstevel
2581708Sstevel /*
2591708Sstevel * Refresh the bd info
2601708Sstevel * we need to wait until all cpus are out of reset
2611708Sstevel */
2621708Sstevel for (i = 0; i < SG_MAX_CPUS_PER_BD; i++)
2631708Sstevel if (sbdp_is_cpu_present(wnode, bd, i) &&
2641708Sstevel sbdp_is_cpu_in_reset(wnode, bd, i) == 1) {
2651708Sstevel break;
2661708Sstevel }
2671708Sstevel
2681708Sstevel if (i == SG_MAX_CPUS_PER_BD) {
2691708Sstevel /*
2701708Sstevel * All cpus are out of reset so it is safe to
2711708Sstevel * update the bd info
2721708Sstevel */
2731708Sstevel sbdp_add_new_bd_info(wnode, bd);
2741708Sstevel }
2751708Sstevel
2761708Sstevel out:
2771708Sstevel if (rv != 0)
2781708Sstevel sbdp_set_err(sep, ESGT_WAKEUPCPU, NULL);
2791708Sstevel
2801708Sstevel return (rv);
2811708Sstevel }
2821708Sstevel
2831708Sstevel int
sbdp_cpu_poweron(struct cpu * cp)2841708Sstevel sbdp_cpu_poweron(struct cpu *cp)
2851708Sstevel {
2861708Sstevel int cpuid;
2871708Sstevel int ntries;
2881708Sstevel pnode_t nodeid;
2891708Sstevel extern void restart_other_cpu(int);
2901708Sstevel static fn_t f = "sbdp_cpu_poweron";
2911708Sstevel
2921708Sstevel SBDP_DBG_FUNC("%s\n", f);
2931708Sstevel
2941708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
2951708Sstevel
2961708Sstevel ntries = sbdp_cpu_ntries;
2971708Sstevel cpuid = cp->cpu_id;
2981708Sstevel
2991708Sstevel nodeid = cpunodes[cpuid].nodeid;
3001708Sstevel ASSERT(nodeid != (pnode_t)0);
3011708Sstevel
3021708Sstevel /*
3031708Sstevel * This is a safe guard in case the CPU has taken a trap
3041708Sstevel * and idling in POST.
3051708Sstevel */
3061708Sstevel if (SBDP_INJECT_ERROR(f, 0) ||
3071708Sstevel prom_serengeti_wakeupcpu(nodeid) != 0) {
3081708Sstevel
3091708Sstevel return (EBUSY);
3101708Sstevel }
3111708Sstevel
3121708Sstevel cp->cpu_flags &= ~CPU_POWEROFF;
3131708Sstevel
3141708Sstevel /*
3151708Sstevel * NOTE: restart_other_cpu pauses cpus during the
3161708Sstevel * slave cpu start. This helps to quiesce the
3171708Sstevel * bus traffic a bit which makes the tick sync
3181708Sstevel * routine in the prom more robust.
3191708Sstevel */
3201708Sstevel SBDP_DBG_CPU("%s: COLD START for cpu (%d)\n", f, cpuid);
3211708Sstevel
3221708Sstevel restart_other_cpu(cpuid);
3231708Sstevel
3241708Sstevel SBDP_DBG_CPU("after restarting other cpus\n");
3251708Sstevel
3261708Sstevel /*
3271708Sstevel * Wait for the cpu to reach its idle thread before
3281708Sstevel * we zap him with a request to blow away the mappings
3291708Sstevel * he (might) have for the sbdp_shutdown_asm code
3301708Sstevel * he may have executed on unconfigure.
3311708Sstevel */
3321708Sstevel while ((cp->cpu_thread != cp->cpu_idle_thread) && (ntries > 0)) {
3331708Sstevel DELAY(sbdp_cpu_delay);
3341708Sstevel ntries--;
3351708Sstevel }
3361708Sstevel
3371708Sstevel SBDP_DBG_CPU("%s: waited %d out of %d loops for cpu %d\n",
3381708Sstevel f, sbdp_cpu_ntries - ntries, sbdp_cpu_ntries, cpuid);
3391708Sstevel
3401708Sstevel return (0);
3411708Sstevel }
3421708Sstevel
3431708Sstevel
3441708Sstevel #define SBDP_CPU_SRAM_ADDR 0x7fff0900000ull
3451708Sstevel #define SBDP_CPU_SRAM_SIZE 0x20000ull
3461708Sstevel
3471708Sstevel static const char cpyren_key[] = "COPYREN";
3481708Sstevel
3491708Sstevel static uint64_t bbsram_pa;
3501708Sstevel static uint_t bbsram_size;
3511708Sstevel
3521708Sstevel typedef struct {
3531708Sstevel caddr_t vaddr;
3541708Sstevel pgcnt_t npages;
3551708Sstevel uint64_t *pa;
3561708Sstevel uint_t *size;
3571708Sstevel } sbdp_cpu_sram_map_t;
3581708Sstevel
3591708Sstevel int
sbdp_cpu_poweroff(struct cpu * cp)3601708Sstevel sbdp_cpu_poweroff(struct cpu *cp)
3611708Sstevel {
3621708Sstevel processorid_t cpuid;
3631708Sstevel static void sbdp_cpu_shutdown_self(void);
3641708Sstevel pnode_t nodeid;
3651708Sstevel sbdp_cpu_sram_map_t map;
3661708Sstevel static fn_t f = "sbdp_cpu_poweroff";
3671708Sstevel
3681708Sstevel SBDP_DBG_FUNC("%s\n", f);
3691708Sstevel
3701708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
3711708Sstevel
3721708Sstevel /*
3731708Sstevel * Capture all CPUs (except for detaching proc) to prevent
3741708Sstevel * crosscalls to the detaching proc until it has cleared its
3751708Sstevel * bit in cpu_ready_set.
3761708Sstevel */
3771708Sstevel cpuid = cp->cpu_id;
3781708Sstevel
3791708Sstevel nodeid = cpunodes[cpuid].nodeid;
3801708Sstevel ASSERT(nodeid != (pnode_t)0);
3811708Sstevel
3821708Sstevel *sbdp_valp = 0ull;
3831708Sstevel /*
3841708Sstevel * Do the cpu sram mapping now. This avoids problems with
3851708Sstevel * mutexes and high PILS
3861708Sstevel */
3871708Sstevel if (SBDP_INJECT_ERROR(f, 0) ||
3881708Sstevel cpusram_map(&map.vaddr, &map.npages) != DDI_SUCCESS) {
3891708Sstevel return (EBUSY);
3901708Sstevel }
3911708Sstevel
3921708Sstevel map.pa = &bbsram_pa;
3931708Sstevel map.size = &bbsram_size;
3941708Sstevel
3951708Sstevel /*
3961708Sstevel * Do a cross call to the cpu so it obtains the base address
3971708Sstevel */
3981708Sstevel xc_one(cpuid, sbdp_get_cpu_sram_addr, (uint64_t)&map,
3991708Sstevel (uint64_t)NULL);
4001708Sstevel
4011708Sstevel cpusram_unmap(&map.vaddr, map.npages);
4021708Sstevel
4031708Sstevel if (SBDP_INJECT_ERROR(f, 1) || bbsram_size == 0) {
4041708Sstevel cmn_err(CE_WARN, "cpu%d: Key \"%s\" missing from CPU SRAM TOC",
4051708Sstevel cpuid, cpyren_key);
4061708Sstevel return (EBUSY);
4071708Sstevel }
4081708Sstevel
4091708Sstevel if ((bbsram_pa & MMU_PAGEOFFSET) != 0) {
4101708Sstevel cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" not page aligned, "
4111708Sstevel "offset = 0x%lx", cpuid, cpyren_key,
412*11311SSurya.Prakki@Sun.COM (bbsram_pa - (uint64_t)SBDP_CPU_SRAM_ADDR));
4131708Sstevel return (EBUSY);
4141708Sstevel }
4151708Sstevel
4161708Sstevel if (bbsram_size < MMU_PAGESIZE) {
4171708Sstevel cmn_err(CE_WARN, "cpu%d: CPU SRAM key \"%s\" too small, "
4181708Sstevel "size = 0x%x", cpuid, cpyren_key, bbsram_size);
4191708Sstevel return (EBUSY);
4201708Sstevel }
4211708Sstevel
4221708Sstevel /*
4231708Sstevel * Capture all CPUs (except for detaching proc) to prevent
4241708Sstevel * crosscalls to the detaching proc until it has cleared its
4251708Sstevel * bit in cpu_ready_set.
4261708Sstevel *
4271708Sstevel * The CPU's remain paused and the prom_mutex is known to be free.
4281708Sstevel * This prevents the x-trap victim from blocking when doing prom
4291708Sstevel * IEEE-1275 calls at a high PIL level.
4301708Sstevel */
4311708Sstevel
4321708Sstevel promsafe_pause_cpus();
4331708Sstevel
4341708Sstevel /*
4351708Sstevel * Quiesce interrupts on the target CPU. We do this by setting
4361708Sstevel * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
4371708Sstevel * prevent it from receiving cross calls and cross traps.
4381708Sstevel * This prevents the processor from receiving any new soft interrupts.
4391708Sstevel */
4401708Sstevel
4411708Sstevel mp_cpu_quiesce(cp);
4421708Sstevel
4431708Sstevel /* tell the prom the cpu is going away */
4441708Sstevel if (SBDP_INJECT_ERROR(f, 2) || prom_serengeti_cpu_off(nodeid) != 0)
4451708Sstevel return (EBUSY);
4461708Sstevel
4471708Sstevel /*
4481708Sstevel * An sir instruction is issued at the end of the shutdown
4491708Sstevel * routine to make the CPU go through POST and re-enter OBP.
4501708Sstevel */
4511708Sstevel xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
4521708Sstevel (uint64_t)sbdp_cpu_shutdown_self, 0);
4531708Sstevel
4541708Sstevel *sbdp_valp = 3ull;
4551708Sstevel
4561708Sstevel start_cpus();
4571708Sstevel
4581708Sstevel /*
4591708Sstevel * Wait until we reach the OBP idle loop or time out.
4601708Sstevel * prom_serengeti_wakeupcpu waits for up to 60 seconds for the
4611708Sstevel * CPU to reach OBP idle loop.
4621708Sstevel */
4631708Sstevel if (SBDP_INJECT_ERROR(f, 3) ||
4641708Sstevel prom_serengeti_wakeupcpu(nodeid) != 0) {
4651708Sstevel
4661708Sstevel /*
4671708Sstevel * If it fails here, we still consider the unconfigure
4681708Sstevel * operation as successful.
4691708Sstevel */
4701708Sstevel cmn_err(CE_WARN, "cpu%d: CPU failed to enter OBP idle loop.\n",
4711708Sstevel cpuid);
4721708Sstevel }
4731708Sstevel
4741708Sstevel ASSERT(!(CPU_IN_SET(cpu_ready_set, cpuid)));
4751708Sstevel
4761708Sstevel bbsram_pa = 0;
4771708Sstevel bbsram_size = 0;
4781708Sstevel
4791708Sstevel return (0);
4801708Sstevel }
4811708Sstevel
4821708Sstevel processorid_t
sbdp_get_cpuid(sbdp_handle_t * hp,dev_info_t * dip)4831708Sstevel sbdp_get_cpuid(sbdp_handle_t *hp, dev_info_t *dip)
4841708Sstevel {
4851708Sstevel int cpuid;
4861708Sstevel char type[OBP_MAXPROPNAME];
4871708Sstevel pnode_t nodeid;
4881708Sstevel sbd_error_t *sep;
4891708Sstevel static fn_t f = "sbdp_get_cpuid";
4901708Sstevel
4911708Sstevel SBDP_DBG_FUNC("%s\n", f);
4921708Sstevel
4931708Sstevel nodeid = ddi_get_nodeid(dip);
4941708Sstevel if (sbdp_is_node_bad(nodeid))
4951708Sstevel return (-1);
4961708Sstevel
4971708Sstevel sep = hp->h_err;
4981708Sstevel
4991708Sstevel if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
5001708Sstevel (void) prom_getprop(nodeid, "device_type", (caddr_t)type);
5011708Sstevel else {
5021708Sstevel sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
5031708Sstevel return (-1);
5041708Sstevel }
5051708Sstevel
5061708Sstevel if (strcmp(type, "cpu") != 0) {
5071708Sstevel sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
5081708Sstevel return (-1);
5091708Sstevel }
5101708Sstevel
5111708Sstevel /*
5121708Sstevel * Check to see if property "cpuid" exists first.
5131708Sstevel * If not, check for "portid".
5141708Sstevel */
5151708Sstevel if (prom_getprop(nodeid, "cpuid", (caddr_t)&cpuid) == -1)
5161708Sstevel if (prom_getprop(nodeid, "portid", (caddr_t)&cpuid) == -1) {
5171708Sstevel
5181708Sstevel return (-1);
5191708Sstevel }
5201708Sstevel
5211708Sstevel return ((processorid_t)cpuid & SG_CPU_ID_MASK);
5221708Sstevel }
5231708Sstevel
5241708Sstevel int
sbdp_cpu_get_impl(sbdp_handle_t * hp,dev_info_t * dip)5251708Sstevel sbdp_cpu_get_impl(sbdp_handle_t *hp, dev_info_t *dip)
5261708Sstevel {
5271708Sstevel int impl;
5281708Sstevel char type[OBP_MAXPROPNAME];
5291708Sstevel pnode_t nodeid;
5301708Sstevel sbd_error_t *sep;
5311708Sstevel static fn_t f = "sbdp_cpu_get_impl";
5321708Sstevel
5331708Sstevel SBDP_DBG_FUNC("%s\n", f);
5341708Sstevel
5351708Sstevel nodeid = ddi_get_nodeid(dip);
5361708Sstevel if (sbdp_is_node_bad(nodeid))
5371708Sstevel return (-1);
5381708Sstevel
5391708Sstevel sep = hp->h_err;
5401708Sstevel
5411708Sstevel if (prom_getproplen(nodeid, "device_type") < OBP_MAXPROPNAME)
5421708Sstevel (void) prom_getprop(nodeid, "device_type", (caddr_t)type);
5431708Sstevel else {
5441708Sstevel sbdp_set_err(sep, ESGT_NO_DEV_TYPE, NULL);
5451708Sstevel return (-1);
5461708Sstevel }
5471708Sstevel
5481708Sstevel if (strcmp(type, "cpu") != 0) {
5491708Sstevel sbdp_set_err(sep, ESGT_NOT_CPUTYPE, NULL);
5501708Sstevel return (-1);
5511708Sstevel }
5521708Sstevel
5531708Sstevel /*
5541708Sstevel * Get the implementation# property.
5551708Sstevel */
5561708Sstevel if (prom_getprop(nodeid, "implementation#", (caddr_t)&impl) == -1)
5571708Sstevel return (-1);
5581708Sstevel
5591708Sstevel return (impl);
5601708Sstevel }
5611708Sstevel
5621708Sstevel struct sbdp_prom_get_node_args {
5631708Sstevel pnode_t node; /* current node */
5641708Sstevel processorid_t portid; /* portid we are looking for */
5651708Sstevel pnode_t result_node; /* node found with the above portid */
5661708Sstevel };
5671708Sstevel
5681708Sstevel pnode_t
sbdp_find_nearby_cpu_by_portid(pnode_t nodeid,processorid_t portid)5691708Sstevel sbdp_find_nearby_cpu_by_portid(pnode_t nodeid, processorid_t portid)
5701708Sstevel {
5711708Sstevel struct sbdp_prom_get_node_args arg;
5721708Sstevel static fn_t f = "sbdp_find_nearby_cpu_by_portid";
5731708Sstevel
5741708Sstevel SBDP_DBG_FUNC("%s\n", f);
5751708Sstevel
5761708Sstevel arg.node = nodeid;
5771708Sstevel arg.portid = portid;
5781708Sstevel (void) prom_tree_access(sbdp_prom_get_cpu, &arg, NULL);
5791708Sstevel
5801708Sstevel return (arg.result_node);
5811708Sstevel }
5821708Sstevel
5831708Sstevel /*ARGSUSED*/
5841708Sstevel static int
sbdp_prom_get_cpu(void * arg,int changed)5851708Sstevel sbdp_prom_get_cpu(void *arg, int changed)
5861708Sstevel {
5871708Sstevel int portid;
5881708Sstevel pnode_t parent, cur_node;
5891708Sstevel struct sbdp_prom_get_node_args *argp = arg;
5901708Sstevel static fn_t f = "sbdp_prom_get_cpu";
5911708Sstevel
5921708Sstevel SBDP_DBG_FUNC("%s\n", f);
5931708Sstevel
5941708Sstevel parent = prom_parentnode(argp->node);
5951708Sstevel
5961708Sstevel for (cur_node = prom_childnode(parent); cur_node != OBP_NONODE;
5971708Sstevel cur_node = prom_nextnode(cur_node)) {
5981708Sstevel
5991708Sstevel if (prom_getprop(cur_node, OBP_PORTID, (caddr_t)&portid) < 0)
6001708Sstevel continue;
6011708Sstevel
6021708Sstevel if ((portid == argp->portid) && (cur_node != argp->node))
6031708Sstevel break;
6041708Sstevel }
6051708Sstevel
6061708Sstevel argp->result_node = cur_node;
6071708Sstevel
6081708Sstevel return (0);
6091708Sstevel }
6101708Sstevel
6111708Sstevel
6121708Sstevel /*
6131708Sstevel * A detaching CPU is xcalled with an xtrap to sbdp_cpu_stop_self() after
6141708Sstevel * it has been offlined. The function of this routine is to get the cpu
6151708Sstevel * spinning in a safe place. The requirement is that the system will not
6161708Sstevel * reference anything on the detaching board (memory and i/o is detached
6171708Sstevel * elsewhere) and that the CPU not reference anything on any other board
6181708Sstevel * in the system. This isolation is required during and after the writes
6191708Sstevel * to the domain masks to remove the board from the domain.
6201708Sstevel *
6211708Sstevel * To accomplish this isolation the following is done:
6221708Sstevel * 0) Map the CPUSRAM to obtain the correct address in SRAM
6231708Sstevel * 1) Create a locked mapping to a location in CPU SRAM where
6241708Sstevel * the cpu will execute.
6251708Sstevel * 2) Copy the target function (sbdp_shutdown_asm) in which
6261708Sstevel * the cpu will execute into CPU SRAM.
6271708Sstevel * 3) Jump into function with CPU SRAM.
6281708Sstevel * Function will:
6291708Sstevel * 3.1) Flush its Ecache (displacement).
6301708Sstevel * 3.2) Flush its Dcache with HW mechanism.
6311708Sstevel * 3.3) Flush its Icache with HW mechanism.
6321708Sstevel * 3.4) Flush all valid and _unlocked_ D-TLB entries.
6331708Sstevel * 3.5) Flush all valid and _unlocked_ I-TLB entries.
6341708Sstevel * 4) Jump into a tight loop.
6351708Sstevel */
6361708Sstevel
6371708Sstevel static void
sbdp_cpu_stop_self(uint64_t pa)6381708Sstevel sbdp_cpu_stop_self(uint64_t pa)
6391708Sstevel {
6401708Sstevel cpu_t *cp = CPU;
6411708Sstevel int cpuid = cp->cpu_id;
6421708Sstevel tte_t tte;
6431708Sstevel volatile uint_t *src, *dst;
6441708Sstevel uint_t funclen;
6451708Sstevel sbdp_shutdown_t sht;
6461708Sstevel uint_t bbsram_pfn;
6471708Sstevel uint64_t bbsram_addr;
6481708Sstevel void (*bbsram_func)(sbdp_shutdown_t *);
6491708Sstevel extern void sbdp_shutdown_asm(sbdp_shutdown_t *);
6501708Sstevel extern void sbdp_shutdown_asm_end(void);
6511708Sstevel
6521708Sstevel funclen = (uint_t)sbdp_shutdown_asm_end - (uint_t)sbdp_shutdown_asm;
6531708Sstevel ASSERT(funclen <= MMU_PAGESIZE);
6541708Sstevel ASSERT(bbsram_pa != 0);
6551708Sstevel ASSERT((bbsram_pa & MMU_PAGEOFFSET) == 0);
6561708Sstevel ASSERT(bbsram_size >= MMU_PAGESIZE);
6571708Sstevel
6581708Sstevel stdphys(pa, 3);
6591708Sstevel bbsram_pfn = (uint_t)(bbsram_pa >> MMU_PAGESHIFT);
6601708Sstevel
6611708Sstevel bbsram_addr = (uint64_t)sbdp_shutdown_va;
6621708Sstevel sht.estack = bbsram_addr + MMU_PAGESIZE;
6631708Sstevel sht.flushaddr = ecache_flushaddr;
6641708Sstevel
6651708Sstevel tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
6661708Sstevel TTE_PFN_INTHI(bbsram_pfn);
6671708Sstevel tte.tte_intlo = TTE_PFN_INTLO(bbsram_pfn) |
6681708Sstevel TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
6692241Shuah sfmmu_dtlb_ld_kva(sbdp_shutdown_va, &tte); /* load dtlb */
6702241Shuah sfmmu_itlb_ld_kva(sbdp_shutdown_va, &tte); /* load itlb */
6711708Sstevel
6721708Sstevel for (src = (uint_t *)sbdp_shutdown_asm, dst = (uint_t *)bbsram_addr;
6731708Sstevel src < (uint_t *)sbdp_shutdown_asm_end; src++, dst++)
6741708Sstevel *dst = *src;
6751708Sstevel
6761708Sstevel bbsram_func = (void (*)())bbsram_addr;
6771708Sstevel sht.size = (uint32_t)cpunodes[cpuid].ecache_size << 1;
6781708Sstevel sht.linesize = (uint32_t)cpunodes[cpuid].ecache_linesize;
6791708Sstevel sht.physaddr = pa;
6801708Sstevel
6811708Sstevel /*
6821708Sstevel * Signal to sbdp_cpu_poweroff() that we're just
6831708Sstevel * about done.
6841708Sstevel */
6851708Sstevel cp->cpu_m.in_prom = 1;
6861708Sstevel
6871708Sstevel stdphys(pa, 4);
6881708Sstevel (*bbsram_func)(&sht);
6891708Sstevel }
6901708Sstevel
6911708Sstevel /* ARGSUSED */
6921708Sstevel void
sbdp_get_cpu_sram_addr(uint64_t arg1,uint64_t arg2)6931708Sstevel sbdp_get_cpu_sram_addr(uint64_t arg1, uint64_t arg2)
6941708Sstevel {
6951708Sstevel uint64_t *pap;
6961708Sstevel uint_t *sizep;
6971708Sstevel struct iosram_toc *tocp;
6981708Sstevel uint_t offset;
6991708Sstevel uint_t size;
7001708Sstevel sbdp_cpu_sram_map_t *map;
7011708Sstevel int i;
7021708Sstevel fn_t f = "sbdp_get_cpu_sram_addr";
7031708Sstevel
7041708Sstevel SBDP_DBG_FUNC("%s\n", f);
7051708Sstevel
7061708Sstevel map = (sbdp_cpu_sram_map_t *)arg1;
7071708Sstevel tocp = (struct iosram_toc *)map->vaddr;
7081708Sstevel pap = map->pa;
7091708Sstevel sizep = map->size;
7101708Sstevel
7111708Sstevel for (i = 0; i < tocp->iosram_tagno; i++) {
7121708Sstevel if (strcmp(tocp->iosram_keys[i].key, cpyren_key) == 0)
7131708Sstevel break;
7141708Sstevel }
7151708Sstevel if (i == tocp->iosram_tagno) {
7161708Sstevel *pap = 0;
7171708Sstevel *sizep = 0;
7181708Sstevel return;
7191708Sstevel }
7201708Sstevel offset = tocp->iosram_keys[i].offset;
7211708Sstevel size = tocp->iosram_keys[i].size;
7221708Sstevel
7231708Sstevel /*
7241708Sstevel * The address we want is the begining of cpusram + offset
7251708Sstevel */
7261708Sstevel *pap = SBDP_CPU_SRAM_ADDR + offset;
7271708Sstevel
7281708Sstevel *sizep = size;
7291708Sstevel }
7301708Sstevel
7311708Sstevel static int
cpusram_map(caddr_t * vaddrp,pgcnt_t * npp)7321708Sstevel cpusram_map(caddr_t *vaddrp, pgcnt_t *npp)
7331708Sstevel {
7341708Sstevel uint_t pgoffset;
7351708Sstevel pgcnt_t npages;
7361708Sstevel pfn_t pfn;
7371708Sstevel uint64_t base;
7381708Sstevel caddr_t kaddr;
7391708Sstevel uint_t mapping_attr;
7401708Sstevel
7411708Sstevel base = (uint64_t)SBDP_CPU_SRAM_ADDR & (~MMU_PAGEOFFSET);
7421708Sstevel pfn = mmu_btop(base);
7431708Sstevel
7441708Sstevel /*
7451708Sstevel * Do a quick sanity check to make sure we are in I/O space.
7461708Sstevel */
7471708Sstevel if (pf_is_memory(pfn))
7481708Sstevel return (DDI_FAILURE);
7491708Sstevel
7501708Sstevel pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
7511708Sstevel npages = mmu_btopr(SBDP_CPU_SRAM_SIZE + pgoffset);
7521708Sstevel
7531708Sstevel kaddr = vmem_alloc(heap_arena, ptob(npages), VM_NOSLEEP);
7541708Sstevel if (kaddr == NULL)
7551708Sstevel return (DDI_ME_NORESOURCES);
7561708Sstevel
7571708Sstevel mapping_attr = PROT_READ;
7581708Sstevel /*
7591708Sstevel * Now map in the pages we've allocated...
7601708Sstevel */
7611708Sstevel hat_devload(kas.a_hat, kaddr, ptob(npages), pfn, mapping_attr,
7621708Sstevel HAT_LOAD_LOCK);
7631708Sstevel
7641708Sstevel *vaddrp = kaddr + pgoffset;
7651708Sstevel *npp = npages;
7661708Sstevel
7671708Sstevel return (DDI_SUCCESS);
7681708Sstevel }
7691708Sstevel
7701708Sstevel static void
cpusram_unmap(caddr_t * vaddrp,pgcnt_t npages)7711708Sstevel cpusram_unmap(caddr_t *vaddrp, pgcnt_t npages)
7721708Sstevel {
7731708Sstevel uint_t pgoffset;
7741708Sstevel caddr_t base;
7751708Sstevel caddr_t addr = *vaddrp;
7761708Sstevel
7771708Sstevel
7781708Sstevel pgoffset = (ulong_t)SBDP_CPU_SRAM_ADDR & MMU_PAGEOFFSET;
7791708Sstevel base = addr - pgoffset;
7801708Sstevel hat_unload(kas.a_hat, base, ptob(npages), HAT_UNLOAD_UNLOCK);
7811708Sstevel vmem_free(heap_arena, base, ptob(npages));
7821708Sstevel
7831708Sstevel *vaddrp = 0;
7841708Sstevel }
7851708Sstevel
7861708Sstevel
7871708Sstevel static void
sbdp_cpu_shutdown_self(void)7881708Sstevel sbdp_cpu_shutdown_self(void)
7891708Sstevel {
7901708Sstevel cpu_t *cp = CPU;
7911708Sstevel int cpuid = cp->cpu_id;
7921708Sstevel extern void flush_windows(void);
7931708Sstevel uint64_t pa = va_to_pa((void *)sbdp_valp);
7941708Sstevel
7951708Sstevel stdphys(pa, 8);
7961708Sstevel flush_windows();
7971708Sstevel
7981708Sstevel (void) spl8();
7991708Sstevel
8001708Sstevel stdphys(pa, 6);
8011708Sstevel
8021708Sstevel ASSERT(cp->cpu_intr_actv == 0);
8031708Sstevel ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
8041708Sstevel cp->cpu_thread == cp->cpu_startup_thread);
8051708Sstevel
8061708Sstevel cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
8071708Sstevel
8081708Sstevel CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
8091708Sstevel
8101708Sstevel stdphys(pa, 7);
8111708Sstevel sbdp_cpu_stop_self(pa);
8121708Sstevel
8131708Sstevel cmn_err(CE_PANIC, "sbdp_cpu_shutdown_self: CPU %d FAILED TO SHUTDOWN",
8141708Sstevel cpuid);
8151708Sstevel }
8161708Sstevel
8171708Sstevel typedef struct {
8181708Sstevel int node;
8191708Sstevel int board;
8201708Sstevel int non_panther_cpus;
8211708Sstevel } sbdp_node_walk_t;
8221708Sstevel
8231708Sstevel static int
sbdp_find_non_panther_cpus(dev_info_t * dip,void * node_args)8241708Sstevel sbdp_find_non_panther_cpus(dev_info_t *dip, void *node_args)
8251708Sstevel {
8261708Sstevel int impl, cpuid, portid;
8271708Sstevel int buflen;
8281708Sstevel char buf[OBP_MAXPROPNAME];
8291708Sstevel sbdp_node_walk_t *args = (sbdp_node_walk_t *)node_args;
8301708Sstevel
8311708Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
8321708Sstevel DDI_PROP_DONTPASS, OBP_DEVICETYPE, (caddr_t)buf,
8331708Sstevel &buflen) != DDI_PROP_SUCCESS) {
8341708Sstevel return (DDI_WALK_CONTINUE);
8351708Sstevel }
8361708Sstevel
8371708Sstevel if (strcmp(buf, "cpu") != 0) {
8381708Sstevel return (DDI_WALK_CONTINUE);
8391708Sstevel }
8401708Sstevel
8411708Sstevel if ((impl = ddi_getprop(DDI_DEV_T_ANY, dip,
8421708Sstevel DDI_PROP_DONTPASS, "implementation#", -1)) == -1) {
8431708Sstevel return (DDI_WALK_CONTINUE);
8441708Sstevel }
8451708Sstevel
8461708Sstevel if ((cpuid = ddi_getprop(DDI_DEV_T_ANY, dip,
8471708Sstevel DDI_PROP_DONTPASS, "cpuid", -1)) == -1) {
8481708Sstevel return (DDI_WALK_CONTINUE);
8491708Sstevel }
8501708Sstevel
8511708Sstevel portid = SG_CPUID_TO_PORTID(cpuid);
8521708Sstevel
8531708Sstevel /* filter out nodes not on this board */
8541708Sstevel if (SG_PORTID_TO_BOARD_NUM(portid) != args->board ||
8551708Sstevel SG_PORTID_TO_NODEID(portid) != args->node) {
8561708Sstevel return (DDI_WALK_PRUNECHILD);
8571708Sstevel }
8581708Sstevel
8591708Sstevel switch (impl) {
8601708Sstevel case CHEETAH_IMPL:
8611708Sstevel case CHEETAH_PLUS_IMPL:
8621708Sstevel case JAGUAR_IMPL:
8631708Sstevel args->non_panther_cpus++;
8641708Sstevel break;
8651708Sstevel case PANTHER_IMPL:
8661708Sstevel break;
8671708Sstevel default:
8681708Sstevel ASSERT(0);
8691708Sstevel args->non_panther_cpus++;
8701708Sstevel break;
8711708Sstevel }
8721708Sstevel
8731708Sstevel SBDP_DBG_CPU("cpuid=0x%x, portid=0x%x, impl=0x%x, device_type=%s",
8741708Sstevel cpuid, portid, impl, buf);
8751708Sstevel
8761708Sstevel return (DDI_WALK_CONTINUE);
8771708Sstevel }
8781708Sstevel
8791708Sstevel int
sbdp_board_non_panther_cpus(int node,int board)8801708Sstevel sbdp_board_non_panther_cpus(int node, int board)
8811708Sstevel {
8821708Sstevel sbdp_node_walk_t arg = {0};
8831708Sstevel
8841708Sstevel arg.node = node;
8851708Sstevel arg.board = board;
8861708Sstevel
8871708Sstevel /*
8881708Sstevel * Root node doesn't have to be held.
8891708Sstevel */
8901708Sstevel ddi_walk_devs(ddi_root_node(), sbdp_find_non_panther_cpus,
8911708Sstevel (void *)&arg);
8921708Sstevel
8931708Sstevel return (arg.non_panther_cpus);
8941708Sstevel }
895