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
dr_cpu_unit_is_sane(dr_board_t * bp,dr_cpu_unit_t * cp)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
dr_errno2ecode(int error)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
dr_cpu_set_prop(dr_cpu_unit_t * cp)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
dr_init_cpu_unit(dr_cpu_unit_t * cp)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
dr_pre_attach_cpu(dr_handle_t * hp,dr_common_unit_t ** devlist,int devnum)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
dr_attach_cpu(dr_handle_t * hp,dr_common_unit_t * cp)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
dr_post_attach_cpu(dr_handle_t * hp,dr_common_unit_t ** devlist,int devnum)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
dr_pre_release_cpu(dr_handle_t * hp,dr_common_unit_t ** devlist,int devnum)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
dr_pre_detach_cpu(dr_handle_t * hp,dr_common_unit_t ** devlist,int devnum)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
dr_detach_cpu(dr_handle_t * hp,dr_common_unit_t * cp)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
dr_post_detach_cpu(dr_handle_t * hp,dr_common_unit_t ** devlist,int devnum)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
dr_fill_cpu_stat(dr_cpu_unit_t * cp,drmach_status_t * pstat,sbd_cpu_stat_t * csp)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
dr_fill_cmp_stat(sbd_cpu_stat_t * csp,int ncores,int impl,sbd_cmp_stat_t * psp)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
dr_cpu_status(dr_handle_t * hp,dr_devset_t devset,sbd_dev_stat_t * dsp)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
dr_cancel_cpu(dr_cpu_unit_t * up)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
dr_disconnect_cpu(dr_cpu_unit_t * up)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