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 #include <sys/debug.h>
28 #include <sys/types.h>
29 #include <sys/varargs.h>
30 #include <sys/errno.h>
31 #include <sys/cred.h>
32 #include <sys/dditypes.h>
33 #include <sys/devops.h>
34 #include <sys/modctl.h>
35 #include <sys/poll.h>
36 #include <sys/conf.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/sunndi.h>
40 #include <sys/ndi_impldefs.h>
41 #include <sys/stat.h>
42 #include <sys/kmem.h>
43 #include <sys/vmem.h>
44 #include <sys/processor.h>
45 #include <sys/spitregs.h>
46 #include <sys/cpuvar.h>
47 #include <sys/cpupart.h>
48 #include <sys/mem_config.h>
49 #include <sys/ddi_impldefs.h>
50 #include <sys/systm.h>
51 #include <sys/machsystm.h>
52 #include <sys/autoconf.h>
53 #include <sys/cmn_err.h>
54 #include <sys/sysmacros.h>
55 #include <sys/x_call.h>
56 #include <sys/promif.h>
57 #include <sys/prom_plat.h>
58 #include <sys/membar.h>
59 #include <vm/seg_kmem.h>
60 #include <sys/mem_cage.h>
61 #include <sys/stack.h>
62 #include <sys/archsystm.h>
63 #include <vm/hat_sfmmu.h>
64 #include <sys/pte.h>
65 #include <sys/mmu.h>
66 #include <sys/cpu_module.h>
67 #include <sys/obpdefs.h>
68 #include <sys/note.h>
69
70 #include <sys/starfire.h> /* plat_max_... decls */
71 #include <sys/cvc.h>
72 #include <sys/cpu_sgnblk_defs.h>
73 #include <sys/drmach.h>
74 #include <sys/dr_util.h>
75 #include <sys/pda.h>
76
77 #include <sys/sysevent.h>
78 #include <sys/sysevent/dr.h>
79 #include <sys/sysevent/eventdefs.h>
80
81
82 extern void bcopy32_il(uint64_t, uint64_t);
83 extern void flush_ecache_il(
84 uint64_t physaddr, int size, int linesz);
85 extern uint_t ldphysio_il(uint64_t physaddr);
86 extern void stphysio_il(uint64_t physaddr, uint_t value);
87
88 extern uint64_t mc_get_mem_alignment(void);
89 extern uint64_t mc_get_asr_addr(pnode_t);
90 extern uint64_t mc_get_idle_addr(pnode_t);
91 extern uint64_t mc_get_alignment_mask(pnode_t);
92 extern int mc_read_asr(pnode_t, uint_t *);
93 extern int mc_write_asr(pnode_t, uint_t);
94 extern uint64_t mc_asr_to_pa(uint_t);
95 extern uint_t mc_pa_to_asr(uint_t, uint64_t);
96
97 extern int pc_madr_add(int, int, int, int);
98
99 typedef struct {
100 struct drmach_node *node;
101 void *data;
102 } drmach_node_walk_args_t;
103
104 typedef struct drmach_node {
105 void *here;
106
107 pnode_t (*get_dnode)(struct drmach_node *node);
108 int (*walk)(struct drmach_node *node, void *data,
109 int (*cb)(drmach_node_walk_args_t *args));
110 } drmach_node_t;
111
112 typedef struct {
113 int min_index;
114 int max_index;
115 int arr_sz;
116 drmachid_t *arr;
117 } drmach_array_t;
118
119 typedef struct {
120 void *isa;
121
122 sbd_error_t *(*release)(drmachid_t);
123 sbd_error_t *(*status)(drmachid_t, drmach_status_t *);
124
125 char name[MAXNAMELEN];
126 } drmach_common_t;
127
128 typedef struct {
129 drmach_common_t cm;
130 int bnum;
131 int assigned;
132 int powered;
133 int connect_cpuid;
134 int cond;
135 drmach_node_t *tree;
136 drmach_array_t *devices;
137 } drmach_board_t;
138
139 typedef struct {
140 drmach_common_t cm;
141 drmach_board_t *bp;
142 int unum;
143 int busy;
144 int powered;
145 const char *type;
146 drmach_node_t *node;
147 } drmach_device_t;
148
149 typedef struct {
150 int flags;
151 drmach_device_t *dp;
152 sbd_error_t *err;
153 dev_info_t *dip;
154 } drmach_config_args_t;
155
156 typedef struct {
157 uint64_t idle_addr;
158 drmach_device_t *mem;
159 } drmach_mc_idle_script_t;
160
161 typedef struct {
162 uint64_t masr_addr;
163 uint_t masr;
164 uint_t _filler;
165 } drmach_rename_script_t;
166
167 typedef struct {
168 void (*run)(void *arg);
169 caddr_t data;
170 pda_handle_t *ph;
171 struct memlist *c_ml;
172 uint64_t s_copybasepa;
173 uint64_t t_copybasepa;
174 drmach_device_t *restless_mc; /* diagnostic output */
175 } drmach_copy_rename_program_t;
176
177 typedef enum {
178 DO_IDLE,
179 DO_UNIDLE,
180 DO_PAUSE,
181 DO_UNPAUSE
182 } drmach_iopc_op_t;
183
184 typedef struct {
185 drmach_board_t *obj;
186 int ndevs;
187 void *a;
188 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t);
189 sbd_error_t *err;
190 } drmach_board_cb_data_t;
191
192 static caddr_t drmach_shutdown_va;
193
194 static int drmach_initialized;
195 static drmach_array_t *drmach_boards;
196
197 static int drmach_cpu_delay = 100;
198 static int drmach_cpu_ntries = 50000;
199
200 volatile uchar_t *drmach_xt_mb;
201
202 /*
203 * Do not change the drmach_shutdown_mbox structure without
204 * considering the drmach_shutdown_asm assembly language code.
205 */
206 struct drmach_shutdown_mbox {
207 uint64_t estack;
208 uint64_t flushaddr;
209 int size;
210 int linesize;
211 uint64_t physaddr;
212 };
213 struct drmach_shutdown_mbox *drmach_shutdown_asm_mbox;
214 static sbd_error_t *drmach_device_new(drmach_node_t *,
215 drmach_board_t *, drmach_device_t **);
216 static sbd_error_t *drmach_cpu_new(drmach_device_t *);
217 static sbd_error_t *drmach_mem_new(drmach_device_t *);
218 static sbd_error_t *drmach_io_new(drmach_device_t *);
219
220 extern struct cpu *SIGBCPU;
221
222 #ifdef DEBUG
223
224 #define DRMACH_PR if (drmach_debug) printf
225 int drmach_debug = 0; /* set to non-zero to enable debug messages */
226 #else
227
228 #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf
229 #endif /* DEBUG */
230
231 #define DRMACH_OBJ(id) ((drmach_common_t *)id)
232
233 #define DRMACH_IS_BOARD_ID(id) \
234 ((id != 0) && \
235 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
236
237 #define DRMACH_IS_CPU_ID(id) \
238 ((id != 0) && \
239 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
240
241 #define DRMACH_IS_MEM_ID(id) \
242 ((id != 0) && \
243 (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
244
245 #define DRMACH_IS_IO_ID(id) \
246 ((id != 0) && \
247 (DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
248
249 #define DRMACH_IS_DEVICE_ID(id) \
250 ((id != 0) && \
251 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
252 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
253 DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
254
255 #define DRMACH_IS_ID(id) \
256 ((id != 0) && \
257 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \
258 DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
259 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
260 DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
261
262 #define DRMACH_CPUID2BNUM(cpuid) \
263 ((cpuid) / MAX_CPU_UNITS_PER_BOARD)
264
265 #define DRMACH_INTERNAL_ERROR() \
266 drerr_new(1, ESTF_INTERNAL, drmach_ie_fmt, __LINE__)
267 static char *drmach_ie_fmt = "drmach.c %d";
268
269 static struct {
270 const char *name;
271 const char *type;
272 sbd_error_t *(*new)(drmach_device_t *);
273 } name2type[] = {
274 { "SUNW,UltraSPARC", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
275 { "mem-unit", DRMACH_DEVTYPE_MEM, drmach_mem_new },
276 { "pci", DRMACH_DEVTYPE_PCI, drmach_io_new },
277 { "sbus", DRMACH_DEVTYPE_SBUS, drmach_io_new },
278 };
279
280 /* node types to cleanup when a board is unconfigured */
281 #define MISC_COUNTER_TIMER_DEVNAME "counter-timer"
282 #define MISC_PERF_COUNTER_DEVNAME "perf-counter"
283
284 /* utility */
285 #define MBYTE (1048576ull)
286
287 /*
288 * This is necessary because the CPU support needs
289 * to call cvc_assign_iocpu.
290 */
291 #ifndef lint
292 char _depends_on[] = "drv/cvc";
293 #endif /* lint */
294
295 /*
296 * drmach autoconfiguration data structures and interfaces
297 */
298
299 extern struct mod_ops mod_miscops;
300
301 static struct modlmisc modlmisc = {
302 &mod_miscops,
303 "Sun Enterprise 10000 DR"
304 };
305
306 static struct modlinkage modlinkage = {
307 MODREV_1,
308 (void *)&modlmisc,
309 NULL
310 };
311
312 static kmutex_t drmach_i_lock;
313
314 int
_init(void)315 _init(void)
316 {
317 int err;
318
319 /* check that we have the correct version of obp */
320 if (prom_test("SUNW,UE10000,add-brd") != 0) {
321
322 cmn_err(CE_WARN, "!OBP/SSP upgrade is required to enable "
323 "DR Functionality");
324
325 return (-1);
326 }
327
328 mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL);
329
330 drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena,
331 NCPU * sizeof (uchar_t), VM_SLEEP);
332 drmach_shutdown_asm_mbox = (struct drmach_shutdown_mbox *)
333 vmem_alloc(static_alloc_arena, sizeof (struct drmach_shutdown_mbox),
334 VM_SLEEP);
335
336 if ((err = mod_install(&modlinkage)) != 0) {
337 mutex_destroy(&drmach_i_lock);
338 vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
339 NCPU * sizeof (uchar_t));
340 vmem_free(static_alloc_arena, (void *)drmach_shutdown_asm_mbox,
341 sizeof (struct drmach_shutdown_mbox));
342 }
343
344 return (err);
345 }
346
347 int
_fini(void)348 _fini(void)
349 {
350 static int drmach_fini(void);
351
352 if (drmach_fini())
353 return (DDI_FAILURE);
354 else
355 return (mod_remove(&modlinkage));
356 }
357
358 int
_info(struct modinfo * modinfop)359 _info(struct modinfo *modinfop)
360 {
361 return (mod_info(&modlinkage, modinfop));
362 }
363
364 static pnode_t
drmach_node_obp_get_dnode(drmach_node_t * np)365 drmach_node_obp_get_dnode(drmach_node_t *np)
366 {
367 return ((pnode_t)(uintptr_t)np->here);
368 }
369
370 static int
drmach_node_obp_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))371 drmach_node_obp_walk(drmach_node_t *np, void *data,
372 int (*cb)(drmach_node_walk_args_t *args))
373 {
374 pnode_t nodeid;
375 int rv;
376 drmach_node_walk_args_t args;
377
378 /* initialized args structure for callback */
379 args.node = np;
380 args.data = data;
381
382 nodeid = prom_childnode(prom_rootnode());
383
384 /* save our new position with in the tree */
385 np->here = (void *)(uintptr_t)nodeid;
386
387 rv = 0;
388 while (nodeid != OBP_NONODE) {
389 rv = (*cb)(&args);
390 if (rv)
391 break;
392
393 nodeid = prom_nextnode(nodeid);
394
395 /* save our new position with in the tree */
396 np->here = (void *)(uintptr_t)nodeid;
397 }
398
399 return (rv);
400 }
401
402 static drmach_node_t *
drmach_node_new(void)403 drmach_node_new(void)
404 {
405 drmach_node_t *np;
406
407 np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
408
409 np->get_dnode = drmach_node_obp_get_dnode;
410 np->walk = drmach_node_obp_walk;
411
412 return (np);
413 }
414
415 static void
drmach_node_dispose(drmach_node_t * np)416 drmach_node_dispose(drmach_node_t *np)
417 {
418 kmem_free(np, sizeof (*np));
419 }
420
421 static dev_info_t *
drmach_node_get_dip(drmach_node_t * np)422 drmach_node_get_dip(drmach_node_t *np)
423 {
424 pnode_t nodeid;
425
426 nodeid = np->get_dnode(np);
427 if (nodeid == OBP_NONODE)
428 return (NULL);
429 else {
430 dev_info_t *dip;
431
432 /* The root node doesn't have to be held */
433 dip = e_ddi_nodeid_to_dip(nodeid);
434 if (dip) {
435 /*
436 * Branch rooted at dip is already held, so release
437 * hold acquired in e_ddi_nodeid_to_dip()
438 */
439 ddi_release_devi(dip);
440 ASSERT(e_ddi_branch_held(dip));
441 }
442
443 return (dip);
444 }
445 /*NOTREACHED*/
446 }
447
448 static pnode_t
drmach_node_get_dnode(drmach_node_t * np)449 drmach_node_get_dnode(drmach_node_t *np)
450 {
451 return (np->get_dnode(np));
452 }
453
454 static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))455 drmach_node_walk(drmach_node_t *np, void *param,
456 int (*cb)(drmach_node_walk_args_t *args))
457 {
458 return (np->walk(np, param, cb));
459 }
460
461 static int
drmach_node_get_prop(drmach_node_t * np,char * name,void * buf)462 drmach_node_get_prop(drmach_node_t *np, char *name, void *buf)
463 {
464 pnode_t nodeid;
465 int rv;
466
467 nodeid = np->get_dnode(np);
468 if (nodeid == OBP_NONODE)
469 rv = -1;
470 else if (prom_getproplen(nodeid, (caddr_t)name) < 0)
471 rv = -1;
472 else {
473 (void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf);
474 rv = 0;
475 }
476
477 return (rv);
478 }
479
480 static int
drmach_node_get_proplen(drmach_node_t * np,char * name,int * len)481 drmach_node_get_proplen(drmach_node_t *np, char *name, int *len)
482 {
483 pnode_t nodeid;
484 int rv;
485
486 nodeid = np->get_dnode(np);
487 if (nodeid == OBP_NONODE)
488 rv = -1;
489 else {
490 *len = prom_getproplen(nodeid, (caddr_t)name);
491 rv = (*len < 0 ? -1 : 0);
492 }
493
494 return (rv);
495 }
496
497 static drmachid_t
drmach_node_dup(drmach_node_t * np)498 drmach_node_dup(drmach_node_t *np)
499 {
500 drmach_node_t *dup;
501
502 dup = drmach_node_new();
503 dup->here = np->here;
504
505 return (dup);
506 }
507
508 /*
509 * drmach_array provides convenient array construction, access,
510 * bounds checking and array destruction logic.
511 */
512
513 static drmach_array_t *
drmach_array_new(int min_index,int max_index)514 drmach_array_new(int min_index, int max_index)
515 {
516 drmach_array_t *arr;
517
518 arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
519
520 arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
521 if (arr->arr_sz > 0) {
522 arr->min_index = min_index;
523 arr->max_index = max_index;
524
525 arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
526 return (arr);
527 } else {
528 kmem_free(arr, sizeof (*arr));
529 return (0);
530 }
531 }
532
533 static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)534 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
535 {
536 if (idx < arr->min_index || idx > arr->max_index)
537 return (-1);
538 else {
539 arr->arr[idx - arr->min_index] = val;
540 return (0);
541 }
542 /*NOTREACHED*/
543 }
544
545 static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)546 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
547 {
548 if (idx < arr->min_index || idx > arr->max_index)
549 return (-1);
550 else {
551 *val = arr->arr[idx - arr->min_index];
552 return (0);
553 }
554 /*NOTREACHED*/
555 }
556
557 static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)558 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
559 {
560 int rv;
561
562 *idx = arr->min_index;
563 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
564 *idx += 1;
565
566 return (rv);
567 }
568
569 static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)570 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
571 {
572 int rv;
573
574 *idx += 1;
575 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
576 *idx += 1;
577
578 return (rv);
579 }
580
581 static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))582 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
583 {
584 drmachid_t val;
585 int idx;
586 int rv;
587
588 rv = drmach_array_first(arr, &idx, &val);
589 while (rv == 0) {
590 (*disposer)(val);
591 rv = drmach_array_next(arr, &idx, &val);
592 }
593
594 kmem_free(arr->arr, arr->arr_sz);
595 kmem_free(arr, sizeof (*arr));
596 }
597
598 /*ARGSUSED*/
599 static int
drmach_prom_select(pnode_t nodeid,void * arg,uint_t flags)600 drmach_prom_select(pnode_t nodeid, void *arg, uint_t flags)
601 {
602 int rprop[64];
603 pnode_t saved;
604 drmach_config_args_t *ap = (drmach_config_args_t *)arg;
605 drmach_device_t *dp = ap->dp;
606 sbd_error_t *err;
607
608 saved = drmach_node_get_dnode(dp->node);
609
610 if (nodeid != saved)
611 return (DDI_FAILURE);
612
613 if (saved == OBP_NONODE) {
614 err = DRMACH_INTERNAL_ERROR();
615 DRERR_SET_C(&ap->err, &err);
616 return (DDI_FAILURE);
617 }
618
619 if (prom_getprop(nodeid, OBP_REG, (caddr_t)rprop) <= 0) {
620 return (DDI_FAILURE);
621 }
622
623 return (DDI_SUCCESS);
624 }
625
626 /*ARGSUSED*/
627 static void
drmach_branch_callback(dev_info_t * rdip,void * arg,uint_t flags)628 drmach_branch_callback(dev_info_t *rdip, void *arg, uint_t flags)
629 {
630 drmach_config_args_t *ap = (drmach_config_args_t *)arg;
631
632 ASSERT(ap->dip == NULL);
633
634 ap->dip = rdip;
635 }
636
637 sbd_error_t *
drmach_configure(drmachid_t id,int flags)638 drmach_configure(drmachid_t id, int flags)
639 {
640 drmach_device_t *dp;
641 sbd_error_t *err;
642 drmach_config_args_t ca;
643 devi_branch_t b = {0};
644 dev_info_t *fdip = NULL;
645
646 if (!DRMACH_IS_DEVICE_ID(id))
647 return (drerr_new(0, ESTF_INAPPROP, NULL));
648 dp = id;
649
650 ca.dp = dp;
651 ca.flags = flags;
652 ca.err = NULL; /* will be set if error detected */
653 ca.dip = NULL;
654
655 b.arg = &ca;
656 b.type = DEVI_BRANCH_PROM;
657 b.create.prom_branch_select = drmach_prom_select;
658 b.devi_branch_callback = drmach_branch_callback;
659
660 if (e_ddi_branch_create(ddi_root_node(), &b, &fdip,
661 DEVI_BRANCH_CHILD | DEVI_BRANCH_CONFIGURE) != 0) {
662 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
663
664 /*
665 * If non-NULL, fdip is returned held and must be released.
666 */
667 if (fdip != NULL) {
668 (void) ddi_pathname(fdip, path);
669 ddi_release_devi(fdip);
670 } else if (ca.dip != NULL) {
671 /* safe to call ddi_pathname as dip already held */
672 (void) ddi_pathname(ca.dip, path);
673 } else {
674 (void) strcpy(path, "<none>");
675 }
676
677 err = drerr_new(1, ESTF_DRVFAIL, path);
678 DRERR_SET_C(&ca.err, &err);
679 kmem_free(path, MAXPATHLEN);
680 }
681
682 return (ca.err);
683 }
684
685 static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,drmach_device_t ** dpp)686 drmach_device_new(drmach_node_t *node,
687 drmach_board_t *bp, drmach_device_t **dpp)
688 {
689 int i;
690 int rv;
691 drmach_device_t *dp;
692 sbd_error_t *err;
693 char name[OBP_MAXDRVNAME];
694
695 rv = drmach_node_get_prop(node, OBP_NAME, name);
696 if (rv) {
697 /* every node is expected to have a name */
698 err = drerr_new(1, ESTF_GETPROP,
699 "PROM Node 0x%x: property %s",
700 (uint_t)node->get_dnode(node), OBP_NAME);
701
702 return (err);
703 }
704
705 /*
706 * The node currently being examined is not listed in the name2type[]
707 * array. In this case, the node is no interest to drmach. Both
708 * dp and err are initialized here to yield nothing (no device or
709 * error structure) for this case.
710 */
711 for (i = 0; i < sizeof (name2type) / sizeof (name2type[0]); i++)
712 if (strcmp(name2type[i].name, name) == 0)
713 break;
714
715 if (i < sizeof (name2type) / sizeof (name2type[0])) {
716 dp = kmem_zalloc(sizeof (drmach_device_t), KM_SLEEP);
717
718 dp->bp = bp;
719 dp->unum = -1;
720 dp->node = drmach_node_dup(node);
721 dp->type = name2type[i].type;
722
723 err = (name2type[i].new)(dp);
724 if (err) {
725 drmach_node_dispose(node);
726 kmem_free(dp, sizeof (*dp));
727 dp = NULL;
728 }
729
730 *dpp = dp;
731 return (err);
732 }
733
734 /*
735 * The node currently being examined is not listed in the name2type[]
736 * array. In this case, the node is no interest to drmach. Both
737 * dp and err are initialized here to yield nothing (no device or
738 * error structure) for this case.
739 */
740 *dpp = NULL;
741 return (NULL);
742 }
743
744 static void
drmach_device_dispose(drmachid_t id)745 drmach_device_dispose(drmachid_t id)
746 {
747 drmach_device_t *self = id;
748
749 if (self->node)
750 drmach_node_dispose(self->node);
751
752 kmem_free(self, sizeof (*self));
753 }
754
755 static sbd_error_t *
drmach_device_get_prop(drmach_device_t * dp,char * name,void * buf)756 drmach_device_get_prop(drmach_device_t *dp, char *name, void *buf)
757 {
758 sbd_error_t *err = NULL;
759 int rv;
760
761 rv = drmach_node_get_prop(dp->node, name, buf);
762 if (rv) {
763 err = drerr_new(1, ESTF_GETPROP,
764 "%s::%s: property %s",
765 dp->bp->cm.name, dp->cm.name, name);
766 }
767
768 return (err);
769 }
770
771 static sbd_error_t *
drmach_device_get_proplen(drmach_device_t * dp,char * name,int * len)772 drmach_device_get_proplen(drmach_device_t *dp, char *name, int *len)
773 {
774 sbd_error_t *err = NULL;
775 int rv;
776
777 rv = drmach_node_get_proplen(dp->node, name, len);
778 if (rv) {
779 err = drerr_new(1, ESTF_GETPROPLEN,
780 "%s::%s: property %s",
781 dp->bp->cm.name, dp->cm.name, name);
782 }
783
784 return (err);
785 }
786
787 static drmach_board_t *
drmach_board_new(int bnum)788 drmach_board_new(int bnum)
789 {
790 static sbd_error_t *drmach_board_release(drmachid_t);
791 static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
792
793 drmach_board_t *bp;
794
795 bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
796
797 bp->cm.isa = (void *)drmach_board_new;
798 bp->cm.release = drmach_board_release;
799 bp->cm.status = drmach_board_status;
800
801 (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
802
803 bp->bnum = bnum;
804 bp->devices = NULL;
805 bp->connect_cpuid = -1;
806 bp->tree = drmach_node_new();
807 bp->assigned = !drmach_initialized;
808 bp->powered = !drmach_initialized;
809
810 (void) drmach_array_set(drmach_boards, bnum, bp);
811 return (bp);
812 }
813
814 static void
drmach_board_dispose(drmachid_t id)815 drmach_board_dispose(drmachid_t id)
816 {
817 drmach_board_t *bp;
818
819 ASSERT(DRMACH_IS_BOARD_ID(id));
820 bp = id;
821
822 if (bp->tree)
823 drmach_node_dispose(bp->tree);
824
825 if (bp->devices)
826 drmach_array_dispose(bp->devices, drmach_device_dispose);
827
828 kmem_free(bp, sizeof (*bp));
829 }
830
831 static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)832 drmach_board_status(drmachid_t id, drmach_status_t *stat)
833 {
834 sbd_error_t *err = NULL;
835 drmach_board_t *bp;
836
837 if (!DRMACH_IS_BOARD_ID(id))
838 return (drerr_new(0, ESTF_INAPPROP, NULL));
839 bp = id;
840
841 stat->assigned = bp->assigned;
842 stat->powered = bp->powered;
843 stat->busy = 0; /* assume not busy */
844 stat->configured = 0; /* assume not configured */
845 stat->empty = 0;
846 stat->cond = bp->cond = SBD_COND_OK;
847 (void) strncpy(stat->type, "System Brd", sizeof (stat->type));
848 stat->info[0] = '\0';
849
850 if (bp->devices) {
851 int rv;
852 int d_idx;
853 drmachid_t d_id;
854
855 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
856 while (rv == 0) {
857 drmach_status_t d_stat;
858
859 err = drmach_status(d_id, &d_stat);
860 if (err)
861 break;
862
863 stat->busy |= d_stat.busy;
864 stat->configured |= d_stat.configured;
865
866 rv = drmach_array_next(bp->devices, &d_idx, &d_id);
867 }
868 }
869
870 return (err);
871 }
872
873 /* a simple routine to reduce redundancy of this common logic */
874 static pda_handle_t
drmach_pda_open(void)875 drmach_pda_open(void)
876 {
877 pda_handle_t ph;
878
879 ph = pda_open();
880 if (ph == NULL) {
881 /* catch in debug kernels */
882 ASSERT(0);
883 cmn_err(CE_WARN, "pda_open failed");
884 }
885
886 return (ph);
887 }
888
889 #ifdef DEBUG
890 int drmach_init_break = 0;
891 #endif
892
893 static int
hold_rele_branch(dev_info_t * rdip,void * arg)894 hold_rele_branch(dev_info_t *rdip, void *arg)
895 {
896 int i;
897 int *holdp = (int *)arg;
898 char *name = ddi_node_name(rdip);
899
900 /*
901 * For Starfire, we must be children of the root devinfo node
902 */
903 ASSERT(ddi_get_parent(rdip) == ddi_root_node());
904
905 for (i = 0; i < sizeof (name2type) / sizeof (name2type[0]); i++)
906 if (strcmp(name2type[i].name, name) == 0)
907 break;
908
909 if (i == sizeof (name2type) / sizeof (name2type[0])) {
910 /* Not of interest to us */
911 return (DDI_WALK_PRUNECHILD);
912 }
913
914 if (*holdp) {
915 ASSERT(!e_ddi_branch_held(rdip));
916 e_ddi_branch_hold(rdip);
917 } else {
918 ASSERT(e_ddi_branch_held(rdip));
919 e_ddi_branch_rele(rdip);
920 }
921
922 return (DDI_WALK_PRUNECHILD);
923 }
924
925 static int
drmach_init(void)926 drmach_init(void)
927 {
928 pnode_t nodeid;
929 dev_info_t *rdip;
930 int hold, circ;
931
932 #ifdef DEBUG
933 if (drmach_init_break)
934 debug_enter("drmach_init: drmach_init_break set\n");
935 #endif
936 mutex_enter(&drmach_i_lock);
937 if (drmach_initialized) {
938 mutex_exit(&drmach_i_lock);
939 return (0);
940 }
941
942 drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
943
944 nodeid = prom_childnode(prom_rootnode());
945 do {
946 int bnum;
947 drmachid_t id;
948
949 bnum = -1;
950 (void) prom_getprop(nodeid, OBP_BOARDNUM, (caddr_t)&bnum);
951 if (bnum == -1)
952 continue;
953
954 if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
955 cmn_err(CE_WARN, "OBP node 0x%x has"
956 " invalid property value, %s=%d",
957 nodeid, OBP_BOARDNUM, bnum);
958
959 /* clean up */
960 drmach_array_dispose(
961 drmach_boards, drmach_board_dispose);
962
963 mutex_exit(&drmach_i_lock);
964 return (-1);
965 } else if (id == NULL)
966 (void) drmach_board_new(bnum);
967 } while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE);
968
969 drmach_shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
970
971 /*
972 * Walk immediate children of devinfo root node and hold
973 * all devinfo branches of interest.
974 */
975 hold = 1;
976 rdip = ddi_root_node();
977
978 ndi_devi_enter(rdip, &circ);
979 ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
980 ndi_devi_exit(rdip, circ);
981
982 drmach_initialized = 1;
983
984 mutex_exit(&drmach_i_lock);
985
986 return (0);
987 }
988
989 static int
drmach_fini(void)990 drmach_fini(void)
991 {
992 dev_info_t *rdip;
993 int hold, circ;
994
995 if (drmach_initialized) {
996 int busy = 0;
997 int rv;
998 int idx;
999 drmachid_t id;
1000
1001 ASSERT(drmach_boards != NULL);
1002
1003 rv = drmach_array_first(drmach_boards, &idx, &id);
1004 while (rv == 0) {
1005 sbd_error_t *err;
1006 drmach_status_t stat;
1007
1008 err = drmach_board_status(id, &stat);
1009 if (err) {
1010 /* catch in debug kernels */
1011 ASSERT(0);
1012 sbd_err_clear(&err);
1013 busy = 1;
1014 } else
1015 busy |= stat.busy;
1016
1017 rv = drmach_array_next(drmach_boards, &idx, &id);
1018 }
1019
1020 if (busy)
1021 return (-1);
1022
1023 drmach_array_dispose(drmach_boards, drmach_board_dispose);
1024 drmach_boards = NULL;
1025
1026 vmem_free(heap_arena, drmach_shutdown_va, PAGESIZE);
1027
1028 /*
1029 * Walk immediate children of the root devinfo node
1030 * releasing holds acquired on branches in drmach_init()
1031 */
1032 hold = 0;
1033 rdip = ddi_root_node();
1034
1035 ndi_devi_enter(rdip, &circ);
1036 ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
1037 ndi_devi_exit(rdip, circ);
1038
1039 mutex_destroy(&drmach_i_lock);
1040
1041 drmach_initialized = 0;
1042 }
1043 if (drmach_xt_mb != NULL) {
1044 vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
1045 NCPU * sizeof (uchar_t));
1046 }
1047 if (drmach_shutdown_asm_mbox != NULL) {
1048 vmem_free(static_alloc_arena, (void *)drmach_shutdown_asm_mbox,
1049 sizeof (struct drmach_shutdown_mbox));
1050 }
1051 return (0);
1052 }
1053
1054 static sbd_error_t *
drmach_get_mc_asr_addr(drmachid_t id,uint64_t * pa)1055 drmach_get_mc_asr_addr(drmachid_t id, uint64_t *pa)
1056 {
1057 drmach_device_t *dp;
1058 pnode_t nodeid;
1059 uint64_t addr;
1060
1061 if (!DRMACH_IS_MEM_ID(id))
1062 return (drerr_new(0, ESTF_INAPPROP, NULL));
1063 dp = id;
1064
1065 nodeid = drmach_node_get_dnode(dp->node);
1066 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1067 return (DRMACH_INTERNAL_ERROR());
1068
1069 addr = mc_get_asr_addr(nodeid);
1070 if (addr == (uint64_t)-1)
1071 return (DRMACH_INTERNAL_ERROR());
1072
1073 *pa = addr;
1074 return (NULL);
1075 }
1076
1077 static sbd_error_t *
drmach_get_mc_idle_addr(drmachid_t id,uint64_t * pa)1078 drmach_get_mc_idle_addr(drmachid_t id, uint64_t *pa)
1079 {
1080 drmach_device_t *dp;
1081 pnode_t nodeid;
1082 uint64_t addr;
1083
1084 if (!DRMACH_IS_MEM_ID(id))
1085 return (drerr_new(0, ESTF_INAPPROP, NULL));
1086 dp = id;
1087
1088 nodeid = drmach_node_get_dnode(dp->node);
1089 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1090 return (DRMACH_INTERNAL_ERROR());
1091
1092 addr = mc_get_idle_addr(nodeid);
1093 if (addr == (uint64_t)-1)
1094 return (DRMACH_INTERNAL_ERROR());
1095
1096 *pa = addr;
1097 return (NULL);
1098 }
1099
1100 static sbd_error_t *
drmach_read_mc_asr(drmachid_t id,uint_t * mcregp)1101 drmach_read_mc_asr(drmachid_t id, uint_t *mcregp)
1102 {
1103 drmach_device_t *dp;
1104 pnode_t nodeid;
1105 sbd_error_t *err;
1106
1107 if (!DRMACH_IS_MEM_ID(id))
1108 return (drerr_new(0, ESTF_INAPPROP, NULL));
1109 dp = id;
1110
1111 nodeid = drmach_node_get_dnode(dp->node);
1112 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1113 err = DRMACH_INTERNAL_ERROR();
1114 else if (mc_read_asr(nodeid, mcregp) == -1)
1115 err = DRMACH_INTERNAL_ERROR();
1116 else
1117 err = NULL;
1118
1119 return (err);
1120 }
1121
1122 static sbd_error_t *
drmach_write_mc_asr(drmachid_t id,uint_t mcreg)1123 drmach_write_mc_asr(drmachid_t id, uint_t mcreg)
1124 {
1125 drmach_device_t *dp;
1126 pnode_t nodeid;
1127 sbd_error_t *err;
1128
1129 if (!DRMACH_IS_MEM_ID(id))
1130 return (drerr_new(0, ESTF_INAPPROP, NULL));
1131 dp = id;
1132
1133 nodeid = drmach_node_get_dnode(dp->node);
1134 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
1135 err = DRMACH_INTERNAL_ERROR();
1136 else if (mc_write_asr(nodeid, mcreg) == -1)
1137 err = DRMACH_INTERNAL_ERROR();
1138 else
1139 err = NULL;
1140
1141 return (err);
1142 }
1143
1144 static sbd_error_t *
drmach_prep_rename_script(drmach_device_t * s_mem,drmach_device_t * t_mem,uint64_t t_slice_offset,caddr_t buf,int buflen)1145 drmach_prep_rename_script(drmach_device_t *s_mem, drmach_device_t *t_mem,
1146 uint64_t t_slice_offset, caddr_t buf, int buflen)
1147 {
1148 int i, b, m;
1149 drmach_mc_idle_script_t *isp;
1150 drmach_rename_script_t *rsp;
1151 int s_bd, t_bd;
1152 uint_t s_masr, t_masr;
1153 uint64_t s_new_basepa, t_new_basepa;
1154 int b_idx, rv;
1155 sbd_error_t *err;
1156 drmachid_t b_id;
1157 drmach_board_t *brd;
1158
1159 #ifdef DEBUG
1160 /*
1161 * Starfire CPU/MEM/IO boards have only one MC per board.
1162 * This function has been coded with that fact in mind.
1163 */
1164 ASSERT(MAX_MEM_UNITS_PER_BOARD == 1);
1165
1166 /*
1167 * calculate the maximum space that could be consumed,
1168 * then verify the available buffer space is adequate.
1169 */
1170 m = sizeof (drmach_mc_idle_script_t *) * 2; /* two MCs */
1171 b = sizeof (drmach_rename_script_t *) * 3 * MAX_CPU_UNITS_PER_BOARD;
1172 b += sizeof (drmach_rename_script_t *) * 3 * MAX_IO_UNITS_PER_BOARD;
1173 b *= MAX_BOARDS;
1174 b += sizeof (drmach_rename_script_t *) * 3;
1175 b += sizeof (drmach_rename_script_t *) * 1;
1176 ASSERT(m + b < buflen);
1177 #endif
1178
1179 /*
1180 * construct an array of MC idle register addresses of
1181 * both MCs. The array is zero terminated -- as expected
1182 * by drmach_copy_rename_prog__relocatable().
1183 */
1184 isp = (drmach_mc_idle_script_t *)buf;
1185
1186 /* source mc */
1187 err = drmach_get_mc_idle_addr(s_mem, &isp->idle_addr);
1188 if (err)
1189 return (err);
1190 isp->mem = s_mem;
1191 isp += 1;
1192
1193 /* target mc */
1194 err = drmach_get_mc_idle_addr(t_mem, &isp->idle_addr);
1195 if (err)
1196 return (err);
1197 isp->mem = t_mem;
1198 isp += 1;
1199
1200 /* terminator */
1201 isp->idle_addr = 0;
1202 isp->mem = NULL;
1203 isp += 1;
1204
1205 /* fetch source mc asr register value */
1206 err = drmach_read_mc_asr(s_mem, &s_masr);
1207 if (err)
1208 return (err);
1209 else if (s_masr & STARFIRE_MC_INTERLEAVE_MASK) {
1210 return (drerr_new(1, ESTF_INTERBOARD, "%s::%s",
1211 s_mem->bp->cm.name, s_mem->cm.name));
1212 }
1213
1214 /* fetch target mc asr register value */
1215 err = drmach_read_mc_asr(t_mem, &t_masr);
1216 if (err)
1217 return (err);
1218 else if (t_masr & STARFIRE_MC_INTERLEAVE_MASK) {
1219 return (drerr_new(1, ESTF_INTERBOARD, "%s::%s",
1220 t_mem->bp->cm.name, t_mem->cm.name));
1221 }
1222
1223 /* get new source base pa from target's masr */
1224 s_new_basepa = mc_asr_to_pa(t_masr);
1225
1226 /*
1227 * remove any existing slice offset to realign
1228 * memory with board's slice boundary
1229 */
1230 s_new_basepa &= ~ (mc_get_mem_alignment() - 1);
1231
1232 /* get new target base pa from source's masr */
1233 t_new_basepa = mc_asr_to_pa(s_masr);
1234
1235 /* remove any existing slice offset, then apply new offset */
1236 t_new_basepa &= ~ (mc_get_mem_alignment() - 1);
1237 t_new_basepa += t_slice_offset;
1238
1239 /* encode new base pa into s_masr. turn off mem present bit */
1240 s_masr = mc_pa_to_asr(s_masr, s_new_basepa);
1241 s_masr &= ~STARFIRE_MC_MEM_PRESENT_MASK;
1242
1243 /* encode new base pa into t_masr. turn on mem present bit */
1244 t_masr = mc_pa_to_asr(t_masr, t_new_basepa);
1245 t_masr |= STARFIRE_MC_MEM_PRESENT_MASK;
1246
1247 /*
1248 * Step 0: Mark source memory as not present.
1249 */
1250 m = 0;
1251 rsp = (drmach_rename_script_t *)isp;
1252 err = drmach_get_mc_asr_addr(s_mem, &rsp[m].masr_addr);
1253 if (err)
1254 return (err);
1255 rsp[m].masr = s_masr;
1256 m++;
1257
1258 /*
1259 * Step 1: Write source base address to target MC
1260 * with present bit off.
1261 */
1262 err = drmach_get_mc_asr_addr(t_mem, &rsp[m].masr_addr);
1263 if (err)
1264 return (err);
1265 rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK;
1266 m++;
1267
1268 /*
1269 * Step 2: Now rewrite target reg with present bit on.
1270 */
1271 rsp[m].masr_addr = rsp[m-1].masr_addr;
1272 rsp[m].masr = t_masr;
1273 m++;
1274
1275 s_bd = s_mem->bp->bnum;
1276 t_bd = t_mem->bp->bnum;
1277
1278 DRMACH_PR("preparing script for CPU and IO units:\n");
1279
1280 rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
1281 if (rv) {
1282 /* catch this in debug kernels */
1283 ASSERT(0);
1284 return (DRMACH_INTERNAL_ERROR());
1285 }
1286
1287 do {
1288 int d_idx;
1289 drmachid_t d_id;
1290 drmach_device_t *device;
1291
1292 ASSERT(DRMACH_IS_BOARD_ID(b_id));
1293 brd = b_id;
1294 b = brd->bnum;
1295
1296 /*
1297 * Step 3: Update PC MADR tables for CPUs.
1298 */
1299 if (brd->devices == NULL) {
1300 /* devices not initialized */
1301 continue;
1302 }
1303
1304 rv = drmach_array_first(brd->devices, &d_idx, &d_id);
1305 if (rv) {
1306 /* must mean no devices on this board */
1307 break;
1308 }
1309
1310 DRMACH_PR("\t%s\n", brd->cm.name);
1311
1312 do {
1313 ASSERT(DRMACH_IS_DEVICE_ID(d_id));
1314
1315 if (!DRMACH_IS_CPU_ID(d_id))
1316 continue;
1317
1318 device = d_id;
1319 i = device->unum;
1320
1321 DRMACH_PR("\t\t%s\n", device->cm.name);
1322
1323 /*
1324 * Disabled detaching mem node.
1325 */
1326 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, s_bd, i);
1327 rsp[m].masr = s_masr;
1328 m++;
1329 /*
1330 * Always write masr with present bit
1331 * off and then again with it on.
1332 */
1333 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, t_bd, i);
1334 rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK;
1335 m++;
1336 rsp[m].masr_addr = rsp[m-1].masr_addr;
1337 rsp[m].masr = t_masr;
1338 m++;
1339
1340 } while (drmach_array_next(brd->devices, &d_idx, &d_id) == 0);
1341
1342 /*
1343 * Step 4: Update PC MADR tables for IOs.
1344 */
1345 rv = drmach_array_first(brd->devices, &d_idx, &d_id);
1346 /* this worked for previous loop, must work here too */
1347 ASSERT(rv == 0);
1348
1349 do {
1350 ASSERT(DRMACH_IS_DEVICE_ID(d_id));
1351
1352 if (!DRMACH_IS_IO_ID(d_id))
1353 continue;
1354
1355 device = d_id;
1356 i = device->unum;
1357
1358 DRMACH_PR("\t\t%s\n", device->cm.name);
1359
1360 /*
1361 * Disabled detaching mem node.
1362 */
1363 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, s_bd, i+4);
1364 rsp[m].masr = s_masr;
1365 m++;
1366 /*
1367 * Always write masr with present bit
1368 * off and then again with it on.
1369 */
1370 rsp[m].masr_addr = STARFIRE_PC_MADR_ADDR(b, t_bd, i+4);
1371 rsp[m].masr = t_masr & ~STARFIRE_MC_MEM_PRESENT_MASK;
1372 m++;
1373 rsp[m].masr_addr = rsp[m-1].masr_addr;
1374 rsp[m].masr = t_masr;
1375 m++;
1376
1377 } while (drmach_array_next(brd->devices, &d_idx, &d_id) == 0);
1378 } while (drmach_array_next(drmach_boards, &b_idx, &b_id) == 0);
1379
1380 /*
1381 * Zero masr_addr value indicates the END.
1382 */
1383 rsp[m].masr_addr = 0ull;
1384 rsp[m].masr = 0;
1385 DRMACH_PR("number of steps in rename script = %d\n", m);
1386 m++;
1387
1388 /* paranoia */
1389 ASSERT((caddr_t)&rsp[m] <= buf + buflen);
1390
1391 #ifdef DEBUG
1392 {
1393 int j;
1394
1395 DRMACH_PR("mc idle register address list:");
1396 isp = (drmach_mc_idle_script_t *)buf;
1397 DRMACH_PR("source mc idle addr 0x%lx, mem id %p",
1398 isp[0].idle_addr, (void *)isp[0].mem);
1399 DRMACH_PR("target mc idle addr 0x%lx, mem id %p",
1400 isp[1].idle_addr, (void *)isp[1].mem);
1401 ASSERT(isp[2].idle_addr == 0);
1402
1403 DRMACH_PR("copy-rename script:");
1404 for (j = 0; j < m; j++) {
1405 DRMACH_PR("0x%lx = 0x%08x",
1406 rsp[j].masr_addr, rsp[j].masr);
1407 }
1408
1409 DELAY(1000000);
1410 }
1411 #endif
1412
1413 /* return number of bytes consumed */
1414 b = (caddr_t)&rsp[m] - buf;
1415 DRMACH_PR("total number of bytes consumed is %d\n", b);
1416 ASSERT(b <= buflen);
1417
1418 #ifdef lint
1419 buflen = buflen;
1420 #endif
1421
1422 return (NULL);
1423 }
1424
1425 /*
1426 * The routine performs the necessary memory COPY and MC adr SWITCH.
1427 * Both operations MUST be at the same "level" so that the stack is
1428 * maintained correctly between the copy and switch. The switch
1429 * portion implements a caching mechanism to guarantee the code text
1430 * is cached prior to execution. This is to guard against possible
1431 * memory access while the MC adr's are being modified.
1432 *
1433 * IMPORTANT: The _drmach_copy_rename_end() function must immediately
1434 * follow drmach_copy_rename_prog__relocatable() so that the correct
1435 * "length" of the drmach_copy_rename_prog__relocatable can be
1436 * calculated. This routine MUST be a LEAF function, i.e. it can
1437 * make NO function calls, primarily for two reasons:
1438 *
1439 * 1. We must keep the stack consistent across the "switch".
1440 * 2. Function calls are compiled to relative offsets, and
1441 * we execute this function we'll be executing it from
1442 * a copied version in a different area of memory, thus
1443 * the relative offsets will be bogus.
1444 *
1445 * Moreover, it must have the "__relocatable" suffix to inform DTrace
1446 * providers (and anything else, for that matter) that this
1447 * function's text is manually relocated elsewhere before it is
1448 * executed. That is, it cannot be safely instrumented with any
1449 * methodology that is PC-relative.
1450 */
1451 static void
drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t * prog)1452 drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog)
1453 {
1454 extern void drmach_exec_script_il(drmach_rename_script_t *rsp);
1455
1456 drmach_mc_idle_script_t *isp;
1457 struct memlist *ml;
1458 int csize;
1459 int lnsize;
1460 uint64_t caddr;
1461
1462 isp = (drmach_mc_idle_script_t *)prog->data;
1463
1464 caddr = ecache_flushaddr;
1465 csize = (cpunodes[CPU->cpu_id].ecache_size << 1);
1466 lnsize = cpunodes[CPU->cpu_id].ecache_linesize;
1467
1468 /*
1469 * DO COPY.
1470 */
1471 for (ml = prog->c_ml; ml; ml = ml->ml_next) {
1472 uint64_t s_pa, t_pa;
1473 uint64_t nbytes;
1474
1475 s_pa = prog->s_copybasepa + ml->ml_address;
1476 t_pa = prog->t_copybasepa + ml->ml_address;
1477 nbytes = ml->ml_size;
1478
1479 while (nbytes != 0ull) {
1480 /*
1481 * This copy does NOT use an ASI
1482 * that avoids the Ecache, therefore
1483 * the dst_pa addresses may remain
1484 * in our Ecache after the dst_pa
1485 * has been removed from the system.
1486 * A subsequent write-back to memory
1487 * will cause an ARB-stop because the
1488 * physical address no longer exists
1489 * in the system. Therefore we must
1490 * flush out local Ecache after we
1491 * finish the copy.
1492 */
1493
1494 /* copy 32 bytes at src_pa to dst_pa */
1495 bcopy32_il(s_pa, t_pa);
1496
1497 /* increment by 32 bytes */
1498 s_pa += (4 * sizeof (uint64_t));
1499 t_pa += (4 * sizeof (uint64_t));
1500
1501 /* decrement by 32 bytes */
1502 nbytes -= (4 * sizeof (uint64_t));
1503 }
1504 }
1505
1506 /*
1507 * Since bcopy32_il() does NOT use an ASI to bypass
1508 * the Ecache, we need to flush our Ecache after
1509 * the copy is complete.
1510 */
1511 flush_ecache_il(caddr, csize, lnsize); /* inline version */
1512
1513 /*
1514 * Wait for MCs to go idle.
1515 */
1516 do {
1517 register int t = 10;
1518 register uint_t v;
1519
1520 /* loop t cycles waiting for each mc to indicate it's idle */
1521 do {
1522 v = ldphysio_il(isp->idle_addr)
1523 & STARFIRE_MC_IDLE_MASK;
1524
1525 } while (v != STARFIRE_MC_IDLE_MASK && t-- > 0);
1526
1527 /* bailout if timedout */
1528 if (t <= 0) {
1529 prog->restless_mc = isp->mem;
1530 return;
1531 }
1532
1533 isp += 1;
1534
1535 /* stop if terminating zero has been reached */
1536 } while (isp->idle_addr != 0);
1537
1538 /* advance passed terminating zero */
1539 isp += 1;
1540
1541 /*
1542 * The following inline assembly routine caches
1543 * the rename script and then caches the code that
1544 * will do the rename. This is necessary
1545 * so that we don't have any memory references during
1546 * the reprogramming. We accomplish this by first
1547 * jumping through the code to guarantee it's cached
1548 * before we actually execute it.
1549 */
1550 drmach_exec_script_il((drmach_rename_script_t *)isp);
1551 }
1552
1553 static void
drmach_copy_rename_end(void)1554 drmach_copy_rename_end(void)
1555 {
1556 /*
1557 * IMPORTANT: This function's location MUST be located immediately
1558 * following drmach_copy_rename_prog__relocatable to
1559 * accurately estimate its size. Note that this assumes
1560 * the compiler keeps these functions in the order in
1561 * which they appear :-o
1562 */
1563 }
1564
1565 sbd_error_t *
drmach_copy_rename_init(drmachid_t t_id,uint64_t t_slice_offset,drmachid_t s_id,struct memlist * c_ml,drmachid_t * pgm_id)1566 drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset,
1567 drmachid_t s_id, struct memlist *c_ml, drmachid_t *pgm_id)
1568 {
1569 drmach_device_t *s_mem;
1570 drmach_device_t *t_mem;
1571 struct memlist *x_ml;
1572 uint64_t off_mask, s_copybasepa, t_copybasepa, t_basepa;
1573 int len;
1574 caddr_t bp, wp;
1575 pda_handle_t ph;
1576 sbd_error_t *err;
1577 drmach_copy_rename_program_t *prog;
1578
1579 if (!DRMACH_IS_MEM_ID(s_id))
1580 return (drerr_new(0, ESTF_INAPPROP, NULL));
1581 if (!DRMACH_IS_MEM_ID(t_id))
1582 return (drerr_new(0, ESTF_INAPPROP, NULL));
1583 s_mem = s_id;
1584 t_mem = t_id;
1585
1586 /* get starting physical address of target memory */
1587 err = drmach_mem_get_base_physaddr(t_id, &t_basepa);
1588 if (err)
1589 return (err);
1590
1591 /* calculate slice offset mask from slice size */
1592 off_mask = mc_get_mem_alignment() - 1;
1593
1594 /* calculate source and target base pa */
1595 s_copybasepa = c_ml->ml_address;
1596 t_copybasepa =
1597 t_basepa + ((c_ml->ml_address & off_mask) - t_slice_offset);
1598
1599 /* paranoia */
1600 ASSERT((c_ml->ml_address & off_mask) >= t_slice_offset);
1601
1602 /* adjust copy memlist addresses to be relative to copy base pa */
1603 x_ml = c_ml;
1604 while (x_ml != NULL) {
1605 x_ml->ml_address -= s_copybasepa;
1606 x_ml = x_ml->ml_next;
1607 }
1608
1609 #ifdef DEBUG
1610 {
1611 uint64_t s_basepa, s_size, t_size;
1612
1613 x_ml = c_ml;
1614 while (x_ml->ml_next != NULL)
1615 x_ml = x_ml->ml_next;
1616
1617 DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n",
1618 s_copybasepa,
1619 s_copybasepa + x_ml->ml_address + x_ml->ml_size);
1620
1621 DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n",
1622 t_copybasepa,
1623 t_copybasepa + x_ml->ml_address + x_ml->ml_size);
1624
1625 DRMACH_PR("copy memlist (relative to copy base pa):\n");
1626 MEMLIST_DUMP(c_ml);
1627
1628 err = drmach_mem_get_base_physaddr(s_id, &s_basepa);
1629 ASSERT(err == NULL);
1630
1631 err = drmach_mem_get_size(s_id, &s_size);
1632 ASSERT(err == NULL);
1633
1634 err = drmach_mem_get_size(t_id, &t_size);
1635 ASSERT(err == NULL);
1636
1637 DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n",
1638 s_basepa, s_size);
1639 DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n",
1640 t_basepa, t_size);
1641
1642 ASSERT(s_copybasepa + x_ml->ml_address + x_ml->ml_size <=
1643 s_basepa + s_size);
1644 ASSERT(t_copybasepa + x_ml->ml_address + x_ml->ml_size <=
1645 t_basepa + t_size);
1646 }
1647 #endif
1648
1649 ph = drmach_pda_open();
1650 if (ph == NULL)
1651 return (DRMACH_INTERNAL_ERROR());
1652
1653 /*
1654 * bp will be page aligned, since we're calling
1655 * kmem_zalloc() with an exact multiple of PAGESIZE.
1656 */
1657 wp = bp = kmem_zalloc(PAGESIZE, KM_SLEEP);
1658
1659 /* allocate space for copy rename struct */
1660 len = sizeof (drmach_copy_rename_program_t);
1661 DRMACH_PR("prog = 0x%p, header len %d\n", (void *)wp, len);
1662 prog = (drmach_copy_rename_program_t *)wp;
1663 wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1);
1664
1665 /*
1666 * Copy the code for the copy-rename routine into
1667 * a page aligned piece of memory. We do this to guarantee
1668 * that we're executing within the same page and thus reduce
1669 * the possibility of cache collisions between different
1670 * pages.
1671 */
1672 len = (int)((ulong_t)drmach_copy_rename_end -
1673 (ulong_t)drmach_copy_rename_prog__relocatable);
1674 ASSERT(wp + len < bp + PAGESIZE);
1675 bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
1676
1677 DRMACH_PR("copy-rename function 0x%p, len %d\n", (void *)wp, len);
1678 prog->run = (void (*)())wp;
1679 wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1);
1680
1681 /*
1682 * Prepare data page that will contain script of
1683 * operations to perform during copy-rename.
1684 * Allocate temporary buffer to hold script.
1685 */
1686 err = drmach_prep_rename_script(s_mem, t_mem, t_slice_offset,
1687 wp, PAGESIZE - (wp - bp));
1688 if (err) {
1689 (void) drmach_copy_rename_fini(prog);
1690 return (err);
1691 }
1692
1693 DRMACH_PR("copy-rename script 0x%p, len %d\n", (void *)wp, len);
1694 prog->data = wp;
1695 wp += (len + ecache_alignsize - 1) & ~ (ecache_alignsize - 1);
1696
1697 prog->ph = ph;
1698 prog->s_copybasepa = s_copybasepa;
1699 prog->t_copybasepa = t_copybasepa;
1700 prog->c_ml = c_ml;
1701 *pgm_id = prog;
1702
1703 return (NULL);
1704 }
1705
1706 sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)1707 drmach_copy_rename_fini(drmachid_t id)
1708 {
1709 drmach_copy_rename_program_t *prog = id;
1710 sbd_error_t *err = NULL;
1711
1712 if (prog->c_ml != NULL)
1713 memlist_delete(prog->c_ml);
1714
1715 if (prog->ph != NULL)
1716 pda_close(prog->ph);
1717
1718 if (prog->restless_mc != 0) {
1719 cmn_err(CE_WARN, "MC did not idle; OBP Node 0x%x",
1720 (uint_t)drmach_node_get_dnode(prog->restless_mc->node));
1721
1722 err = DRMACH_INTERNAL_ERROR();
1723 }
1724
1725 kmem_free(prog, PAGESIZE);
1726
1727 return (err);
1728 }
1729
1730 static sbd_error_t *
drmach_io_new(drmach_device_t * dp)1731 drmach_io_new(drmach_device_t *dp)
1732 {
1733 static sbd_error_t *drmach_io_release(drmachid_t);
1734 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
1735
1736 sbd_error_t *err;
1737 int portid;
1738
1739 err = drmach_device_get_prop(dp, "upa-portid", &portid);
1740 if (err == NULL) {
1741 ASSERT(portid & 0x40);
1742 dp->unum = portid & 1;
1743 }
1744
1745 dp->cm.isa = (void *)drmach_io_new;
1746 dp->cm.release = drmach_io_release;
1747 dp->cm.status = drmach_io_status;
1748
1749 (void) snprintf(dp->cm.name, sizeof (dp->cm.name), "%s%d", dp->type,
1750 dp->unum);
1751
1752 return (err);
1753 }
1754
1755 static void
drmach_iopc_op(pda_handle_t ph,drmach_iopc_op_t op)1756 drmach_iopc_op(pda_handle_t ph, drmach_iopc_op_t op)
1757 {
1758 register int b;
1759
1760 for (b = 0; b < MAX_BOARDS; b++) {
1761 int p;
1762 ushort_t bda_ioc;
1763 board_desc_t *bdesc;
1764
1765 if (pda_board_present(ph, b) == 0)
1766 continue;
1767
1768 bdesc = (board_desc_t *)pda_get_board_info(ph, b);
1769 /*
1770 * Update PCs for IOCs.
1771 */
1772 bda_ioc = bdesc->bda_ioc;
1773 for (p = 0; p < MAX_IOCS; p++) {
1774 u_longlong_t idle_addr;
1775 uchar_t value;
1776
1777 if (BDA_NBL(bda_ioc, p) != BDAN_GOOD)
1778 continue;
1779
1780 idle_addr = STARFIRE_BB_PC_ADDR(b, p, 1);
1781
1782 switch (op) {
1783 case DO_PAUSE:
1784 value = STARFIRE_BB_PC_PAUSE(p);
1785 break;
1786
1787 case DO_IDLE:
1788 value = STARFIRE_BB_PC_IDLE(p);
1789 break;
1790
1791 case DO_UNPAUSE:
1792 value = ldbphysio(idle_addr);
1793 value &= ~STARFIRE_BB_PC_PAUSE(p);
1794 break;
1795
1796 case DO_UNIDLE:
1797 value = ldbphysio(idle_addr);
1798 value &= ~STARFIRE_BB_PC_IDLE(p);
1799 break;
1800
1801 default:
1802 cmn_err(CE_PANIC,
1803 "drmach_iopc_op: unknown op (%d)",
1804 (int)op);
1805 /*NOTREACHED*/
1806 }
1807 stbphysio(idle_addr, value);
1808 }
1809 }
1810 }
1811
1812 void
drmach_copy_rename(drmachid_t id)1813 drmach_copy_rename(drmachid_t id)
1814 {
1815 drmach_copy_rename_program_t *prog = id;
1816 uint64_t neer;
1817
1818 /*
1819 * UPA IDLE
1820 * Protocol = PAUSE -> IDLE -> UNPAUSE
1821 * In reality since we only "idle" the IOPCs it's sufficient
1822 * to just issue the IDLE operation since (in theory) all IOPCs
1823 * in the field are PC6. However, we'll be robust and do the
1824 * proper workaround protocol so that we never have to worry!
1825 */
1826 drmach_iopc_op(prog->ph, DO_PAUSE);
1827 drmach_iopc_op(prog->ph, DO_IDLE);
1828 DELAY(100);
1829 drmach_iopc_op(prog->ph, DO_UNPAUSE);
1830 DELAY(100);
1831
1832 /* disable CE reporting */
1833 neer = get_error_enable();
1834 set_error_enable(neer & ~EER_CEEN);
1835
1836 /* run the copy/rename program */
1837 prog->run(prog);
1838
1839 /* enable CE reporting */
1840 set_error_enable(neer);
1841
1842 /*
1843 * UPA UNIDLE
1844 * Protocol = UNIDLE
1845 */
1846 drmach_iopc_op(prog->ph, DO_UNIDLE);
1847 DELAY(100);
1848 }
1849
1850 /*
1851 * The counter-timer and perf-counter nodes are not being cleaned
1852 * up after a board that was present at start of day is detached.
1853 * If the board has become unconfigured with this operation, walk
1854 * the prom tree and find all counter-timer and perf-counter nodes
1855 * that have the same board number as the board that was just
1856 * unconfigured and remove them.
1857 */
1858 static sbd_error_t *
drmach_remove_counter_nodes(drmachid_t id)1859 drmach_remove_counter_nodes(drmachid_t id)
1860 {
1861 int num;
1862 char name[OBP_MAXDRVNAME];
1863 pnode_t child;
1864 dev_info_t *dip;
1865 sbd_error_t *err;
1866 drmach_status_t stat;
1867 drmach_board_t *bp;
1868
1869 if (!DRMACH_IS_BOARD_ID(id)) {
1870 return (drerr_new(0, ESTF_INAPPROP, NULL));
1871 }
1872
1873 if ((err = drmach_board_status(id, &stat)) != NULL) {
1874 return (err);
1875 }
1876
1877 /*
1878 * Only clean up the counter-timer and perf-counter
1879 * nodes when the entire board is unconfigured.
1880 */
1881 if (stat.configured) {
1882 return (NULL);
1883 }
1884
1885 bp = (drmach_board_t *)id;
1886
1887 err = NULL;
1888
1889 for (child = prom_childnode(prom_rootnode()); child != OBP_NONODE;
1890 child = prom_nextnode(child)) {
1891
1892 if (prom_getprop(child, OBP_BOARDNUM, (caddr_t)&num) == -1) {
1893 continue;
1894 }
1895
1896 if (bp->bnum != num) {
1897 continue;
1898 }
1899
1900 if (prom_getprop(child, OBP_NAME, (caddr_t)name) == -1) {
1901 continue;
1902 }
1903
1904 if (strncmp(name, MISC_COUNTER_TIMER_DEVNAME, OBP_MAXDRVNAME) &&
1905 strncmp(name, MISC_PERF_COUNTER_DEVNAME, OBP_MAXDRVNAME)) {
1906 continue;
1907 }
1908
1909 /* Root node doesn't have to be held */
1910 dip = e_ddi_nodeid_to_dip(child);
1911
1912 /*
1913 * If the node is only in the OBP tree, then
1914 * we don't have to remove it.
1915 */
1916 if (dip) {
1917 dev_info_t *fdip = NULL;
1918
1919 DRMACH_PR("removing %s devinfo node\n", name);
1920
1921 e_ddi_branch_hold(dip);
1922 ddi_release_devi(dip); /* held in e_ddi_nodeid_to_dip */
1923
1924 if (e_ddi_branch_destroy(dip, &fdip, 0)) {
1925 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1926
1927 /*
1928 * If non-NULL, fdip is held and must be
1929 * released.
1930 */
1931 if (fdip != NULL) {
1932 (void) ddi_pathname(fdip, path);
1933 ddi_release_devi(fdip);
1934 } else {
1935 (void) ddi_pathname(dip, path);
1936 }
1937
1938 err = drerr_new(1, ESTF_DRVFAIL, path);
1939 kmem_free(path, MAXPATHLEN);
1940 e_ddi_branch_rele(dip);
1941 break;
1942 }
1943 }
1944 }
1945
1946 return (err);
1947 }
1948
1949 /*ARGSUSED*/
1950 sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)1951 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
1952 {
1953 /* allow status and ncm operations to always succeed */
1954 if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
1955 return (NULL);
1956 }
1957
1958 /* check all other commands for the required option string */
1959 if ((opts->size > 0) && (opts->copts != NULL)) {
1960
1961 DRMACH_PR("platform options: %s\n", opts->copts);
1962
1963 if (strstr(opts->copts, "xfdr") != NULL) {
1964 return (NULL);
1965 }
1966 }
1967
1968 return (drerr_new(0, ESTF_SUPPORT, NULL));
1969 }
1970
1971 /*ARGSUSED*/
1972 sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)1973 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
1974 {
1975 sbd_error_t *err = NULL;
1976
1977 switch (cmd) {
1978 case SBD_CMD_UNCONFIGURE:
1979
1980 err = drmach_remove_counter_nodes(id);
1981 break;
1982
1983 case SBD_CMD_CONFIGURE:
1984 case SBD_CMD_DISCONNECT:
1985 case SBD_CMD_CONNECT:
1986 case SBD_CMD_GETNCM:
1987 case SBD_CMD_STATUS:
1988 break;
1989
1990 default:
1991 break;
1992 }
1993
1994 return (err);
1995 }
1996
1997 sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)1998 drmach_board_assign(int bnum, drmachid_t *id)
1999 {
2000 sbd_error_t *err;
2001
2002 if (!drmach_initialized && drmach_init() == -1) {
2003 err = DRMACH_INTERNAL_ERROR();
2004 } else if (drmach_array_get(drmach_boards, bnum, id) == -1) {
2005 err = drerr_new(1, ESTF_BNUM, "%d", bnum);
2006 } else if (*id != NULL) {
2007 err = NULL;
2008 } else {
2009 drmach_board_t *bp;
2010
2011 *id = (drmachid_t)drmach_board_new(bnum);
2012 bp = *id;
2013 bp->assigned = 1;
2014 err = NULL;
2015 }
2016
2017 return (err);
2018 }
2019
2020 static int
drmach_attach_board(void * arg)2021 drmach_attach_board(void *arg)
2022 {
2023 drmach_board_t *obj = (drmach_board_t *)arg;
2024 cpuset_t cset;
2025 int retval;
2026
2027 /*
2028 * OBP disables traps during the board probe.
2029 * So, in order to prevent cross-call/cross-trap timeouts,
2030 * and thus panics, we effectively block anybody from
2031 * issuing xc's/xt's by doing a promsafe_xc_attention.
2032 * In the previous version of Starfire DR (2.6), a timeout
2033 * suspension mechanism was implemented in the send-mondo
2034 * assembly. That mechanism is unnecessary with the
2035 * existence of xc_attention/xc_dismissed.
2036 */
2037 cset = cpu_ready_set;
2038 promsafe_xc_attention(cset);
2039
2040 retval = prom_starfire_add_brd(obj->connect_cpuid);
2041
2042 xc_dismissed(cset);
2043
2044 return (retval);
2045 }
2046
2047 sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)2048 drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
2049 {
2050 drmach_board_t *obj = (drmach_board_t *)id;
2051 int retval;
2052 sbd_error_t *err;
2053 char *cptr, *copts;
2054
2055 if (!DRMACH_IS_BOARD_ID(id))
2056 return (drerr_new(0, ESTF_INAPPROP, NULL));
2057
2058 if (opts->size > 0)
2059 copts = opts->copts;
2060
2061 if ((cptr = strstr(copts, "cpuid=")) != NULL) {
2062 int cpuid;
2063
2064 cptr += strlen("cpuid=");
2065 cpuid = stoi(&cptr);
2066
2067 if (DRMACH_CPUID2BNUM(cpuid) == obj->bnum) {
2068 obj->connect_cpuid = cpuid;
2069 obj->assigned = 1;
2070 } else
2071 return (drerr_new(1, ESTF_SETCPUVAL, "%d", cpuid));
2072 } else {
2073 /* cpuid was not specified */
2074 obj->connect_cpuid = -1;
2075 }
2076
2077 if (obj->connect_cpuid == -1) {
2078 err = drerr_new(1, ESTF_NOCPUID, obj->cm.name);
2079 return (err);
2080 }
2081
2082 cmn_err(CE_CONT, "DRMACH: PROM attach %s CPU %d\n",
2083 obj->cm.name, obj->connect_cpuid);
2084
2085 retval = prom_tree_update(drmach_attach_board, obj);
2086
2087 if (retval == 0)
2088 err = NULL;
2089 else {
2090 cmn_err(CE_WARN, "prom error: prom_starfire_add_brd(%d) "
2091 "returned %d", obj->connect_cpuid, retval);
2092
2093 err = drerr_new(1, ESTF_PROBE, obj->cm.name);
2094 }
2095
2096 obj->connect_cpuid = -1;
2097
2098 return (err);
2099 }
2100
2101 /*ARGSUSED*/
2102 sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)2103 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
2104 {
2105 drmach_board_t *bp;
2106 int rv;
2107 int d_idx; /* device index */
2108 drmachid_t d_id; /* device ID */
2109 sbd_error_t *err;
2110
2111 if (!DRMACH_IS_BOARD_ID(id))
2112 return (drerr_new(0, ESTF_INAPPROP, NULL));
2113
2114 bp = id;
2115
2116 /*
2117 * We need to make sure all of the board's device nodes
2118 * have been removed from the Solaris device tree before
2119 * continuing with the disconnect. Otherwise, we could
2120 * disconnect the board and remove the OBP device tree
2121 * nodes with Solaris device tree nodes remaining.
2122 *
2123 * On Starfire, Solaris device tree nodes are deleted
2124 * during unconfigure by drmach_unconfigure(). It's
2125 * necessary to do this here because drmach_unconfigure()
2126 * failures are not handled during unconfigure.
2127 */
2128 if (bp->devices) {
2129 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
2130 while (rv == 0) {
2131 err = drmach_unconfigure(d_id, DRMACH_DEVI_REMOVE);
2132 if (err)
2133 return (err);
2134
2135 rv = drmach_array_next(bp->devices, &d_idx, &d_id);
2136 }
2137 }
2138
2139 /*
2140 * Starfire board Solaris device tree counter nodes,
2141 * which are only present on start-of-day boards, are
2142 * removed in the dr_post_op() code flow after the
2143 * board is unconfigured. We call the counter node
2144 * removal function here because unconfigure errors
2145 * can cause the dr_post_op() function to be skipped
2146 * after an unconfigure operation even though all of
2147 * the board's devices have been transitioned to the
2148 * unconfigured state.
2149 */
2150 err = drmach_remove_counter_nodes(id);
2151 if (err)
2152 return (err);
2153
2154 return (NULL);
2155 }
2156
2157 static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)2158 drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
2159 {
2160 drmach_node_t *node = args->node;
2161 drmach_board_cb_data_t *data = args->data;
2162 drmach_board_t *obj = data->obj;
2163
2164 int rv;
2165 int bnum;
2166 drmach_device_t *device;
2167
2168 rv = drmach_node_get_prop(node, OBP_BOARDNUM, &bnum);
2169 if (rv) {
2170 /*
2171 * if the node does not have a board# property, then
2172 * by that information alone it is known that drmach
2173 * is not interested in it.
2174 */
2175 return (0);
2176 } else if (bnum != obj->bnum)
2177 return (0);
2178
2179 /*
2180 * Create a device data structure from this node data.
2181 * The call may yield nothing if the node is not of interest
2182 * to drmach.
2183 */
2184 data->err = drmach_device_new(node, obj, &device);
2185 if (data->err)
2186 return (-1);
2187 else if (device == NULL) {
2188 /*
2189 * drmach_device_new examined the node we passed in
2190 * and determined that it was one not of interest to
2191 * drmach. So, it is skipped.
2192 */
2193 return (0);
2194 }
2195
2196 rv = drmach_array_set(obj->devices, data->ndevs++, device);
2197 if (rv) {
2198 drmach_device_dispose(device);
2199 data->err = DRMACH_INTERNAL_ERROR();
2200 return (-1);
2201 }
2202
2203 data->err = (*data->found)(data->a, device->type, device->unum, device);
2204 return (data->err == NULL ? 0 : -1);
2205 }
2206
2207 sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))2208 drmach_board_find_devices(drmachid_t id, void *a,
2209 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
2210 {
2211 extern int plat_max_cpu_units_per_board();
2212 extern int plat_max_mem_units_per_board();
2213 extern int plat_max_io_units_per_board();
2214
2215 drmach_board_t *obj = (drmach_board_t *)id;
2216 sbd_error_t *err;
2217 int max_devices;
2218 int rv;
2219 drmach_board_cb_data_t data;
2220
2221 max_devices = plat_max_cpu_units_per_board();
2222 max_devices += plat_max_mem_units_per_board();
2223 max_devices += plat_max_io_units_per_board();
2224
2225 obj->devices = drmach_array_new(0, max_devices);
2226
2227 data.obj = obj;
2228 data.ndevs = 0;
2229 data.found = found;
2230 data.a = a;
2231 data.err = NULL;
2232
2233 rv = drmach_node_walk(obj->tree, &data, drmach_board_find_devices_cb);
2234 if (rv == 0)
2235 err = NULL;
2236 else {
2237 drmach_array_dispose(obj->devices, drmach_device_dispose);
2238 obj->devices = NULL;
2239
2240 if (data.err)
2241 err = data.err;
2242 else
2243 err = DRMACH_INTERNAL_ERROR();
2244 }
2245
2246 return (err);
2247 }
2248
2249 int
drmach_board_lookup(int bnum,drmachid_t * id)2250 drmach_board_lookup(int bnum, drmachid_t *id)
2251 {
2252 int rv = 0;
2253
2254 if (!drmach_initialized && drmach_init() == -1) {
2255 *id = 0;
2256 rv = -1;
2257 } else if (drmach_array_get(drmach_boards, bnum, id)) {
2258 *id = 0;
2259 rv = -1;
2260 }
2261 return (rv);
2262 }
2263
2264 sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)2265 drmach_board_name(int bnum, char *buf, int buflen)
2266 {
2267 (void) snprintf(buf, buflen, "SB%d", bnum);
2268 return (NULL);
2269 }
2270
2271 sbd_error_t *
drmach_board_poweroff(drmachid_t id)2272 drmach_board_poweroff(drmachid_t id)
2273 {
2274 drmach_board_t *bp;
2275 sbd_error_t *err;
2276 drmach_status_t stat;
2277
2278 if (!DRMACH_IS_BOARD_ID(id))
2279 return (drerr_new(0, ESTF_INAPPROP, NULL));
2280 bp = id;
2281
2282 err = drmach_board_status(id, &stat);
2283 if (err)
2284 return (err);
2285 else if (stat.configured || stat.busy)
2286 return (drerr_new(0, ESTF_CONFIGBUSY, bp->cm.name));
2287 else {
2288 /* board power off is essentially a noop for Starfire */
2289 bp->powered = 0;
2290 return (NULL);
2291 }
2292 /*NOTREACHED*/
2293 }
2294
2295 sbd_error_t *
drmach_board_poweron(drmachid_t id)2296 drmach_board_poweron(drmachid_t id)
2297 {
2298 drmach_board_t *bp;
2299
2300 if (!DRMACH_IS_BOARD_ID(id))
2301 return (drerr_new(0, ESTF_INAPPROP, NULL));
2302 bp = id;
2303
2304 /* board power on is essentially a noop for Starfire */
2305 bp->powered = 1;
2306
2307 return (NULL);
2308 }
2309
2310 static sbd_error_t *
drmach_board_release(drmachid_t id)2311 drmach_board_release(drmachid_t id)
2312 {
2313 if (!DRMACH_IS_BOARD_ID(id))
2314 return (drerr_new(0, ESTF_INAPPROP, NULL));
2315 return (NULL);
2316 }
2317
2318 /*ARGSUSED*/
2319 sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)2320 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
2321 {
2322 return (NULL);
2323 }
2324
2325 sbd_error_t *
drmach_board_unassign(drmachid_t id)2326 drmach_board_unassign(drmachid_t id)
2327 {
2328 drmach_board_t *bp;
2329 sbd_error_t *err;
2330 drmach_status_t stat;
2331
2332 if (!DRMACH_IS_BOARD_ID(id))
2333 return (drerr_new(0, ESTF_INAPPROP, NULL));
2334 bp = id;
2335
2336 err = drmach_board_status(id, &stat);
2337 if (err)
2338 return (err);
2339 else if (stat.configured || stat.busy)
2340 return (drerr_new(0, ESTF_CONFIGBUSY, bp->cm.name));
2341 else if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
2342 return (DRMACH_INTERNAL_ERROR());
2343 else {
2344 drmach_board_dispose(bp);
2345 return (NULL);
2346 }
2347 /*NOTREACHED*/
2348 }
2349
2350 static sbd_error_t *
drmach_cpu_new(drmach_device_t * dp)2351 drmach_cpu_new(drmach_device_t *dp)
2352 {
2353 static sbd_error_t *drmach_cpu_release(drmachid_t);
2354 static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
2355
2356 sbd_error_t *err;
2357 int portid;
2358
2359 err = drmach_device_get_prop(dp, "upa-portid", &portid);
2360 if (err == NULL)
2361 dp->unum = portid & 3;
2362
2363 dp->cm.isa = (void *)drmach_cpu_new;
2364 dp->cm.release = drmach_cpu_release;
2365 dp->cm.status = drmach_cpu_status;
2366
2367 (void) snprintf(dp->cm.name, sizeof (dp->cm.name), "%s%d", dp->type,
2368 dp->unum);
2369
2370 return (err);
2371 }
2372
2373 /*
2374 * drmach_cpu_obp_detach()
2375 * This requires two steps, first, we must put the cpuid into the OBP
2376 * idle loop (Idle in Program) state. Then we call OBP to place the CPU
2377 * into the "Detached" state, which does any special processing to
2378 * actually detach the cpu, such as flushing ecache, and also ensures
2379 * that a subsequent breakpoint won't restart the cpu (if it was just in
2380 * Idle in Program state).
2381 */
2382 static void
drmach_cpu_obp_detach(int cpuid)2383 drmach_cpu_obp_detach(int cpuid)
2384 {
2385 /*
2386 * Cpu may not be under OBP's control. Eg, if cpu exited to download
2387 * helper on a prior attach.
2388 */
2389 if (CPU_SGN_EXISTS(cpuid) &&
2390 !SGN_CPU_IS_OS(cpuid) &&
2391 !SGN_CPU_IS_OBP(cpuid)) {
2392 cmn_err(CE_WARN,
2393 "unexpected signature (0x%x) for cpu %d",
2394 get_cpu_sgn(cpuid), cpuid);
2395 }
2396
2397 /*
2398 * Now we place the CPU into the "Detached" idle loop in OBP.
2399 * This is so that the CPU won't be restarted if we break into
2400 * OBP with a breakpoint or BREAK key from the console, and also
2401 * if we need to do any special processing, such as flushing the
2402 * cpu's ecache, disabling interrupts (by turning of the ET bit in
2403 * the PSR) and/or spinning in BBSRAM rather than global memory.
2404 */
2405 DRMACH_PR("prom_starfire_rm_cpu(%d)\n", cpuid);
2406 prom_starfire_rm_cpu(cpuid);
2407 }
2408
2409 /*
2410 * drmach_cpu_obp_is_detached() returns TRUE if the cpu sigblock signature state
2411 * is SIGBST_DETACHED; otherwise it returns FALSE. This routine should only
2412 * be called after we have asked OBP to detach the CPU. It should NOT be
2413 * called as a check during any other flow.
2414 */
2415 static int
drmach_cpu_obp_is_detached(int cpuid)2416 drmach_cpu_obp_is_detached(int cpuid)
2417 {
2418 if (!CPU_SGN_EXISTS(cpuid) ||
2419 (SGN_CPU_IS_OS(cpuid) && SGN_CPU_STATE_IS_DETACHED(cpuid)))
2420 return (1);
2421 else
2422 return (0);
2423 }
2424
2425 static int
drmach_cpu_start(struct cpu * cp)2426 drmach_cpu_start(struct cpu *cp)
2427 {
2428 int cpuid = cp->cpu_id;
2429 int ntries = drmach_cpu_ntries;
2430 extern void restart_other_cpu(int);
2431
2432 ASSERT(MUTEX_HELD(&cpu_lock));
2433 ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
2434
2435 cp->cpu_flags &= ~CPU_POWEROFF;
2436
2437 /*
2438 * NOTE: restart_other_cpu pauses cpus during the
2439 * slave cpu start. This helps to quiesce the
2440 * bus traffic a bit which makes the tick sync
2441 * routine in the prom more robust.
2442 */
2443 DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
2444
2445 prom_starfire_add_cpu(cpuid);
2446
2447 restart_other_cpu(cpuid);
2448
2449 /*
2450 * Wait for the cpu to reach its idle thread before
2451 * we zap him with a request to blow away the mappings
2452 * he (might) have for the drmach_shutdown_asm code
2453 * he may have executed on unconfigure.
2454 */
2455 while ((cp->cpu_thread != cp->cpu_idle_thread) && (ntries > 0)) {
2456 DELAY(drmach_cpu_delay);
2457 ntries--;
2458 }
2459
2460 DRMACH_PR("waited %d out of %d loops for cpu %d\n",
2461 drmach_cpu_ntries - ntries, drmach_cpu_ntries, cpuid);
2462
2463 xt_one(cpuid, vtag_flushpage_tl1,
2464 (uint64_t)drmach_shutdown_va, (uint64_t)ksfmmup);
2465
2466 return (0);
2467 }
2468
2469 /*
2470 * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after
2471 * it has been offlined. The function of this routine is to get the cpu
2472 * spinning in a safe place. The requirement is that the system will not
2473 * reference anything on the detaching board (memory and i/o is detached
2474 * elsewhere) and that the CPU not reference anything on any other board
2475 * in the system. This isolation is required during and after the writes
2476 * to the domain masks to remove the board from the domain.
2477 *
2478 * To accomplish this isolation the following is done:
2479 * 1) Create a locked mapping to a location in BBSRAM where
2480 * the cpu will execute.
2481 * 2) Copy the target function (drmach_shutdown_asm) in which
2482 * the cpu will execute into BBSRAM.
2483 * 3) Jump into function with BBSRAM.
2484 * Function will:
2485 * 3.1) Flush its Ecache (displacement).
2486 * 3.2) Flush its Dcache with HW mechanism.
2487 * 3.3) Flush its Icache with HW mechanism.
2488 * 3.4) Flush all valid and _unlocked_ D-TLB entries.
2489 * 3.5) Flush all valid and _unlocked_ I-TLB entries.
2490 * 3.6) Clear xt_mb to signal completion. Note: cache line is
2491 * recovered by drmach_cpu_poweroff().
2492 * 4) Jump into a tight loop.
2493 */
2494 #define DRMACH_BBSRAM_OFFSET 0x1000
2495
2496 static void
drmach_cpu_stop_self(void)2497 drmach_cpu_stop_self(void)
2498 {
2499 int cpuid = (int)CPU->cpu_id;
2500 tte_t tte;
2501 volatile uint_t *src, *dst;
2502 uint_t funclen;
2503 uint64_t bbsram_pa, bbsram_offset;
2504 uint_t bbsram_pfn;
2505 uint64_t bbsram_addr;
2506 void (*bbsram_func)(uint64_t);
2507 extern void drmach_shutdown_asm(uint64_t);
2508 extern void drmach_shutdown_asm_end(void);
2509
2510 funclen = (uint_t)drmach_shutdown_asm_end - (uint_t)drmach_shutdown_asm;
2511 ASSERT(funclen <= MMU_PAGESIZE);
2512 /*
2513 * We'll start from the 0th's base.
2514 */
2515 bbsram_pa = STARFIRE_UPAID2UPS(cpuid) | STARFIRE_PSI_BASE;
2516 bbsram_offset = bbsram_pa | 0xfe0ULL;
2517 bbsram_pa += ldphysio(bbsram_offset) + DRMACH_BBSRAM_OFFSET;
2518
2519 bbsram_pfn = (uint_t)(bbsram_pa >> MMU_PAGESHIFT);
2520
2521 bbsram_addr = (uint64_t)drmach_shutdown_va;
2522 drmach_shutdown_asm_mbox->estack = bbsram_addr + (uint64_t)funclen;
2523
2524 tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
2525 TTE_PFN_INTHI(bbsram_pfn);
2526 tte.tte_intlo = TTE_PFN_INTLO(bbsram_pfn) |
2527 TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
2528 sfmmu_dtlb_ld_kva(drmach_shutdown_va, &tte); /* load dtlb */
2529 sfmmu_itlb_ld_kva(drmach_shutdown_va, &tte); /* load itlb */
2530
2531 for (src = (uint_t *)drmach_shutdown_asm, dst = (uint_t *)bbsram_addr;
2532 src < (uint_t *)drmach_shutdown_asm_end; src++, dst++)
2533 *dst = *src;
2534
2535 bbsram_func = (void (*)())bbsram_addr;
2536 drmach_shutdown_asm_mbox->flushaddr = ecache_flushaddr;
2537 drmach_shutdown_asm_mbox->size = (cpunodes[cpuid].ecache_size << 1);
2538 drmach_shutdown_asm_mbox->linesize = cpunodes[cpuid].ecache_linesize;
2539 drmach_shutdown_asm_mbox->physaddr =
2540 va_to_pa((void *)&drmach_xt_mb[cpuid]);
2541
2542 /*
2543 * Signal to drmach_cpu_poweroff() is via drmach_xt_mb cleared
2544 * by asm code
2545 */
2546
2547 (*bbsram_func)(va_to_pa((void *)drmach_shutdown_asm_mbox));
2548 }
2549
2550 static void
drmach_cpu_shutdown_self(void)2551 drmach_cpu_shutdown_self(void)
2552 {
2553 cpu_t *cp = CPU;
2554 int cpuid = cp->cpu_id;
2555 extern void flush_windows(void);
2556
2557 flush_windows();
2558
2559 (void) spl8();
2560
2561 ASSERT(cp->cpu_intr_actv == 0);
2562 ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
2563 cp->cpu_thread == cp->cpu_startup_thread);
2564
2565 cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
2566
2567 drmach_cpu_stop_self();
2568
2569 cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid);
2570 }
2571
2572 /* a helper routine to keep the math in one place */
2573 static processorid_t
drmach_cpu_calc_id(drmach_device_t * dp)2574 drmach_cpu_calc_id(drmach_device_t *dp)
2575 {
2576 return (dp->bp->bnum * MAX_CPU_UNITS_PER_BOARD + dp->unum);
2577 }
2578
2579 /*
2580 * Move bootproc (SIGBCPU) to another cpu. If dst_cpu is NULL, a
2581 * destination cpu is chosen from the set of cpus not located on the
2582 * same board as the current bootproc cpu.
2583 */
2584 static sbd_error_t *
drmach_cpu_juggle_bootproc(drmach_device_t * dst_cpu)2585 drmach_cpu_juggle_bootproc(drmach_device_t *dst_cpu)
2586 {
2587 processorid_t cpuid;
2588 struct cpu *cp;
2589 sbd_error_t *err;
2590 int rv;
2591
2592 ASSERT(MUTEX_HELD(&cpu_lock));
2593
2594 /* dst_cpu is NULL when target cpu is unspecified. So, pick one. */
2595 if (dst_cpu == NULL) {
2596 int avoid_board = DRMACH_CPUID2BNUM(SIGBCPU->cpu_id);
2597 int max_cpuid = MAX_BOARDS * MAX_CPU_UNITS_PER_BOARD;
2598
2599 for (cpuid = 0; cpuid < max_cpuid; cpuid++)
2600 if (DRMACH_CPUID2BNUM(cpuid) != avoid_board) {
2601 cp = cpu_get(cpuid);
2602 if (cp != NULL && cpu_is_online(cp))
2603 break;
2604 }
2605
2606 if (cpuid == max_cpuid) {
2607 err = drerr_new(1, ESTF_JUGGLE, NULL);
2608 return (err);
2609 }
2610
2611 /* else, cp points to the selected target cpu */
2612 } else {
2613 cpuid = drmach_cpu_calc_id(dst_cpu);
2614
2615 if ((cp = cpu_get(cpuid)) == NULL) {
2616 err = drerr_new(1, ESTF_NODEV, "%s::%s",
2617 dst_cpu->bp->cm.name, dst_cpu->cm.name);
2618 return (err);
2619 }
2620
2621 if (cpuid == SIGBCPU->cpu_id) {
2622 cmn_err(CE_WARN,
2623 "SIGBCPU(%d) same as new selection(%d)",
2624 SIGBCPU->cpu_id, cpuid);
2625
2626 /* technically not an error, but a no-op */
2627 return (NULL);
2628 }
2629 }
2630
2631 cmn_err(CE_NOTE, "?relocating SIGBCPU from %d to %d",
2632 SIGBCPU->cpu_id, cpuid);
2633
2634 DRMACH_PR("moving SIGBCPU to CPU %d\n", cpuid);
2635
2636 /*
2637 * Tell OBP to initialize cvc-offset field of new CPU0
2638 * so that it's in sync with OBP and cvc_server
2639 */
2640 prom_starfire_init_console(cpuid);
2641
2642 /*
2643 * Assign cvc to new cpu0's bbsram for I/O. This has to be
2644 * done BEFORE cpu0 is moved via obp, since this logic
2645 * will cause obp_helper to switch to a different bbsram for
2646 * cvc I/O. We don't want cvc writing to a buffer from which
2647 * nobody will pick up the data!
2648 */
2649 cvc_assign_iocpu(cpuid);
2650
2651 rv = prom_starfire_move_cpu0(cpuid);
2652
2653 if (rv == 0) {
2654 SIGBCPU = cp;
2655
2656 DRMACH_PR("successfully juggled to CPU %d\n", cpuid);
2657 return (NULL);
2658 } else {
2659 DRMACH_PR("prom error: prom_starfire_move_cpu0(%d) "
2660 "returned %d\n", cpuid, rv);
2661
2662 /*
2663 * The move failed, hopefully obp_helper is still back
2664 * at the old bootproc. Move cvc back there.
2665 */
2666 cvc_assign_iocpu(SIGBCPU->cpu_id);
2667
2668
2669 err = drerr_new(1, ESTF_MOVESIGB, "CPU %d", cpuid);
2670 return (err);
2671 }
2672 /*NOTREACHED*/
2673 }
2674
2675 static sbd_error_t *
drmach_cpu_release(drmachid_t id)2676 drmach_cpu_release(drmachid_t id)
2677 {
2678 drmach_device_t *dp;
2679 processorid_t cpuid;
2680 struct cpu *cp;
2681 sbd_error_t *err;
2682
2683 if (!DRMACH_IS_CPU_ID(id))
2684 return (drerr_new(0, ESTF_INAPPROP, NULL));
2685 dp = id;
2686 cpuid = drmach_cpu_calc_id(dp);
2687
2688 ASSERT(MUTEX_HELD(&cpu_lock));
2689
2690 cp = cpu_get(cpuid);
2691 if (cp == NULL)
2692 err = DRMACH_INTERNAL_ERROR();
2693 else if (SIGBCPU->cpu_id == cp->cpu_id)
2694 err = drmach_cpu_juggle_bootproc(NULL);
2695 else
2696 err = NULL;
2697
2698 return (err);
2699 }
2700
2701 static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)2702 drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
2703 {
2704 drmach_device_t *dp;
2705
2706 ASSERT(DRMACH_IS_CPU_ID(id));
2707 dp = id;
2708
2709 stat->assigned = dp->bp->assigned;
2710 stat->powered = dp->bp->powered;
2711 mutex_enter(&cpu_lock);
2712 stat->configured = (cpu_get(drmach_cpu_calc_id(dp)) != NULL);
2713 mutex_exit(&cpu_lock);
2714 stat->busy = dp->busy;
2715 (void) strncpy(stat->type, dp->type, sizeof (stat->type));
2716 stat->info[0] = '\0';
2717
2718 return (NULL);
2719 }
2720
2721 sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)2722 drmach_cpu_disconnect(drmachid_t id)
2723 {
2724 drmach_device_t *cpu;
2725 int cpuid;
2726 int ntries;
2727 int p;
2728 u_longlong_t pc_addr;
2729 uchar_t rvalue;
2730
2731 if (!DRMACH_IS_CPU_ID(id))
2732 return (drerr_new(0, ESTF_INAPPROP, NULL));
2733 cpu = id;
2734
2735 cpuid = drmach_cpu_calc_id(cpu);
2736 if (SIGBCPU->cpu_id == cpuid) {
2737 /* this cpu is SIGBCPU, can't disconnect */
2738 return (drerr_new(1, ESTF_HASSIGB, "%s::%s",
2739 cpu->bp->cm.name, cpu->cm.name));
2740 }
2741
2742 /*
2743 * Make sure SIGBST_DETACHED is set before
2744 * mapping out the sig block.
2745 */
2746 ntries = drmach_cpu_ntries;
2747 while (!drmach_cpu_obp_is_detached(cpuid) && ntries) {
2748 DELAY(drmach_cpu_delay);
2749 ntries--;
2750 }
2751 if (!drmach_cpu_obp_is_detached(cpuid)) {
2752 cmn_err(CE_WARN, "failed to mark cpu %d detached in sigblock",
2753 cpuid);
2754 }
2755
2756 /* map out signature block */
2757 if (CPU_SGN_EXISTS(cpuid)) {
2758 CPU_SGN_MAPOUT(cpuid);
2759 }
2760
2761 /*
2762 * We now PC IDLE the processor to guarantee we
2763 * stop any transactions from coming from it.
2764 */
2765 p = cpu->unum & 1;
2766 pc_addr = STARFIRE_BB_PC_ADDR(cpu->bp->bnum, cpu->unum, 0);
2767
2768 DRMACH_PR("PC idle cpu %d (addr = 0x%llx, port = %d, p = %d)",
2769 drmach_cpu_calc_id(cpu), pc_addr, cpu->unum, p);
2770
2771 rvalue = ldbphysio(pc_addr);
2772 rvalue |= STARFIRE_BB_PC_IDLE(p);
2773 stbphysio(pc_addr, rvalue);
2774 DELAY(50000);
2775
2776 return (NULL);
2777 }
2778
2779 sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)2780 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
2781 {
2782 drmach_device_t *cpu;
2783
2784 if (!DRMACH_IS_CPU_ID(id))
2785 return (drerr_new(0, ESTF_INAPPROP, NULL));
2786 cpu = id;
2787
2788 *cpuid = drmach_cpu_calc_id(cpu);
2789 return (NULL);
2790 }
2791
2792 sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)2793 drmach_cpu_get_impl(drmachid_t id, int *ip)
2794 {
2795 drmach_device_t *cpu;
2796 int impl;
2797
2798 if (!DRMACH_IS_CPU_ID(id))
2799 return (drerr_new(0, ESTF_INAPPROP, NULL));
2800
2801 cpu = id;
2802
2803 if (drmach_node_get_prop(cpu->node, "implementation#", &impl) == -1) {
2804 return (DRMACH_INTERNAL_ERROR());
2805 }
2806
2807 *ip = impl;
2808
2809 return (NULL);
2810 }
2811
2812 void
drmach_cpu_flush_ecache_sync(void)2813 drmach_cpu_flush_ecache_sync(void)
2814 {
2815 ASSERT(curthread->t_bound_cpu == CPU);
2816
2817 /*
2818 * Now let's flush our ecache thereby removing all references
2819 * to the target (detaching) memory from all ecache's in
2820 * system.
2821 */
2822 cpu_flush_ecache();
2823
2824 /*
2825 * Delay 100 usec out of paranoia to insure everything
2826 * (hardware queues) has drained before we start reprogramming
2827 * the hardware.
2828 */
2829 DELAY(100);
2830 }
2831
2832 sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)2833 drmach_get_dip(drmachid_t id, dev_info_t **dip)
2834 {
2835 drmach_device_t *dp;
2836
2837 if (!DRMACH_IS_DEVICE_ID(id))
2838 return (drerr_new(0, ESTF_INAPPROP, NULL));
2839 dp = id;
2840
2841 *dip = drmach_node_get_dip(dp->node);
2842 return (NULL);
2843 }
2844
2845 sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)2846 drmach_io_is_attached(drmachid_t id, int *yes)
2847 {
2848 drmach_device_t *dp;
2849 dev_info_t *dip;
2850 int state;
2851
2852 if (!DRMACH_IS_IO_ID(id))
2853 return (drerr_new(0, ESTF_INAPPROP, NULL));
2854 dp = id;
2855
2856 dip = drmach_node_get_dip(dp->node);
2857 if (dip == NULL) {
2858 *yes = 0;
2859 return (NULL);
2860 }
2861
2862 state = ddi_get_devstate(dip);
2863 *yes = (i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP));
2864
2865 return (NULL);
2866 }
2867
2868 sbd_error_t *
drmach_io_pre_release(drmachid_t id)2869 drmach_io_pre_release(drmachid_t id)
2870 {
2871 if (!DRMACH_IS_IO_ID(id))
2872 return (drerr_new(0, ESTF_INAPPROP, NULL));
2873 return (NULL);
2874 }
2875
2876 static sbd_error_t *
drmach_io_release(drmachid_t id)2877 drmach_io_release(drmachid_t id)
2878 {
2879 if (!DRMACH_IS_IO_ID(id))
2880 return (drerr_new(0, ESTF_INAPPROP, NULL));
2881 return (NULL);
2882 }
2883
2884 sbd_error_t *
drmach_io_unrelease(drmachid_t id)2885 drmach_io_unrelease(drmachid_t id)
2886 {
2887 if (!DRMACH_IS_IO_ID(id))
2888 return (drerr_new(0, ESTF_INAPPROP, NULL));
2889 return (NULL);
2890 }
2891
2892 /*ARGSUSED*/
2893 sbd_error_t *
drmach_io_post_release(drmachid_t id)2894 drmach_io_post_release(drmachid_t id)
2895 {
2896 return (NULL);
2897 }
2898
2899 /*ARGSUSED*/
2900 sbd_error_t *
drmach_io_post_attach(drmachid_t id)2901 drmach_io_post_attach(drmachid_t id)
2902 {
2903 return (NULL);
2904 }
2905
2906 static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)2907 drmach_io_status(drmachid_t id, drmach_status_t *stat)
2908 {
2909 drmach_device_t *dp;
2910 sbd_error_t *err;
2911 int configured;
2912
2913 ASSERT(DRMACH_IS_IO_ID(id));
2914 dp = id;
2915
2916 err = drmach_io_is_attached(id, &configured);
2917 if (err)
2918 return (err);
2919
2920 stat->assigned = dp->bp->assigned;
2921 stat->powered = dp->bp->powered;
2922 stat->configured = (configured != 0);
2923 stat->busy = dp->busy;
2924 (void) strncpy(stat->type, dp->type, sizeof (stat->type));
2925 stat->info[0] = '\0';
2926
2927 return (NULL);
2928 }
2929
2930 static sbd_error_t *
drmach_mem_new(drmach_device_t * dp)2931 drmach_mem_new(drmach_device_t *dp)
2932 {
2933 static sbd_error_t *drmach_mem_release(drmachid_t);
2934 static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
2935
2936 dp->unum = 0;
2937 dp->cm.isa = (void *)drmach_mem_new;
2938 dp->cm.release = drmach_mem_release;
2939 dp->cm.status = drmach_mem_status;
2940
2941 (void) snprintf(dp->cm.name, sizeof (dp->cm.name), "%s", dp->type);
2942
2943 return (NULL);
2944 }
2945
2946 sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)2947 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
2948 {
2949 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
2950 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
2951 pda_handle_t ph;
2952 int rv;
2953
2954 ASSERT(size != 0);
2955
2956 if (!DRMACH_IS_MEM_ID(id))
2957 return (drerr_new(0, ESTF_INAPPROP, NULL));
2958
2959 rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
2960 if (rv == ENOMEM) {
2961 cmn_err(CE_WARN, "%lu megabytes not available to kernel cage",
2962 (ulong_t)(size == 0 ? 0 : size / MBYTE));
2963 } else if (rv != 0) {
2964 /* catch this in debug kernels */
2965 ASSERT(0);
2966
2967 cmn_err(CE_WARN, "unexpected kcage_range_add"
2968 " return value %d", rv);
2969 }
2970
2971 /*
2972 * Update the PDA (post2obp) structure with the
2973 * range of the newly added memory.
2974 */
2975 ph = drmach_pda_open();
2976 if (ph != NULL) {
2977 pda_mem_add_span(ph, basepa, size);
2978 pda_close(ph);
2979 }
2980
2981 return (NULL);
2982 }
2983
2984 sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)2985 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
2986 {
2987 drmach_device_t *mem = id;
2988 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
2989 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
2990 uint_t mcreg;
2991 sbd_error_t *err;
2992 pda_handle_t ph;
2993 int rv;
2994
2995 err = drmach_read_mc_asr(id, &mcreg);
2996 if (err)
2997 return (err);
2998 else if (mcreg & STARFIRE_MC_INTERLEAVE_MASK) {
2999 return (drerr_new(1, ESTF_INTERBOARD, "%s::%s",
3000 mem->bp->cm.name, mem->cm.name));
3001 }
3002
3003 if (size > 0) {
3004 rv = kcage_range_delete_post_mem_del(basepfn, npages);
3005 if (rv != 0) {
3006 cmn_err(CE_WARN,
3007 "unexpected kcage_range_delete_post_mem_del"
3008 " return value %d", rv);
3009 return (DRMACH_INTERNAL_ERROR());
3010 }
3011 }
3012
3013 /*
3014 * Update the PDA (post2obp) structure with the
3015 * range of removed memory.
3016 */
3017 ph = drmach_pda_open();
3018 if (ph != NULL) {
3019 if (size > 0)
3020 pda_mem_del_span(ph, basepa, size);
3021
3022 /* update PDA to board's new mc register settings */
3023 pda_mem_sync(ph, mem->bp->bnum, 0);
3024
3025 pda_close(ph);
3026 }
3027
3028 return (NULL);
3029 }
3030
3031 /* support routine for enable and disable */
3032 static sbd_error_t *
drmach_mem_update_interconnect(drmachid_t id,uint_t mcreg)3033 drmach_mem_update_interconnect(drmachid_t id, uint_t mcreg)
3034 {
3035 drmach_device_t *dp;
3036 pda_handle_t ph;
3037 int b;
3038
3039 if (!DRMACH_IS_MEM_ID(id))
3040 return (drerr_new(0, ESTF_INAPPROP, NULL));
3041 dp = id;
3042
3043 ph = drmach_pda_open();
3044 if (ph == NULL)
3045 return (DRMACH_INTERNAL_ERROR());
3046
3047 for (b = 0; b < MAX_BOARDS; b++) {
3048 int p;
3049 int rv;
3050 ushort_t bda_proc, bda_ioc;
3051 board_desc_t *bdesc;
3052
3053 if (pda_board_present(ph, b) == 0)
3054 continue;
3055
3056 bdesc = (board_desc_t *)pda_get_board_info(ph, b);
3057
3058 /*
3059 * Update PCs for CPUs.
3060 */
3061
3062 /* make sure definition in platmod is in sync with pda */
3063 ASSERT(MAX_PROCMODS == MAX_CPU_UNITS_PER_BOARD);
3064
3065 bda_proc = bdesc->bda_proc;
3066 for (p = 0; p < MAX_PROCMODS; p++) {
3067 if (BDA_NBL(bda_proc, p) != BDAN_GOOD)
3068 continue;
3069
3070 rv = pc_madr_add(b, dp->bp->bnum, p, mcreg);
3071 if (rv) {
3072 pda_close(ph);
3073 return (DRMACH_INTERNAL_ERROR());
3074 }
3075 }
3076
3077 /*
3078 * Update PCs for IOCs.
3079 */
3080
3081 /* make sure definition in platmod is in sync with pda */
3082 ASSERT(MAX_IOCS == MAX_IO_UNITS_PER_BOARD);
3083
3084 bda_ioc = bdesc->bda_ioc;
3085 for (p = 0; p < MAX_IOCS; p++) {
3086 if (BDA_NBL(bda_ioc, p) != BDAN_GOOD)
3087 continue;
3088
3089 rv = pc_madr_add(b, dp->bp->bnum, p + 4, mcreg);
3090 if (rv) {
3091 pda_close(ph);
3092 return (DRMACH_INTERNAL_ERROR());
3093 }
3094 }
3095 }
3096
3097 pda_close(ph);
3098 return (NULL);
3099 }
3100
3101 sbd_error_t *
drmach_mem_disable(drmachid_t id)3102 drmach_mem_disable(drmachid_t id)
3103 {
3104 sbd_error_t *err;
3105 uint_t mcreg;
3106
3107 err = drmach_read_mc_asr(id, &mcreg);
3108 if (err == NULL) {
3109 ASSERT(mcreg & STARFIRE_MC_MEM_PRESENT_MASK);
3110
3111 /* Turn off presence bit. */
3112 mcreg &= ~STARFIRE_MC_MEM_PRESENT_MASK;
3113
3114 err = drmach_mem_update_interconnect(id, mcreg);
3115 if (err == NULL)
3116 err = drmach_write_mc_asr(id, mcreg);
3117 }
3118
3119 return (err);
3120 }
3121
3122 sbd_error_t *
drmach_mem_enable(drmachid_t id)3123 drmach_mem_enable(drmachid_t id)
3124 {
3125 sbd_error_t *err;
3126 uint_t mcreg;
3127
3128 err = drmach_read_mc_asr(id, &mcreg);
3129 if (err == NULL) {
3130 mcreg |= STARFIRE_MC_MEM_PRESENT_MASK;
3131
3132 err = drmach_write_mc_asr(id, mcreg);
3133 if (err == NULL)
3134 err = drmach_mem_update_interconnect(id, mcreg);
3135 }
3136
3137 return (err);
3138 }
3139
3140 sbd_error_t *
drmach_mem_get_alignment(drmachid_t id,uint64_t * mask)3141 drmach_mem_get_alignment(drmachid_t id, uint64_t *mask)
3142 {
3143 drmach_device_t *mem;
3144 sbd_error_t *err;
3145 pnode_t nodeid;
3146
3147 if (!DRMACH_IS_MEM_ID(id))
3148 return (drerr_new(0, ESTF_INAPPROP, NULL));
3149 mem = id;
3150
3151 nodeid = drmach_node_get_dnode(mem->node);
3152 if (nodeid == OBP_NONODE || nodeid == OBP_BADNODE)
3153 err = DRMACH_INTERNAL_ERROR();
3154 else {
3155 uint64_t size;
3156
3157 size = mc_get_alignment_mask(nodeid);
3158 if (size == (uint64_t)-1)
3159 err = DRMACH_INTERNAL_ERROR();
3160 else {
3161 *mask = size - 1;
3162 err = NULL;
3163 }
3164 }
3165
3166 return (err);
3167 }
3168
3169 sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * pa)3170 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
3171 {
3172 sbd_error_t *err;
3173 uint_t mcreg;
3174
3175 err = drmach_read_mc_asr(id, &mcreg);
3176 if (err == NULL)
3177 *pa = mc_asr_to_pa(mcreg);
3178
3179 return (err);
3180 }
3181
3182 /*
3183 * Use of this routine after copy/rename will yield incorrect results,
3184 * because the OBP MEMAVAIL property will not correctly reflect the
3185 * programming of the MCs.
3186 */
3187 sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)3188 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
3189 {
3190 drmach_device_t *mem;
3191 int rv, i, rlen, rblks;
3192 sbd_error_t *err;
3193 struct memlist *mlist;
3194 struct sf_memunit_regspec *rlist;
3195
3196 if (!DRMACH_IS_MEM_ID(id))
3197 return (drerr_new(0, ESTF_INAPPROP, NULL));
3198 mem = id;
3199
3200 err = drmach_device_get_proplen(mem, "dr-available", &rlen);
3201 if (err)
3202 return (err);
3203
3204 rlist = kmem_zalloc(rlen, KM_SLEEP);
3205
3206 err = drmach_device_get_prop(mem, "dr-available", rlist);
3207 if (err) {
3208 kmem_free(rlist, rlen);
3209 return (err);
3210 }
3211
3212 mlist = NULL;
3213 rblks = rlen / sizeof (struct sf_memunit_regspec);
3214 for (i = 0; i < rblks; i++) {
3215 uint64_t addr, size;
3216
3217 addr = (uint64_t)rlist[i].regspec_addr_hi << 32;
3218 addr |= (uint64_t)rlist[i].regspec_addr_lo;
3219 size = (uint64_t)rlist[i].regspec_size_hi << 32;
3220 size |= (uint64_t)rlist[i].regspec_size_lo;
3221
3222 mlist = memlist_add_span(mlist, addr, size);
3223 }
3224
3225 kmem_free(rlist, rlen);
3226
3227 /*
3228 * Make sure the incoming memlist doesn't already
3229 * intersect with what's present in the system (phys_install).
3230 */
3231 memlist_read_lock();
3232 rv = memlist_intersect(phys_install, mlist);
3233 memlist_read_unlock();
3234 if (rv) {
3235 #ifdef DEBUG
3236 DRMACH_PR("OBP derived memlist intersects"
3237 " with phys_install\n");
3238 memlist_dump(mlist);
3239
3240 DRMACH_PR("phys_install memlist:\n");
3241 memlist_dump(phys_install);
3242 #endif
3243
3244 memlist_delete(mlist);
3245 return (DRMACH_INTERNAL_ERROR());
3246 }
3247
3248 #ifdef DEBUG
3249 DRMACH_PR("OBP derived memlist:");
3250 memlist_dump(mlist);
3251 #endif
3252
3253 *ml = mlist;
3254 return (NULL);
3255 }
3256
3257 sbd_error_t *
drmach_mem_get_size(drmachid_t id,uint64_t * bytes)3258 drmach_mem_get_size(drmachid_t id, uint64_t *bytes)
3259 {
3260 drmach_device_t *mem;
3261 pda_handle_t ph;
3262 pgcnt_t npages;
3263
3264 if (!DRMACH_IS_MEM_ID(id))
3265 return (drerr_new(0, ESTF_INAPPROP, NULL));
3266 mem = id;
3267
3268 ph = drmach_pda_open();
3269 if (ph == NULL)
3270 return (DRMACH_INTERNAL_ERROR());
3271
3272 npages = pda_get_mem_size(ph, mem->bp->bnum);
3273 *bytes = (uint64_t)npages << PAGESHIFT;
3274
3275 pda_close(ph);
3276 return (NULL);
3277 }
3278
3279 sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)3280 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
3281 {
3282 if (!DRMACH_IS_MEM_ID(id))
3283 return (drerr_new(0, ESTF_INAPPROP, NULL));
3284
3285 *bytes = mc_get_mem_alignment();
3286 return (NULL);
3287 }
3288
3289 /* field debugging tool */
3290 processorid_t drmach_mem_cpu_affinity_nail = 0;
3291
3292 processorid_t
drmach_mem_cpu_affinity(drmachid_t id)3293 drmach_mem_cpu_affinity(drmachid_t id)
3294 {
3295 drmach_device_t *mp;
3296 drmach_board_t *bp;
3297 processorid_t cpuid;
3298
3299 if (!DRMACH_IS_MEM_ID(id))
3300 return (CPU_CURRENT);
3301
3302 if (drmach_mem_cpu_affinity_nail) {
3303 cpuid = drmach_mem_cpu_affinity_nail;
3304
3305 if (cpuid < 0 || cpuid > NCPU)
3306 return (CPU_CURRENT);
3307
3308 mutex_enter(&cpu_lock);
3309 if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid]))
3310 cpuid = CPU_CURRENT;
3311 mutex_exit(&cpu_lock);
3312
3313 return (cpuid);
3314 }
3315
3316 /* try to choose a proc on the target board */
3317 mp = id;
3318 bp = mp->bp;
3319 if (bp->devices) {
3320 int rv;
3321 int d_idx;
3322 drmachid_t d_id;
3323
3324 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
3325 while (rv == 0) {
3326 if (DRMACH_IS_CPU_ID(d_id)) {
3327 cpuid = drmach_cpu_calc_id(d_id);
3328
3329 mutex_enter(&cpu_lock);
3330 if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) {
3331 mutex_exit(&cpu_lock);
3332 DRMACH_PR("drmach_mem_cpu_affinity: "
3333 "selected cpuid=%d\n", cpuid);
3334 return (cpuid);
3335 } else {
3336 mutex_exit(&cpu_lock);
3337 }
3338 }
3339
3340 rv = drmach_array_next(bp->devices, &d_idx, &d_id);
3341 }
3342 }
3343
3344 /* otherwise, this proc, wherever it is */
3345 DRMACH_PR("drmach_mem_cpu_affinity: using default CPU_CURRENT\n");
3346
3347 return (CPU_CURRENT);
3348 }
3349
3350 static sbd_error_t *
drmach_mem_release(drmachid_t id)3351 drmach_mem_release(drmachid_t id)
3352 {
3353 if (!DRMACH_IS_MEM_ID(id))
3354 return (drerr_new(0, ESTF_INAPPROP, NULL));
3355 return (NULL);
3356 }
3357
3358 static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)3359 drmach_mem_status(drmachid_t id, drmach_status_t *stat)
3360 {
3361 drmach_device_t *dp;
3362 sbd_error_t *err;
3363 uint64_t pa, slice_size;
3364 struct memlist *ml;
3365
3366 ASSERT(DRMACH_IS_MEM_ID(id));
3367 dp = id;
3368
3369 /* get starting physical address of target memory */
3370 err = drmach_mem_get_base_physaddr(id, &pa);
3371 if (err)
3372 return (err);
3373
3374 /* round down to slice boundary */
3375 slice_size = mc_get_mem_alignment();
3376 pa &= ~ (slice_size - 1);
3377
3378 /* stop at first span that is in slice */
3379 memlist_read_lock();
3380 for (ml = phys_install; ml; ml = ml->ml_next)
3381 if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
3382 break;
3383 memlist_read_unlock();
3384
3385 stat->assigned = dp->bp->assigned;
3386 stat->powered = dp->bp->powered;
3387 stat->configured = (ml != NULL);
3388 stat->busy = dp->busy;
3389 (void) strncpy(stat->type, dp->type, sizeof (stat->type));
3390 stat->info[0] = '\0';
3391
3392 return (NULL);
3393 }
3394
3395 static int
drmach_detach_board(void * arg)3396 drmach_detach_board(void *arg)
3397 {
3398 cpuset_t cset;
3399 int retval;
3400 drmach_board_t *bp = (drmach_board_t *)arg;
3401
3402 cset = cpu_ready_set;
3403 promsafe_xc_attention(cset);
3404
3405 retval = prom_starfire_rm_brd(bp->bnum);
3406
3407 xc_dismissed(cset);
3408
3409 return (retval);
3410 }
3411
3412 sbd_error_t *
drmach_board_deprobe(drmachid_t id)3413 drmach_board_deprobe(drmachid_t id)
3414 {
3415 drmach_board_t *bp;
3416 int retval;
3417
3418 if (!DRMACH_IS_BOARD_ID(id))
3419 return (drerr_new(0, ESTF_INAPPROP, NULL));
3420 bp = id;
3421
3422 cmn_err(CE_CONT, "DR: PROM detach board %d\n", bp->bnum);
3423
3424 retval = prom_tree_update(drmach_detach_board, bp);
3425
3426 if (retval == 0)
3427 return (NULL);
3428 else {
3429 cmn_err(CE_WARN, "prom error: prom_starfire_rm_brd(%d) "
3430 "returned %d", bp->bnum, retval);
3431 return (drerr_new(1, ESTF_DEPROBE, "%s", bp->cm.name));
3432 }
3433 }
3434
3435 /*ARGSUSED*/
3436 static sbd_error_t *
drmach_pt_juggle_bootproc(drmachid_t id,drmach_opts_t * opts)3437 drmach_pt_juggle_bootproc(drmachid_t id, drmach_opts_t *opts)
3438 {
3439 drmach_device_t *cpu;
3440 sbd_error_t *err;
3441
3442 if (!DRMACH_IS_CPU_ID(id))
3443 return (drerr_new(0, ESTF_INAPPROP, NULL));
3444 cpu = id;
3445
3446 mutex_enter(&cpu_lock);
3447
3448 err = drmach_cpu_juggle_bootproc(cpu);
3449
3450 mutex_exit(&cpu_lock);
3451
3452 return (err);
3453 }
3454
3455 /*ARGSUSED*/
3456 static sbd_error_t *
drmach_pt_dump_pdainfo(drmachid_t id,drmach_opts_t * opts)3457 drmach_pt_dump_pdainfo(drmachid_t id, drmach_opts_t *opts)
3458 {
3459 drmach_board_t *bp;
3460 int board;
3461 int i;
3462 pda_handle_t ph;
3463 board_desc_t *bdesc;
3464
3465 if (!DRMACH_IS_BOARD_ID(id))
3466 return (drerr_new(0, ESTF_INAPPROP, NULL));
3467 bp = id;
3468 board = bp->bnum;
3469
3470 ph = drmach_pda_open();
3471 if (ph == NULL)
3472 return (DRMACH_INTERNAL_ERROR());
3473
3474 if (pda_board_present(ph, board) == 0) {
3475 cmn_err(CE_CONT, "board %d is MISSING\n", board);
3476 pda_close(ph);
3477 return (DRMACH_INTERNAL_ERROR());
3478 }
3479
3480 cmn_err(CE_CONT, "board %d is PRESENT\n", board);
3481
3482 bdesc = (board_desc_t *)pda_get_board_info(ph, board);
3483 if (bdesc == NULL) {
3484 cmn_err(CE_CONT,
3485 "no board descriptor found for board %d\n",
3486 board);
3487 pda_close(ph);
3488 return (DRMACH_INTERNAL_ERROR());
3489 }
3490
3491 /* make sure definition in platmod is in sync with pda */
3492 ASSERT(MAX_PROCMODS == MAX_CPU_UNITS_PER_BOARD);
3493
3494 for (i = 0; i < MAX_PROCMODS; i++) {
3495 if (BDA_NBL(bdesc->bda_proc, i) == BDAN_GOOD)
3496 cmn_err(CE_CONT,
3497 "proc %d.%d PRESENT\n", board, i);
3498 else
3499 cmn_err(CE_CONT,
3500 "proc %d.%d MISSING\n", board, i);
3501 }
3502
3503 for (i = 0; i < MAX_MGROUPS; i++) {
3504 if (BDA_NBL(bdesc->bda_mgroup, i) == BDAN_GOOD)
3505 cmn_err(CE_CONT,
3506 "mgroup %d.%d PRESENT\n", board, i);
3507 else
3508 cmn_err(CE_CONT,
3509 "mgroup %d.%d MISSING\n", board, i);
3510 }
3511
3512 /* make sure definition in platmod is in sync with pda */
3513 ASSERT(MAX_IOCS == MAX_IO_UNITS_PER_BOARD);
3514
3515 for (i = 0; i < MAX_IOCS; i++) {
3516 int s;
3517
3518 if (BDA_NBL(bdesc->bda_ioc, i) == BDAN_GOOD) {
3519 cmn_err(CE_CONT,
3520 "ioc %d.%d PRESENT\n", board, i);
3521 for (s = 0; s < MAX_SLOTS_PER_IOC; s++) {
3522 if (BDA_NBL(bdesc->bda_ios[i], s) != BDAN_GOOD)
3523 continue;
3524 cmn_err(CE_CONT,
3525 "..scard %d.%d.%d PRESENT\n",
3526 board, i, s);
3527 }
3528 } else {
3529 cmn_err(CE_CONT,
3530 "ioc %d.%d MISSING\n",
3531 board, i);
3532 }
3533 }
3534
3535 cmn_err(CE_CONT,
3536 "board %d memsize = %d pages\n",
3537 board, pda_get_mem_size(ph, board));
3538
3539 pda_close(ph);
3540
3541 return (NULL);
3542 }
3543
3544 /*ARGSUSED*/
3545 sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)3546 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
3547 {
3548 struct memlist *ml;
3549 uint64_t src_pa;
3550 uint64_t dst_pa;
3551 uint64_t dst;
3552
3553 dst_pa = va_to_pa(&dst);
3554
3555 memlist_read_lock();
3556 for (ml = phys_install; ml; ml = ml->ml_next) {
3557 uint64_t nbytes;
3558
3559 src_pa = ml->ml_address;
3560 nbytes = ml->ml_size;
3561
3562 while (nbytes != 0ull) {
3563
3564 /* copy 32 bytes at arc_pa to dst_pa */
3565 bcopy32_il(src_pa, dst_pa);
3566
3567 /* increment by 32 bytes */
3568 src_pa += (4 * sizeof (uint64_t));
3569
3570 /* decrement by 32 bytes */
3571 nbytes -= (4 * sizeof (uint64_t));
3572 }
3573 }
3574 memlist_read_unlock();
3575
3576 return (NULL);
3577 }
3578
3579 static struct {
3580 const char *name;
3581 sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts);
3582 } drmach_pt_arr[] = {
3583 { "juggle", drmach_pt_juggle_bootproc },
3584 { "pda", drmach_pt_dump_pdainfo },
3585 { "readmem", drmach_pt_readmem },
3586
3587 /* the following line must always be last */
3588 { NULL, NULL }
3589 };
3590
3591 /*ARGSUSED*/
3592 sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)3593 drmach_passthru(drmachid_t id, drmach_opts_t *opts)
3594 {
3595 int i;
3596 sbd_error_t *err;
3597
3598 i = 0;
3599 while (drmach_pt_arr[i].name != NULL) {
3600 int len = strlen(drmach_pt_arr[i].name);
3601
3602 if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
3603 break;
3604
3605 i += 1;
3606 }
3607
3608 if (drmach_pt_arr[i].name == NULL)
3609 err = drerr_new(0, ESTF_UNKPTCMD, opts->copts);
3610 else
3611 err = (*drmach_pt_arr[i].handler)(id, opts);
3612
3613 return (err);
3614 }
3615
3616 sbd_error_t *
drmach_release(drmachid_t id)3617 drmach_release(drmachid_t id)
3618 {
3619 drmach_common_t *cp;
3620 if (!DRMACH_IS_DEVICE_ID(id))
3621 return (drerr_new(0, ESTF_INAPPROP, NULL));
3622 cp = id;
3623
3624 return (cp->release(id));
3625 }
3626
3627 sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)3628 drmach_status(drmachid_t id, drmach_status_t *stat)
3629 {
3630 drmach_common_t *cp;
3631
3632 if (!DRMACH_IS_ID(id))
3633 return (drerr_new(0, ESTF_NOTID, NULL));
3634 cp = id;
3635
3636 return (cp->status(id, stat));
3637 }
3638
3639 sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)3640 drmach_unconfigure(drmachid_t id, int flags)
3641 {
3642 drmach_device_t *dp;
3643 pnode_t nodeid;
3644 dev_info_t *dip, *fdip = NULL;
3645
3646 if (!DRMACH_IS_DEVICE_ID(id))
3647 return (drerr_new(0, ESTF_INAPPROP, NULL));
3648
3649 dp = id;
3650
3651 nodeid = drmach_node_get_dnode(dp->node);
3652 if (nodeid == OBP_NONODE)
3653 return (DRMACH_INTERNAL_ERROR());
3654
3655 dip = e_ddi_nodeid_to_dip(nodeid);
3656 if (dip == NULL)
3657 return (NULL);
3658
3659 /*
3660 * Branch already held, so hold acquired in
3661 * e_ddi_nodeid_to_dip() can be released
3662 */
3663 ddi_release_devi(dip);
3664
3665 if (flags & DEVI_BRANCH_DESTROY)
3666 flags |= DEVI_BRANCH_EVENT;
3667
3668 /*
3669 * Force flag is no longer necessary. See starcat/io/drmach.c
3670 * for details.
3671 */
3672 ASSERT(e_ddi_branch_held(dip));
3673 if (e_ddi_branch_unconfigure(dip, &fdip, flags)) {
3674 sbd_error_t *err;
3675 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
3676
3677 /*
3678 * If non-NULL, fdip is returned held and must be released.
3679 */
3680 if (fdip != NULL) {
3681 (void) ddi_pathname(fdip, path);
3682 ndi_rele_devi(fdip);
3683 } else {
3684 (void) ddi_pathname(dip, path);
3685 }
3686
3687 err = drerr_new(1, ESTF_DRVFAIL, path);
3688
3689 kmem_free(path, MAXPATHLEN);
3690
3691 return (err);
3692 }
3693
3694 return (NULL);
3695 }
3696
3697 /*
3698 * drmach interfaces to legacy Starfire platmod logic
3699 * linkage via runtime symbol look up, called from plat_cpu_power*
3700 */
3701
3702 /*
3703 * Start up a cpu. It is possible that we're attempting to restart
3704 * the cpu after an UNCONFIGURE in which case the cpu will be
3705 * spinning in its cache. So, all we have to do is wakeup him up.
3706 * Under normal circumstances the cpu will be coming from a previous
3707 * CONNECT and thus will be spinning in OBP. In both cases, the
3708 * startup sequence is the same.
3709 */
3710 int
drmach_cpu_poweron(struct cpu * cp)3711 drmach_cpu_poweron(struct cpu *cp)
3712 {
3713 DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
3714
3715 ASSERT(MUTEX_HELD(&cpu_lock));
3716
3717 if (drmach_cpu_start(cp) != 0)
3718 return (EBUSY);
3719 else
3720 return (0);
3721 }
3722
3723 int
drmach_cpu_poweroff(struct cpu * cp)3724 drmach_cpu_poweroff(struct cpu *cp)
3725 {
3726 int ntries, cnt;
3727 processorid_t cpuid = cp->cpu_id;
3728 void drmach_cpu_shutdown_self(void);
3729
3730 DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
3731
3732 ASSERT(MUTEX_HELD(&cpu_lock));
3733
3734 /*
3735 * Capture all CPUs (except for detaching proc) to prevent
3736 * crosscalls to the detaching proc until it has cleared its
3737 * bit in cpu_ready_set.
3738 *
3739 * The CPU's remain paused and the prom_mutex is known to be free.
3740 * This prevents the x-trap victim from blocking when doing prom
3741 * IEEE-1275 calls at a high PIL level.
3742 */
3743 promsafe_pause_cpus();
3744
3745 /*
3746 * Quiesce interrupts on the target CPU. We do this by setting
3747 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
3748 * prevent it from receiving cross calls and cross traps.
3749 * This prevents the processor from receiving any new soft interrupts.
3750 */
3751 mp_cpu_quiesce(cp);
3752
3753 /* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */
3754 drmach_xt_mb[cpuid] = 0x80;
3755
3756 xt_one_unchecked(cpuid, (xcfunc_t *)idle_stop_xcall,
3757 (uint64_t)drmach_cpu_shutdown_self, NULL);
3758
3759 ntries = drmach_cpu_ntries;
3760 cnt = 0;
3761 while (drmach_xt_mb[cpuid] && ntries) {
3762 DELAY(drmach_cpu_delay);
3763 ntries--;
3764 cnt++;
3765 }
3766
3767 drmach_xt_mb[cpuid] = 0; /* steal the cache line back */
3768
3769 start_cpus();
3770
3771 DRMACH_PR("waited %d out of %d tries for "
3772 "drmach_cpu_shutdown_self on cpu%d",
3773 drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id);
3774
3775 drmach_cpu_obp_detach(cpuid);
3776
3777 CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
3778
3779 return (0);
3780 }
3781
3782 /*ARGSUSED*/
3783 int
drmach_verify_sr(dev_info_t * dip,int sflag)3784 drmach_verify_sr(dev_info_t *dip, int sflag)
3785 {
3786 return (0);
3787 }
3788
3789 void
drmach_suspend_last(void)3790 drmach_suspend_last(void)
3791 {
3792 }
3793
3794 void
drmach_resume_first(void)3795 drmach_resume_first(void)
3796 {
3797 }
3798
3799 /*
3800 * Log a DR sysevent.
3801 * Return value: 0 success, non-zero failure.
3802 */
3803 int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)3804 drmach_log_sysevent(int board, char *hint, int flag, int verbose)
3805 {
3806 sysevent_t *ev;
3807 sysevent_id_t eid;
3808 int rv, km_flag;
3809 sysevent_value_t evnt_val;
3810 sysevent_attr_list_t *evnt_attr_list = NULL;
3811 char attach_pnt[MAXNAMELEN];
3812
3813 km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
3814 attach_pnt[0] = '\0';
3815 if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
3816 rv = -1;
3817 goto logexit;
3818 }
3819 if (verbose)
3820 DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
3821 attach_pnt, hint, flag, verbose);
3822
3823 if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
3824 SUNW_KERN_PUB"dr", km_flag)) == NULL) {
3825 rv = -2;
3826 goto logexit;
3827 }
3828 evnt_val.value_type = SE_DATA_TYPE_STRING;
3829 evnt_val.value.sv_string = attach_pnt;
3830 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
3831 &evnt_val, km_flag)) != 0)
3832 goto logexit;
3833
3834 evnt_val.value_type = SE_DATA_TYPE_STRING;
3835 evnt_val.value.sv_string = hint;
3836 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
3837 &evnt_val, km_flag)) != 0) {
3838 sysevent_free_attr(evnt_attr_list);
3839 goto logexit;
3840 }
3841
3842 (void) sysevent_attach_attributes(ev, evnt_attr_list);
3843
3844 /*
3845 * Log the event but do not sleep waiting for its
3846 * delivery. This provides insulation from syseventd.
3847 */
3848 rv = log_sysevent(ev, SE_NOSLEEP, &eid);
3849
3850 logexit:
3851 if (ev)
3852 sysevent_free(ev);
3853 if ((rv != 0) && verbose)
3854 cmn_err(CE_WARN,
3855 "drmach_log_sysevent failed (rv %d) for %s %s\n",
3856 rv, attach_pnt, hint);
3857
3858 return (rv);
3859 }
3860
3861 /*ARGSUSED*/
3862 int
drmach_allow_memrange_modify(drmachid_t id)3863 drmach_allow_memrange_modify(drmachid_t id)
3864 {
3865 return (1); /* TRUE */
3866 }
3867