1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26/*
27 * Copyright 2019 Peter Tribble.
28 * Copyright 2019 Joyent, Inc.
29 */
30
31/*
32 * CPU support routines for DR
33 */
34
35#include <sys/debug.h>
36#include <sys/types.h>
37#include <sys/errno.h>
38#include <sys/cred.h>
39#include <sys/dditypes.h>
40#include <sys/devops.h>
41#include <sys/modctl.h>
42#include <sys/poll.h>
43#include <sys/conf.h>
44#include <sys/ddi.h>
45#include <sys/sunddi.h>
46#include <sys/sunndi.h>
47#include <sys/ddi_impldefs.h>
48#include <sys/ndi_impldefs.h>
49#include <sys/stat.h>
50#include <sys/kmem.h>
51#include <sys/processor.h>
52#include <sys/cpuvar.h>
53#include <sys/mem_config.h>
54#include <sys/promif.h>
55#include <sys/x_call.h>
56#include <sys/cpu_sgnblk_defs.h>
57#include <sys/membar.h>
58#include <sys/stack.h>
59#include <sys/sysmacros.h>
60#include <sys/machsystm.h>
61#include <sys/spitregs.h>
62
63#include <sys/archsystm.h>
64#include <vm/hat_sfmmu.h>
65#include <sys/pte.h>
66#include <sys/mmu.h>
67#include <sys/x_call.h>
68#include <sys/cpu_module.h>
69#include <sys/cheetahregs.h>
70
71#include <sys/autoconf.h>
72#include <sys/cmn_err.h>
73
74#include <sys/sbdpriv.h>
75
76void
77sbd_cpu_set_prop(sbd_cpu_unit_t *cp, dev_info_t *dip)
78{
79	uint32_t	clock_freq;
80	int		ecache_size = 0;
81	char		*cache_str = NULL;
82
83	/* read in the CPU speed */
84	clock_freq = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
85	    DDI_PROP_DONTPASS, "clock-frequency", 0);
86
87	ASSERT(clock_freq != 0);
88
89	/*
90	 * The ecache property string is not the same
91	 * for all CPU implementations.
92	 */
93	switch (cp->sbc_cpu_impl) {
94	case CHEETAH_IMPL:
95	case CHEETAH_PLUS_IMPL:
96		cache_str = "ecache-size";
97		break;
98	case JAGUAR_IMPL:
99		cache_str = "l2-cache-size";
100		break;
101	case PANTHER_IMPL:
102		cache_str = "l3-cache-size";
103		break;
104	default:
105		cmn_err(CE_WARN, "cpu implementation type "
106		    "is an unknown %d value", cp->sbc_cpu_impl);
107		ASSERT(0);
108		break;
109	}
110
111	if (cache_str != NULL) {
112		/* read in the ecache size */
113		ecache_size = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
114		    DDI_PROP_DONTPASS, cache_str, 0);
115	}
116
117	/*
118	 * In the case the size is still 0,
119	 * a zero value will be displayed running non-debug.
120	 */
121	ASSERT(ecache_size != 0);
122
123	/* convert to the proper units */
124	cp->sbc_speed = (clock_freq + 500000) / 1000000;
125	cp->sbc_ecache = ecache_size / (1024 * 1024);
126}
127
128static void
129sbd_fill_cpu_stat(sbd_cpu_unit_t *cp, dev_info_t *dip, sbd_cpu_stat_t *csp)
130{
131	int		namelen;
132
133	bzero((caddr_t)csp, sizeof (*csp));
134	csp->cs_type = cp->sbc_cm.sbdev_type;
135	csp->cs_unit = cp->sbc_cm.sbdev_unum;
136	namelen = sizeof (csp->cs_name);
137	(void) ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
138	    OBP_DEVICETYPE, (caddr_t)csp->cs_name, &namelen);
139	csp->cs_busy = cp->sbc_cm.sbdev_busy;
140	csp->cs_time = cp->sbc_cm.sbdev_time;
141	csp->cs_ostate = cp->sbc_cm.sbdev_ostate;
142	csp->cs_cpuid = cp->sbc_cpu_id;
143	csp->cs_suspend = 0;
144
145	/*
146	 * If we have marked the cpu's condition previously
147	 * then don't rewrite it
148	 */
149	if (csp->cs_cond != SBD_COND_UNUSABLE)
150		csp->cs_cond = sbd_get_comp_cond(dip);
151
152	/*
153	 * If the speed and ecache properties have not been
154	 * cached yet, read them in from the device tree.
155	 */
156	if ((cp->sbc_speed == 0) || (cp->sbc_ecache == 0))
157		sbd_cpu_set_prop(cp, dip);
158
159	/* use the cached speed and ecache values */
160	csp->cs_speed = cp->sbc_speed;
161	csp->cs_ecache = cp->sbc_ecache;
162}
163
164static void
165sbd_fill_cmp_stat(sbd_cpu_stat_t *csp, int ncores, int impl,
166    sbd_cmp_stat_t *psp)
167{
168	int	core;
169
170	ASSERT(csp && psp && (ncores >= 1));
171
172	bzero((caddr_t)psp, sizeof (*psp));
173
174	/*
175	 * Fill in the common status information based
176	 * on the data for the first core.
177	 */
178	psp->ps_type = SBD_COMP_CMP;
179	psp->ps_unit = SBD_CMP_NUM(csp->cs_unit);
180	(void) strncpy(psp->ps_name, csp->cs_name, sizeof (psp->ps_name));
181	psp->ps_cond = csp->cs_cond;
182	psp->ps_busy = csp->cs_busy;
183	psp->ps_time = csp->cs_time;
184	psp->ps_ostate = csp->cs_ostate;
185	psp->ps_suspend = csp->cs_suspend;
186
187	/* CMP specific status data */
188	*psp->ps_cpuid = csp->cs_cpuid;
189	psp->ps_ncores = 1;
190	psp->ps_speed = csp->cs_speed;
191	psp->ps_ecache = csp->cs_ecache;
192
193	/*
194	 * Walk through the data for the remaining cores.
195	 * Make any adjustments to the common status data,
196	 * or the shared CMP specific data if necessary.
197	 */
198	for (core = 1; core < ncores; core++) {
199
200		/*
201		 * The following properties should be the same
202		 * for all the cores of the CMP.
203		 */
204		ASSERT(psp->ps_unit == SBD_CMP_NUM(csp[core].cs_unit));
205		ASSERT(psp->ps_speed == csp[core].cs_speed);
206
207		psp->ps_cpuid[core] = csp[core].cs_cpuid;
208		psp->ps_ncores++;
209
210		/*
211		 * Jaguar has a split ecache, so the ecache
212		 * for each core must be added together to
213		 * get the total ecache for the whole chip.
214		 */
215		if (IS_JAGUAR(impl)) {
216			psp->ps_ecache += csp[core].cs_ecache;
217		}
218
219		/* adjust time if necessary */
220		if (csp[core].cs_time > psp->ps_time) {
221			psp->ps_time = csp[core].cs_time;
222		}
223
224		psp->ps_busy |= csp[core].cs_busy;
225
226		/*
227		 * If any of the cores are configured, the
228		 * entire CMP is marked as configured.
229		 */
230		if (csp[core].cs_ostate == SBD_STAT_CONFIGURED) {
231			psp->ps_ostate = csp[core].cs_ostate;
232		}
233	}
234}
235
236int
237sbd_cpu_flags(sbd_handle_t *hp, sbd_devset_t devset, sbd_dev_stat_t *dsp)
238{
239	int		cmp;
240	int		ncpu;
241	sbd_board_t	*sbp;
242	sbdp_handle_t	*hdp;
243	sbd_cpu_stat_t	cstat[MAX_CORES_PER_CMP];
244
245	sbp = SBDH2BD(hp->h_sbd);
246	hdp = sbd_get_sbdp_handle(sbp, hp);
247
248	/*
249	 * Grab the status lock before accessing the dip as we allow
250	 * concurrent status and branch unconfigure and disconnect.
251	 *
252	 * The disconnect thread clears the present devset first
253	 * and then destroys dips. It is possible that the status
254	 * thread checks the present devset before they are cleared
255	 * but accesses the dip after they are destroyed causing a
256	 * panic. To prevent this, the status thread should check
257	 * the present devset and access dips with status lock held.
258	 * Similarly disconnect thread should clear the present devset
259	 * and destroy dips with status lock held.
260	 */
261	mutex_enter(&sbp->sb_slock);
262
263	/*
264	 * Only look for requested devices that are actually present.
265	 */
266	devset &= SBD_DEVS_PRESENT(sbp);
267
268	/*
269	 * Treat every CPU as a CMP.  In the case where the
270	 * device is not a CMP, treat it as a CMP with only
271	 * one core.
272	 */
273	for (cmp = ncpu = 0; cmp < MAX_CMP_UNITS_PER_BOARD; cmp++) {
274
275		int		ncores;
276		int		core;
277		dev_info_t	*dip;
278		sbd_cpu_unit_t	*cp;
279		sbd_cmp_stat_t	*psp;
280
281		if (DEVSET_IN_SET(devset, SBD_COMP_CMP, cmp) == 0)
282			continue;
283
284		ncores = 0;
285
286		for (core = 0; core < MAX_CORES_PER_CMP; core++) {
287			int	unit;
288
289			unit = sbdp_portid_to_cpu_unit(cmp, core);
290
291			/*
292			 * Check to make sure the cpu is in a state
293			 * where its fully initialized.
294			 */
295			if (SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit) ==
296			    SBD_STATE_EMPTY)
297				continue;
298
299			dip = sbp->sb_devlist[NIX(SBD_COMP_CMP)][unit];
300			if (dip == NULL)
301				continue;
302
303			cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
304
305			sbd_fill_cpu_stat(cp, dip, &cstat[ncores++]);
306		}
307
308		if (ncores == 0)
309			continue;
310
311		/*
312		 * Store the data to the outgoing array. If the
313		 * device is a CMP, combine all the data for the
314		 * cores into a single stat structure.
315		 *
316		 * The check for a CMP device uses the last core
317		 * found, assuming that all cores will have the
318		 * same implementation.
319		 */
320		if (CPU_IMPL_IS_CMP(cp->sbc_cpu_impl)) {
321			psp = (sbd_cmp_stat_t *)dsp;
322			sbd_fill_cmp_stat(cstat, ncores, cp->sbc_cpu_impl, psp);
323		} else {
324			ASSERT(ncores == 1);
325			bcopy(cstat, dsp, sizeof (sbd_cpu_stat_t));
326		}
327
328		dsp++;
329		ncpu++;
330	}
331
332	mutex_exit(&sbp->sb_slock);
333
334	sbd_release_sbdp_handle(hdp);
335
336	return (ncpu);
337}
338
339int
340sbd_pre_release_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
341{
342	int		i, rv = 0, unit;
343	dev_info_t	*dip;
344	processorid_t	cpuid;
345	struct cpu	*cpup;
346	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
347	sbderror_t	*ep = SBD_HD2ERR(hp);
348	sbd_cpu_unit_t	*cp;
349	static fn_t	f = "sbd_pre_release_cpu";
350	sbdp_handle_t	*hdp;
351
352	hdp = sbd_get_sbdp_handle(sbp, hp);
353	/*
354	 * May have to juggle bootproc in release_component
355	 */
356	mutex_enter(&cpu_lock);
357
358	for (i = 0; i < devnum; i++, devlist++) {
359		dip = devlist->dv_dip;
360
361		cpuid = sbdp_get_cpuid(hdp, dip);
362		if (cpuid < 0) {
363			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
364				cmn_err(CE_WARN,
365					"sbd:%s: failed to get cpuid for "
366					"dip (0x%p)", f, (void *)dip);
367				continue;
368			} else {
369				SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
370				break;
371			}
372		}
373
374
375		unit = sbdp_get_unit_num(hdp, dip);
376		if (unit < 0) {
377			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
378			cmn_err(CE_WARN,
379				"sbd:%s: failed to get unit (cpu %d)",
380				f, cpuid);
381				continue;
382			} else {
383				SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
384				break;
385			}
386		}
387
388		cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
389		cp->sbc_cpu_flags = cpu[cpuid]->cpu_flags;
390
391		if (cpu_flagged_active(cp->sbc_cpu_flags)) {
392			int cpu_offline_flags = 0;
393
394			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE)
395				cpu_offline_flags = CPU_FORCED;
396			PR_CPU("%s: offlining cpuid %d unit %d", f,
397				cpuid, unit);
398			if (cpu_offline(cpu[cpuid], cpu_offline_flags)) {
399				cmn_err(CE_WARN,
400					"%s: failed to offline cpu %d",
401					f, cpuid);
402				rv = -1;
403				SBD_SET_ERR(ep, ESBD_OFFLINE);
404				SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
405				cpup = cpu_get(cpuid);
406				if (cpup && disp_bound_threads(cpup, 0)) {
407					cmn_err(CE_WARN, "sbd:%s: thread(s) "
408						"bound to cpu %d",
409						f, cpup->cpu_id);
410				}
411				break;
412			}
413		}
414
415		if (rv == 0) {
416			if (sbdp_release_component(hdp, dip)) {
417				SBD_GET_PERR(hdp->h_err, ep);
418				break;
419			}
420		}
421
422		if (rv)
423			break;
424	}
425
426	mutex_exit(&cpu_lock);
427
428	if (rv) {
429		/*
430		 * Need to unwind others since at this level (pre-release)
431		 * the device state has not yet transitioned and failures
432		 * will prevent us from reaching the "post" release
433		 * function where states are normally transitioned.
434		 */
435		for (; i >= 0; i--, devlist--) {
436			dip = devlist->dv_dip;
437			unit = sbdp_get_unit_num(hdp, dip);
438			if (unit < 0) {
439				cmn_err(CE_WARN,
440					"sbd:%s: failed to get unit for "
441					"dip (0x%p)", f, (void *)dip);
442				break;
443			}
444			(void) sbd_cancel_cpu(hp, unit);
445		}
446	}
447
448	SBD_INJECT_ERR(SBD_OFFLINE_CPU_PSEUDO_ERR,
449		hp->h_err, EIO,
450		ESBD_OFFLINE,
451		sbp->sb_cpupath[devnum - 1]);
452
453	sbd_release_sbdp_handle(hdp);
454
455	return (rv);
456}
457
458int
459sbd_pre_attach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
460{
461	int		i;
462	int		unit;
463	processorid_t	cpuid;
464	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
465	sbd_istate_t	dstate;
466	dev_info_t	*dip;
467	static fn_t	f = "sbd_pre_attach_cpu";
468	sbdp_handle_t	*hdp;
469
470	PR_CPU("%s...\n", f);
471
472	hdp = sbd_get_sbdp_handle(sbp, hp);
473
474	for (i = 0; i < devnum; i++, devlist++) {
475		dip = devlist->dv_dip;
476
477		ASSERT(sbd_is_cmp_child(dip) || e_ddi_branch_held(dip));
478
479		cpuid = sbdp_get_cpuid(hdp, dip);
480		if (cpuid < 0) {
481			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
482				cmn_err(CE_WARN,
483					"sbd:%s: failed to get cpuid for "
484					"dip (0x%p)", f, (void *)dip);
485				continue;
486			} else {
487				SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
488				break;
489			}
490		}
491
492		unit = sbdp_get_unit_num(hdp, dip);
493		if (unit < 0) {
494			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
495			cmn_err(CE_WARN,
496				"sbd:%s: failed to get unit (cpu %d)",
497				f, cpuid);
498				continue;
499			} else {
500				SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
501				break;
502			}
503		}
504
505		PR_CPU("%s: attach cpu-unit (%d.%d)\n",
506			f, sbp->sb_num, unit);
507
508		dstate = SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit);
509
510		if (dstate == SBD_STATE_UNCONFIGURED) {
511			/*
512			 * If we're coming from the UNCONFIGURED
513			 * state then the cpu's sigblock will
514			 * still be mapped in.  Need to unmap it
515			 * before continuing with attachment.
516			 */
517			PR_CPU("%s: unmapping sigblk for cpu %d\n",
518				f, cpuid);
519		}
520
521	}
522
523	mutex_enter(&cpu_lock);
524
525	sbd_release_sbdp_handle(hdp);
526
527	return (0);
528}
529
530int
531sbd_post_attach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
532{
533	int		i;
534	sbderror_t	*ep = SBD_HD2ERR(hp);
535	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
536	processorid_t	cpuid;
537	struct cpu	*cp;
538	dev_info_t	*dip;
539	int		err = ESBD_NOERROR;
540	sbdp_handle_t	*hdp;
541	static fn_t	f = "sbd_post_attach_cpu";
542	sbd_cpu_unit_t	*cpup;
543	int		unit;
544
545	hdp = sbd_get_sbdp_handle(sbp, hp);
546
547	/* Startup and online newly-attached CPUs */
548	for (i = 0; i < devnum; i++, devlist++) {
549		dip = devlist->dv_dip;
550		cpuid = sbdp_get_cpuid(hdp, dip);
551		if (cpuid < 0) {
552			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
553				cmn_err(CE_WARN,
554				    "sbd:%s: failed to get cpuid for "
555				    "dip (0x%p)", f, (void *)dip);
556				continue;
557			} else {
558				SBD_GET_PERR(hdp->h_err, ep);
559				break;
560			}
561		}
562
563		cp = cpu_get(cpuid);
564
565		if (cp == NULL) {
566			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
567				cmn_err(CE_WARN,
568				    "sbd:%s: cpu_get failed for cpu %d",
569				    f, cpuid);
570				continue;
571			} else {
572				SBD_SET_ERR(ep, ESBD_INTERNAL);
573				SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
574				break;
575			}
576		}
577
578		if (cpu_is_poweredoff(cp)) {
579			if (cpu_poweron(cp) != 0) {
580				SBD_SET_ERR(ep, ESBD_CPUSTART);
581				SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
582				cmn_err(CE_WARN,
583				    "%s: failed to power-on cpu %d",
584				    f, cpuid);
585				break;
586			}
587			SBD_INJECT_ERR(SBD_POWERON_CPU_PSEUDO_ERR,
588			    ep, EIO,
589			    ESBD_CPUSTOP,
590			    sbp->sb_cpupath[i]);
591			PR_CPU("%s: cpu %d powered ON\n", f, cpuid);
592		}
593
594		if (cpu_is_offline(cp)) {
595			PR_CPU("%s: onlining cpu %d...\n", f, cpuid);
596
597			if (cpu_online(cp, 0) != 0) {
598				SBD_SET_ERR(ep, ESBD_ONLINE);
599				SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
600				cmn_err(CE_WARN,
601				    "%s: failed to online cpu %d",
602				    f, cp->cpu_id);
603			}
604			SBD_INJECT_ERR(SBD_ONLINE_CPU_PSEUDO_ERR,
605			    ep, EIO,
606			    ESBD_ONLINE,
607			    sbp->sb_cpupath[i]);
608		}
609
610		/*
611		 * if there is no error mark the cpu as OK to use
612		 */
613		if (SBD_GET_ERR(ep) == 0) {
614			unit = sbdp_get_unit_num(hdp, dip);
615			if (unit < 0) {
616				if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
617					cmn_err(CE_WARN,
618					    "sbd:%s: failed to get unit "
619					    "(cpu %d)", f, cpuid);
620					continue;
621				} else {
622					SBD_GET_PERR(hdp->h_err,
623					    SBD_HD2ERR(hp));
624					break;
625				}
626			}
627			cpup = SBD_GET_BOARD_CPUUNIT(sbp, unit);
628			cpup->sbc_cm.sbdev_cond = SBD_COND_OK;
629		}
630	}
631
632	mutex_exit(&cpu_lock);
633
634	sbd_release_sbdp_handle(hdp);
635
636	if (err != ESBD_NOERROR) {
637		return (-1);
638	} else {
639		return (0);
640	}
641}
642
643int
644sbd_pre_detach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
645{
646	int		i;
647	int		unit;
648	processorid_t	cpuid;
649	dev_info_t	*dip;
650	struct cpu	*cpu;
651	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
652	sbderror_t	*ep = SBD_HD2ERR(hp);
653	static fn_t	f = "sbd_pre_detach_cpu";
654	sbdp_handle_t	*hdp;
655	int		rv = 0;
656
657	PR_CPU("%s...\n", f);
658
659	hdp = sbd_get_sbdp_handle(sbp, hp);
660
661	mutex_enter(&cpu_lock);
662
663	for (i = 0; i < devnum; i++, devlist++) {
664		dip = devlist->dv_dip;
665		cpuid = sbdp_get_cpuid(hdp, dip);
666		if (cpuid < 0) {
667			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
668				cmn_err(CE_WARN,
669				    "sbd:%s: failed to get cpuid for "
670				    "dip (0x%p)", f, (void *)dip);
671				continue;
672			} else {
673				SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
674				break;
675			}
676		}
677
678		cpu = cpu_get(cpuid);
679
680		if (cpu == NULL) {
681			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
682				cmn_err(CE_WARN,
683				    "sbd:%s: failed to get cpu %d",
684				    f, cpuid);
685				continue;
686			} else {
687				SBD_SET_ERR(ep, ESBD_INTERNAL);
688				SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
689				break;
690			}
691		}
692
693		unit = sbdp_get_unit_num(hdp, dip);
694		if (unit < 0) {
695			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
696				cmn_err(CE_WARN,
697				    "sbd:%s: failed to get unit (cpu %d)",
698				    f, cpuid);
699				continue;
700			} else {
701				SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
702				break;
703			}
704		}
705
706		PR_CPU("%s: OS detach cpu-unit (%d.%d)\n",
707		    f, sbp->sb_num, unit);
708
709		/*
710		 * CPUs were offlined during Release.
711		 */
712		if (cpu_is_poweredoff(cpu)) {
713			PR_CPU("%s: cpu %d already powered OFF\n", f, cpuid);
714			continue;
715		}
716
717		if (cpu_is_offline(cpu)) {
718			int	e;
719
720			if (e = cpu_poweroff(cpu)) {
721				cmn_err(CE_WARN,
722				    "%s: failed to power-off cpu %d "
723				    "(errno %d)",
724				    f, cpu->cpu_id, e);
725				SBD_SET_ERR(ep, ESBD_CPUSTOP);
726				SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
727
728				rv = -1;
729				break;
730			} else {
731				PR_CPU("%s: cpu %d powered OFF\n",
732					f, cpuid);
733			}
734		} else {
735			cmn_err(CE_WARN, "%s: cpu %d still active",
736				f, cpu->cpu_id);
737			SBD_SET_ERR(ep, ESBD_BUSY);
738			SBD_SET_ERRSTR(ep, sbp->sb_cpupath[i]);
739			rv = -1;
740			break;
741		}
742	}
743
744	sbd_release_sbdp_handle(hdp);
745
746	return (rv);
747}
748
749int
750sbd_post_detach_cpu(sbd_handle_t *hp, sbd_devlist_t *devlist, int devnum)
751{
752	static fn_t	f = "sbd_post_detach_cpu";
753	int		i;
754	sbderror_t	*ep = SBD_HD2ERR(hp);
755	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
756	processorid_t	cpuid;
757	dev_info_t	*dip;
758	sbdp_handle_t	*hdp;
759	sbd_cpu_unit_t	*cpup;
760	int		unit;
761
762	PR_CPU("%s...\n", f);
763
764	/*
765	 * We should be holding the cpu_lock at this point,
766	 * and should have blocked device tree changes.
767	 */
768	ASSERT(MUTEX_HELD(&cpu_lock));
769
770	for (i = 0; i < devnum; i++, devlist++) {
771		dip = devlist->dv_dip;
772		hdp = sbd_get_sbdp_handle(sbp, hp);
773		cpuid = sbdp_get_cpuid(hdp, dip);
774		if (cpuid < 0) {
775			if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
776				cmn_err(CE_WARN,
777					"sbd:%s: failed to get cpuid for "
778					"dip (0x%p)", f, (void *)dip);
779				continue;
780			} else {
781				SBD_GET_PERR(hdp->h_err, ep);
782				break;
783			}
784		}
785		/*
786		 * if there is no error mark the cpu as unusable
787		 */
788		if (SBD_GET_ERR(ep) == 0) {
789			unit = sbdp_get_unit_num(hdp, dip);
790			if (unit < 0) {
791				if (hp->h_flags & SBD_IOCTL_FLAG_FORCE) {
792					cmn_err(CE_WARN,
793					    "sbd:%s: failed to get unit "
794					    "(cpu %d)", f, cpuid);
795					continue;
796				} else {
797					SBD_GET_PERR(hdp->h_err,
798					    SBD_HD2ERR(hp));
799					break;
800				}
801			}
802			cpup = SBD_GET_BOARD_CPUUNIT(sbp, unit);
803			cpup->sbc_cm.sbdev_cond = SBD_COND_UNUSABLE;
804		}
805		sbd_release_sbdp_handle(hdp);
806	}
807
808	mutex_exit(&cpu_lock);
809
810
811	return (0);
812}
813
814/*
815 * Cancel previous release operation for cpu.  For cpus this means simply
816 * bringing cpus that were offline back online.  Note that they had to have been
817 * online at the time they were released.  If attempting to power on or online
818 * a CPU fails, SBD_CPUERR_FATAL is returned to indicate that the CPU appears to
819 * be unsalvageable.  If a CPU reaches an online or nointr state but can't be
820 * taken to a "lesser" state, SBD_CPUERR_RECOVERABLE is returned to indicate
821 * that it was not returned to its original state but appears to be functional.
822 * Note that the latter case can occur due to unexpected but non-erroneous CPU
823 * manipulation (e.g. by the "psradm" command) during the DR operation.
824 */
825int
826sbd_cancel_cpu(sbd_handle_t *hp, int unit)
827{
828	int		rv = SBD_CPUERR_NONE;
829	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
830	sbderror_t	*ep = SBD_HD2ERR(hp);
831	sbd_cpu_unit_t	*cp;
832	static fn_t	f = "sbd_cancel_cpu";
833	struct cpu	*cpup;
834	int		cpu_offline_flags = 0;
835
836	PR_ALL("%s...\n", f);
837
838	cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
839
840	/*
841	 * If CPU should remain off, nothing needs to be done.
842	 */
843	if (cpu_flagged_poweredoff(cp->sbc_cpu_flags))
844		return (rv);
845
846	if (hp->h_flags & SBD_IOCTL_FLAG_FORCE)
847		cpu_offline_flags = CPU_FORCED;
848
849	/*
850	 * CPU had been either offline, online, or set to no-intr.  We
851	 * will return a component to its original state that it was
852	 * prior to the failed DR operation.  There is a possible race
853	 * condition between the calls to this function and re-obtaining
854	 * the cpu_lock where a cpu state could change.  Because of this
855	 * we can't externally document that we are trying to roll cpus
856	 * back to their original state, but we believe a best effort
857	 * should be made.
858	 */
859
860	mutex_enter(&cpu_lock);
861	cpup = cpu[cp->sbc_cpu_id];
862
863	/*
864	 * The following will compare the cpu's current state with a
865	 * snapshot of its state taken before the failed DR operation
866	 * had started.
867	 */
868	/* POWEROFF */
869	if (cpu_is_poweredoff(cpup)) {
870		if (cpu_poweron(cpup)) {
871			cmn_err(CE_WARN,
872			    "sbd:%s: failed to power-on cpu %d",
873			    f, cp->sbc_cpu_id);
874			SBD_SET_ERR(ep, ESBD_CPUSTART);
875			SBD_SET_ERRSTR(ep, sbp->sb_cpupath[unit]);
876			rv = SBD_CPUERR_FATAL;
877			goto out;
878		}
879		SBD_INJECT_ERR(SBD_POWERON_CPU_PSEUDO_ERR,
880		    hp->h_err, EIO,
881		    ESBD_CPUSTART,
882		    sbp->sb_cpupath[unit]);
883	}
884
885	/* OFFLINE */
886	if (cpu_is_offline(cpup)) {
887		if (cpu_flagged_offline(cp->sbc_cpu_flags)) {
888			PR_CPU("%s: leaving cpu %d OFFLINE\n",
889			    f, cp->sbc_cpu_id);
890		} else if (cpu_online(cpup, 0)) {
891			cmn_err(CE_WARN,
892			    "sbd:%s: failed to online cpu %d",
893			    f, cp->sbc_cpu_id);
894			SBD_SET_ERR(ep, ESBD_ONLINE);
895			SBD_SET_ERRSTR(ep, sbp->sb_cpupath[unit]);
896			rv = SBD_CPUERR_FATAL;
897			goto out;
898		} else {
899			SBD_INJECT_ERR(SBD_ONLINE_CPU_PSEUDO_ERR,
900			    hp->h_err, EIO,
901			    ESBD_ONLINE,
902			    sbp->sb_cpupath[unit]);
903		}
904	}
905
906	/* ONLINE */
907	if (cpu_is_online(cpup)) {
908		if (cpu_flagged_online(cp->sbc_cpu_flags)) {
909			PR_CPU("%s: setting cpu %d ONLINE\n",
910			    f, cp->sbc_cpu_id);
911		} else if (cpu_flagged_offline(cp->sbc_cpu_flags)) {
912			if (cpu_offline(cpup, cpu_offline_flags)) {
913				cmn_err(CE_WARN,
914				    "sbd:%s: failed to offline"
915				    " cpu %d", f, cp->sbc_cpu_id);
916				rv = SBD_CPUERR_RECOVERABLE;
917				goto out;
918			}
919		} else if (cpu_flagged_nointr(cp->sbc_cpu_flags)) {
920			if (cpu_intr_disable(cpup)) {
921				cmn_err(CE_WARN, "%s: failed to "
922				    "disable interrupts on cpu %d",
923				    f, cp->sbc_cpu_id);
924				rv = SBD_CPUERR_RECOVERABLE;
925			} else {
926				PR_CPU("%s: setting cpu %d to NOINTR"
927				    " (was online)\n",
928				    f, cp->sbc_cpu_id);
929			}
930			goto out;
931		}
932	}
933
934	/* NOINTR */
935	if (cpu_is_nointr(cpup)) {
936		if (cpu_flagged_online(cp->sbc_cpu_flags)) {
937			cpu_intr_enable(cpup);
938			PR_CPU("%s: setting cpu %d ONLINE"
939			    "(was nointr)\n",
940			    f, cp->sbc_cpu_id);
941		}
942		if (cpu_flagged_offline(cp->sbc_cpu_flags)) {
943			if (cpu_offline(cpup, cpu_offline_flags)) {
944				cmn_err(CE_WARN,
945				    "sbd:%s: failed to offline"
946				    " cpu %d", f, cp->sbc_cpu_id);
947				rv = SBD_CPUERR_RECOVERABLE;
948			}
949		}
950	}
951out:
952	mutex_exit(&cpu_lock);
953
954	return (rv);
955}
956
957int
958sbd_connect_cpu(sbd_board_t *sbp, int unit)
959{
960	int		rv;
961	processorid_t	cpuid;
962	struct cpu	*cpu;
963	dev_info_t	*dip;
964	sbdp_handle_t	*hdp;
965	extern kmutex_t	cpu_lock;
966	static fn_t	f = "sbd_connect_cpu";
967	sbd_handle_t	*hp = MACHBD2HD(sbp);
968
969	/*
970	 * get dip for cpu just located in tree walk
971	 */
972	if (SBD_DEV_IS_PRESENT(sbp, SBD_COMP_CPU, unit)) {
973		dip = sbp->sb_devlist[NIX(SBD_COMP_CPU)][unit];
974		if (dip == NULL) {
975			cmn_err(CE_WARN,
976			"sbd:%s: bad dip for cpu unit %d board %d",
977			f, unit, sbp->sb_num);
978			return (-1);
979		}
980		PR_CPU("%s...\n", f);
981	} else {
982		return (0);
983	}
984
985	/*
986	 * if sbd has attached this cpu, no need to bring
987	 * it out of reset
988	 */
989	if (SBD_DEV_IS_ATTACHED(sbp, SBD_COMP_CPU, unit)) {
990		return (0);
991	}
992
993	hdp = sbd_get_sbdp_handle(sbp, hp);
994
995	cpuid = sbdp_get_cpuid(hdp, dip);
996	if (cpuid == -1) {
997		sbd_release_sbdp_handle(hdp);
998		return (-1);
999	}
1000
1001	/*
1002	 * if the cpu is already under Solaris control,
1003	 * do not wake it up
1004	 */
1005	mutex_enter(&cpu_lock);
1006	cpu = cpu_get(cpuid);
1007	mutex_exit(&cpu_lock);
1008	if (cpu != NULL) {
1009		sbd_release_sbdp_handle(hdp);
1010		return (0);
1011	}
1012
1013	rv = sbdp_connect_cpu(hdp, dip, cpuid);
1014
1015	if (rv != 0) {
1016		sbp->sb_memaccess_ok = 0;
1017		cmn_err(CE_WARN,
1018			"sbd:%s: failed to wake up cpu unit %d board %d",
1019			f, unit, sbp->sb_num);
1020		sbd_release_sbdp_handle(hdp);
1021		return (rv);
1022	}
1023	sbd_release_sbdp_handle(hdp);
1024
1025	return (rv);
1026}
1027
1028int
1029sbd_disconnect_cpu(sbd_handle_t *hp, int unit)
1030{
1031	sbd_board_t	*sbp = SBDH2BD(hp->h_sbd);
1032	int		rv;
1033	dev_info_t	*dip;
1034	sbdp_handle_t	*hdp;
1035	sbd_cpu_unit_t *cp;
1036	processorid_t   cpuid;
1037	static fn_t	f = "sbd_disconnect_cpu";
1038
1039	PR_CPU("%s...\n", f);
1040
1041	ASSERT((SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit) ==
1042						SBD_STATE_CONNECTED) ||
1043		(SBD_DEVICE_STATE(sbp, SBD_COMP_CPU, unit) ==
1044						SBD_STATE_UNCONFIGURED));
1045
1046	cp = SBD_GET_BOARD_CPUUNIT(sbp, unit);
1047
1048	cpuid = cp->sbc_cpu_id;
1049
1050	dip = sbp->sb_devlist[NIX(SBD_COMP_CPU)][unit];
1051
1052	hdp = sbd_get_sbdp_handle(sbp, hp);
1053
1054	rv = sbdp_disconnect_cpu(hdp, dip, cpuid);
1055
1056	if (rv != 0) {
1057		SBD_GET_PERR(hdp->h_err, SBD_HD2ERR(hp));
1058	}
1059	sbd_release_sbdp_handle(hdp);
1060
1061	return (rv);
1062}
1063