xref: /illumos-gate/usr/src/uts/i86pc/io/dr/dr_cpu.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /*
27  * Copyright (c) 2010, Intel Corporation.
28  * All rights reserved.
29  */
30 
31 /*
32  * CPU support routines for DR
33  */
34 
35 #include <sys/note.h>
36 #include <sys/debug.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
39 #include <sys/dditypes.h>
40 #include <sys/ddi.h>
41 #include <sys/sunddi.h>
42 #include <sys/sunndi.h>
43 #include <sys/ndi_impldefs.h>
44 #include <sys/kmem.h>
45 #include <sys/processor.h>
46 #include <sys/cpuvar.h>
47 #include <sys/promif.h>
48 #include <sys/sysmacros.h>
49 #include <sys/archsystm.h>
50 #include <sys/machsystm.h>
51 #include <sys/cpu_module.h>
52 #include <sys/cmn_err.h>
53 
54 #include <sys/dr.h>
55 #include <sys/dr_util.h>
56 
57 /* for the DR*INTERNAL_ERROR macros.  see sys/dr.h. */
58 static char *dr_ie_fmt = "dr_cpu.c %d";
59 
60 int
61 dr_cpu_unit_is_sane(dr_board_t *bp, dr_cpu_unit_t *cp)
62 {
63 #ifdef DEBUG
64 	ASSERT(cp->sbc_cm.sbdev_bp == bp);
65 	ASSERT(cp->sbc_cm.sbdev_type == SBD_COMP_CPU);
66 #else
67 	_NOTE(ARGUNUSED(bp))
68 	_NOTE(ARGUNUSED(cp))
69 #endif
70 
71 	return (1);
72 }
73 
74 static int
75 dr_errno2ecode(int error)
76 {
77 	int	rv;
78 
79 	switch (error) {
80 	case EBUSY:
81 		rv = ESBD_BUSY;
82 		break;
83 	case EINVAL:
84 		rv = ESBD_INVAL;
85 		break;
86 	case EALREADY:
87 		rv = ESBD_ALREADY;
88 		break;
89 	case ENODEV:
90 		rv = ESBD_NODEV;
91 		break;
92 	case ENOMEM:
93 		rv = ESBD_NOMEM;
94 		break;
95 	default:
96 		rv = ESBD_INVAL;
97 	}
98 
99 	return (rv);
100 }
101 
102 /*
103  * On x86, the "clock-frequency" and cache size device properties may be
104  * unavailable before CPU starts. If they are unavailabe, just set them to zero.
105  */
106 static void
107 dr_cpu_set_prop(dr_cpu_unit_t *cp)
108 {
109 	sbd_error_t	*err;
110 	dev_info_t	*dip;
111 	uint64_t	clock_freq;
112 	int		ecache_size = 0;
113 	char		*cache_str = NULL;
114 
115 	err = drmach_get_dip(cp->sbc_cm.sbdev_id, &dip);
116 	if (err) {
117 		DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
118 		return;
119 	}
120 
121 	if (dip == NULL) {
122 		DR_DEV_INTERNAL_ERROR(&cp->sbc_cm);
123 		return;
124 	}
125 
126 	/* read in the CPU speed */
127 	clock_freq = (unsigned int)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
128 	    DDI_PROP_DONTPASS, "clock-frequency", 0);
129 
130 	/*
131 	 * The ecache property string is not the same
132 	 * for all CPU implementations.
133 	 */
134 	switch (cp->sbc_cpu_impl) {
135 	case X86_CPU_IMPL_NEHALEM_EX:
136 		cache_str = "l3-cache-size";
137 		break;
138 	default:
139 		cmn_err(CE_WARN, "Unknown cpu implementation=0x%x",
140 		    cp->sbc_cpu_impl);
141 		break;
142 	}
143 
144 	if (cache_str != NULL) {
145 		/* read in the ecache size */
146 		/*
147 		 * If the property is not found in the CPU node,
148 		 * it has to be kept in the core or cmp node so
149 		 * we just keep looking.
150 		 */
151 
152 		ecache_size = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
153 		    cache_str, 0);
154 	}
155 
156 	/* convert to the proper units */
157 	cp->sbc_speed = (clock_freq + 500000) / 1000000;
158 	cp->sbc_ecache = ecache_size / (1024 * 1024);
159 }
160 
161 void
162 dr_init_cpu_unit(dr_cpu_unit_t *cp)
163 {
164 	sbd_error_t	*err;
165 	dr_state_t	new_state;
166 	int		cpuid;
167 	int		impl;
168 
169 	if (DR_DEV_IS_ATTACHED(&cp->sbc_cm)) {
170 		new_state = DR_STATE_CONFIGURED;
171 		cp->sbc_cm.sbdev_cond = SBD_COND_OK;
172 	} else if (DR_DEV_IS_PRESENT(&cp->sbc_cm)) {
173 		new_state = DR_STATE_CONNECTED;
174 		cp->sbc_cm.sbdev_cond = SBD_COND_OK;
175 	} else {
176 		new_state = DR_STATE_EMPTY;
177 		cp->sbc_cm.sbdev_cond = SBD_COND_UNKNOWN;
178 	}
179 
180 	if (DR_DEV_IS_PRESENT(&cp->sbc_cm)) {
181 		err = drmach_cpu_get_id(cp->sbc_cm.sbdev_id, &cpuid);
182 		if (err) {
183 			DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
184 			new_state = DR_STATE_FATAL;
185 			goto done;
186 		}
187 
188 		err = drmach_cpu_get_impl(cp->sbc_cm.sbdev_id, &impl);
189 		if (err) {
190 			DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
191 			new_state = DR_STATE_FATAL;
192 			goto done;
193 		}
194 	} else {
195 		cp->sbc_cpu_id = -1;
196 		cp->sbc_cpu_impl = -1;
197 		goto done;
198 	}
199 
200 	cp->sbc_cpu_id = cpuid;
201 	cp->sbc_cpu_impl = impl;
202 
203 	/* if true at init time, it must always be true */
204 	ASSERT(dr_cpu_unit_is_sane(cp->sbc_cm.sbdev_bp, cp));
205 
206 	mutex_enter(&cpu_lock);
207 	if ((cpuid >= 0) && cpu[cpuid])
208 		cp->sbc_cpu_flags = cpu[cpuid]->cpu_flags;
209 	else
210 		cp->sbc_cpu_flags = P_OFFLINE | P_POWEROFF;
211 	mutex_exit(&cpu_lock);
212 
213 	dr_cpu_set_prop(cp);
214 
215 done:
216 	/* delay transition until fully initialized */
217 	dr_device_transition(&cp->sbc_cm, new_state);
218 }
219 
220 int
221 dr_pre_attach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
222 {
223 	int		i;
224 	static fn_t	f = "dr_pre_attach_cpu";
225 
226 	PR_CPU("%s...\n", f);
227 
228 	for (i = 0; i < devnum; i++) {
229 		dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i];
230 
231 		ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
232 
233 		/*
234 		 * Print a console message for each attachment
235 		 * point. For CMP devices, this means that only
236 		 * one message should be printed, no matter how
237 		 * many cores are actually present.
238 		 */
239 		if ((up->sbc_cm.sbdev_unum % MAX_CORES_PER_CMP) == 0) {
240 			cmn_err(CE_CONT, "OS configure %s",
241 			    up->sbc_cm.sbdev_path);
242 		}
243 	}
244 
245 	/*
246 	 * Block out status threads while creating
247 	 * devinfo tree branches
248 	 */
249 	dr_lock_status(hp->h_bd);
250 	ndi_devi_enter(ddi_root_node(), (int *)(&hp->h_ndi));
251 	mutex_enter(&cpu_lock);
252 
253 	return (0);
254 }
255 
256 /*ARGSUSED*/
257 void
258 dr_attach_cpu(dr_handle_t *hp, dr_common_unit_t *cp)
259 {
260 	sbd_error_t	*err;
261 	processorid_t	 cpuid;
262 	int		 rv;
263 
264 	ASSERT(MUTEX_HELD(&cpu_lock));
265 
266 	err = drmach_configure(cp->sbdev_id, 0);
267 	if (err) {
268 		DRERR_SET_C(&cp->sbdev_error, &err);
269 		return;
270 	}
271 
272 	err = drmach_cpu_get_id(cp->sbdev_id, &cpuid);
273 	if (err) {
274 		DRERR_SET_C(&cp->sbdev_error, &err);
275 
276 		err = drmach_unconfigure(cp->sbdev_id, DEVI_BRANCH_DESTROY);
277 		if (err)
278 			sbd_err_clear(&err);
279 	} else if ((rv = cpu_configure(cpuid)) != 0) {
280 		dr_dev_err(CE_WARN, cp, dr_errno2ecode(rv));
281 		err = drmach_unconfigure(cp->sbdev_id, DEVI_BRANCH_DESTROY);
282 		if (err)
283 			sbd_err_clear(&err);
284 	} else {
285 		dr_cpu_unit_t *up = (dr_cpu_unit_t *)cp;
286 		up->sbc_cpu_id = cpuid;
287 	}
288 }
289 
290 /*
291  * dr_post_attach_cpu
292  *
293  * sbd error policy: Does not stop on error.  Processes all units in list.
294  */
295 int
296 dr_post_attach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
297 {
298 	int		i;
299 	int		errflag = 0;
300 	static fn_t	f = "dr_post_attach_cpu";
301 
302 	PR_CPU("%s...\n", f);
303 
304 	/* Startup and online newly-attached CPUs */
305 	for (i = 0; i < devnum; i++) {
306 		dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i];
307 		struct cpu	*cp;
308 
309 		ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
310 
311 		cp = cpu_get(up->sbc_cpu_id);
312 		if (cp == NULL) {
313 			cmn_err(CE_WARN, "%s: cpu_get failed for cpu %d",
314 			    f, up->sbc_cpu_id);
315 			continue;
316 		}
317 
318 		if (cpu_is_poweredoff(cp)) {
319 			if (cpu_poweron(cp) != 0) {
320 				dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_CPUSTART);
321 				errflag = 1;
322 			}
323 			PR_CPU("%s: cpu %d powered ON\n", f, up->sbc_cpu_id);
324 		}
325 
326 		if (cpu_is_offline(cp)) {
327 			PR_CPU("%s: onlining cpu %d...\n", f, up->sbc_cpu_id);
328 
329 			if (cpu_online(cp) != 0) {
330 				dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_ONLINE);
331 				errflag = 1;
332 			}
333 		}
334 
335 	}
336 
337 	mutex_exit(&cpu_lock);
338 	ndi_devi_exit(ddi_root_node(), hp->h_ndi);
339 	dr_unlock_status(hp->h_bd);
340 
341 	if (errflag)
342 		return (-1);
343 	else
344 		return (0);
345 }
346 
347 /*
348  * dr_pre_release_cpu
349  *
350  * sbd error policy: Stops on first error.
351  */
352 int
353 dr_pre_release_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
354 {
355 	int		c, cix, i, lastoffline = -1, rv = 0;
356 	processorid_t	cpuid;
357 	struct cpu	*cp;
358 	dr_cpu_unit_t	*up;
359 	dr_devset_t	devset;
360 	sbd_dev_stat_t	*ds;
361 	static fn_t	f = "dr_pre_release_cpu";
362 	int		cpu_flags = 0;
363 
364 	devset = DR_DEVS_PRESENT(hp->h_bd);
365 
366 	/* allocate status struct storage. */
367 	ds = (sbd_dev_stat_t *) kmem_zalloc(sizeof (sbd_dev_stat_t) *
368 	    MAX_CPU_UNITS_PER_BOARD, KM_SLEEP);
369 
370 	cix = dr_cpu_status(hp, devset, ds);
371 
372 	mutex_enter(&cpu_lock);
373 
374 	for (i = 0; i < devnum; i++) {
375 		up = (dr_cpu_unit_t *)devlist[i];
376 		if (!DR_DEV_IS_ATTACHED(&up->sbc_cm)) {
377 			continue;
378 		}
379 		ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
380 
381 		/*
382 		 * On x86 systems, some CPUs can't be unconfigured.
383 		 * For example, CPU0 can't be unconfigured because many other
384 		 * components have a dependency on it.
385 		 * This check determines if a CPU is currently in use and
386 		 * returns a "Device busy" error if so.
387 		 */
388 		for (c = 0; c < cix; c++) {
389 			if (ds[c].d_cpu.cs_unit == up->sbc_cm.sbdev_unum) {
390 				if (ds[c].d_cpu.cs_busy) {
391 					dr_dev_err(CE_WARN, &up->sbc_cm,
392 					    ESBD_BUSY);
393 					rv = -1;
394 					break;
395 				}
396 			}
397 		}
398 		if (c < cix)
399 			break;
400 
401 		cpuid = up->sbc_cpu_id;
402 		if ((cp = cpu_get(cpuid)) == NULL) {
403 			dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE);
404 			rv = -1;
405 			break;
406 		}
407 
408 		/* used by dr_cancel_cpu during error flow */
409 		up->sbc_cpu_flags = cp->cpu_flags;
410 
411 		if (CPU_ACTIVE(cp)) {
412 			if (dr_cmd_flags(hp) & SBD_FLAG_FORCE)
413 				cpu_flags = CPU_FORCED;
414 
415 			PR_CPU("%s: offlining cpu %d\n", f, cpuid);
416 			if (cpu_offline(cp, cpu_flags)) {
417 				PR_CPU("%s: failed to offline cpu %d\n", f,
418 				    cpuid);
419 				dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE);
420 				if (disp_bound_threads(cp, 0)) {
421 					cmn_err(CE_WARN, "%s: thread(s) bound "
422 					    "to cpu %d", f, cp->cpu_id);
423 				}
424 				rv = -1;
425 				break;
426 			} else
427 				lastoffline = i;
428 		}
429 
430 		if (!rv) {
431 			sbd_error_t *err;
432 
433 			err = drmach_release(up->sbc_cm.sbdev_id);
434 			if (err) {
435 				DRERR_SET_C(&up->sbc_cm.sbdev_error, &err);
436 				rv = -1;
437 				break;
438 			}
439 		}
440 	}
441 
442 	mutex_exit(&cpu_lock);
443 
444 	if (rv) {
445 		/*
446 		 * Need to unwind others since at this level (pre-release)
447 		 * the device state has not yet transitioned and failures
448 		 * will prevent us from reaching the "post" release
449 		 * function where states are normally transitioned.
450 		 */
451 		for (i = lastoffline; i >= 0; i--) {
452 			up = (dr_cpu_unit_t *)devlist[i];
453 			(void) dr_cancel_cpu(up);
454 		}
455 	}
456 
457 	kmem_free(ds, sizeof (sbd_dev_stat_t) * MAX_CPU_UNITS_PER_BOARD);
458 	return (rv);
459 }
460 
461 /*
462  * dr_pre_detach_cpu
463  *
464  * sbd error policy: Stops on first error.
465  */
466 int
467 dr_pre_detach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
468 {
469 	_NOTE(ARGUNUSED(hp))
470 
471 	int		i;
472 	int		cpu_flags = 0;
473 	static fn_t	f = "dr_pre_detach_cpu";
474 
475 	PR_CPU("%s...\n", f);
476 
477 	/*
478 	 * Block out status threads while destroying devinfo tree
479 	 * branches
480 	 */
481 	dr_lock_status(hp->h_bd);
482 	mutex_enter(&cpu_lock);
483 
484 	for (i = 0; i < devnum; i++) {
485 		dr_cpu_unit_t *up = (dr_cpu_unit_t *)devlist[i];
486 		struct cpu	*cp;
487 
488 		if (!DR_DEV_IS_ATTACHED(&up->sbc_cm)) {
489 			continue;
490 		}
491 
492 		ASSERT(dr_cpu_unit_is_sane(hp->h_bd, up));
493 
494 		cp = cpu_get(up->sbc_cpu_id);
495 		if (cp == NULL)
496 			continue;
497 
498 		/*
499 		 * Print a console message for each attachment
500 		 * point. For CMP devices, this means that only
501 		 * one message should be printed, no matter how
502 		 * many cores are actually present.
503 		 */
504 		if ((up->sbc_cm.sbdev_unum % MAX_CORES_PER_CMP) == 0) {
505 			cmn_err(CE_CONT, "OS unconfigure %s\n",
506 			    up->sbc_cm.sbdev_path);
507 		}
508 
509 		/*
510 		 * CPUs were offlined during Release.
511 		 */
512 		if (cpu_is_poweredoff(cp)) {
513 			PR_CPU("%s: cpu %d already powered OFF\n",
514 			    f, up->sbc_cpu_id);
515 			continue;
516 		}
517 
518 		if (!cpu_is_offline(cp)) {
519 			if (dr_cmd_flags(hp) & SBD_FLAG_FORCE)
520 				cpu_flags = CPU_FORCED;
521 			/* cpu was onlined after release.  Offline it again */
522 			PR_CPU("%s: offlining cpu %d\n", f, up->sbc_cpu_id);
523 			if (cpu_offline(cp, cpu_flags)) {
524 				PR_CPU("%s: failed to offline cpu %d\n",
525 				    f, up->sbc_cpu_id);
526 				dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_OFFLINE);
527 				if (disp_bound_threads(cp, 0)) {
528 					cmn_err(CE_WARN, "%s: thread(s) bound "
529 					    "to cpu %d", f, cp->cpu_id);
530 				}
531 				goto err;
532 			}
533 		}
534 		if (cpu_poweroff(cp) != 0) {
535 			dr_dev_err(CE_WARN, &up->sbc_cm, ESBD_CPUSTOP);
536 			goto err;
537 		} else {
538 			PR_CPU("%s: cpu %d powered OFF\n", f, up->sbc_cpu_id);
539 		}
540 	}
541 
542 	return (0);
543 
544 err:
545 	mutex_exit(&cpu_lock);
546 	dr_unlock_status(hp->h_bd);
547 	return (-1);
548 }
549 
550 /*ARGSUSED*/
551 void
552 dr_detach_cpu(dr_handle_t *hp, dr_common_unit_t *cp)
553 {
554 	sbd_error_t	*err;
555 	processorid_t	 cpuid;
556 	int		 rv;
557 	dr_cpu_unit_t	*up = (dr_cpu_unit_t *)cp;
558 
559 	ASSERT(MUTEX_HELD(&cpu_lock));
560 
561 	if (!DR_DEV_IS_ATTACHED(&up->sbc_cm)) {
562 		return;
563 	}
564 
565 	err = drmach_cpu_get_id(cp->sbdev_id, &cpuid);
566 	if (err) {
567 		DRERR_SET_C(&cp->sbdev_error, &err);
568 	} else if ((rv = cpu_unconfigure(cpuid)) != 0) {
569 		dr_dev_err(CE_IGNORE, cp, dr_errno2ecode(rv));
570 	} else {
571 		err = drmach_unconfigure(cp->sbdev_id, DEVI_BRANCH_DESTROY);
572 		if (err) {
573 			DRERR_SET_C(&cp->sbdev_error, &err);
574 		} else {
575 			up->sbc_cpu_id = -1;
576 		}
577 	}
578 }
579 
580 /*ARGSUSED1*/
581 int
582 dr_post_detach_cpu(dr_handle_t *hp, dr_common_unit_t **devlist, int devnum)
583 {
584 	static fn_t	f = "dr_post_detach_cpu";
585 
586 	PR_CPU("%s...\n", f);
587 	hp->h_ndi = 0;
588 
589 	mutex_exit(&cpu_lock);
590 	dr_unlock_status(hp->h_bd);
591 
592 	return (0);
593 }
594 
595 static void
596 dr_fill_cpu_stat(dr_cpu_unit_t *cp, drmach_status_t *pstat, sbd_cpu_stat_t *csp)
597 {
598 	ASSERT(cp && pstat && csp);
599 
600 	/* Fill in the common status information */
601 	bzero((caddr_t)csp, sizeof (*csp));
602 	csp->cs_type = cp->sbc_cm.sbdev_type;
603 	csp->cs_unit = cp->sbc_cm.sbdev_unum;
604 	(void) strlcpy(csp->cs_name, pstat->type, sizeof (csp->cs_name));
605 	csp->cs_cond = cp->sbc_cm.sbdev_cond;
606 	csp->cs_busy = cp->sbc_cm.sbdev_busy | pstat->busy;
607 	csp->cs_time = cp->sbc_cm.sbdev_time;
608 	csp->cs_ostate = cp->sbc_cm.sbdev_ostate;
609 	csp->cs_suspend = 0;
610 
611 	/* CPU specific status data */
612 	csp->cs_cpuid = cp->sbc_cpu_id;
613 
614 	/*
615 	 * If the speed and ecache properties have not been
616 	 * cached yet, read them in from the device tree.
617 	 */
618 	if ((cp->sbc_speed == 0) || (cp->sbc_ecache == 0))
619 		dr_cpu_set_prop(cp);
620 
621 	/* use the cached speed and ecache values */
622 	csp->cs_speed = cp->sbc_speed;
623 	csp->cs_ecache = cp->sbc_ecache;
624 
625 	mutex_enter(&cpu_lock);
626 	if (!cpu_get(csp->cs_cpuid)) {
627 		/* ostate must be UNCONFIGURED */
628 		csp->cs_cm.c_ostate = SBD_STAT_UNCONFIGURED;
629 	}
630 	mutex_exit(&cpu_lock);
631 }
632 
633 /*ARGSUSED2*/
634 static void
635 dr_fill_cmp_stat(sbd_cpu_stat_t *csp, int ncores, int impl, sbd_cmp_stat_t *psp)
636 {
637 	int	core;
638 
639 	ASSERT(csp && psp && (ncores >= 1));
640 
641 	bzero((caddr_t)psp, sizeof (*psp));
642 
643 	/*
644 	 * Fill in the common status information based
645 	 * on the data for the first core.
646 	 */
647 	psp->ps_type = SBD_COMP_CMP;
648 	psp->ps_unit = DR_UNUM2SBD_UNUM(csp->cs_unit, SBD_COMP_CMP);
649 	(void) strlcpy(psp->ps_name, csp->cs_name, sizeof (psp->ps_name));
650 	psp->ps_cond = csp->cs_cond;
651 	psp->ps_busy = csp->cs_busy;
652 	psp->ps_time = csp->cs_time;
653 	psp->ps_ostate = csp->cs_ostate;
654 	psp->ps_suspend = csp->cs_suspend;
655 
656 	/* CMP specific status data */
657 	*psp->ps_cpuid = csp->cs_cpuid;
658 	psp->ps_ncores = 1;
659 	psp->ps_speed = csp->cs_speed;
660 	psp->ps_ecache = csp->cs_ecache;
661 
662 	/*
663 	 * Walk through the data for the remaining cores.
664 	 * Make any adjustments to the common status data,
665 	 * or the shared CMP specific data if necessary.
666 	 */
667 	for (core = 1; core < ncores; core++) {
668 		/*
669 		 * The following properties should be the same
670 		 * for all the cores of the CMP.
671 		 */
672 		ASSERT(psp->ps_unit == DR_UNUM2SBD_UNUM(csp[core].cs_unit,
673 		    SBD_COMP_CMP));
674 
675 		if (csp[core].cs_speed > psp->ps_speed)
676 			psp->ps_speed = csp[core].cs_speed;
677 		if (csp[core].cs_ecache > psp->ps_ecache)
678 			psp->ps_ecache = csp[core].cs_ecache;
679 
680 		psp->ps_cpuid[core] = csp[core].cs_cpuid;
681 		psp->ps_ncores++;
682 
683 		/* adjust time if necessary */
684 		if (csp[core].cs_time > psp->ps_time) {
685 			psp->ps_time = csp[core].cs_time;
686 		}
687 
688 		psp->ps_busy |= csp[core].cs_busy;
689 
690 		/*
691 		 * If any of the cores are configured, the
692 		 * entire CMP is marked as configured.
693 		 */
694 		if (csp[core].cs_ostate == SBD_STAT_CONFIGURED) {
695 			psp->ps_ostate = csp[core].cs_ostate;
696 		}
697 	}
698 }
699 
700 int
701 dr_cpu_status(dr_handle_t *hp, dr_devset_t devset, sbd_dev_stat_t *dsp)
702 {
703 	int		cmp;
704 	int		core;
705 	int		ncpu;
706 	dr_board_t	*bp;
707 	sbd_cpu_stat_t	*cstat;
708 	int		impl;
709 
710 	bp = hp->h_bd;
711 	ncpu = 0;
712 
713 	devset &= DR_DEVS_PRESENT(bp);
714 	cstat = kmem_zalloc(sizeof (sbd_cpu_stat_t) * MAX_CORES_PER_CMP,
715 	    KM_SLEEP);
716 
717 	/*
718 	 * Treat every CPU as a CMP. In the case where the
719 	 * device is not a CMP, treat it as a CMP with only
720 	 * one core.
721 	 */
722 	for (cmp = 0; cmp < MAX_CMP_UNITS_PER_BOARD; cmp++) {
723 		int		ncores;
724 		dr_cpu_unit_t	*cp;
725 		drmach_status_t	pstat;
726 		sbd_error_t	*err;
727 		sbd_cmp_stat_t	*psp;
728 
729 		if ((devset & DEVSET(SBD_COMP_CMP, cmp)) == 0) {
730 			continue;
731 		}
732 
733 		ncores = 0;
734 
735 		for (core = 0; core < MAX_CORES_PER_CMP; core++) {
736 
737 			cp = dr_get_cpu_unit(bp, DR_CMP_CORE_UNUM(cmp, core));
738 
739 			if (cp->sbc_cm.sbdev_state == DR_STATE_EMPTY) {
740 				/* present, but not fully initialized */
741 				continue;
742 			}
743 
744 			ASSERT(dr_cpu_unit_is_sane(hp->h_bd, cp));
745 
746 			/* skip if not present */
747 			if (cp->sbc_cm.sbdev_id == (drmachid_t)0) {
748 				continue;
749 			}
750 
751 			/* fetch platform status */
752 			err = drmach_status(cp->sbc_cm.sbdev_id, &pstat);
753 			if (err) {
754 				DRERR_SET_C(&cp->sbc_cm.sbdev_error, &err);
755 				continue;
756 			}
757 
758 			dr_fill_cpu_stat(cp, &pstat, &cstat[ncores++]);
759 			/*
760 			 * We should set impl here because the last core
761 			 * found might be EMPTY or not present.
762 			 */
763 			impl = cp->sbc_cpu_impl;
764 		}
765 
766 		if (ncores == 0) {
767 			continue;
768 		}
769 
770 		/*
771 		 * Store the data to the outgoing array. If the
772 		 * device is a CMP, combine all the data for the
773 		 * cores into a single stat structure.
774 		 *
775 		 * The check for a CMP device uses the last core
776 		 * found, assuming that all cores will have the
777 		 * same implementation.
778 		 */
779 		if (CPU_IMPL_IS_CMP(impl)) {
780 			psp = (sbd_cmp_stat_t *)dsp;
781 			dr_fill_cmp_stat(cstat, ncores, impl, psp);
782 		} else {
783 			ASSERT(ncores == 1);
784 			bcopy(cstat, dsp, sizeof (sbd_cpu_stat_t));
785 		}
786 
787 		dsp++;
788 		ncpu++;
789 	}
790 
791 	kmem_free(cstat, sizeof (sbd_cpu_stat_t) * MAX_CORES_PER_CMP);
792 
793 	return (ncpu);
794 }
795 
796 /*
797  * Cancel previous release operation for cpu.
798  * For cpus this means simply bringing cpus that
799  * were offline back online.  Note that they had
800  * to have been online at the time there were
801  * released.
802  */
803 int
804 dr_cancel_cpu(dr_cpu_unit_t *up)
805 {
806 	int		rv = 0;
807 	static fn_t	f = "dr_cancel_cpu";
808 
809 	ASSERT(dr_cpu_unit_is_sane(up->sbc_cm.sbdev_bp, up));
810 
811 	if (cpu_flagged_active(up->sbc_cpu_flags)) {
812 		struct cpu	*cp;
813 
814 		/*
815 		 * CPU had been online, go ahead
816 		 * bring it back online.
817 		 */
818 		PR_CPU("%s: bringing cpu %d back ONLINE\n", f, up->sbc_cpu_id);
819 
820 		mutex_enter(&cpu_lock);
821 		cp = cpu[up->sbc_cpu_id];
822 
823 		if (cpu_is_poweredoff(cp)) {
824 			if (cpu_poweron(cp)) {
825 				cmn_err(CE_WARN, "%s: failed to power-on "
826 				    "cpu %d", f, up->sbc_cpu_id);
827 				rv = -1;
828 			}
829 		}
830 
831 		if (rv == 0 && cpu_is_offline(cp)) {
832 			if (cpu_online(cp)) {
833 				cmn_err(CE_WARN, "%s: failed to online cpu %d",
834 				    f, up->sbc_cpu_id);
835 				rv = -1;
836 			}
837 		}
838 
839 		if (rv == 0 && cpu_is_online(cp)) {
840 			if (cpu_flagged_nointr(up->sbc_cpu_flags)) {
841 				if (cpu_intr_disable(cp) != 0) {
842 					cmn_err(CE_WARN, "%s: failed to "
843 					    "disable interrupts on cpu %d", f,
844 					    up->sbc_cpu_id);
845 				}
846 			}
847 		}
848 
849 		mutex_exit(&cpu_lock);
850 	}
851 
852 	return (rv);
853 }
854 
855 int
856 dr_disconnect_cpu(dr_cpu_unit_t *up)
857 {
858 	sbd_error_t	*err;
859 	static fn_t	f = "dr_disconnect_cpu";
860 
861 	PR_CPU("%s...\n", f);
862 
863 	ASSERT((up->sbc_cm.sbdev_state == DR_STATE_CONNECTED) ||
864 	    (up->sbc_cm.sbdev_state == DR_STATE_UNCONFIGURED));
865 
866 	ASSERT(dr_cpu_unit_is_sane(up->sbc_cm.sbdev_bp, up));
867 
868 	if (up->sbc_cm.sbdev_state == DR_STATE_CONNECTED) {
869 		/*
870 		 * Cpus were never brought in and so are still
871 		 * effectively disconnected, so nothing to do here.
872 		 */
873 		PR_CPU("%s: cpu %d never brought in\n", f, up->sbc_cpu_id);
874 		return (0);
875 	}
876 
877 	err = drmach_cpu_disconnect(up->sbc_cm.sbdev_id);
878 	if (err == NULL)
879 		return (0);
880 	else {
881 		DRERR_SET_C(&up->sbc_cm.sbdev_error, &err);
882 		return (-1);
883 	}
884 	/*NOTREACHED*/
885 }
886