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