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