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/opl_olympus_regs.h>
45 #include <sys/cpuvar.h>
46 #include <sys/cpupart.h>
47 #include <sys/mem_config.h>
48 #include <sys/ddi_impldefs.h>
49 #include <sys/systm.h>
50 #include <sys/machsystm.h>
51 #include <sys/autoconf.h>
52 #include <sys/cmn_err.h>
53 #include <sys/sysmacros.h>
54 #include <sys/x_call.h>
55 #include <sys/promif.h>
56 #include <sys/prom_plat.h>
57 #include <sys/membar.h>
58 #include <vm/seg_kmem.h>
59 #include <sys/mem_cage.h>
60 #include <sys/stack.h>
61 #include <sys/archsystm.h>
62 #include <vm/hat_sfmmu.h>
63 #include <sys/pte.h>
64 #include <sys/mmu.h>
65 #include <sys/cpu_module.h>
66 #include <sys/obpdefs.h>
67 #include <sys/note.h>
68 #include <sys/ontrap.h>
69 #include <sys/cpu_sgnblk_defs.h>
70 #include <sys/opl.h>
71 #include <sys/cpu_impl.h>
72
73
74 #include <sys/promimpl.h>
75 #include <sys/prom_plat.h>
76 #include <sys/kobj.h>
77
78 #include <sys/sysevent.h>
79 #include <sys/sysevent/dr.h>
80 #include <sys/sysevent/eventdefs.h>
81
82 #include <sys/drmach.h>
83 #include <sys/dr_util.h>
84
85 #include <sys/fcode.h>
86 #include <sys/opl_cfg.h>
87
88 extern void bcopy32_il(uint64_t, uint64_t);
89 extern void flush_cache_il(void);
90 extern void drmach_sleep_il(void);
91
92 typedef struct {
93 struct drmach_node *node;
94 void *data;
95 } drmach_node_walk_args_t;
96
97 typedef struct drmach_node {
98 void *here;
99
100 pnode_t (*get_dnode)(struct drmach_node *node);
101 int (*walk)(struct drmach_node *node, void *data,
102 int (*cb)(drmach_node_walk_args_t *args));
103 dev_info_t *(*n_getdip)(struct drmach_node *node);
104 int (*n_getproplen)(struct drmach_node *node, char *name,
105 int *len);
106 int (*n_getprop)(struct drmach_node *node, char *name,
107 void *buf, int len);
108 int (*get_parent)(struct drmach_node *node,
109 struct drmach_node *pnode);
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 void (*dispose)(drmachid_t);
123 sbd_error_t *(*release)(drmachid_t);
124 sbd_error_t *(*status)(drmachid_t, drmach_status_t *);
125
126 char name[MAXNAMELEN];
127 } drmach_common_t;
128
129 typedef struct {
130 uint32_t core_present;
131 uint32_t core_hotadded;
132 uint32_t core_started;
133 } drmach_cmp_t;
134
135 typedef struct {
136 drmach_common_t cm;
137 int bnum;
138 int assigned;
139 int powered;
140 int connected;
141 int cond;
142 drmach_node_t *tree;
143 drmach_array_t *devices;
144 int boot_board; /* if board exists on bootup */
145 drmach_cmp_t cores[OPL_MAX_COREID_PER_BOARD];
146 } drmach_board_t;
147
148 typedef struct {
149 drmach_common_t cm;
150 drmach_board_t *bp;
151 int unum;
152 int portid;
153 int busy;
154 int powered;
155 const char *type;
156 drmach_node_t *node;
157 } drmach_device_t;
158
159 typedef struct drmach_cpu {
160 drmach_device_t dev;
161 processorid_t cpuid;
162 int sb;
163 int chipid;
164 int coreid;
165 int strandid;
166 int status;
167 #define OPL_CPU_HOTADDED 1
168 } drmach_cpu_t;
169
170 typedef struct drmach_mem {
171 drmach_device_t dev;
172 uint64_t slice_base;
173 uint64_t slice_size;
174 uint64_t base_pa; /* lowest installed memory base */
175 uint64_t nbytes; /* size of installed memory */
176 struct memlist *memlist;
177 } drmach_mem_t;
178
179 typedef struct drmach_io {
180 drmach_device_t dev;
181 int channel;
182 int leaf;
183 } drmach_io_t;
184
185 typedef struct drmach_domain_info {
186 uint32_t floating;
187 int allow_dr;
188 } drmach_domain_info_t;
189
190 drmach_domain_info_t drmach_domain;
191
192 typedef struct {
193 int flags;
194 drmach_device_t *dp;
195 sbd_error_t *err;
196 dev_info_t *dip;
197 } drmach_config_args_t;
198
199 typedef struct {
200 drmach_board_t *obj;
201 int ndevs;
202 void *a;
203 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t);
204 sbd_error_t *err;
205 } drmach_board_cb_data_t;
206
207 static drmach_array_t *drmach_boards;
208
209 static sbd_error_t *drmach_device_new(drmach_node_t *,
210 drmach_board_t *, int, drmachid_t *);
211 static sbd_error_t *drmach_cpu_new(drmach_device_t *, drmachid_t *);
212 static sbd_error_t *drmach_mem_new(drmach_device_t *, drmachid_t *);
213 static sbd_error_t *drmach_io_new(drmach_device_t *, drmachid_t *);
214
215 static dev_info_t *drmach_node_ddi_get_dip(drmach_node_t *np);
216 static int drmach_node_ddi_get_prop(drmach_node_t *np,
217 char *name, void *buf, int len);
218 static int drmach_node_ddi_get_proplen(drmach_node_t *np,
219 char *name, int *len);
220
221 static int drmach_get_portid(drmach_node_t *);
222 static sbd_error_t *drmach_i_status(drmachid_t, drmach_status_t *);
223 static int opl_check_dr_status();
224 static void drmach_io_dispose(drmachid_t);
225 static sbd_error_t *drmach_io_release(drmachid_t);
226 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
227 static int drmach_init(void);
228 static void drmach_fini(void);
229 static void drmach_swap_pa(drmach_mem_t *, drmach_mem_t *);
230 static drmach_board_t *drmach_get_board_by_bnum(int);
231
232 static sbd_error_t *drmach_board_release(drmachid_t);
233 static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
234 static void drmach_cpu_dispose(drmachid_t);
235 static sbd_error_t *drmach_cpu_release(drmachid_t);
236 static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
237 static void drmach_mem_dispose(drmachid_t);
238 static sbd_error_t *drmach_mem_release(drmachid_t);
239 static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
240
241 /* options for the second argument in drmach_add_remove_cpu() */
242 #define HOTADD_CPU 1
243 #define HOTREMOVE_CPU 2
244
245 #define ON_BOARD_CORE_NUM(x) (((uint_t)(x) / OPL_MAX_STRANDID_PER_CORE) & \
246 (OPL_MAX_COREID_PER_BOARD - 1))
247
248 extern struct cpu *SIGBCPU;
249
250 static int drmach_name2type_idx(char *);
251 static drmach_board_t *drmach_board_new(int, int);
252
253 #ifdef DEBUG
254
255 #define DRMACH_PR if (drmach_debug) printf
256 int drmach_debug = 1; /* set to non-zero to enable debug messages */
257 #else
258
259 #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf
260 #endif /* DEBUG */
261
262
263 #define DRMACH_OBJ(id) ((drmach_common_t *)id)
264
265 #define DRMACH_NULL_ID(id) ((id) == 0)
266
267 #define DRMACH_IS_BOARD_ID(id) \
268 ((id != 0) && \
269 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
270
271 #define DRMACH_IS_CPU_ID(id) \
272 ((id != 0) && \
273 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
274
275 #define DRMACH_IS_MEM_ID(id) \
276 ((id != 0) && \
277 (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
278
279 #define DRMACH_IS_IO_ID(id) \
280 ((id != 0) && \
281 (DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
282
283 #define DRMACH_IS_DEVICE_ID(id) \
284 ((id != 0) && \
285 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
286 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
287 DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
288
289 #define DRMACH_IS_ID(id) \
290 ((id != 0) && \
291 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \
292 DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
293 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
294 DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
295
296 #define DRMACH_INTERNAL_ERROR() \
297 drerr_new(1, EOPL_INTERNAL, drmach_ie_fmt, __LINE__)
298
299 static char *drmach_ie_fmt = "drmach.c %d";
300
301 static struct {
302 const char *name;
303 const char *type;
304 sbd_error_t *(*new)(drmach_device_t *, drmachid_t *);
305 } drmach_name2type[] = {
306 { "cpu", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
307 { "pseudo-mc", DRMACH_DEVTYPE_MEM, drmach_mem_new },
308 { "pci", DRMACH_DEVTYPE_PCI, drmach_io_new },
309 };
310
311 /* utility */
312 #define MBYTE (1048576ull)
313
314 /*
315 * drmach autoconfiguration data structures and interfaces
316 */
317
318 extern struct mod_ops mod_miscops;
319
320 static struct modlmisc modlmisc = {
321 &mod_miscops,
322 "OPL DR 1.1"
323 };
324
325 static struct modlinkage modlinkage = {
326 MODREV_1,
327 (void *)&modlmisc,
328 NULL
329 };
330
331 static krwlock_t drmach_boards_rwlock;
332
333 typedef const char *fn_t;
334
335 int
_init(void)336 _init(void)
337 {
338 int err;
339
340 if ((err = drmach_init()) != 0) {
341 return (err);
342 }
343
344 if ((err = mod_install(&modlinkage)) != 0) {
345 drmach_fini();
346 }
347
348 return (err);
349 }
350
351 int
_fini(void)352 _fini(void)
353 {
354 int err;
355
356 if ((err = mod_remove(&modlinkage)) == 0)
357 drmach_fini();
358
359 return (err);
360 }
361
362 int
_info(struct modinfo * modinfop)363 _info(struct modinfo *modinfop)
364 {
365 return (mod_info(&modlinkage, modinfop));
366 }
367
368 struct drmach_mc_lookup {
369 int bnum;
370 drmach_board_t *bp;
371 dev_info_t *dip; /* rv - set if found */
372 };
373
374 #define _ptob64(p) ((uint64_t)(p) << PAGESHIFT)
375 #define _b64top(b) ((pgcnt_t)((b) >> PAGESHIFT))
376
377 static int
drmach_setup_mc_info(dev_info_t * dip,drmach_mem_t * mp)378 drmach_setup_mc_info(dev_info_t *dip, drmach_mem_t *mp)
379 {
380 uint64_t memory_ranges[128];
381 int len;
382 struct memlist *ml;
383 int rv;
384 hwd_sb_t *hwd;
385 hwd_memory_t *pm;
386
387 len = sizeof (memory_ranges);
388 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
389 "sb-mem-ranges", (caddr_t)&memory_ranges[0], &len) !=
390 DDI_PROP_SUCCESS) {
391 mp->slice_base = 0;
392 mp->slice_size = 0;
393 return (-1);
394 }
395 mp->slice_base = memory_ranges[0];
396 mp->slice_size = memory_ranges[1];
397
398 if (!mp->dev.bp->boot_board) {
399 int i;
400
401 rv = opl_read_hwd(mp->dev.bp->bnum, NULL, NULL, NULL, &hwd);
402
403 if (rv != 0) {
404 return (-1);
405 }
406
407 ml = NULL;
408 pm = &hwd->sb_cmu.cmu_memory;
409 for (i = 0; i < HWD_MAX_MEM_CHUNKS; i++) {
410 if (pm->mem_chunks[i].chnk_size > 0) {
411 ml = memlist_add_span(ml,
412 pm->mem_chunks[i].chnk_start_address,
413 pm->mem_chunks[i].chnk_size);
414 }
415 }
416 } else {
417 /*
418 * we intersect phys_install to get base_pa.
419 * This only works at bootup time.
420 */
421
422 memlist_read_lock();
423 ml = memlist_dup(phys_install);
424 memlist_read_unlock();
425
426 ml = memlist_del_span(ml, 0ull, mp->slice_base);
427 if (ml) {
428 uint64_t basepa, endpa;
429 endpa = _ptob64(physmax + 1);
430
431 basepa = mp->slice_base + mp->slice_size;
432
433 ml = memlist_del_span(ml, basepa, endpa - basepa);
434 }
435 }
436
437 if (ml) {
438 uint64_t nbytes = 0;
439 struct memlist *p;
440 for (p = ml; p; p = p->ml_next) {
441 nbytes += p->ml_size;
442 }
443 if ((mp->nbytes = nbytes) > 0)
444 mp->base_pa = ml->ml_address;
445 else
446 mp->base_pa = 0;
447 mp->memlist = ml;
448 } else {
449 mp->base_pa = 0;
450 mp->nbytes = 0;
451 }
452 return (0);
453 }
454
455
456 struct drmach_hotcpu {
457 drmach_board_t *bp;
458 int bnum;
459 int core_id;
460 int rv;
461 int option;
462 };
463
464 static int
drmach_cpu_cb(dev_info_t * dip,void * arg)465 drmach_cpu_cb(dev_info_t *dip, void *arg)
466 {
467 struct drmach_hotcpu *p = (struct drmach_hotcpu *)arg;
468 char name[OBP_MAXDRVNAME];
469 int len = OBP_MAXDRVNAME;
470 int bnum, core_id, strand_id;
471 drmach_board_t *bp;
472
473 if (dip == ddi_root_node()) {
474 return (DDI_WALK_CONTINUE);
475 }
476
477 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
478 DDI_PROP_DONTPASS, "name",
479 (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
480 return (DDI_WALK_PRUNECHILD);
481 }
482
483 /* only cmp has board number */
484 bnum = -1;
485 len = sizeof (bnum);
486 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
487 DDI_PROP_DONTPASS, OBP_BOARDNUM,
488 (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
489 bnum = -1;
490 }
491
492 if (strcmp(name, "cmp") == 0) {
493 if (bnum != p->bnum)
494 return (DDI_WALK_PRUNECHILD);
495 return (DDI_WALK_CONTINUE);
496 }
497 /* we have already pruned all unwanted cores and cpu's above */
498 if (strcmp(name, "core") == 0) {
499 return (DDI_WALK_CONTINUE);
500 }
501 if (strcmp(name, "cpu") == 0) {
502 processorid_t cpuid;
503 len = sizeof (cpuid);
504 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
505 DDI_PROP_DONTPASS, "cpuid",
506 (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
507 p->rv = -1;
508 return (DDI_WALK_TERMINATE);
509 }
510
511 core_id = p->core_id;
512
513 bnum = LSB_ID(cpuid);
514
515 if (ON_BOARD_CORE_NUM(cpuid) != core_id)
516 return (DDI_WALK_CONTINUE);
517
518 bp = p->bp;
519 ASSERT(bnum == bp->bnum);
520
521 if (p->option == HOTADD_CPU) {
522 if (prom_hotaddcpu(cpuid) != 0) {
523 p->rv = -1;
524 return (DDI_WALK_TERMINATE);
525 }
526 strand_id = STRAND_ID(cpuid);
527 bp->cores[core_id].core_hotadded |= (1 << strand_id);
528 } else if (p->option == HOTREMOVE_CPU) {
529 if (prom_hotremovecpu(cpuid) != 0) {
530 p->rv = -1;
531 return (DDI_WALK_TERMINATE);
532 }
533 strand_id = STRAND_ID(cpuid);
534 bp->cores[core_id].core_hotadded &= ~(1 << strand_id);
535 }
536 return (DDI_WALK_CONTINUE);
537 }
538
539 return (DDI_WALK_PRUNECHILD);
540 }
541
542
543 static int
drmach_add_remove_cpu(int bnum,int core_id,int option)544 drmach_add_remove_cpu(int bnum, int core_id, int option)
545 {
546 struct drmach_hotcpu arg;
547 drmach_board_t *bp;
548
549 bp = drmach_get_board_by_bnum(bnum);
550 ASSERT(bp);
551
552 arg.bp = bp;
553 arg.bnum = bnum;
554 arg.core_id = core_id;
555 arg.rv = 0;
556 arg.option = option;
557 ddi_walk_devs(ddi_root_node(), drmach_cpu_cb, (void *)&arg);
558 return (arg.rv);
559 }
560
561 struct drmach_setup_core_arg {
562 drmach_board_t *bp;
563 };
564
565 static int
drmach_setup_core_cb(dev_info_t * dip,void * arg)566 drmach_setup_core_cb(dev_info_t *dip, void *arg)
567 {
568 struct drmach_setup_core_arg *p = (struct drmach_setup_core_arg *)arg;
569 char name[OBP_MAXDRVNAME];
570 int len = OBP_MAXDRVNAME;
571 int bnum;
572 int core_id, strand_id;
573
574 if (dip == ddi_root_node()) {
575 return (DDI_WALK_CONTINUE);
576 }
577
578 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
579 DDI_PROP_DONTPASS, "name",
580 (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
581 return (DDI_WALK_PRUNECHILD);
582 }
583
584 /* only cmp has board number */
585 bnum = -1;
586 len = sizeof (bnum);
587 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
588 DDI_PROP_DONTPASS, OBP_BOARDNUM,
589 (caddr_t)&bnum, &len) != DDI_PROP_SUCCESS) {
590 bnum = -1;
591 }
592
593 if (strcmp(name, "cmp") == 0) {
594 if (bnum != p->bp->bnum)
595 return (DDI_WALK_PRUNECHILD);
596 return (DDI_WALK_CONTINUE);
597 }
598 /* we have already pruned all unwanted cores and cpu's above */
599 if (strcmp(name, "core") == 0) {
600 return (DDI_WALK_CONTINUE);
601 }
602 if (strcmp(name, "cpu") == 0) {
603 processorid_t cpuid;
604 len = sizeof (cpuid);
605 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
606 DDI_PROP_DONTPASS, "cpuid",
607 (caddr_t)&cpuid, &len) != DDI_PROP_SUCCESS) {
608 return (DDI_WALK_TERMINATE);
609 }
610 bnum = LSB_ID(cpuid);
611 ASSERT(bnum == p->bp->bnum);
612 core_id = ON_BOARD_CORE_NUM(cpuid);
613 strand_id = STRAND_ID(cpuid);
614 p->bp->cores[core_id].core_present |= (1 << strand_id);
615 return (DDI_WALK_CONTINUE);
616 }
617
618 return (DDI_WALK_PRUNECHILD);
619 }
620
621
622 static void
drmach_setup_core_info(drmach_board_t * obj)623 drmach_setup_core_info(drmach_board_t *obj)
624 {
625 struct drmach_setup_core_arg arg;
626 int i;
627
628 for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
629 obj->cores[i].core_present = 0;
630 obj->cores[i].core_hotadded = 0;
631 obj->cores[i].core_started = 0;
632 }
633 arg.bp = obj;
634 ddi_walk_devs(ddi_root_node(), drmach_setup_core_cb, (void *)&arg);
635
636 for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
637 if (obj->boot_board) {
638 obj->cores[i].core_hotadded =
639 obj->cores[i].core_started =
640 obj->cores[i].core_present;
641 }
642 }
643 }
644
645 /*
646 * drmach_node_* routines serve the purpose of separating the
647 * rest of the code from the device tree and OBP. This is necessary
648 * because of In-Kernel-Probing. Devices probed after stod, are probed
649 * by the in-kernel-prober, not OBP. These devices, therefore, do not
650 * have dnode ids.
651 */
652
653 typedef struct {
654 drmach_node_walk_args_t *nwargs;
655 int (*cb)(drmach_node_walk_args_t *args);
656 int err;
657 } drmach_node_ddi_walk_args_t;
658
659 static int
drmach_node_ddi_walk_cb(dev_info_t * dip,void * arg)660 drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
661 {
662 drmach_node_ddi_walk_args_t *nargs;
663
664 nargs = (drmach_node_ddi_walk_args_t *)arg;
665
666 /*
667 * dip doesn't have to be held here as we are called
668 * from ddi_walk_devs() which holds the dip.
669 */
670 nargs->nwargs->node->here = (void *)dip;
671
672 nargs->err = nargs->cb(nargs->nwargs);
673
674
675 /*
676 * Set "here" to NULL so that unheld dip is not accessible
677 * outside ddi_walk_devs()
678 */
679 nargs->nwargs->node->here = NULL;
680
681 if (nargs->err)
682 return (DDI_WALK_TERMINATE);
683 else
684 return (DDI_WALK_CONTINUE);
685 }
686
687 static int
drmach_node_ddi_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))688 drmach_node_ddi_walk(drmach_node_t *np, void *data,
689 int (*cb)(drmach_node_walk_args_t *args))
690 {
691 drmach_node_walk_args_t args;
692 drmach_node_ddi_walk_args_t nargs;
693
694
695 /* initialized args structure for callback */
696 args.node = np;
697 args.data = data;
698
699 nargs.nwargs = &args;
700 nargs.cb = cb;
701 nargs.err = 0;
702
703 /*
704 * Root node doesn't have to be held in any way.
705 */
706 ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
707
708 return (nargs.err);
709 }
710
711 static int
drmach_node_ddi_get_parent(drmach_node_t * np,drmach_node_t * pp)712 drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
713 {
714 dev_info_t *ndip;
715 static char *fn = "drmach_node_ddi_get_parent";
716
717 ndip = np->n_getdip(np);
718 if (ndip == NULL) {
719 cmn_err(CE_WARN, "%s: NULL dip", fn);
720 return (-1);
721 }
722
723 bcopy(np, pp, sizeof (drmach_node_t));
724
725 pp->here = (void *)ddi_get_parent(ndip);
726 if (pp->here == NULL) {
727 cmn_err(CE_WARN, "%s: NULL parent dip", fn);
728 return (-1);
729 }
730
731 return (0);
732 }
733
734 /*ARGSUSED*/
735 static pnode_t
drmach_node_ddi_get_dnode(drmach_node_t * np)736 drmach_node_ddi_get_dnode(drmach_node_t *np)
737 {
738 return ((pnode_t)NULL);
739 }
740
741 static drmach_node_t *
drmach_node_new(void)742 drmach_node_new(void)
743 {
744 drmach_node_t *np;
745
746 np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
747
748 np->get_dnode = drmach_node_ddi_get_dnode;
749 np->walk = drmach_node_ddi_walk;
750 np->n_getdip = drmach_node_ddi_get_dip;
751 np->n_getproplen = drmach_node_ddi_get_proplen;
752 np->n_getprop = drmach_node_ddi_get_prop;
753 np->get_parent = drmach_node_ddi_get_parent;
754
755 return (np);
756 }
757
758 static void
drmach_node_dispose(drmach_node_t * np)759 drmach_node_dispose(drmach_node_t *np)
760 {
761 kmem_free(np, sizeof (*np));
762 }
763
764 static dev_info_t *
drmach_node_ddi_get_dip(drmach_node_t * np)765 drmach_node_ddi_get_dip(drmach_node_t *np)
766 {
767 return ((dev_info_t *)np->here);
768 }
769
770 static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))771 drmach_node_walk(drmach_node_t *np, void *param,
772 int (*cb)(drmach_node_walk_args_t *args))
773 {
774 return (np->walk(np, param, cb));
775 }
776
777 static int
drmach_node_ddi_get_prop(drmach_node_t * np,char * name,void * buf,int len)778 drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
779 {
780 int rv = 0;
781 dev_info_t *ndip;
782 static char *fn = "drmach_node_ddi_get_prop";
783
784
785 ndip = np->n_getdip(np);
786 if (ndip == NULL) {
787 cmn_err(CE_WARN, "%s: NULL dip", fn);
788 rv = -1;
789 } else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
790 DDI_PROP_DONTPASS, name,
791 (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
792 rv = -1;
793 }
794
795 return (rv);
796 }
797
798 static int
drmach_node_ddi_get_proplen(drmach_node_t * np,char * name,int * len)799 drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
800 {
801 int rv = 0;
802 dev_info_t *ndip;
803
804 ndip = np->n_getdip(np);
805 if (ndip == NULL) {
806 rv = -1;
807 } else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, name,
808 len) != DDI_PROP_SUCCESS) {
809 rv = -1;
810 }
811
812 return (rv);
813 }
814
815 static drmachid_t
drmach_node_dup(drmach_node_t * np)816 drmach_node_dup(drmach_node_t *np)
817 {
818 drmach_node_t *dup;
819
820 dup = drmach_node_new();
821 dup->here = np->here;
822 dup->get_dnode = np->get_dnode;
823 dup->walk = np->walk;
824 dup->n_getdip = np->n_getdip;
825 dup->n_getproplen = np->n_getproplen;
826 dup->n_getprop = np->n_getprop;
827 dup->get_parent = np->get_parent;
828
829 return (dup);
830 }
831
832 /*
833 * drmach_array provides convenient array construction, access,
834 * bounds checking and array destruction logic.
835 */
836
837 static drmach_array_t *
drmach_array_new(int min_index,int max_index)838 drmach_array_new(int min_index, int max_index)
839 {
840 drmach_array_t *arr;
841
842 arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
843
844 arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
845 if (arr->arr_sz > 0) {
846 arr->min_index = min_index;
847 arr->max_index = max_index;
848
849 arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
850 return (arr);
851 } else {
852 kmem_free(arr, sizeof (*arr));
853 return (0);
854 }
855 }
856
857 static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)858 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
859 {
860 if (idx < arr->min_index || idx > arr->max_index)
861 return (-1);
862 else {
863 arr->arr[idx - arr->min_index] = val;
864 return (0);
865 }
866 /*NOTREACHED*/
867 }
868
869 static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)870 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
871 {
872 if (idx < arr->min_index || idx > arr->max_index)
873 return (-1);
874 else {
875 *val = arr->arr[idx - arr->min_index];
876 return (0);
877 }
878 /*NOTREACHED*/
879 }
880
881 static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)882 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
883 {
884 int rv;
885
886 *idx = arr->min_index;
887 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
888 *idx += 1;
889
890 return (rv);
891 }
892
893 static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)894 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
895 {
896 int rv;
897
898 *idx += 1;
899 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
900 *idx += 1;
901
902 return (rv);
903 }
904
905 static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))906 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
907 {
908 drmachid_t val;
909 int idx;
910 int rv;
911
912 rv = drmach_array_first(arr, &idx, &val);
913 while (rv == 0) {
914 (*disposer)(val);
915 rv = drmach_array_next(arr, &idx, &val);
916 }
917
918 kmem_free(arr->arr, arr->arr_sz);
919 kmem_free(arr, sizeof (*arr));
920 }
921
922 static drmach_board_t *
drmach_get_board_by_bnum(int bnum)923 drmach_get_board_by_bnum(int bnum)
924 {
925 drmachid_t id;
926
927 if (drmach_array_get(drmach_boards, bnum, &id) == 0)
928 return ((drmach_board_t *)id);
929 else
930 return (NULL);
931 }
932
933 static pnode_t
drmach_node_get_dnode(drmach_node_t * np)934 drmach_node_get_dnode(drmach_node_t *np)
935 {
936 return (np->get_dnode(np));
937 }
938
939 /*ARGSUSED*/
940 sbd_error_t *
drmach_configure(drmachid_t id,int flags)941 drmach_configure(drmachid_t id, int flags)
942 {
943 drmach_device_t *dp;
944 sbd_error_t *err = NULL;
945 dev_info_t *rdip;
946 dev_info_t *fdip = NULL;
947
948 if (DRMACH_IS_CPU_ID(id)) {
949 return (NULL);
950 }
951 if (!DRMACH_IS_DEVICE_ID(id))
952 return (drerr_new(0, EOPL_INAPPROP, NULL));
953 dp = id;
954 rdip = dp->node->n_getdip(dp->node);
955
956 ASSERT(rdip);
957
958 ASSERT(e_ddi_branch_held(rdip));
959
960 if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
961 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
962 dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
963
964 (void) ddi_pathname(dip, path);
965 err = drerr_new(1, EOPL_DRVFAIL, path);
966
967 kmem_free(path, MAXPATHLEN);
968
969 /* If non-NULL, fdip is returned held and must be released */
970 if (fdip != NULL)
971 ddi_release_devi(fdip);
972 }
973
974 return (err);
975 }
976
977
978 static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,int portid,drmachid_t * idp)979 drmach_device_new(drmach_node_t *node,
980 drmach_board_t *bp, int portid, drmachid_t *idp)
981 {
982 int i;
983 int rv;
984 drmach_device_t proto;
985 sbd_error_t *err;
986 char name[OBP_MAXDRVNAME];
987
988 rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
989 if (rv) {
990 /* every node is expected to have a name */
991 err = drerr_new(1, EOPL_GETPROP, "device node %s: property %s",
992 ddi_node_name(node->n_getdip(node)), "name");
993 return (err);
994 }
995
996 /*
997 * The node currently being examined is not listed in the name2type[]
998 * array. In this case, the node is no interest to drmach. Both
999 * dp and err are initialized here to yield nothing (no device or
1000 * error structure) for this case.
1001 */
1002 i = drmach_name2type_idx(name);
1003
1004
1005 if (i < 0) {
1006 *idp = (drmachid_t)0;
1007 return (NULL);
1008 }
1009
1010 /* device specific new function will set unum */
1011
1012 bzero(&proto, sizeof (proto));
1013 proto.type = drmach_name2type[i].type;
1014 proto.bp = bp;
1015 proto.node = node;
1016 proto.portid = portid;
1017
1018 return (drmach_name2type[i].new(&proto, idp));
1019 }
1020
1021 static void
drmach_device_dispose(drmachid_t id)1022 drmach_device_dispose(drmachid_t id)
1023 {
1024 drmach_device_t *self = id;
1025
1026 self->cm.dispose(id);
1027 }
1028
1029
1030 static drmach_board_t *
drmach_board_new(int bnum,int boot_board)1031 drmach_board_new(int bnum, int boot_board)
1032 {
1033 drmach_board_t *bp;
1034
1035 bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
1036
1037 bp->cm.isa = (void *)drmach_board_new;
1038 bp->cm.release = drmach_board_release;
1039 bp->cm.status = drmach_board_status;
1040
1041 (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
1042
1043 bp->bnum = bnum;
1044 bp->devices = NULL;
1045 bp->connected = boot_board;
1046 bp->tree = drmach_node_new();
1047 bp->assigned = boot_board;
1048 bp->powered = boot_board;
1049 bp->boot_board = boot_board;
1050
1051 /*
1052 * If this is not bootup initialization, we have to wait till
1053 * IKP sets up the device nodes in drmach_board_connect().
1054 */
1055 if (boot_board)
1056 drmach_setup_core_info(bp);
1057
1058 (void) drmach_array_set(drmach_boards, bnum, bp);
1059 return (bp);
1060 }
1061
1062 static void
drmach_board_dispose(drmachid_t id)1063 drmach_board_dispose(drmachid_t id)
1064 {
1065 drmach_board_t *bp;
1066
1067 ASSERT(DRMACH_IS_BOARD_ID(id));
1068 bp = id;
1069
1070 if (bp->tree)
1071 drmach_node_dispose(bp->tree);
1072
1073 if (bp->devices)
1074 drmach_array_dispose(bp->devices, drmach_device_dispose);
1075
1076 kmem_free(bp, sizeof (*bp));
1077 }
1078
1079 static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)1080 drmach_board_status(drmachid_t id, drmach_status_t *stat)
1081 {
1082 sbd_error_t *err = NULL;
1083 drmach_board_t *bp;
1084
1085 if (!DRMACH_IS_BOARD_ID(id))
1086 return (drerr_new(0, EOPL_INAPPROP, NULL));
1087 bp = id;
1088
1089 stat->assigned = bp->assigned;
1090 stat->powered = bp->powered;
1091 stat->busy = 0; /* assume not busy */
1092 stat->configured = 0; /* assume not configured */
1093 stat->empty = 0;
1094 stat->cond = bp->cond = SBD_COND_OK;
1095 (void) strncpy(stat->type, "System Brd", sizeof (stat->type));
1096 stat->info[0] = '\0';
1097
1098 if (bp->devices) {
1099 int rv;
1100 int d_idx;
1101 drmachid_t d_id;
1102
1103 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
1104 while (rv == 0) {
1105 drmach_status_t d_stat;
1106
1107 err = drmach_i_status(d_id, &d_stat);
1108 if (err)
1109 break;
1110
1111 stat->busy |= d_stat.busy;
1112 stat->configured |= d_stat.configured;
1113
1114 rv = drmach_array_next(bp->devices, &d_idx, &d_id);
1115 }
1116 }
1117
1118 return (err);
1119 }
1120
1121 int
drmach_board_is_floating(drmachid_t id)1122 drmach_board_is_floating(drmachid_t id)
1123 {
1124 drmach_board_t *bp;
1125
1126 if (!DRMACH_IS_BOARD_ID(id))
1127 return (0);
1128
1129 bp = (drmach_board_t *)id;
1130
1131 return ((drmach_domain.floating & (1 << bp->bnum)) ? 1 : 0);
1132 }
1133
1134 static int
drmach_init(void)1135 drmach_init(void)
1136 {
1137 dev_info_t *rdip;
1138 int i, rv, len;
1139 int *floating;
1140
1141 rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
1142
1143 drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
1144
1145 rdip = ddi_root_node();
1146
1147 if (ddi_getproplen(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1148 "floating-boards", &len) != DDI_PROP_SUCCESS) {
1149 cmn_err(CE_WARN, "Cannot get floating-boards proplen\n");
1150 } else {
1151 floating = (int *)kmem_alloc(len, KM_SLEEP);
1152 rv = ddi_prop_op(DDI_DEV_T_ANY, rdip, PROP_LEN_AND_VAL_BUF,
1153 DDI_PROP_DONTPASS, "floating-boards", (caddr_t)floating,
1154 &len);
1155 if (rv != DDI_PROP_SUCCESS) {
1156 cmn_err(CE_WARN, "Cannot get floating-boards prop\n");
1157 } else {
1158 drmach_domain.floating = 0;
1159 for (i = 0; i < len / sizeof (int); i++) {
1160 drmach_domain.floating |= (1 << floating[i]);
1161 }
1162 }
1163 kmem_free(floating, len);
1164 }
1165 drmach_domain.allow_dr = opl_check_dr_status();
1166
1167 rdip = ddi_get_child(ddi_root_node());
1168 do {
1169 int bnum;
1170 drmachid_t id;
1171
1172 bnum = -1;
1173 bnum = ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
1174 OBP_BOARDNUM, -1);
1175 if (bnum == -1)
1176 continue;
1177
1178 if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
1179 cmn_err(CE_WARN, "Device node 0x%p has invalid "
1180 "property value, %s=%d", (void *)rdip,
1181 OBP_BOARDNUM, bnum);
1182 goto error;
1183 } else if (id == NULL) {
1184 (void) drmach_board_new(bnum, 1);
1185 }
1186 } while ((rdip = ddi_get_next_sibling(rdip)) != NULL);
1187
1188 opl_hold_devtree();
1189
1190 /*
1191 * Initialize the IKP feature.
1192 *
1193 * This can be done only after DR has acquired a hold on all the
1194 * device nodes that are interesting to IKP.
1195 */
1196 if (opl_init_cfg() != 0) {
1197 cmn_err(CE_WARN, "DR - IKP initialization failed");
1198
1199 opl_release_devtree();
1200
1201 goto error;
1202 }
1203
1204 return (0);
1205 error:
1206 drmach_array_dispose(drmach_boards, drmach_board_dispose);
1207 rw_destroy(&drmach_boards_rwlock);
1208 return (ENXIO);
1209 }
1210
1211 static void
drmach_fini(void)1212 drmach_fini(void)
1213 {
1214 rw_enter(&drmach_boards_rwlock, RW_WRITER);
1215 drmach_array_dispose(drmach_boards, drmach_board_dispose);
1216 drmach_boards = NULL;
1217 rw_exit(&drmach_boards_rwlock);
1218
1219 /*
1220 * Walk immediate children of the root devinfo node
1221 * releasing holds acquired on branches in drmach_init()
1222 */
1223
1224 opl_release_devtree();
1225
1226 rw_destroy(&drmach_boards_rwlock);
1227 }
1228
1229 /*
1230 * Each system board contains 2 Oberon PCI bridge and
1231 * 1 CMUCH.
1232 * Each oberon has 2 channels.
1233 * Each channel has 2 pci-ex leaf.
1234 * Each CMUCH has 1 pci bus.
1235 *
1236 *
1237 * Device Path:
1238 * /pci@<portid>,reg
1239 *
1240 * where
1241 * portid[10] = 0
1242 * portid[9:0] = LLEAF_ID[9:0] of the Oberon Channel
1243 *
1244 * LLEAF_ID[9:8] = 0
1245 * LLEAF_ID[8:4] = LSB_ID[4:0]
1246 * LLEAF_ID[3:1] = IO Channel#[2:0] (0,1,2,3 for Oberon)
1247 * channel 4 is pcicmu
1248 * LLEAF_ID[0] = PCI Leaf Number (0 for leaf-A, 1 for leaf-B)
1249 *
1250 * Properties:
1251 * name = pci
1252 * device_type = "pciex"
1253 * board# = LSBID
1254 * reg = int32 * 2, Oberon CSR space of the leaf and the UBC space
1255 * portid = Jupiter Bus Device ID ((LSB_ID << 3)|pciport#)
1256 */
1257
1258 static sbd_error_t *
drmach_io_new(drmach_device_t * proto,drmachid_t * idp)1259 drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
1260 {
1261 drmach_io_t *ip;
1262
1263 int portid;
1264
1265 portid = proto->portid;
1266 ASSERT(portid != -1);
1267 proto->unum = portid & (MAX_IO_UNITS_PER_BOARD - 1);
1268
1269 ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
1270 bcopy(proto, &ip->dev, sizeof (ip->dev));
1271 ip->dev.node = drmach_node_dup(proto->node);
1272 ip->dev.cm.isa = (void *)drmach_io_new;
1273 ip->dev.cm.dispose = drmach_io_dispose;
1274 ip->dev.cm.release = drmach_io_release;
1275 ip->dev.cm.status = drmach_io_status;
1276 ip->channel = (portid >> 1) & 0x7;
1277 ip->leaf = (portid & 0x1);
1278
1279 (void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
1280 ip->dev.type, ip->dev.unum);
1281
1282 *idp = (drmachid_t)ip;
1283 return (NULL);
1284 }
1285
1286
1287 static void
drmach_io_dispose(drmachid_t id)1288 drmach_io_dispose(drmachid_t id)
1289 {
1290 drmach_io_t *self;
1291
1292 ASSERT(DRMACH_IS_IO_ID(id));
1293
1294 self = id;
1295 if (self->dev.node)
1296 drmach_node_dispose(self->dev.node);
1297
1298 kmem_free(self, sizeof (*self));
1299 }
1300
1301 /*ARGSUSED*/
1302 sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)1303 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
1304 {
1305 drmach_board_t *bp = (drmach_board_t *)id;
1306 sbd_error_t *err = NULL;
1307
1308 /* allow status and ncm operations to always succeed */
1309 if ((cmd == SBD_CMD_STATUS) || (cmd == SBD_CMD_GETNCM)) {
1310 return (NULL);
1311 }
1312
1313 /* check all other commands for the required option string */
1314
1315 if ((opts->size > 0) && (opts->copts != NULL)) {
1316
1317 DRMACH_PR("platform options: %s\n", opts->copts);
1318
1319 if (strstr(opts->copts, "opldr") == NULL) {
1320 err = drerr_new(1, EOPL_SUPPORT, NULL);
1321 }
1322 } else {
1323 err = drerr_new(1, EOPL_SUPPORT, NULL);
1324 }
1325
1326 if (!err && id && DRMACH_IS_BOARD_ID(id)) {
1327 switch (cmd) {
1328 case SBD_CMD_TEST:
1329 case SBD_CMD_STATUS:
1330 case SBD_CMD_GETNCM:
1331 break;
1332 case SBD_CMD_CONNECT:
1333 if (bp->connected)
1334 err = drerr_new(0, ESBD_STATE, NULL);
1335 else if (!drmach_domain.allow_dr)
1336 err = drerr_new(1, EOPL_SUPPORT, NULL);
1337 break;
1338 case SBD_CMD_DISCONNECT:
1339 if (!bp->connected)
1340 err = drerr_new(0, ESBD_STATE, NULL);
1341 else if (!drmach_domain.allow_dr)
1342 err = drerr_new(1, EOPL_SUPPORT, NULL);
1343 break;
1344 default:
1345 if (!drmach_domain.allow_dr)
1346 err = drerr_new(1, EOPL_SUPPORT, NULL);
1347 break;
1348
1349 }
1350 }
1351
1352 return (err);
1353 }
1354
1355 /*ARGSUSED*/
1356 sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)1357 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
1358 {
1359 return (NULL);
1360 }
1361
1362 sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)1363 drmach_board_assign(int bnum, drmachid_t *id)
1364 {
1365 sbd_error_t *err = NULL;
1366
1367 rw_enter(&drmach_boards_rwlock, RW_WRITER);
1368
1369 if (drmach_array_get(drmach_boards, bnum, id) == -1) {
1370 err = drerr_new(1, EOPL_BNUM, "%d", bnum);
1371 } else {
1372 drmach_board_t *bp;
1373
1374 if (*id)
1375 rw_downgrade(&drmach_boards_rwlock);
1376
1377 bp = *id;
1378 if (!(*id))
1379 bp = *id =
1380 (drmachid_t)drmach_board_new(bnum, 0);
1381 bp->assigned = 1;
1382 }
1383
1384 rw_exit(&drmach_boards_rwlock);
1385
1386 return (err);
1387 }
1388
1389 /*ARGSUSED*/
1390 sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)1391 drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
1392 {
1393 extern int cpu_alljupiter;
1394 drmach_board_t *obj = (drmach_board_t *)id;
1395 unsigned cpu_impl;
1396
1397 if (!DRMACH_IS_BOARD_ID(id))
1398 return (drerr_new(0, EOPL_INAPPROP, NULL));
1399
1400 if (opl_probe_sb(obj->bnum, &cpu_impl) != 0)
1401 return (drerr_new(1, EOPL_PROBE, NULL));
1402
1403 if (cpu_alljupiter) {
1404 if (cpu_impl & (1 << OLYMPUS_C_IMPL)) {
1405 (void) opl_unprobe_sb(obj->bnum);
1406 return (drerr_new(1, EOPL_MIXED_CPU, NULL));
1407 }
1408 }
1409
1410 (void) prom_attach_notice(obj->bnum);
1411
1412 drmach_setup_core_info(obj);
1413
1414 obj->connected = 1;
1415
1416 return (NULL);
1417 }
1418
1419 static int drmach_cache_flush_flag[NCPU];
1420
1421 /*ARGSUSED*/
1422 static void
drmach_flush_cache(uint64_t id,uint64_t dummy)1423 drmach_flush_cache(uint64_t id, uint64_t dummy)
1424 {
1425 extern void cpu_flush_ecache(void);
1426
1427 cpu_flush_ecache();
1428 drmach_cache_flush_flag[id] = 0;
1429 }
1430
1431 static void
drmach_flush_all()1432 drmach_flush_all()
1433 {
1434 cpuset_t xc_cpuset;
1435 int i;
1436
1437 xc_cpuset = cpu_ready_set;
1438 for (i = 0; i < NCPU; i++) {
1439 if (CPU_IN_SET(xc_cpuset, i)) {
1440 drmach_cache_flush_flag[i] = 1;
1441 xc_one(i, drmach_flush_cache, i, 0);
1442 while (drmach_cache_flush_flag[i]) {
1443 DELAY(1000);
1444 }
1445 }
1446 }
1447 }
1448
1449 static int
drmach_disconnect_cpus(drmach_board_t * bp)1450 drmach_disconnect_cpus(drmach_board_t *bp)
1451 {
1452 int i, bnum;
1453
1454 bnum = bp->bnum;
1455
1456 for (i = 0; i < OPL_MAX_COREID_PER_BOARD; i++) {
1457 if (bp->cores[i].core_present) {
1458 if (bp->cores[i].core_started)
1459 return (-1);
1460 if (bp->cores[i].core_hotadded) {
1461 if (drmach_add_remove_cpu(bnum, i,
1462 HOTREMOVE_CPU)) {
1463 cmn_err(CE_WARN, "Failed to remove "
1464 "CMP %d on board %d\n", i, bnum);
1465 return (-1);
1466 }
1467 }
1468 }
1469 }
1470 return (0);
1471 }
1472
1473 /*ARGSUSED*/
1474 sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)1475 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
1476 {
1477 drmach_board_t *obj;
1478 int rv = 0;
1479 sbd_error_t *err = NULL;
1480
1481 if (DRMACH_NULL_ID(id))
1482 return (NULL);
1483
1484 if (!DRMACH_IS_BOARD_ID(id))
1485 return (drerr_new(0, EOPL_INAPPROP, NULL));
1486
1487 obj = (drmach_board_t *)id;
1488
1489 if (drmach_disconnect_cpus(obj)) {
1490 err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
1491 return (err);
1492 }
1493
1494 rv = opl_unprobe_sb(obj->bnum);
1495
1496 if (rv == 0) {
1497 (void) prom_detach_notice(obj->bnum);
1498 obj->connected = 0;
1499
1500 } else
1501 err = drerr_new(1, EOPL_DEPROBE, obj->cm.name);
1502
1503 return (err);
1504 }
1505
1506 static int
drmach_get_portid(drmach_node_t * np)1507 drmach_get_portid(drmach_node_t *np)
1508 {
1509 int portid;
1510 char type[OBP_MAXPROPNAME];
1511
1512 if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
1513 return (portid);
1514
1515 /*
1516 * Get the device_type property to see if we should
1517 * continue processing this node.
1518 */
1519 if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
1520 return (-1);
1521
1522 if (strcmp(type, OPL_CPU_NODE) == 0) {
1523 /*
1524 * We return cpuid because it has no portid
1525 */
1526 if (np->n_getprop(np, "cpuid", &portid, sizeof (portid)) == 0)
1527 return (portid);
1528 }
1529
1530 return (-1);
1531 }
1532
1533 /*
1534 * This is a helper function to determine if a given
1535 * node should be considered for a dr operation according
1536 * to predefined dr type nodes and the node's name.
1537 * Formal Parameter : The name of a device node.
1538 * Return Value: -1, name does not map to a valid dr type.
1539 * A value greater or equal to 0, name is a valid dr type.
1540 */
1541 static int
drmach_name2type_idx(char * name)1542 drmach_name2type_idx(char *name)
1543 {
1544 int index, ntypes;
1545
1546 if (name == NULL)
1547 return (-1);
1548
1549 /*
1550 * Determine how many possible types are currently supported
1551 * for dr.
1552 */
1553 ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
1554
1555 /* Determine if the node's name correspond to a predefined type. */
1556 for (index = 0; index < ntypes; index++) {
1557 if (strcmp(drmach_name2type[index].name, name) == 0)
1558 /* The node is an allowed type for dr. */
1559 return (index);
1560 }
1561
1562 /*
1563 * If the name of the node does not map to any of the
1564 * types in the array drmach_name2type then the node is not of
1565 * interest to dr.
1566 */
1567 return (-1);
1568 }
1569
1570 /*
1571 * there is some complication on OPL:
1572 * - pseudo-mc nodes do not have portid property
1573 * - portid[9:5] of cmp node is LSB #, portid[7:3] of pci is LSB#
1574 * - cmp has board#
1575 * - core and cpu nodes do not have portid and board# properties
1576 * starcat uses portid to derive the board# but that does not work
1577 * for us. starfire reads board# property to filter the devices.
1578 * That does not work either. So for these specific device,
1579 * we use specific hard coded methods to get the board# -
1580 * cpu: LSB# = CPUID[9:5]
1581 */
1582
1583 static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)1584 drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
1585 {
1586 drmach_node_t *node = args->node;
1587 drmach_board_cb_data_t *data = args->data;
1588 drmach_board_t *obj = data->obj;
1589
1590 int rv, portid;
1591 int bnum;
1592 drmachid_t id;
1593 drmach_device_t *device;
1594 char name[OBP_MAXDRVNAME];
1595
1596 portid = drmach_get_portid(node);
1597 /*
1598 * core, cpu and pseudo-mc do not have portid
1599 * we use cpuid as the portid of the cpu node
1600 * for pseudo-mc, we do not use portid info.
1601 */
1602
1603 rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
1604 if (rv)
1605 return (0);
1606
1607
1608 rv = node->n_getprop(node, OBP_BOARDNUM, &bnum, sizeof (bnum));
1609
1610 if (rv) {
1611 /*
1612 * cpu does not have board# property. We use
1613 * CPUID[9:5]
1614 */
1615 if (strcmp("cpu", name) == 0) {
1616 bnum = (portid >> 5) & 0x1f;
1617 } else
1618 return (0);
1619 }
1620
1621
1622 if (bnum != obj->bnum)
1623 return (0);
1624
1625 if (drmach_name2type_idx(name) < 0) {
1626 return (0);
1627 }
1628
1629 /*
1630 * Create a device data structure from this node data.
1631 * The call may yield nothing if the node is not of interest
1632 * to drmach.
1633 */
1634 data->err = drmach_device_new(node, obj, portid, &id);
1635 if (data->err)
1636 return (-1);
1637 else if (!id) {
1638 /*
1639 * drmach_device_new examined the node we passed in
1640 * and determined that it was one not of interest to
1641 * drmach. So, it is skipped.
1642 */
1643 return (0);
1644 }
1645
1646 rv = drmach_array_set(obj->devices, data->ndevs++, id);
1647 if (rv) {
1648 data->err = DRMACH_INTERNAL_ERROR();
1649 return (-1);
1650 }
1651 device = id;
1652
1653 data->err = (*data->found)(data->a, device->type, device->unum, id);
1654 return (data->err == NULL ? 0 : -1);
1655 }
1656
1657 sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))1658 drmach_board_find_devices(drmachid_t id, void *a,
1659 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
1660 {
1661 drmach_board_t *bp = (drmach_board_t *)id;
1662 sbd_error_t *err;
1663 int max_devices;
1664 int rv;
1665 drmach_board_cb_data_t data;
1666
1667
1668 if (!DRMACH_IS_BOARD_ID(id))
1669 return (drerr_new(0, EOPL_INAPPROP, NULL));
1670
1671 max_devices = MAX_CPU_UNITS_PER_BOARD;
1672 max_devices += MAX_MEM_UNITS_PER_BOARD;
1673 max_devices += MAX_IO_UNITS_PER_BOARD;
1674
1675 bp->devices = drmach_array_new(0, max_devices);
1676
1677 if (bp->tree == NULL)
1678 bp->tree = drmach_node_new();
1679
1680 data.obj = bp;
1681 data.ndevs = 0;
1682 data.found = found;
1683 data.a = a;
1684 data.err = NULL;
1685
1686 rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
1687 if (rv == 0)
1688 err = NULL;
1689 else {
1690 drmach_array_dispose(bp->devices, drmach_device_dispose);
1691 bp->devices = NULL;
1692
1693 if (data.err)
1694 err = data.err;
1695 else
1696 err = DRMACH_INTERNAL_ERROR();
1697 }
1698
1699 return (err);
1700 }
1701
1702 int
drmach_board_lookup(int bnum,drmachid_t * id)1703 drmach_board_lookup(int bnum, drmachid_t *id)
1704 {
1705 int rv = 0;
1706
1707 rw_enter(&drmach_boards_rwlock, RW_READER);
1708 if (drmach_array_get(drmach_boards, bnum, id)) {
1709 *id = 0;
1710 rv = -1;
1711 }
1712 rw_exit(&drmach_boards_rwlock);
1713 return (rv);
1714 }
1715
1716 sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)1717 drmach_board_name(int bnum, char *buf, int buflen)
1718 {
1719 (void) snprintf(buf, buflen, "SB%d", bnum);
1720 return (NULL);
1721 }
1722
1723 sbd_error_t *
drmach_board_poweroff(drmachid_t id)1724 drmach_board_poweroff(drmachid_t id)
1725 {
1726 drmach_board_t *bp;
1727 sbd_error_t *err;
1728 drmach_status_t stat;
1729
1730 if (DRMACH_NULL_ID(id))
1731 return (NULL);
1732
1733 if (!DRMACH_IS_BOARD_ID(id))
1734 return (drerr_new(0, EOPL_INAPPROP, NULL));
1735 bp = id;
1736
1737 err = drmach_board_status(id, &stat);
1738
1739 if (!err) {
1740 if (stat.configured || stat.busy)
1741 err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
1742 else {
1743 bp->powered = 0;
1744 }
1745 }
1746 return (err);
1747 }
1748
1749 sbd_error_t *
drmach_board_poweron(drmachid_t id)1750 drmach_board_poweron(drmachid_t id)
1751 {
1752 drmach_board_t *bp;
1753
1754 if (!DRMACH_IS_BOARD_ID(id))
1755 return (drerr_new(0, EOPL_INAPPROP, NULL));
1756 bp = id;
1757
1758 bp->powered = 1;
1759
1760 return (NULL);
1761 }
1762
1763 static sbd_error_t *
drmach_board_release(drmachid_t id)1764 drmach_board_release(drmachid_t id)
1765 {
1766 if (!DRMACH_IS_BOARD_ID(id))
1767 return (drerr_new(0, EOPL_INAPPROP, NULL));
1768 return (NULL);
1769 }
1770
1771 /*ARGSUSED*/
1772 sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)1773 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
1774 {
1775 return (NULL);
1776 }
1777
1778 sbd_error_t *
drmach_board_unassign(drmachid_t id)1779 drmach_board_unassign(drmachid_t id)
1780 {
1781 drmach_board_t *bp;
1782 sbd_error_t *err;
1783 drmach_status_t stat;
1784
1785 if (DRMACH_NULL_ID(id))
1786 return (NULL);
1787
1788 if (!DRMACH_IS_BOARD_ID(id)) {
1789 return (drerr_new(0, EOPL_INAPPROP, NULL));
1790 }
1791 bp = id;
1792
1793 rw_enter(&drmach_boards_rwlock, RW_WRITER);
1794
1795 err = drmach_board_status(id, &stat);
1796 if (err) {
1797 rw_exit(&drmach_boards_rwlock);
1798 return (err);
1799 }
1800 if (stat.configured || stat.busy) {
1801 err = drerr_new(0, EOPL_CONFIGBUSY, bp->cm.name);
1802 } else {
1803 if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
1804 err = DRMACH_INTERNAL_ERROR();
1805 else
1806 drmach_board_dispose(bp);
1807 }
1808 rw_exit(&drmach_boards_rwlock);
1809 return (err);
1810 }
1811
1812 /*
1813 * We have to do more on OPL - e.g. set up sram tte, read cpuid, strand id,
1814 * implementation #, etc
1815 */
1816
1817 static sbd_error_t *
drmach_cpu_new(drmach_device_t * proto,drmachid_t * idp)1818 drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
1819 {
1820 int portid;
1821 drmach_cpu_t *cp = NULL;
1822
1823 /* portid is CPUID of the node */
1824 portid = proto->portid;
1825 ASSERT(portid != -1);
1826
1827 /* unum = (CMP/CHIP ID) + (ON_BOARD_CORE_NUM * MAX_CMPID_PER_BOARD) */
1828 proto->unum = ((portid/OPL_MAX_CPUID_PER_CMP) &
1829 (OPL_MAX_CMPID_PER_BOARD - 1)) +
1830 ((portid & (OPL_MAX_CPUID_PER_CMP - 1)) *
1831 (OPL_MAX_CMPID_PER_BOARD));
1832
1833 cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
1834 bcopy(proto, &cp->dev, sizeof (cp->dev));
1835 cp->dev.node = drmach_node_dup(proto->node);
1836 cp->dev.cm.isa = (void *)drmach_cpu_new;
1837 cp->dev.cm.dispose = drmach_cpu_dispose;
1838 cp->dev.cm.release = drmach_cpu_release;
1839 cp->dev.cm.status = drmach_cpu_status;
1840
1841 (void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
1842 cp->dev.type, cp->dev.unum);
1843
1844 /*
1845 * CPU ID representation
1846 * CPUID[9:5] = SB#
1847 * CPUID[4:3] = Chip#
1848 * CPUID[2:1] = Core# (Only 2 core for OPL)
1849 * CPUID[0:0] = Strand#
1850 */
1851
1852 /*
1853 * reg property of the strand contains strand ID
1854 * reg property of the parent node contains core ID
1855 * We should use them.
1856 */
1857 cp->cpuid = portid;
1858 cp->sb = (portid >> 5) & 0x1f;
1859 cp->chipid = (portid >> 3) & 0x3;
1860 cp->coreid = (portid >> 1) & 0x3;
1861 cp->strandid = portid & 0x1;
1862
1863 *idp = (drmachid_t)cp;
1864 return (NULL);
1865 }
1866
1867
1868 static void
drmach_cpu_dispose(drmachid_t id)1869 drmach_cpu_dispose(drmachid_t id)
1870 {
1871 drmach_cpu_t *self;
1872
1873 ASSERT(DRMACH_IS_CPU_ID(id));
1874
1875 self = id;
1876 if (self->dev.node)
1877 drmach_node_dispose(self->dev.node);
1878
1879 kmem_free(self, sizeof (*self));
1880 }
1881
1882 static int
drmach_cpu_start(struct cpu * cp)1883 drmach_cpu_start(struct cpu *cp)
1884 {
1885 int cpuid = cp->cpu_id;
1886 extern int restart_other_cpu(int);
1887
1888 ASSERT(MUTEX_HELD(&cpu_lock));
1889 ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
1890
1891 cp->cpu_flags &= ~CPU_POWEROFF;
1892
1893 /*
1894 * NOTE: restart_other_cpu pauses cpus during the
1895 * slave cpu start. This helps to quiesce the
1896 * bus traffic a bit which makes the tick sync
1897 * routine in the prom more robust.
1898 */
1899 DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
1900
1901 (void) restart_other_cpu(cpuid);
1902
1903 return (0);
1904 }
1905
1906 static sbd_error_t *
drmach_cpu_release(drmachid_t id)1907 drmach_cpu_release(drmachid_t id)
1908 {
1909 if (!DRMACH_IS_CPU_ID(id))
1910 return (drerr_new(0, EOPL_INAPPROP, NULL));
1911
1912 return (NULL);
1913 }
1914
1915 static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)1916 drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
1917 {
1918 drmach_cpu_t *cp;
1919 drmach_device_t *dp;
1920
1921 ASSERT(DRMACH_IS_CPU_ID(id));
1922 cp = (drmach_cpu_t *)id;
1923 dp = &cp->dev;
1924
1925 stat->assigned = dp->bp->assigned;
1926 stat->powered = dp->bp->powered;
1927 mutex_enter(&cpu_lock);
1928 stat->configured = (cpu_get(cp->cpuid) != NULL);
1929 mutex_exit(&cpu_lock);
1930 stat->busy = dp->busy;
1931 (void) strncpy(stat->type, dp->type, sizeof (stat->type));
1932 stat->info[0] = '\0';
1933
1934 return (NULL);
1935 }
1936
1937 sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)1938 drmach_cpu_disconnect(drmachid_t id)
1939 {
1940
1941 if (!DRMACH_IS_CPU_ID(id))
1942 return (drerr_new(0, EOPL_INAPPROP, NULL));
1943
1944 return (NULL);
1945 }
1946
1947 sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)1948 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
1949 {
1950 drmach_cpu_t *cpu;
1951
1952 if (!DRMACH_IS_CPU_ID(id))
1953 return (drerr_new(0, EOPL_INAPPROP, NULL));
1954 cpu = (drmach_cpu_t *)id;
1955
1956 /* get from cpu directly on OPL */
1957 *cpuid = cpu->cpuid;
1958 return (NULL);
1959 }
1960
1961 sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)1962 drmach_cpu_get_impl(drmachid_t id, int *ip)
1963 {
1964 drmach_device_t *cpu;
1965 drmach_node_t *np;
1966 drmach_node_t pp;
1967 int impl;
1968 char type[OBP_MAXPROPNAME];
1969
1970 if (!DRMACH_IS_CPU_ID(id))
1971 return (drerr_new(0, EOPL_INAPPROP, NULL));
1972
1973 cpu = id;
1974 np = cpu->node;
1975
1976 if (np->get_parent(np, &pp) != 0) {
1977 return (DRMACH_INTERNAL_ERROR());
1978 }
1979
1980 /* the parent should be core */
1981
1982 if (pp.n_getprop(&pp, "device_type", &type, sizeof (type)) != 0) {
1983 return (drerr_new(0, EOPL_GETPROP, NULL));
1984 }
1985
1986 if (strcmp(type, OPL_CORE_NODE) == 0) {
1987 if (pp.n_getprop(&pp, "implementation#", &impl,
1988 sizeof (impl)) != 0) {
1989 return (drerr_new(0, EOPL_GETPROP, NULL));
1990 }
1991 } else {
1992 return (DRMACH_INTERNAL_ERROR());
1993 }
1994
1995 *ip = impl;
1996
1997 return (NULL);
1998 }
1999
2000 sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)2001 drmach_get_dip(drmachid_t id, dev_info_t **dip)
2002 {
2003 drmach_device_t *dp;
2004
2005 if (!DRMACH_IS_DEVICE_ID(id))
2006 return (drerr_new(0, EOPL_INAPPROP, NULL));
2007 dp = id;
2008
2009 *dip = dp->node->n_getdip(dp->node);
2010 return (NULL);
2011 }
2012
2013 sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)2014 drmach_io_is_attached(drmachid_t id, int *yes)
2015 {
2016 drmach_device_t *dp;
2017 dev_info_t *dip;
2018 int state;
2019
2020 if (!DRMACH_IS_IO_ID(id))
2021 return (drerr_new(0, EOPL_INAPPROP, NULL));
2022 dp = id;
2023
2024 dip = dp->node->n_getdip(dp->node);
2025 if (dip == NULL) {
2026 *yes = 0;
2027 return (NULL);
2028 }
2029
2030 state = ddi_get_devstate(dip);
2031 *yes = ((i_ddi_node_state(dip) >= DS_ATTACHED) ||
2032 (state == DDI_DEVSTATE_UP));
2033
2034 return (NULL);
2035 }
2036
2037 struct drmach_io_cb {
2038 char *name; /* name of the node */
2039 int (*func)(dev_info_t *);
2040 int rv;
2041 dev_info_t *dip;
2042 };
2043
2044 #define DRMACH_IO_POST_ATTACH 0
2045 #define DRMACH_IO_PRE_RELEASE 1
2046
2047 static int
drmach_io_cb_check(dev_info_t * dip,void * arg)2048 drmach_io_cb_check(dev_info_t *dip, void *arg)
2049 {
2050 struct drmach_io_cb *p = (struct drmach_io_cb *)arg;
2051 char name[OBP_MAXDRVNAME];
2052 int len = OBP_MAXDRVNAME;
2053
2054 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "name",
2055 (caddr_t)name, &len) != DDI_PROP_SUCCESS) {
2056 return (DDI_WALK_PRUNECHILD);
2057 }
2058
2059 if (strcmp(name, p->name) == 0) {
2060 ndi_hold_devi(dip);
2061 p->dip = dip;
2062 return (DDI_WALK_TERMINATE);
2063 }
2064
2065 return (DDI_WALK_CONTINUE);
2066 }
2067
2068
2069 static int
drmach_console_ops(drmachid_t * id,int state)2070 drmach_console_ops(drmachid_t *id, int state)
2071 {
2072 drmach_io_t *obj = (drmach_io_t *)id;
2073 struct drmach_io_cb arg;
2074 int (*msudetp)(dev_info_t *);
2075 int (*msuattp)(dev_info_t *);
2076 dev_info_t *dip, *pdip;
2077 int circ;
2078
2079 /* 4 is pcicmu channel */
2080 if (obj->channel != 4)
2081 return (0);
2082
2083 arg.name = "serial";
2084 arg.func = NULL;
2085 if (state == DRMACH_IO_PRE_RELEASE) {
2086 msudetp = (int (*)(dev_info_t *))
2087 modgetsymvalue("oplmsu_dr_detach", 0);
2088 if (msudetp != NULL)
2089 arg.func = msudetp;
2090 } else if (state == DRMACH_IO_POST_ATTACH) {
2091 msuattp = (int (*)(dev_info_t *))
2092 modgetsymvalue("oplmsu_dr_attach", 0);
2093 if (msuattp != NULL)
2094 arg.func = msuattp;
2095 } else {
2096 return (0);
2097 }
2098
2099 if (arg.func == NULL) {
2100 return (0);
2101 }
2102
2103 arg.rv = 0;
2104 arg.dip = NULL;
2105
2106 dip = obj->dev.node->n_getdip(obj->dev.node);
2107 if (pdip = ddi_get_parent(dip)) {
2108 ndi_hold_devi(pdip);
2109 ndi_devi_enter(pdip, &circ);
2110 } else {
2111 /* this cannot happen unless something bad happens */
2112 return (-1);
2113 }
2114
2115 ddi_walk_devs(dip, drmach_io_cb_check, (void *)&arg);
2116
2117 ndi_devi_exit(pdip, circ);
2118 ndi_rele_devi(pdip);
2119
2120 if (arg.dip) {
2121 arg.rv = (*arg.func)(arg.dip);
2122 ndi_rele_devi(arg.dip);
2123 } else {
2124 arg.rv = -1;
2125 }
2126
2127 return (arg.rv);
2128 }
2129
2130 sbd_error_t *
drmach_io_pre_release(drmachid_t id)2131 drmach_io_pre_release(drmachid_t id)
2132 {
2133 int rv;
2134
2135 if (!DRMACH_IS_IO_ID(id))
2136 return (drerr_new(0, EOPL_INAPPROP, NULL));
2137
2138 rv = drmach_console_ops(id, DRMACH_IO_PRE_RELEASE);
2139
2140 if (rv != 0)
2141 cmn_err(CE_WARN, "IO callback failed in pre-release\n");
2142
2143 return (NULL);
2144 }
2145
2146 static sbd_error_t *
drmach_io_release(drmachid_t id)2147 drmach_io_release(drmachid_t id)
2148 {
2149 if (!DRMACH_IS_IO_ID(id))
2150 return (drerr_new(0, EOPL_INAPPROP, NULL));
2151 return (NULL);
2152 }
2153
2154 sbd_error_t *
drmach_io_unrelease(drmachid_t id)2155 drmach_io_unrelease(drmachid_t id)
2156 {
2157 if (!DRMACH_IS_IO_ID(id))
2158 return (drerr_new(0, EOPL_INAPPROP, NULL));
2159 return (NULL);
2160 }
2161
2162 /*ARGSUSED*/
2163 sbd_error_t *
drmach_io_post_release(drmachid_t id)2164 drmach_io_post_release(drmachid_t id)
2165 {
2166 return (NULL);
2167 }
2168
2169 /*ARGSUSED*/
2170 sbd_error_t *
drmach_io_post_attach(drmachid_t id)2171 drmach_io_post_attach(drmachid_t id)
2172 {
2173 int rv;
2174
2175 if (!DRMACH_IS_IO_ID(id))
2176 return (drerr_new(0, EOPL_INAPPROP, NULL));
2177
2178 rv = drmach_console_ops(id, DRMACH_IO_POST_ATTACH);
2179
2180 if (rv != 0)
2181 cmn_err(CE_WARN, "IO callback failed in post-attach\n");
2182
2183 return (0);
2184 }
2185
2186 static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)2187 drmach_io_status(drmachid_t id, drmach_status_t *stat)
2188 {
2189 drmach_device_t *dp;
2190 sbd_error_t *err;
2191 int configured;
2192
2193 ASSERT(DRMACH_IS_IO_ID(id));
2194 dp = id;
2195
2196 err = drmach_io_is_attached(id, &configured);
2197 if (err)
2198 return (err);
2199
2200 stat->assigned = dp->bp->assigned;
2201 stat->powered = dp->bp->powered;
2202 stat->configured = (configured != 0);
2203 stat->busy = dp->busy;
2204 (void) strncpy(stat->type, dp->type, sizeof (stat->type));
2205 stat->info[0] = '\0';
2206
2207 return (NULL);
2208 }
2209
2210 static sbd_error_t *
drmach_mem_new(drmach_device_t * proto,drmachid_t * idp)2211 drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
2212 {
2213 dev_info_t *dip;
2214 int rv;
2215
2216 drmach_mem_t *mp;
2217
2218 rv = 0;
2219
2220 if ((proto->node->n_getproplen(proto->node, "mc-addr", &rv) < 0) ||
2221 (rv <= 0)) {
2222 *idp = (drmachid_t)0;
2223 return (NULL);
2224 }
2225
2226 mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
2227 proto->unum = 0;
2228
2229 bcopy(proto, &mp->dev, sizeof (mp->dev));
2230 mp->dev.node = drmach_node_dup(proto->node);
2231 mp->dev.cm.isa = (void *)drmach_mem_new;
2232 mp->dev.cm.dispose = drmach_mem_dispose;
2233 mp->dev.cm.release = drmach_mem_release;
2234 mp->dev.cm.status = drmach_mem_status;
2235
2236 (void) snprintf(mp->dev.cm.name, sizeof (mp->dev.cm.name), "%s",
2237 mp->dev.type);
2238
2239 dip = mp->dev.node->n_getdip(mp->dev.node);
2240 if (drmach_setup_mc_info(dip, mp) != 0) {
2241 return (drerr_new(1, EOPL_MC_SETUP, NULL));
2242 }
2243
2244 /* make sure we do not create memoryless nodes */
2245 if (mp->nbytes == 0) {
2246 *idp = (drmachid_t)NULL;
2247 kmem_free(mp, sizeof (drmach_mem_t));
2248 } else
2249 *idp = (drmachid_t)mp;
2250
2251 return (NULL);
2252 }
2253
2254 static void
drmach_mem_dispose(drmachid_t id)2255 drmach_mem_dispose(drmachid_t id)
2256 {
2257 drmach_mem_t *mp;
2258
2259 ASSERT(DRMACH_IS_MEM_ID(id));
2260
2261
2262 mp = id;
2263
2264 if (mp->dev.node)
2265 drmach_node_dispose(mp->dev.node);
2266
2267 if (mp->memlist) {
2268 memlist_delete(mp->memlist);
2269 mp->memlist = NULL;
2270 }
2271
2272 kmem_free(mp, sizeof (*mp));
2273 }
2274
2275 sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)2276 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
2277 {
2278 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
2279 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
2280 int rv;
2281
2282 ASSERT(size != 0);
2283
2284 if (!DRMACH_IS_MEM_ID(id))
2285 return (drerr_new(0, EOPL_INAPPROP, NULL));
2286
2287 rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
2288 if (rv == ENOMEM) {
2289 cmn_err(CE_WARN, "%lu megabytes not available to kernel cage",
2290 (ulong_t)(size == 0 ? 0 : size / MBYTE));
2291 } else if (rv != 0) {
2292 /* catch this in debug kernels */
2293 ASSERT(0);
2294
2295 cmn_err(CE_WARN, "unexpected kcage_range_add return value %d",
2296 rv);
2297 }
2298
2299 if (rv) {
2300 return (DRMACH_INTERNAL_ERROR());
2301 }
2302 else
2303 return (NULL);
2304 }
2305
2306 sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)2307 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
2308 {
2309 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
2310 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
2311 int rv;
2312
2313 if (!DRMACH_IS_MEM_ID(id))
2314 return (drerr_new(0, EOPL_INAPPROP, NULL));
2315
2316 if (size > 0) {
2317 rv = kcage_range_delete_post_mem_del(basepfn, npages);
2318 if (rv != 0) {
2319 cmn_err(CE_WARN,
2320 "unexpected kcage_range_delete_post_mem_del"
2321 " return value %d", rv);
2322 return (DRMACH_INTERNAL_ERROR());
2323 }
2324 }
2325
2326 return (NULL);
2327 }
2328
2329 sbd_error_t *
drmach_mem_disable(drmachid_t id)2330 drmach_mem_disable(drmachid_t id)
2331 {
2332 if (!DRMACH_IS_MEM_ID(id))
2333 return (drerr_new(0, EOPL_INAPPROP, NULL));
2334 else {
2335 drmach_flush_all();
2336 return (NULL);
2337 }
2338 }
2339
2340 sbd_error_t *
drmach_mem_enable(drmachid_t id)2341 drmach_mem_enable(drmachid_t id)
2342 {
2343 if (!DRMACH_IS_MEM_ID(id))
2344 return (drerr_new(0, EOPL_INAPPROP, NULL));
2345 else
2346 return (NULL);
2347 }
2348
2349 sbd_error_t *
drmach_mem_get_info(drmachid_t id,drmach_mem_info_t * mem)2350 drmach_mem_get_info(drmachid_t id, drmach_mem_info_t *mem)
2351 {
2352 drmach_mem_t *mp;
2353
2354 if (!DRMACH_IS_MEM_ID(id))
2355 return (drerr_new(0, EOPL_INAPPROP, NULL));
2356
2357 mp = (drmach_mem_t *)id;
2358
2359 /*
2360 * This is only used by dr to round up/down the memory
2361 * for copying. Our unit of memory isolation is 64 MB.
2362 */
2363
2364 mem->mi_alignment_mask = (64 * 1024 * 1024 - 1);
2365 mem->mi_basepa = mp->base_pa;
2366 mem->mi_size = mp->nbytes;
2367 mem->mi_slice_size = mp->slice_size;
2368
2369 return (NULL);
2370 }
2371
2372 sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * pa)2373 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *pa)
2374 {
2375 drmach_mem_t *mp;
2376
2377 if (!DRMACH_IS_MEM_ID(id))
2378 return (drerr_new(0, EOPL_INAPPROP, NULL));
2379
2380 mp = (drmach_mem_t *)id;
2381
2382 *pa = mp->base_pa;
2383 return (NULL);
2384 }
2385
2386 sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)2387 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
2388 {
2389 drmach_mem_t *mem;
2390 #ifdef DEBUG
2391 int rv;
2392 #endif
2393 struct memlist *mlist;
2394
2395 if (!DRMACH_IS_MEM_ID(id))
2396 return (drerr_new(0, EOPL_INAPPROP, NULL));
2397
2398 mem = (drmach_mem_t *)id;
2399 mlist = memlist_dup(mem->memlist);
2400
2401 #ifdef DEBUG
2402 /*
2403 * Make sure the incoming memlist doesn't already
2404 * intersect with what's present in the system (phys_install).
2405 */
2406 memlist_read_lock();
2407 rv = memlist_intersect(phys_install, mlist);
2408 memlist_read_unlock();
2409 if (rv) {
2410 DRMACH_PR("Derived memlist intersects with phys_install\n");
2411 memlist_dump(mlist);
2412
2413 DRMACH_PR("phys_install memlist:\n");
2414 memlist_dump(phys_install);
2415
2416 memlist_delete(mlist);
2417 return (DRMACH_INTERNAL_ERROR());
2418 }
2419
2420 DRMACH_PR("Derived memlist:");
2421 memlist_dump(mlist);
2422 #endif
2423 *ml = mlist;
2424
2425 return (NULL);
2426 }
2427
2428 sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)2429 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
2430 {
2431 drmach_mem_t *mem;
2432
2433 if (!DRMACH_IS_MEM_ID(id))
2434 return (drerr_new(0, EOPL_INAPPROP, NULL));
2435
2436 mem = (drmach_mem_t *)id;
2437
2438 *bytes = mem->slice_size;
2439
2440 return (NULL);
2441 }
2442
2443
2444 /* ARGSUSED */
2445 processorid_t
drmach_mem_cpu_affinity(drmachid_t id)2446 drmach_mem_cpu_affinity(drmachid_t id)
2447 {
2448 return (CPU_CURRENT);
2449 }
2450
2451 static sbd_error_t *
drmach_mem_release(drmachid_t id)2452 drmach_mem_release(drmachid_t id)
2453 {
2454 if (!DRMACH_IS_MEM_ID(id))
2455 return (drerr_new(0, EOPL_INAPPROP, NULL));
2456 return (NULL);
2457 }
2458
2459 static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)2460 drmach_mem_status(drmachid_t id, drmach_status_t *stat)
2461 {
2462 drmach_mem_t *dp;
2463 uint64_t pa, slice_size;
2464 struct memlist *ml;
2465
2466 ASSERT(DRMACH_IS_MEM_ID(id));
2467 dp = id;
2468
2469 /* get starting physical address of target memory */
2470 pa = dp->base_pa;
2471
2472 /* round down to slice boundary */
2473 slice_size = dp->slice_size;
2474 pa &= ~(slice_size - 1);
2475
2476 /* stop at first span that is in slice */
2477 memlist_read_lock();
2478 for (ml = phys_install; ml; ml = ml->ml_next)
2479 if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
2480 break;
2481 memlist_read_unlock();
2482
2483 stat->assigned = dp->dev.bp->assigned;
2484 stat->powered = dp->dev.bp->powered;
2485 stat->configured = (ml != NULL);
2486 stat->busy = dp->dev.busy;
2487 (void) strncpy(stat->type, dp->dev.type, sizeof (stat->type));
2488 stat->info[0] = '\0';
2489
2490 return (NULL);
2491 }
2492
2493
2494 sbd_error_t *
drmach_board_deprobe(drmachid_t id)2495 drmach_board_deprobe(drmachid_t id)
2496 {
2497 drmach_board_t *bp;
2498
2499 if (!DRMACH_IS_BOARD_ID(id))
2500 return (drerr_new(0, EOPL_INAPPROP, NULL));
2501
2502 bp = id;
2503
2504 cmn_err(CE_CONT, "DR: detach board %d\n", bp->bnum);
2505
2506 if (bp->tree) {
2507 drmach_node_dispose(bp->tree);
2508 bp->tree = NULL;
2509 }
2510 if (bp->devices) {
2511 drmach_array_dispose(bp->devices, drmach_device_dispose);
2512 bp->devices = NULL;
2513 }
2514
2515 bp->boot_board = 0;
2516
2517 return (NULL);
2518 }
2519
2520 /*ARGSUSED*/
2521 static sbd_error_t *
drmach_pt_ikprobe(drmachid_t id,drmach_opts_t * opts)2522 drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
2523 {
2524 drmach_board_t *bp = (drmach_board_t *)id;
2525 sbd_error_t *err = NULL;
2526 int rv;
2527 unsigned cpu_impl;
2528
2529 if (!DRMACH_IS_BOARD_ID(id))
2530 return (drerr_new(0, EOPL_INAPPROP, NULL));
2531
2532 DRMACH_PR("calling opl_probe_board for bnum=%d\n", bp->bnum);
2533 rv = opl_probe_sb(bp->bnum, &cpu_impl);
2534 if (rv != 0) {
2535 err = drerr_new(1, EOPL_PROBE, bp->cm.name);
2536 return (err);
2537 }
2538 return (err);
2539 }
2540
2541 /*ARGSUSED*/
2542 static sbd_error_t *
drmach_pt_ikdeprobe(drmachid_t id,drmach_opts_t * opts)2543 drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
2544 {
2545 drmach_board_t *bp;
2546 sbd_error_t *err = NULL;
2547 int rv;
2548
2549 if (!DRMACH_IS_BOARD_ID(id))
2550 return (drerr_new(0, EOPL_INAPPROP, NULL));
2551 bp = (drmach_board_t *)id;
2552
2553 cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
2554
2555 rv = opl_unprobe_sb(bp->bnum);
2556 if (rv != 0) {
2557 err = drerr_new(1, EOPL_DEPROBE, bp->cm.name);
2558 }
2559
2560 return (err);
2561 }
2562
2563
2564 /*ARGSUSED*/
2565 sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)2566 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
2567 {
2568 struct memlist *ml;
2569 uint64_t src_pa;
2570 uint64_t dst_pa;
2571 uint64_t dst;
2572
2573 dst_pa = va_to_pa(&dst);
2574
2575 memlist_read_lock();
2576 for (ml = phys_install; ml; ml = ml->ml_next) {
2577 uint64_t nbytes;
2578
2579 src_pa = ml->ml_address;
2580 nbytes = ml->ml_size;
2581
2582 while (nbytes != 0ull) {
2583
2584 /* copy 32 bytes at arc_pa to dst_pa */
2585 bcopy32_il(src_pa, dst_pa);
2586
2587 /* increment by 32 bytes */
2588 src_pa += (4 * sizeof (uint64_t));
2589
2590 /* decrement by 32 bytes */
2591 nbytes -= (4 * sizeof (uint64_t));
2592 }
2593 }
2594 memlist_read_unlock();
2595
2596 return (NULL);
2597 }
2598
2599 static struct {
2600 const char *name;
2601 sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts);
2602 } drmach_pt_arr[] = {
2603 { "readmem", drmach_pt_readmem },
2604 { "ikprobe", drmach_pt_ikprobe },
2605 { "ikdeprobe", drmach_pt_ikdeprobe },
2606
2607 /* the following line must always be last */
2608 { NULL, NULL }
2609 };
2610
2611 /*ARGSUSED*/
2612 sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)2613 drmach_passthru(drmachid_t id, drmach_opts_t *opts)
2614 {
2615 int i;
2616 sbd_error_t *err;
2617
2618 i = 0;
2619 while (drmach_pt_arr[i].name != NULL) {
2620 int len = strlen(drmach_pt_arr[i].name);
2621
2622 if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
2623 break;
2624
2625 i += 1;
2626 }
2627
2628 if (drmach_pt_arr[i].name == NULL)
2629 err = drerr_new(0, EOPL_UNKPTCMD, opts->copts);
2630 else
2631 err = (*drmach_pt_arr[i].handler)(id, opts);
2632
2633 return (err);
2634 }
2635
2636 sbd_error_t *
drmach_release(drmachid_t id)2637 drmach_release(drmachid_t id)
2638 {
2639 drmach_common_t *cp;
2640
2641 if (!DRMACH_IS_DEVICE_ID(id))
2642 return (drerr_new(0, EOPL_INAPPROP, NULL));
2643 cp = id;
2644
2645 return (cp->release(id));
2646 }
2647
2648 sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)2649 drmach_status(drmachid_t id, drmach_status_t *stat)
2650 {
2651 drmach_common_t *cp;
2652 sbd_error_t *err;
2653
2654 rw_enter(&drmach_boards_rwlock, RW_READER);
2655
2656 if (!DRMACH_IS_ID(id)) {
2657 rw_exit(&drmach_boards_rwlock);
2658 return (drerr_new(0, EOPL_NOTID, NULL));
2659 }
2660 cp = (drmach_common_t *)id;
2661 err = cp->status(id, stat);
2662
2663 rw_exit(&drmach_boards_rwlock);
2664
2665 return (err);
2666 }
2667
2668 static sbd_error_t *
drmach_i_status(drmachid_t id,drmach_status_t * stat)2669 drmach_i_status(drmachid_t id, drmach_status_t *stat)
2670 {
2671 drmach_common_t *cp;
2672
2673 if (!DRMACH_IS_ID(id))
2674 return (drerr_new(0, EOPL_NOTID, NULL));
2675 cp = id;
2676
2677 return (cp->status(id, stat));
2678 }
2679
2680 /*ARGSUSED*/
2681 sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)2682 drmach_unconfigure(drmachid_t id, int flags)
2683 {
2684 drmach_device_t *dp;
2685 dev_info_t *rdip, *fdip = NULL;
2686 char name[OBP_MAXDRVNAME];
2687 int rv;
2688
2689 if (DRMACH_IS_CPU_ID(id))
2690 return (NULL);
2691
2692 if (!DRMACH_IS_DEVICE_ID(id))
2693 return (drerr_new(0, EOPL_INAPPROP, NULL));
2694
2695 dp = id;
2696
2697 rdip = dp->node->n_getdip(dp->node);
2698
2699 ASSERT(rdip);
2700
2701 rv = dp->node->n_getprop(dp->node, "name", name, OBP_MAXDRVNAME);
2702
2703 if (rv)
2704 return (NULL);
2705
2706 /*
2707 * Note: FORCE flag is no longer necessary under devfs
2708 */
2709
2710 ASSERT(e_ddi_branch_held(rdip));
2711 if (e_ddi_branch_unconfigure(rdip, &fdip, 0)) {
2712 sbd_error_t *err;
2713 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
2714
2715 /*
2716 * If non-NULL, fdip is returned held and must be released.
2717 */
2718 if (fdip != NULL) {
2719 (void) ddi_pathname(fdip, path);
2720 ndi_rele_devi(fdip);
2721 } else {
2722 (void) ddi_pathname(rdip, path);
2723 }
2724
2725 err = drerr_new(1, EOPL_DRVFAIL, path);
2726
2727 kmem_free(path, MAXPATHLEN);
2728
2729 return (err);
2730 }
2731
2732 return (NULL);
2733 }
2734
2735
2736 int
drmach_cpu_poweron(struct cpu * cp)2737 drmach_cpu_poweron(struct cpu *cp)
2738 {
2739 int bnum, cpuid, onb_core_num, strand_id;
2740 drmach_board_t *bp;
2741
2742 DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
2743
2744 cpuid = cp->cpu_id;
2745 bnum = LSB_ID(cpuid);
2746 onb_core_num = ON_BOARD_CORE_NUM(cpuid);
2747 strand_id = STRAND_ID(cpuid);
2748 bp = drmach_get_board_by_bnum(bnum);
2749
2750 ASSERT(bp);
2751 if (bp->cores[onb_core_num].core_hotadded == 0) {
2752 if (drmach_add_remove_cpu(bnum, onb_core_num,
2753 HOTADD_CPU) != 0) {
2754 cmn_err(CE_WARN, "Failed to add CMP %d on board %d\n",
2755 onb_core_num, bnum);
2756 return (EIO);
2757 }
2758 }
2759
2760 ASSERT(MUTEX_HELD(&cpu_lock));
2761
2762 if (drmach_cpu_start(cp) != 0) {
2763 if (bp->cores[onb_core_num].core_started == 0) {
2764 /*
2765 * we must undo the hotadd or no one will do that
2766 * If this fails, we will do this again in
2767 * drmach_board_disconnect.
2768 */
2769 if (drmach_add_remove_cpu(bnum, onb_core_num,
2770 HOTREMOVE_CPU) != 0) {
2771 cmn_err(CE_WARN, "Failed to remove CMP %d "
2772 "on board %d\n", onb_core_num, bnum);
2773 }
2774 }
2775 return (EBUSY);
2776 } else {
2777 bp->cores[onb_core_num].core_started |= (1 << strand_id);
2778 return (0);
2779 }
2780 }
2781
2782 int
drmach_cpu_poweroff(struct cpu * cp)2783 drmach_cpu_poweroff(struct cpu *cp)
2784 {
2785 int rv = 0;
2786 processorid_t cpuid = cp->cpu_id;
2787
2788 DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
2789
2790 ASSERT(MUTEX_HELD(&cpu_lock));
2791
2792 /*
2793 * Capture all CPUs (except for detaching proc) to prevent
2794 * crosscalls to the detaching proc until it has cleared its
2795 * bit in cpu_ready_set.
2796 *
2797 * The CPU's remain paused and the prom_mutex is known to be free.
2798 * This prevents the x-trap victim from blocking when doing prom
2799 * IEEE-1275 calls at a high PIL level.
2800 */
2801
2802 promsafe_pause_cpus();
2803
2804 /*
2805 * Quiesce interrupts on the target CPU. We do this by setting
2806 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
2807 * prevent it from receiving cross calls and cross traps.
2808 * This prevents the processor from receiving any new soft interrupts.
2809 */
2810 mp_cpu_quiesce(cp);
2811
2812 rv = prom_stopcpu_bycpuid(cpuid);
2813 if (rv == 0)
2814 cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
2815
2816 start_cpus();
2817
2818 if (rv == 0) {
2819 int bnum, onb_core_num, strand_id;
2820 drmach_board_t *bp;
2821
2822 CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
2823
2824 bnum = LSB_ID(cpuid);
2825 onb_core_num = ON_BOARD_CORE_NUM(cpuid);
2826 strand_id = STRAND_ID(cpuid);
2827 bp = drmach_get_board_by_bnum(bnum);
2828 ASSERT(bp);
2829
2830 bp->cores[onb_core_num].core_started &= ~(1 << strand_id);
2831 if (bp->cores[onb_core_num].core_started == 0) {
2832 if (drmach_add_remove_cpu(bnum, onb_core_num,
2833 HOTREMOVE_CPU) != 0) {
2834 cmn_err(CE_WARN, "Failed to remove CMP %d LSB "
2835 "%d\n", onb_core_num, bnum);
2836 return (EIO);
2837 }
2838 }
2839 }
2840
2841 return (rv);
2842 }
2843
2844 /*ARGSUSED*/
2845 int
drmach_verify_sr(dev_info_t * dip,int sflag)2846 drmach_verify_sr(dev_info_t *dip, int sflag)
2847 {
2848 return (0);
2849 }
2850
2851 void
drmach_suspend_last(void)2852 drmach_suspend_last(void)
2853 {
2854 }
2855
2856 void
drmach_resume_first(void)2857 drmach_resume_first(void)
2858 {
2859 }
2860
2861 /*
2862 * Log a DR sysevent.
2863 * Return value: 0 success, non-zero failure.
2864 */
2865 int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)2866 drmach_log_sysevent(int board, char *hint, int flag, int verbose)
2867 {
2868 sysevent_t *ev;
2869 sysevent_id_t eid;
2870 int rv, km_flag;
2871 sysevent_value_t evnt_val;
2872 sysevent_attr_list_t *evnt_attr_list = NULL;
2873 char attach_pnt[MAXNAMELEN];
2874
2875 km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
2876 attach_pnt[0] = '\0';
2877 if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
2878 rv = -1;
2879 goto logexit;
2880 }
2881 if (verbose) {
2882 DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
2883 attach_pnt, hint, flag, verbose);
2884 }
2885
2886 if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
2887 SUNW_KERN_PUB"dr", km_flag)) == NULL) {
2888 rv = -2;
2889 goto logexit;
2890 }
2891 evnt_val.value_type = SE_DATA_TYPE_STRING;
2892 evnt_val.value.sv_string = attach_pnt;
2893 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
2894 km_flag)) != 0)
2895 goto logexit;
2896
2897 evnt_val.value_type = SE_DATA_TYPE_STRING;
2898 evnt_val.value.sv_string = hint;
2899 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
2900 km_flag)) != 0) {
2901 sysevent_free_attr(evnt_attr_list);
2902 goto logexit;
2903 }
2904
2905 (void) sysevent_attach_attributes(ev, evnt_attr_list);
2906
2907 /*
2908 * Log the event but do not sleep waiting for its
2909 * delivery. This provides insulation from syseventd.
2910 */
2911 rv = log_sysevent(ev, SE_NOSLEEP, &eid);
2912
2913 logexit:
2914 if (ev)
2915 sysevent_free(ev);
2916 if ((rv != 0) && verbose)
2917 cmn_err(CE_WARN, "drmach_log_sysevent failed (rv %d) for %s "
2918 " %s\n", rv, attach_pnt, hint);
2919
2920 return (rv);
2921 }
2922
2923 #define OPL_DR_STATUS_PROP "dr-status"
2924
2925 static int
opl_check_dr_status()2926 opl_check_dr_status()
2927 {
2928 pnode_t node;
2929 int rtn, len;
2930 char *str;
2931
2932 node = prom_rootnode();
2933 if (node == OBP_BADNODE) {
2934 return (1);
2935 }
2936
2937 len = prom_getproplen(node, OPL_DR_STATUS_PROP);
2938 if (len == -1) {
2939 /*
2940 * dr-status doesn't exist when DR is activated and
2941 * any warning messages aren't needed.
2942 */
2943 return (1);
2944 }
2945
2946 str = (char *)kmem_zalloc(len+1, KM_SLEEP);
2947 rtn = prom_getprop(node, OPL_DR_STATUS_PROP, str);
2948 kmem_free(str, len + 1);
2949 if (rtn == -1) {
2950 return (1);
2951 } else {
2952 return (0);
2953 }
2954 }
2955
2956 /* we are allocating memlist from TLB locked pages to avoid tlbmisses */
2957
2958 static struct memlist *
drmach_memlist_add_span(drmach_copy_rename_program_t * p,struct memlist * mlist,uint64_t base,uint64_t len)2959 drmach_memlist_add_span(drmach_copy_rename_program_t *p,
2960 struct memlist *mlist, uint64_t base, uint64_t len)
2961 {
2962 struct memlist *ml, *tl, *nl;
2963
2964 if (len == 0ull)
2965 return (NULL);
2966
2967 if (mlist == NULL) {
2968 mlist = p->free_mlist;
2969 if (mlist == NULL)
2970 return (NULL);
2971 p->free_mlist = mlist->ml_next;
2972 mlist->ml_address = base;
2973 mlist->ml_size = len;
2974 mlist->ml_next = mlist->ml_prev = NULL;
2975
2976 return (mlist);
2977 }
2978
2979 for (tl = ml = mlist; ml; tl = ml, ml = ml->ml_next) {
2980 if (base < ml->ml_address) {
2981 if ((base + len) < ml->ml_address) {
2982 nl = p->free_mlist;
2983 if (nl == NULL)
2984 return (NULL);
2985 p->free_mlist = nl->ml_next;
2986 nl->ml_address = base;
2987 nl->ml_size = len;
2988 nl->ml_next = ml;
2989 if ((nl->ml_prev = ml->ml_prev) != NULL)
2990 nl->ml_prev->ml_next = nl;
2991 ml->ml_prev = nl;
2992 if (mlist == ml)
2993 mlist = nl;
2994 } else {
2995 ml->ml_size = MAX((base + len),
2996 (ml->ml_address + ml->ml_size)) - base;
2997 ml->ml_address = base;
2998 }
2999 break;
3000
3001 } else if (base <= (ml->ml_address + ml->ml_size)) {
3002 ml->ml_size =
3003 MAX((base + len), (ml->ml_address + ml->ml_size)) -
3004 MIN(ml->ml_address, base);
3005 ml->ml_address = MIN(ml->ml_address, base);
3006 break;
3007 }
3008 }
3009 if (ml == NULL) {
3010 nl = p->free_mlist;
3011 if (nl == NULL)
3012 return (NULL);
3013 p->free_mlist = nl->ml_next;
3014 nl->ml_address = base;
3015 nl->ml_size = len;
3016 nl->ml_next = NULL;
3017 nl->ml_prev = tl;
3018 tl->ml_next = nl;
3019 }
3020
3021 return (mlist);
3022 }
3023
3024 /*
3025 * The routine performs the necessary memory COPY and MC adr SWITCH.
3026 * Both operations MUST be at the same "level" so that the stack is
3027 * maintained correctly between the copy and switch. The switch
3028 * portion implements a caching mechanism to guarantee the code text
3029 * is cached prior to execution. This is to guard against possible
3030 * memory access while the MC adr's are being modified.
3031 *
3032 * IMPORTANT: The _drmach_copy_rename_end() function must immediately
3033 * follow drmach_copy_rename_prog__relocatable() so that the correct
3034 * "length" of the drmach_copy_rename_prog__relocatable can be
3035 * calculated. This routine MUST be a LEAF function, i.e. it can
3036 * make NO function calls, primarily for two reasons:
3037 *
3038 * 1. We must keep the stack consistent across the "switch".
3039 * 2. Function calls are compiled to relative offsets, and
3040 * we execute this function we'll be executing it from
3041 * a copied version in a different area of memory, thus
3042 * the relative offsets will be bogus.
3043 *
3044 * Moreover, it must have the "__relocatable" suffix to inform DTrace
3045 * providers (and anything else, for that matter) that this
3046 * function's text is manually relocated elsewhere before it is
3047 * executed. That is, it cannot be safely instrumented with any
3048 * methodology that is PC-relative.
3049 */
3050
3051 /*
3052 * We multiply this to system_clock_frequency so we
3053 * are setting a delay of fmem_timeout second for
3054 * the rename command.
3055 *
3056 * FMEM command itself should complete within 15 sec.
3057 * We add 2 more sec to be conservative.
3058 *
3059 * Note that there is also a SCF BUSY bit checking
3060 * in drmach_asm.s right before FMEM command is
3061 * issued. XSCF sets the SCF BUSY bit when the
3062 * other domain on the same PSB reboots and it
3063 * will not be able to service the FMEM command
3064 * within 15 sec. After setting the SCF BUSY
3065 * bit, XSCF will wait a while before servicing
3066 * other reboot command so there is no race
3067 * condition.
3068 */
3069
3070 static int fmem_timeout = 17;
3071
3072 /*
3073 * The empirical data on some OPL system shows that
3074 * we can copy 250 MB per second. We set it to
3075 * 80 MB to be conservative. In normal case,
3076 * this timeout does not affect anything.
3077 */
3078
3079 static int min_copy_size_per_sec = 80 * 1024 * 1024;
3080
3081 /*
3082 * This is the timeout value for the xcall synchronization
3083 * to get all the CPU ready to do the parallel copying.
3084 * Even on a fully loaded system, 10 sec. should be long
3085 * enough.
3086 */
3087
3088 static int cpu_xcall_delay = 10;
3089 int drmach_disable_mcopy = 0;
3090
3091 /*
3092 * The following delay loop executes sleep instruction to yield the
3093 * CPU to other strands. If this is not done, some strand will tie
3094 * up the CPU in busy loops while the other strand cannot do useful
3095 * work. The copy procedure will take a much longer time without this.
3096 */
3097 #define DR_DELAY_IL(ms, freq) \
3098 { \
3099 uint64_t start; \
3100 uint64_t nstick; \
3101 volatile uint64_t now; \
3102 nstick = ((uint64_t)ms * freq)/1000; \
3103 start = drmach_get_stick_il(); \
3104 now = start; \
3105 while ((now - start) <= nstick) { \
3106 drmach_sleep_il(); \
3107 now = drmach_get_stick_il(); \
3108 } \
3109 }
3110
3111 /* Each loop is 2ms, timeout at 1000ms */
3112 static int drmach_copy_rename_timeout = 500;
3113
3114 static int
drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t * prog,int cpuid)3115 drmach_copy_rename_prog__relocatable(drmach_copy_rename_program_t *prog,
3116 int cpuid)
3117 {
3118 struct memlist *ml;
3119 register int rtn;
3120 int i;
3121 register uint64_t curr, limit;
3122 extern uint64_t drmach_get_stick_il();
3123 extern void membar_sync_il();
3124 extern void flush_instr_mem_il(void*);
3125 extern void flush_windows_il(void);
3126 uint64_t copy_start;
3127
3128 /*
3129 * flush_windows is moved here to make sure all
3130 * registers used in the callers are flushed to
3131 * memory before the copy.
3132 *
3133 * If flush_windows() is called too early in the
3134 * calling function, the compiler might put some
3135 * data in the local registers after flush_windows().
3136 * After FMA, if there is any fill trap, the registers
3137 * will contain stale data.
3138 */
3139
3140 flush_windows_il();
3141
3142 prog->critical->stat[cpuid] = FMEM_LOOP_COPY_READY;
3143 membar_sync_il();
3144
3145 if (prog->data->cpuid == cpuid) {
3146 limit = drmach_get_stick_il();
3147 limit += cpu_xcall_delay * system_clock_freq;
3148 for (i = 0; i < NCPU; i++) {
3149 if (CPU_IN_SET(prog->data->cpu_slave_set, i)) {
3150 /* wait for all CPU's to be ready */
3151 for (;;) {
3152 if (prog->critical->stat[i] ==
3153 FMEM_LOOP_COPY_READY) {
3154 break;
3155 }
3156 DR_DELAY_IL(1, prog->data->stick_freq);
3157 }
3158 curr = drmach_get_stick_il();
3159 if (curr > limit) {
3160 prog->data->fmem_status.error =
3161 EOPL_FMEM_XC_TIMEOUT;
3162 return (EOPL_FMEM_XC_TIMEOUT);
3163 }
3164 }
3165 }
3166 prog->data->fmem_status.stat = FMEM_LOOP_COPY_READY;
3167 membar_sync_il();
3168 copy_start = drmach_get_stick_il();
3169 } else {
3170 for (;;) {
3171 if (prog->data->fmem_status.stat ==
3172 FMEM_LOOP_COPY_READY) {
3173 break;
3174 }
3175 if (prog->data->fmem_status.error) {
3176 prog->data->error[cpuid] = EOPL_FMEM_TERMINATE;
3177 return (EOPL_FMEM_TERMINATE);
3178 }
3179 DR_DELAY_IL(1, prog->data->stick_freq);
3180 }
3181 }
3182
3183 /*
3184 * DO COPY.
3185 */
3186 if (CPU_IN_SET(prog->data->cpu_copy_set, cpuid)) {
3187 for (ml = prog->data->cpu_ml[cpuid]; ml; ml = ml->ml_next) {
3188 uint64_t s_pa, t_pa;
3189 uint64_t nbytes;
3190
3191 s_pa = prog->data->s_copybasepa + ml->ml_address;
3192 t_pa = prog->data->t_copybasepa + ml->ml_address;
3193 nbytes = ml->ml_size;
3194
3195 while (nbytes != 0ull) {
3196 /*
3197 * If the master has detected error, we just
3198 * bail out
3199 */
3200 if (prog->data->fmem_status.error !=
3201 ESBD_NOERROR) {
3202 prog->data->error[cpuid] =
3203 EOPL_FMEM_TERMINATE;
3204 return (EOPL_FMEM_TERMINATE);
3205 }
3206 /*
3207 * This copy does NOT use an ASI
3208 * that avoids the Ecache, therefore
3209 * the dst_pa addresses may remain
3210 * in our Ecache after the dst_pa
3211 * has been removed from the system.
3212 * A subsequent write-back to memory
3213 * will cause an ARB-stop because the
3214 * physical address no longer exists
3215 * in the system. Therefore we must
3216 * flush out local Ecache after we
3217 * finish the copy.
3218 */
3219
3220 /* copy 32 bytes at src_pa to dst_pa */
3221 bcopy32_il(s_pa, t_pa);
3222
3223 /*
3224 * increment the counter to signal that we are
3225 * alive
3226 */
3227 prog->stat->nbytes[cpuid] += 32;
3228
3229 /* increment by 32 bytes */
3230 s_pa += (4 * sizeof (uint64_t));
3231 t_pa += (4 * sizeof (uint64_t));
3232
3233 /* decrement by 32 bytes */
3234 nbytes -= (4 * sizeof (uint64_t));
3235 }
3236 }
3237 prog->critical->stat[cpuid] = FMEM_LOOP_COPY_DONE;
3238 membar_sync_il();
3239 }
3240
3241 /*
3242 * Since bcopy32_il() does NOT use an ASI to bypass
3243 * the Ecache, we need to flush our Ecache after
3244 * the copy is complete.
3245 */
3246 flush_cache_il();
3247
3248 /*
3249 * drmach_fmem_exec_script()
3250 */
3251 if (prog->data->cpuid == cpuid) {
3252 uint64_t last, now;
3253
3254 limit = copy_start + prog->data->copy_delay;
3255 for (i = 0; i < NCPU; i++) {
3256 if (!CPU_IN_SET(prog->data->cpu_slave_set, i))
3257 continue;
3258
3259 for (;;) {
3260 /*
3261 * we get FMEM_LOOP_FMEM_READY in
3262 * normal case
3263 */
3264 if (prog->critical->stat[i] ==
3265 FMEM_LOOP_FMEM_READY) {
3266 break;
3267 }
3268 /* got error traps */
3269 if (prog->data->error[i] ==
3270 EOPL_FMEM_COPY_ERROR) {
3271 prog->data->fmem_status.error =
3272 EOPL_FMEM_COPY_ERROR;
3273 return (EOPL_FMEM_COPY_ERROR);
3274 }
3275 /*
3276 * if we have not reached limit, wait
3277 * more
3278 */
3279 curr = drmach_get_stick_il();
3280 if (curr <= limit)
3281 continue;
3282
3283 prog->data->slowest_cpuid = i;
3284 prog->data->copy_wait_time = curr - copy_start;
3285
3286 /* now check if slave is alive */
3287 last = prog->stat->nbytes[i];
3288
3289 DR_DELAY_IL(1, prog->data->stick_freq);
3290
3291 now = prog->stat->nbytes[i];
3292 if (now <= last) {
3293 /*
3294 * no progress, perhaps just
3295 * finished
3296 */
3297 DR_DELAY_IL(1, prog->data->stick_freq);
3298 if (prog->critical->stat[i] ==
3299 FMEM_LOOP_FMEM_READY)
3300 break;
3301 /* copy error */
3302 if (prog->data->error[i] ==
3303 EOPL_FMEM_COPY_ERROR) {
3304 prog->data-> fmem_status.error =
3305 EOPL_FMEM_COPY_ERROR;
3306 return (EOPL_FMEM_COPY_ERROR);
3307 }
3308
3309 prog->data->copy_rename_count++;
3310 if (prog->data->copy_rename_count
3311 < drmach_copy_rename_timeout) {
3312 continue;
3313 } else {
3314 prog->data->fmem_status.error =
3315 EOPL_FMEM_COPY_TIMEOUT;
3316 return (EOPL_FMEM_COPY_TIMEOUT);
3317 }
3318 }
3319 }
3320 }
3321
3322 prog->critical->stat[cpuid] = FMEM_LOOP_FMEM_READY;
3323 prog->data->fmem_status.stat = FMEM_LOOP_FMEM_READY;
3324
3325 membar_sync_il();
3326 flush_instr_mem_il((void*) (prog->critical));
3327 /*
3328 * drmach_fmem_exec_script()
3329 */
3330 rtn = prog->critical->fmem((void *)prog->critical, PAGESIZE);
3331 return (rtn);
3332 } else {
3333 flush_instr_mem_il((void*) (prog->critical));
3334 /*
3335 * drmach_fmem_loop_script()
3336 */
3337 rtn = prog->critical->loop((void *)(prog->critical), PAGESIZE,
3338 (void *)&(prog->critical->stat[cpuid]));
3339 prog->data->error[cpuid] = rtn;
3340 /* slave thread does not care the rv */
3341 return (0);
3342 }
3343 }
3344
3345 static void
drmach_copy_rename_end(void)3346 drmach_copy_rename_end(void)
3347 {
3348 /*
3349 * IMPORTANT: This function's location MUST be located immediately
3350 * following drmach_copy_rename_prog__relocatable to
3351 * accurately estimate its size. Note that this assumes
3352 * the compiler keeps these functions in the order in
3353 * which they appear :-o
3354 */
3355 }
3356
3357
3358 static int
drmach_setup_memlist(drmach_copy_rename_program_t * p)3359 drmach_setup_memlist(drmach_copy_rename_program_t *p)
3360 {
3361 struct memlist *ml;
3362 caddr_t buf;
3363 int nbytes, s, n_elements;
3364
3365 nbytes = PAGESIZE;
3366 n_elements = 0;
3367 s = roundup(sizeof (struct memlist), sizeof (void *));
3368 p->free_mlist = NULL;
3369 buf = p->memlist_buffer;
3370 while (nbytes >= sizeof (struct memlist)) {
3371 ml = (struct memlist *)buf;
3372 ml->ml_next = p->free_mlist;
3373 p->free_mlist = ml;
3374 buf += s;
3375 n_elements++;
3376 nbytes -= s;
3377 }
3378 return (n_elements);
3379 }
3380
3381 static void
drmach_lock_critical(caddr_t va,caddr_t new_va)3382 drmach_lock_critical(caddr_t va, caddr_t new_va)
3383 {
3384 tte_t tte;
3385 int i;
3386
3387 kpreempt_disable();
3388
3389 for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
3390 vtag_flushpage(new_va, (uint64_t)ksfmmup);
3391 sfmmu_memtte(&tte, va_to_pfn(va), PROC_DATA|HAT_NOSYNC, TTE8K);
3392 tte.tte_intlo |= TTE_LCK_INT;
3393 sfmmu_dtlb_ld_kva(new_va, &tte);
3394 sfmmu_itlb_ld_kva(new_va, &tte);
3395 va += PAGESIZE;
3396 new_va += PAGESIZE;
3397 }
3398 }
3399
3400 static void
drmach_unlock_critical(caddr_t va)3401 drmach_unlock_critical(caddr_t va)
3402 {
3403 int i;
3404
3405 for (i = 0; i < DRMACH_FMEM_LOCKED_PAGES; i++) {
3406 vtag_flushpage(va, (uint64_t)ksfmmup);
3407 va += PAGESIZE;
3408 }
3409
3410 kpreempt_enable();
3411 }
3412
3413 sbd_error_t *
drmach_copy_rename_init(drmachid_t t_id,drmachid_t s_id,struct memlist * c_ml,drmachid_t * pgm_id)3414 drmach_copy_rename_init(drmachid_t t_id, drmachid_t s_id,
3415 struct memlist *c_ml, drmachid_t *pgm_id)
3416 {
3417 drmach_mem_t *s_mem;
3418 drmach_mem_t *t_mem;
3419 struct memlist *x_ml;
3420 uint64_t s_copybasepa, t_copybasepa;
3421 uint_t len;
3422 caddr_t bp, wp;
3423 int s_bd, t_bd, cpuid, active_cpus, i;
3424 int max_elms, mlist_size, rv;
3425 uint64_t c_addr;
3426 size_t c_size, copy_sz, sz;
3427 extern void drmach_fmem_loop_script();
3428 extern void drmach_fmem_loop_script_rtn();
3429 extern int drmach_fmem_exec_script();
3430 extern void drmach_fmem_exec_script_end();
3431 sbd_error_t *err;
3432 drmach_copy_rename_program_t *prog = NULL;
3433 drmach_copy_rename_program_t *prog_kmem = NULL;
3434 void (*mc_suspend)(void);
3435 void (*mc_resume)(void);
3436 int (*scf_fmem_start)(int, int);
3437 int (*scf_fmem_end)(void);
3438 int (*scf_fmem_cancel)(void);
3439 uint64_t (*scf_get_base_addr)(void);
3440
3441 if (!DRMACH_IS_MEM_ID(s_id))
3442 return (drerr_new(0, EOPL_INAPPROP, NULL));
3443 if (!DRMACH_IS_MEM_ID(t_id))
3444 return (drerr_new(0, EOPL_INAPPROP, NULL));
3445
3446 for (i = 0; i < NCPU; i++) {
3447 int lsb_id, onb_core_num, strand_id;
3448 drmach_board_t *bp;
3449
3450 /*
3451 * this kind of CPU will spin in cache
3452 */
3453 if (CPU_IN_SET(cpu_ready_set, i))
3454 continue;
3455
3456 /*
3457 * Now check for any inactive CPU's that
3458 * have been hotadded. This can only occur in
3459 * error condition in drmach_cpu_poweron().
3460 */
3461 lsb_id = LSB_ID(i);
3462 onb_core_num = ON_BOARD_CORE_NUM(i);
3463 strand_id = STRAND_ID(i);
3464 bp = drmach_get_board_by_bnum(lsb_id);
3465 if (bp == NULL)
3466 continue;
3467 if (bp->cores[onb_core_num].core_hotadded &
3468 (1 << strand_id)) {
3469 if (!(bp->cores[onb_core_num].core_started &
3470 (1 << strand_id))) {
3471 return (drerr_new(1, EOPL_CPU_STATE, NULL));
3472 }
3473 }
3474 }
3475
3476 mc_suspend = (void (*)(void))
3477 modgetsymvalue("opl_mc_suspend", 0);
3478 mc_resume = (void (*)(void))
3479 modgetsymvalue("opl_mc_resume", 0);
3480
3481 if (mc_suspend == NULL || mc_resume == NULL) {
3482 return (drerr_new(1, EOPL_MC_OPL, NULL));
3483 }
3484
3485 scf_fmem_start = (int (*)(int, int))
3486 modgetsymvalue("scf_fmem_start", 0);
3487 if (scf_fmem_start == NULL) {
3488 return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3489 }
3490 scf_fmem_end = (int (*)(void))
3491 modgetsymvalue("scf_fmem_end", 0);
3492 if (scf_fmem_end == NULL) {
3493 return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3494 }
3495 scf_fmem_cancel = (int (*)(void))
3496 modgetsymvalue("scf_fmem_cancel", 0);
3497 if (scf_fmem_cancel == NULL) {
3498 return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3499 }
3500 scf_get_base_addr = (uint64_t (*)(void))
3501 modgetsymvalue("scf_get_base_addr", 0);
3502 if (scf_get_base_addr == NULL) {
3503 return (drerr_new(1, EOPL_SCF_FMEM, NULL));
3504 }
3505 s_mem = s_id;
3506 t_mem = t_id;
3507
3508 s_bd = s_mem->dev.bp->bnum;
3509 t_bd = t_mem->dev.bp->bnum;
3510
3511 /* calculate source and target base pa */
3512
3513 s_copybasepa = s_mem->slice_base;
3514 t_copybasepa = t_mem->slice_base;
3515
3516 /* adjust copy memlist addresses to be relative to copy base pa */
3517 x_ml = c_ml;
3518 mlist_size = 0;
3519 while (x_ml != NULL) {
3520 x_ml->ml_address -= s_copybasepa;
3521 x_ml = x_ml->ml_next;
3522 mlist_size++;
3523 }
3524
3525 /*
3526 * bp will be page aligned, since we're calling
3527 * kmem_zalloc() with an exact multiple of PAGESIZE.
3528 */
3529
3530 prog_kmem = (drmach_copy_rename_program_t *)kmem_zalloc(
3531 DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, KM_SLEEP);
3532
3533 prog_kmem->prog = prog_kmem;
3534
3535 /*
3536 * To avoid MTLB hit, we allocate a new VM space and remap
3537 * the kmem_alloc buffer to that address. This solves
3538 * 2 problems we found:
3539 * - the kmem_alloc buffer can be just a chunk inside
3540 * a much larger, e.g. 4MB buffer and MTLB will occur
3541 * if there are both a 4MB and a 8K TLB mapping to
3542 * the same VA range.
3543 * - the kmem mapping got dropped into the TLB by other
3544 * strands, unintentionally.
3545 * Note that the pointers like data, critical, memlist_buffer,
3546 * and stat inside the copy rename structure are mapped to this
3547 * alternate VM space so we must make sure we lock the TLB mapping
3548 * whenever we access data pointed to by these pointers.
3549 */
3550
3551 prog = prog_kmem->locked_prog = vmem_alloc(heap_arena,
3552 DRMACH_FMEM_LOCKED_PAGES * PAGESIZE, VM_SLEEP);
3553 wp = bp = (caddr_t)prog;
3554
3555 /* Now remap prog_kmem to prog */
3556 drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
3557
3558 /* All pointers in prog are based on the alternate mapping */
3559 prog->data = (drmach_copy_rename_data_t *)roundup(((uint64_t)prog +
3560 sizeof (drmach_copy_rename_program_t)), sizeof (void *));
3561
3562 ASSERT(((uint64_t)prog->data + sizeof (drmach_copy_rename_data_t))
3563 <= ((uint64_t)prog + PAGESIZE));
3564
3565 prog->critical = (drmach_copy_rename_critical_t *)
3566 (wp + DRMACH_FMEM_CRITICAL_PAGE * PAGESIZE);
3567
3568 prog->memlist_buffer = (caddr_t)(wp + DRMACH_FMEM_MLIST_PAGE *
3569 PAGESIZE);
3570
3571 prog->stat = (drmach_cr_stat_t *)(wp + DRMACH_FMEM_STAT_PAGE *
3572 PAGESIZE);
3573
3574 /* LINTED */
3575 ASSERT(sizeof (drmach_cr_stat_t) <= ((DRMACH_FMEM_LOCKED_PAGES -
3576 DRMACH_FMEM_STAT_PAGE) * PAGESIZE));
3577
3578 prog->critical->scf_reg_base = (uint64_t)-1;
3579 prog->critical->scf_td[0] = (s_bd & 0xff);
3580 prog->critical->scf_td[1] = (t_bd & 0xff);
3581 for (i = 2; i < 15; i++) {
3582 prog->critical->scf_td[i] = 0;
3583 }
3584 prog->critical->scf_td[15] = ((0xaa + s_bd + t_bd) & 0xff);
3585
3586 bp = (caddr_t)prog->critical;
3587 len = sizeof (drmach_copy_rename_critical_t);
3588 wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
3589
3590 len = (uint_t)((ulong_t)drmach_copy_rename_end -
3591 (ulong_t)drmach_copy_rename_prog__relocatable);
3592
3593 /*
3594 * We always leave 1K nop's to prevent the processor from
3595 * speculative execution that causes memory access
3596 */
3597 wp = wp + len + 1024;
3598
3599 len = (uint_t)((ulong_t)drmach_fmem_exec_script_end -
3600 (ulong_t)drmach_fmem_exec_script);
3601 /* this is the entry point of the loop script */
3602 wp = wp + len + 1024;
3603
3604 len = (uint_t)((ulong_t)drmach_fmem_exec_script -
3605 (ulong_t)drmach_fmem_loop_script);
3606 wp = wp + len + 1024;
3607
3608 /* now we make sure there is 1K extra */
3609
3610 if ((wp - bp) > PAGESIZE) {
3611 err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
3612 goto out;
3613 }
3614
3615 bp = (caddr_t)prog->critical;
3616 len = sizeof (drmach_copy_rename_critical_t);
3617 wp = (caddr_t)roundup((uint64_t)bp + len, sizeof (void *));
3618
3619 prog->critical->run = (int (*)())(wp);
3620 len = (uint_t)((ulong_t)drmach_copy_rename_end -
3621 (ulong_t)drmach_copy_rename_prog__relocatable);
3622
3623 bcopy((caddr_t)drmach_copy_rename_prog__relocatable, wp, len);
3624
3625 wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
3626
3627 prog->critical->fmem = (int (*)())(wp);
3628 len = (int)((ulong_t)drmach_fmem_exec_script_end -
3629 (ulong_t)drmach_fmem_exec_script);
3630 bcopy((caddr_t)drmach_fmem_exec_script, wp, len);
3631
3632 len = (int)((ulong_t)drmach_fmem_exec_script_end -
3633 (ulong_t)drmach_fmem_exec_script);
3634 wp = (caddr_t)roundup((uint64_t)wp + len, 1024);
3635
3636 prog->critical->loop = (int (*)())(wp);
3637 len = (int)((ulong_t)drmach_fmem_exec_script -
3638 (ulong_t)drmach_fmem_loop_script);
3639 bcopy((caddr_t)drmach_fmem_loop_script, (void *)wp, len);
3640 len = (int)((ulong_t)drmach_fmem_loop_script_rtn-
3641 (ulong_t)drmach_fmem_loop_script);
3642 prog->critical->loop_rtn = (void (*)()) (wp+len);
3643
3644 prog->data->fmem_status.error = ESBD_NOERROR;
3645
3646 /* now we are committed, call SCF, soft suspend mac patrol */
3647 if ((*scf_fmem_start)(s_bd, t_bd)) {
3648 err = drerr_new(1, EOPL_SCF_FMEM_START, NULL);
3649 goto out;
3650 }
3651 prog->data->scf_fmem_end = scf_fmem_end;
3652 prog->data->scf_fmem_cancel = scf_fmem_cancel;
3653 prog->data->scf_get_base_addr = scf_get_base_addr;
3654 prog->data->fmem_status.op |= OPL_FMEM_SCF_START;
3655
3656 /* soft suspend mac patrol */
3657 (*mc_suspend)();
3658 prog->data->fmem_status.op |= OPL_FMEM_MC_SUSPEND;
3659 prog->data->mc_resume = mc_resume;
3660
3661 prog->critical->inst_loop_ret =
3662 *(uint64_t *)(prog->critical->loop_rtn);
3663
3664 /*
3665 * 0x30800000 is op code "ba,a +0"
3666 */
3667
3668 *(uint_t *)(prog->critical->loop_rtn) = (uint_t)(0x30800000);
3669
3670 /*
3671 * set the value of SCF FMEM TIMEOUT
3672 */
3673 prog->critical->delay = fmem_timeout * system_clock_freq;
3674
3675 prog->data->s_mem = (drmachid_t)s_mem;
3676 prog->data->t_mem = (drmachid_t)t_mem;
3677
3678 cpuid = CPU->cpu_id;
3679 prog->data->cpuid = cpuid;
3680 prog->data->cpu_ready_set = cpu_ready_set;
3681 prog->data->cpu_slave_set = cpu_ready_set;
3682 prog->data->slowest_cpuid = (processorid_t)-1;
3683 prog->data->copy_wait_time = 0;
3684 prog->data->copy_rename_count = 0;
3685 CPUSET_DEL(prog->data->cpu_slave_set, cpuid);
3686
3687 for (i = 0; i < NCPU; i++) {
3688 prog->data->cpu_ml[i] = NULL;
3689 }
3690
3691 /*
3692 * max_elms - max number of memlist structures that
3693 * may be allocated for the CPU memory list.
3694 * If there are too many memory span (because
3695 * of fragmentation) than number of memlist
3696 * available, we should return error.
3697 */
3698 max_elms = drmach_setup_memlist(prog);
3699 if (max_elms < mlist_size) {
3700 err = drerr_new(1, EOPL_FMEM_SETUP, NULL);
3701 goto err_out;
3702 }
3703
3704 active_cpus = 0;
3705 if (drmach_disable_mcopy) {
3706 active_cpus = 1;
3707 CPUSET_ADD(prog->data->cpu_copy_set, cpuid);
3708 } else {
3709 int max_cpu_num;
3710 /*
3711 * The parallel copy procedure is going to split some
3712 * of the elements of the original memory copy list.
3713 * The number of added elements can be up to
3714 * (max_cpu_num - 1). It means that max_cpu_num
3715 * should satisfy the following condition:
3716 * (max_cpu_num - 1) + mlist_size <= max_elms.
3717 */
3718 max_cpu_num = max_elms - mlist_size + 1;
3719
3720 for (i = 0; i < NCPU; i++) {
3721 if (CPU_IN_SET(cpu_ready_set, i) &&
3722 CPU_ACTIVE(cpu[i])) {
3723 /*
3724 * To reduce the level-2 cache contention only
3725 * one strand per core will participate
3726 * in the copy. If the strand with even cpu_id
3727 * number is present in the ready set, we will
3728 * include this strand in the copy set. If it
3729 * is not present in the ready set, we check for
3730 * the strand with the consecutive odd cpu_id
3731 * and include it, provided that it is
3732 * present in the ready set.
3733 */
3734 if (!(i & 0x1) ||
3735 !CPU_IN_SET(prog->data->cpu_copy_set,
3736 i - 1)) {
3737 CPUSET_ADD(prog->data->cpu_copy_set, i);
3738 active_cpus++;
3739 /*
3740 * We cannot have more than
3741 * max_cpu_num CPUs in the copy
3742 * set, because each CPU has to
3743 * have at least one element
3744 * long memory copy list.
3745 */
3746 if (active_cpus >= max_cpu_num)
3747 break;
3748
3749 }
3750 }
3751 }
3752 }
3753
3754 x_ml = c_ml;
3755 sz = 0;
3756 while (x_ml != NULL) {
3757 sz += x_ml->ml_size;
3758 x_ml = x_ml->ml_next;
3759 }
3760
3761 copy_sz = sz/active_cpus;
3762 copy_sz = roundup(copy_sz, MMU_PAGESIZE4M);
3763
3764 while (sz > copy_sz*active_cpus) {
3765 copy_sz += MMU_PAGESIZE4M;
3766 }
3767
3768 prog->data->stick_freq = system_clock_freq;
3769 prog->data->copy_delay = ((copy_sz / min_copy_size_per_sec) + 2) *
3770 system_clock_freq;
3771
3772 x_ml = c_ml;
3773 c_addr = x_ml->ml_address;
3774 c_size = x_ml->ml_size;
3775
3776 for (i = 0; i < NCPU; i++) {
3777 prog->stat->nbytes[i] = 0;
3778 if (!CPU_IN_SET(prog->data->cpu_copy_set, i)) {
3779 continue;
3780 }
3781 sz = copy_sz;
3782
3783 while (sz) {
3784 if (c_size > sz) {
3785 if ((prog->data->cpu_ml[i] =
3786 drmach_memlist_add_span(prog,
3787 prog->data->cpu_ml[i],
3788 c_addr, sz)) == NULL) {
3789 cmn_err(CE_WARN,
3790 "Unexpected drmach_memlist_add_span"
3791 " failure.");
3792 err = drerr_new(1, EOPL_FMEM_SETUP,
3793 NULL);
3794 mc_resume();
3795 goto out;
3796 }
3797 c_addr += sz;
3798 c_size -= sz;
3799 break;
3800 } else {
3801 sz -= c_size;
3802 if ((prog->data->cpu_ml[i] =
3803 drmach_memlist_add_span(prog,
3804 prog->data->cpu_ml[i],
3805 c_addr, c_size)) == NULL) {
3806 cmn_err(CE_WARN,
3807 "Unexpected drmach_memlist_add_span"
3808 " failure.");
3809 err = drerr_new(1, EOPL_FMEM_SETUP,
3810 NULL);
3811 mc_resume();
3812 goto out;
3813 }
3814
3815 x_ml = x_ml->ml_next;
3816 if (x_ml != NULL) {
3817 c_addr = x_ml->ml_address;
3818 c_size = x_ml->ml_size;
3819 } else {
3820 goto end;
3821 }
3822 }
3823 }
3824 }
3825 end:
3826 prog->data->s_copybasepa = s_copybasepa;
3827 prog->data->t_copybasepa = t_copybasepa;
3828 prog->data->c_ml = c_ml;
3829 *pgm_id = prog_kmem;
3830
3831 /* Unmap the alternate space. It will have to be remapped again */
3832 drmach_unlock_critical((caddr_t)prog);
3833 return (NULL);
3834
3835 err_out:
3836 mc_resume();
3837 rv = (*prog->data->scf_fmem_cancel)();
3838 if (rv) {
3839 cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x", rv);
3840 }
3841 out:
3842 if (prog != NULL) {
3843 drmach_unlock_critical((caddr_t)prog);
3844 vmem_free(heap_arena, prog, DRMACH_FMEM_LOCKED_PAGES *
3845 PAGESIZE);
3846 }
3847 if (prog_kmem != NULL) {
3848 kmem_free(prog_kmem, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
3849 }
3850 return (err);
3851 }
3852
3853 sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)3854 drmach_copy_rename_fini(drmachid_t id)
3855 {
3856 drmach_copy_rename_program_t *prog = id;
3857 sbd_error_t *err = NULL;
3858 int rv;
3859 uint_t fmem_error;
3860
3861 /*
3862 * Note that we have to delay calling SCF to find out the
3863 * status of the FMEM operation here because SCF cannot
3864 * respond while it is suspended.
3865 * This create a small window when we are sure about the
3866 * base address of the system board.
3867 * If there is any call to mc-opl to get memory unum,
3868 * mc-opl will return UNKNOWN as the unum.
3869 */
3870
3871 /*
3872 * we have to remap again because all the pointer like data,
3873 * critical in prog are based on the alternate vmem space.
3874 */
3875 (void) drmach_lock_critical((caddr_t)prog, (caddr_t)prog->locked_prog);
3876
3877 if (prog->data->c_ml != NULL)
3878 memlist_delete(prog->data->c_ml);
3879
3880 if ((prog->data->fmem_status.op &
3881 (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) !=
3882 (OPL_FMEM_SCF_START | OPL_FMEM_MC_SUSPEND)) {
3883 cmn_err(CE_PANIC, "drmach_copy_rename_fini: invalid op "
3884 "code %x\n", prog->data->fmem_status.op);
3885 }
3886
3887 fmem_error = prog->data->fmem_status.error;
3888 if (fmem_error != ESBD_NOERROR) {
3889 err = drerr_new(1, fmem_error, NULL);
3890 }
3891
3892 /* possible ops are SCF_START, MC_SUSPEND */
3893 if (prog->critical->fmem_issued) {
3894 if (fmem_error != ESBD_NOERROR) {
3895 cmn_err(CE_PANIC, "Irrecoverable FMEM error %d\n",
3896 fmem_error);
3897 }
3898 rv = (*prog->data->scf_fmem_end)();
3899 if (rv) {
3900 cmn_err(CE_PANIC, "scf_fmem_end() failed rv=%d", rv);
3901 }
3902 /*
3903 * If we get here, rename is successful.
3904 * Do all the copy rename post processing.
3905 */
3906 drmach_swap_pa((drmach_mem_t *)prog->data->s_mem,
3907 (drmach_mem_t *)prog->data->t_mem);
3908 } else {
3909 rv = (*prog->data->scf_fmem_cancel)();
3910 if (rv) {
3911 cmn_err(CE_WARN, "scf_fmem_cancel() failed rv=0x%x",
3912 rv);
3913 if (!err) {
3914 err = drerr_new(1, EOPL_SCF_FMEM_CANCEL,
3915 "scf_fmem_cancel() failed. rv = 0x%x", rv);
3916 }
3917 }
3918 }
3919 /* soft resume mac patrol */
3920 (*prog->data->mc_resume)();
3921
3922 drmach_unlock_critical((caddr_t)prog->locked_prog);
3923
3924 vmem_free(heap_arena, prog->locked_prog,
3925 DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
3926 kmem_free(prog, DRMACH_FMEM_LOCKED_PAGES * PAGESIZE);
3927 return (err);
3928 }
3929
3930 /*ARGSUSED*/
3931 static void
drmach_copy_rename_slave(struct regs * rp,drmachid_t id)3932 drmach_copy_rename_slave(struct regs *rp, drmachid_t id)
3933 {
3934 drmach_copy_rename_program_t *prog =
3935 (drmach_copy_rename_program_t *)id;
3936 register int cpuid;
3937 extern void drmach_flush();
3938 extern void membar_sync_il();
3939 extern void drmach_flush_icache();
3940 on_trap_data_t otd;
3941
3942 cpuid = CPU->cpu_id;
3943
3944 if (on_trap(&otd, OT_DATA_EC)) {
3945 no_trap();
3946 prog->data->error[cpuid] = EOPL_FMEM_COPY_ERROR;
3947 prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
3948 drmach_flush_icache();
3949 membar_sync_il();
3950 return;
3951 }
3952
3953
3954 /*
3955 * jmp drmach_copy_rename_prog().
3956 */
3957
3958 drmach_flush(prog->critical, PAGESIZE);
3959 (void) prog->critical->run(prog, cpuid);
3960 drmach_flush_icache();
3961
3962 no_trap();
3963
3964 prog->critical->stat[cpuid] = FMEM_LOOP_EXIT;
3965
3966 membar_sync_il();
3967 }
3968
3969 static void
drmach_swap_pa(drmach_mem_t * s_mem,drmach_mem_t * t_mem)3970 drmach_swap_pa(drmach_mem_t *s_mem, drmach_mem_t *t_mem)
3971 {
3972 uint64_t s_base, t_base;
3973 drmach_board_t *s_board, *t_board;
3974 struct memlist *ml;
3975
3976 s_board = s_mem->dev.bp;
3977 t_board = t_mem->dev.bp;
3978 if (s_board == NULL || t_board == NULL) {
3979 cmn_err(CE_PANIC, "Cannot locate source or target board\n");
3980 return;
3981 }
3982 s_base = s_mem->slice_base;
3983 t_base = t_mem->slice_base;
3984
3985 s_mem->slice_base = t_base;
3986 s_mem->base_pa = (s_mem->base_pa - s_base) + t_base;
3987
3988 for (ml = s_mem->memlist; ml; ml = ml->ml_next) {
3989 ml->ml_address = ml->ml_address - s_base + t_base;
3990 }
3991
3992 t_mem->slice_base = s_base;
3993 t_mem->base_pa = (t_mem->base_pa - t_base) + s_base;
3994
3995 for (ml = t_mem->memlist; ml; ml = ml->ml_next) {
3996 ml->ml_address = ml->ml_address - t_base + s_base;
3997 }
3998
3999 /*
4000 * IKP has to update the sb-mem-ranges for mac patrol driver
4001 * when it resumes, it will re-read the sb-mem-range property
4002 * to get the new base address
4003 */
4004 if (oplcfg_pa_swap(s_board->bnum, t_board->bnum) != 0)
4005 cmn_err(CE_PANIC, "Could not update device nodes\n");
4006 }
4007
4008 void
drmach_copy_rename(drmachid_t id)4009 drmach_copy_rename(drmachid_t id)
4010 {
4011 drmach_copy_rename_program_t *prog_kmem = id;
4012 drmach_copy_rename_program_t *prog;
4013 cpuset_t cpuset;
4014 int cpuid;
4015 uint64_t inst;
4016 register int rtn;
4017 extern int in_sync;
4018 int old_in_sync;
4019 extern void drmach_sys_trap();
4020 extern void drmach_flush();
4021 extern void drmach_flush_icache();
4022 extern uint64_t patch_inst(uint64_t *, uint64_t);
4023 on_trap_data_t otd;
4024
4025
4026 prog = prog_kmem->locked_prog;
4027
4028
4029 /*
4030 * We must immediately drop in the TLB because all pointers
4031 * are based on the alternate vmem space.
4032 */
4033
4034 (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
4035
4036 /*
4037 * we call scf to get the base address here becuase if scf
4038 * has not been suspended yet, the active path can be changing and
4039 * sometimes it is not even mapped. We call the interface when
4040 * the OS has been quiesced.
4041 */
4042 prog->critical->scf_reg_base = (*prog->data->scf_get_base_addr)();
4043
4044 if (prog->critical->scf_reg_base == (uint64_t)-1 ||
4045 prog->critical->scf_reg_base == NULL) {
4046 prog->data->fmem_status.error = EOPL_FMEM_SCF_ERR;
4047 drmach_unlock_critical((caddr_t)prog);
4048 return;
4049 }
4050
4051 cpuset = prog->data->cpu_ready_set;
4052
4053 for (cpuid = 0; cpuid < NCPU; cpuid++) {
4054 if (CPU_IN_SET(cpuset, cpuid)) {
4055 prog->critical->stat[cpuid] = FMEM_LOOP_START;
4056 prog->data->error[cpuid] = ESBD_NOERROR;
4057 }
4058 }
4059
4060 old_in_sync = in_sync;
4061 in_sync = 1;
4062 cpuid = CPU->cpu_id;
4063
4064 CPUSET_DEL(cpuset, cpuid);
4065
4066 for (cpuid = 0; cpuid < NCPU; cpuid++) {
4067 if (CPU_IN_SET(cpuset, cpuid)) {
4068 xc_one(cpuid, (xcfunc_t *)drmach_lock_critical,
4069 (uint64_t)prog_kmem, (uint64_t)prog);
4070 }
4071 }
4072
4073 cpuid = CPU->cpu_id;
4074
4075 xt_some(cpuset, (xcfunc_t *)drmach_sys_trap,
4076 (uint64_t)drmach_copy_rename_slave, (uint64_t)prog);
4077 xt_sync(cpuset);
4078
4079 if (on_trap(&otd, OT_DATA_EC)) {
4080 rtn = EOPL_FMEM_COPY_ERROR;
4081 drmach_flush_icache();
4082 goto done;
4083 }
4084
4085 /*
4086 * jmp drmach_copy_rename_prog().
4087 */
4088
4089 drmach_flush(prog->critical, PAGESIZE);
4090 rtn = prog->critical->run(prog, cpuid);
4091
4092 drmach_flush_icache();
4093
4094
4095 done:
4096 no_trap();
4097 if (rtn == EOPL_FMEM_HW_ERROR) {
4098 kpreempt_enable();
4099 prom_panic("URGENT_ERROR_TRAP is detected during FMEM.\n");
4100 }
4101
4102 /*
4103 * In normal case, all slave CPU's are still spinning in
4104 * the assembly code. The master has to patch the instruction
4105 * to get them out.
4106 * In error case, e.g. COPY_ERROR, some slave CPU's might
4107 * have aborted and already returned and sset LOOP_EXIT status.
4108 * Some CPU might still be copying.
4109 * In any case, some delay is necessary to give them
4110 * enough time to set the LOOP_EXIT status.
4111 */
4112
4113 for (;;) {
4114 inst = patch_inst((uint64_t *)prog->critical->loop_rtn,
4115 prog->critical->inst_loop_ret);
4116 if (prog->critical->inst_loop_ret == inst) {
4117 break;
4118 }
4119 }
4120
4121 for (cpuid = 0; cpuid < NCPU; cpuid++) {
4122 uint64_t last, now;
4123 if (!CPU_IN_SET(cpuset, cpuid)) {
4124 continue;
4125 }
4126 last = prog->stat->nbytes[cpuid];
4127 /*
4128 * Wait for all CPU to exit.
4129 * However we do not want an infinite loop
4130 * so we detect hangup situation here.
4131 * If the slave CPU is still copying data,
4132 * we will continue to wait.
4133 * In error cases, the master has already set
4134 * fmem_status.error to abort the copying.
4135 * 1 m.s delay for them to abort copying and
4136 * return to drmach_copy_rename_slave to set
4137 * FMEM_LOOP_EXIT status should be enough.
4138 */
4139 for (;;) {
4140 if (prog->critical->stat[cpuid] == FMEM_LOOP_EXIT)
4141 break;
4142 drmach_sleep_il();
4143 drv_usecwait(1000);
4144 now = prog->stat->nbytes[cpuid];
4145 if (now <= last) {
4146 drv_usecwait(1000);
4147 if (prog->critical->stat[cpuid] ==
4148 FMEM_LOOP_EXIT)
4149 break;
4150 cmn_err(CE_PANIC, "CPU %d hang during Copy "
4151 "Rename", cpuid);
4152 }
4153 last = now;
4154 }
4155 if (prog->data->error[cpuid] == EOPL_FMEM_HW_ERROR) {
4156 prom_panic("URGENT_ERROR_TRAP is detected during "
4157 "FMEM.\n");
4158 }
4159 }
4160
4161 /*
4162 * This must be done after all strands have exit.
4163 * Removing the TLB entry will affect both strands
4164 * in the same core.
4165 */
4166
4167 for (cpuid = 0; cpuid < NCPU; cpuid++) {
4168 if (CPU_IN_SET(cpuset, cpuid)) {
4169 xc_one(cpuid, (xcfunc_t *)drmach_unlock_critical,
4170 (uint64_t)prog, 0);
4171 }
4172 }
4173
4174 in_sync = old_in_sync;
4175
4176 /*
4177 * we should unlock before the following lock to keep the kpreempt
4178 * count correct.
4179 */
4180 (void) drmach_unlock_critical((caddr_t)prog);
4181
4182 /*
4183 * we must remap again. TLB might have been removed in above xcall.
4184 */
4185
4186 (void) drmach_lock_critical((caddr_t)prog_kmem, (caddr_t)prog);
4187
4188 if (prog->data->fmem_status.error == ESBD_NOERROR)
4189 prog->data->fmem_status.error = rtn;
4190
4191 if (prog->data->copy_wait_time > 0) {
4192 DRMACH_PR("Unexpected long wait time %ld seconds "
4193 "during copy rename on CPU %d\n",
4194 prog->data->copy_wait_time/prog->data->stick_freq,
4195 prog->data->slowest_cpuid);
4196 }
4197 drmach_unlock_critical((caddr_t)prog);
4198 }
4199