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/note.h>
28 #include <sys/debug.h>
29 #include <sys/types.h>
30 #include <sys/varargs.h>
31 #include <sys/errno.h>
32 #include <sys/cred.h>
33 #include <sys/dditypes.h>
34 #include <sys/devops.h>
35 #include <sys/modctl.h>
36 #include <sys/poll.h>
37 #include <sys/conf.h>
38 #include <sys/ddi.h>
39 #include <sys/sunddi.h>
40 #include <sys/sunndi.h>
41 #include <sys/ndi_impldefs.h>
42 #include <sys/stat.h>
43 #include <sys/kmem.h>
44 #include <sys/vmem.h>
45 #include <sys/disp.h>
46 #include <sys/processor.h>
47 #include <sys/cheetahregs.h>
48 #include <sys/cpuvar.h>
49 #include <sys/mem_config.h>
50 #include <sys/ddi_impldefs.h>
51 #include <sys/systm.h>
52 #include <sys/machsystm.h>
53 #include <sys/autoconf.h>
54 #include <sys/cmn_err.h>
55 #include <sys/sysmacros.h>
56 #include <sys/x_call.h>
57 #include <sys/promif.h>
58 #include <sys/prom_plat.h>
59 #include <sys/membar.h>
60 #include <vm/seg_kmem.h>
61 #include <sys/mem_cage.h>
62 #include <sys/stack.h>
63 #include <sys/archsystm.h>
64 #include <vm/hat_sfmmu.h>
65 #include <sys/pte.h>
66 #include <sys/mmu.h>
67 #include <sys/cpu_module.h>
68 #include <sys/obpdefs.h>
69 #include <sys/mboxsc.h>
70 #include <sys/plat_ecc_dimm.h>
71
72 #include <sys/hotplug/hpctrl.h> /* XXX should be included by schpc.h */
73 #include <sys/schpc.h>
74 #include <sys/pci.h>
75
76 #include <sys/starcat.h>
77 #include <sys/cpu_sgnblk_defs.h>
78 #include <sys/drmach.h>
79 #include <sys/dr_util.h>
80 #include <sys/dr_mbx.h>
81 #include <sys/sc_gptwocfg.h>
82 #include <sys/iosramreg.h>
83 #include <sys/iosramio.h>
84 #include <sys/iosramvar.h>
85 #include <sys/axq.h>
86 #include <sys/post/scat_dcd.h>
87 #include <sys/kobj.h>
88 #include <sys/taskq.h>
89 #include <sys/cmp.h>
90 #include <sys/sbd_ioctl.h>
91
92 #include <sys/sysevent.h>
93 #include <sys/sysevent/dr.h>
94 #include <sys/sysevent/eventdefs.h>
95
96 #include <sys/pci/pcisch.h>
97 #include <sys/pci/pci_regs.h>
98
99 #include <sys/ontrap.h>
100
101 /* defined in ../ml/drmach.il.cpp */
102 extern void bcopy32_il(uint64_t, uint64_t);
103 extern void flush_ecache_il(int64_t physaddr, int size, int linesz);
104 extern void flush_dcache_il(void);
105 extern void flush_icache_il(void);
106 extern void flush_pcache_il(void);
107
108 /* defined in ../ml/drmach_asm.s */
109 extern uint64_t lddmcdecode(uint64_t physaddr);
110 extern uint64_t lddsafconfig(void);
111
112 /* XXX here until provided by sys/dman.h */
113 extern int man_dr_attach(dev_info_t *);
114 extern int man_dr_detach(dev_info_t *);
115
116 #define DRMACH_BNUM2EXP(bnum) ((bnum) >> 1)
117 #define DRMACH_BNUM2SLOT(bnum) ((bnum) & 1)
118 #define DRMACH_EXPSLOT2BNUM(exp, slot) (((exp) << 1) + (slot))
119
120 #define DRMACH_SLICE_MASK 0x1Full
121 #define DRMACH_SLICE_TO_PA(s) (((s) & DRMACH_SLICE_MASK) << 37)
122 #define DRMACH_PA_TO_SLICE(a) (((a) >> 37) & DRMACH_SLICE_MASK)
123
124 /*
125 * DRMACH_MEM_SLICE_SIZE and DRMACH_MEM_USABLE_SLICE_SIZE define the
126 * available address space and the usable address space for every slice.
127 * There must be a distinction between the available and usable do to a
128 * restriction imposed by CDC memory size.
129 */
130
131 #define DRMACH_MEM_SLICE_SIZE (1ull << 37) /* 128GB */
132 #define DRMACH_MEM_USABLE_SLICE_SIZE (1ull << 36) /* 64GB */
133
134 #define DRMACH_MC_NBANKS 4
135
136 #define DRMACH_MC_ADDR(mp, bank) ((mp)->madr_pa + 16 + 8 * (bank))
137 #define DRMACH_MC_ASI_ADDR(mp, bank) (DRMACH_MC_ADDR(mp, bank) & 0xFF)
138
139 #define DRMACH_EMU_ACT_STATUS_OFFSET 0x50
140 #define DRMACH_EMU_ACT_STATUS_ADDR(mp) \
141 ((mp)->madr_pa + DRMACH_EMU_ACT_STATUS_OFFSET)
142
143 /*
144 * The Cheetah's Safari Configuration Register and the Schizo's
145 * Safari Control/Status Register place the LPA base and bound fields in
146 * same bit locations with in their register word. This source code takes
147 * advantage of this by defining only one set of LPA encoding/decoding macros
148 * which are shared by various Cheetah and Schizo drmach routines.
149 */
150 #define DRMACH_LPA_BASE_MASK (0x3Full << 3)
151 #define DRMACH_LPA_BND_MASK (0x3Full << 9)
152
153 #define DRMACH_LPA_BASE_TO_PA(scr) (((scr) & DRMACH_LPA_BASE_MASK) << 34)
154 #define DRMACH_LPA_BND_TO_PA(scr) (((scr) & DRMACH_LPA_BND_MASK) << 28)
155 #define DRMACH_PA_TO_LPA_BASE(pa) (((pa) >> 34) & DRMACH_LPA_BASE_MASK)
156 #define DRMACH_PA_TO_LPA_BND(pa) (((pa) >> 28) & DRMACH_LPA_BND_MASK)
157
158 #define DRMACH_L1_SET_LPA(b) \
159 (((b)->flags & DRMACH_NULL_PROC_LPA) == 0)
160
161 #define DRMACH_CPU_SRAM_ADDR 0x7fff0900000ull
162 #define DRMACH_CPU_SRAM_SIZE 0x20000ull
163
164 /*
165 * Name properties for frequently accessed device nodes.
166 */
167 #define DRMACH_CPU_NAMEPROP "cpu"
168 #define DRMACH_CMP_NAMEPROP "cmp"
169 #define DRMACH_AXQ_NAMEPROP "address-extender-queue"
170 #define DRMACH_PCI_NAMEPROP "pci"
171
172 /*
173 * Maximum value of processor Safari Timeout Log (TOL) field of
174 * Safari Config reg (7 secs).
175 */
176 #define DRMACH_SAF_TOL_MAX 7 * 1000000
177
178 /*
179 * drmach_board_t flag definitions
180 */
181 #define DRMACH_NULL_PROC_LPA 0x1
182
183 typedef struct {
184 uint32_t reg_addr_hi;
185 uint32_t reg_addr_lo;
186 uint32_t reg_size_hi;
187 uint32_t reg_size_lo;
188 } drmach_reg_t;
189
190 typedef struct {
191 struct drmach_node *node;
192 void *data;
193 } drmach_node_walk_args_t;
194
195 typedef struct drmach_node {
196 void *here;
197
198 pnode_t (*get_dnode)(struct drmach_node *node);
199 int (*walk)(struct drmach_node *node, void *data,
200 int (*cb)(drmach_node_walk_args_t *args));
201 dev_info_t *(*n_getdip)(struct drmach_node *node);
202 int (*n_getproplen)(struct drmach_node *node, char *name,
203 int *len);
204 int (*n_getprop)(struct drmach_node *node, char *name,
205 void *buf, int len);
206 int (*get_parent)(struct drmach_node *node,
207 struct drmach_node *pnode);
208 } drmach_node_t;
209
210 typedef struct {
211 int min_index;
212 int max_index;
213 int arr_sz;
214 drmachid_t *arr;
215 } drmach_array_t;
216
217 typedef struct {
218 void *isa;
219
220 void (*dispose)(drmachid_t);
221 sbd_error_t *(*release)(drmachid_t);
222 sbd_error_t *(*status)(drmachid_t, drmach_status_t *);
223
224 char name[MAXNAMELEN];
225 } drmach_common_t;
226
227 struct drmach_board;
228 typedef struct drmach_board drmach_board_t;
229
230 typedef struct {
231 drmach_common_t cm;
232 const char *type;
233 drmach_board_t *bp;
234 drmach_node_t *node;
235 int portid;
236 int unum;
237 int busy;
238 int powered;
239 } drmach_device_t;
240
241 typedef struct drmach_cpu {
242 drmach_device_t dev;
243 uint64_t scr_pa;
244 processorid_t cpuid;
245 int coreid;
246 } drmach_cpu_t;
247
248 typedef struct drmach_mem {
249 drmach_device_t dev;
250 struct drmach_mem *next;
251 uint64_t nbytes;
252 uint64_t madr_pa;
253 } drmach_mem_t;
254
255 typedef struct drmach_io {
256 drmach_device_t dev;
257 uint64_t scsr_pa; /* PA of Schizo Control/Status Register */
258 } drmach_io_t;
259
260 struct drmach_board {
261 drmach_common_t cm;
262 int bnum;
263 int assigned;
264 int powered;
265 int connected;
266 int empty;
267 int cond;
268 uint_t cpu_impl;
269 uint_t flags;
270 drmach_node_t *tree;
271 drmach_array_t *devices;
272 drmach_mem_t *mem;
273 uint64_t stardrb_offset;
274 char type[BD_TYPELEN];
275 };
276
277 typedef struct {
278 int flags;
279 drmach_device_t *dp;
280 sbd_error_t *err;
281 dev_info_t *fdip;
282 } drmach_config_args_t;
283
284 typedef struct {
285 drmach_board_t *obj;
286 int ndevs;
287 void *a;
288 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t);
289 sbd_error_t *err;
290 } drmach_board_cb_data_t;
291
292 typedef struct drmach_casmslot {
293 int valid;
294 int slice;
295 } drmach_casmslot_t;
296
297 typedef enum {
298 DRMACH_CR_OK,
299 DRMACH_CR_MC_IDLE_ERR,
300 DRMACH_CR_IOPAUSE_ERR,
301 DRMACH_CR_ONTRAP_ERR
302 } drmach_cr_err_t;
303
304 typedef struct {
305 void *isa;
306 caddr_t data;
307 drmach_mem_t *s_mp;
308 drmach_mem_t *t_mp;
309 struct memlist *c_ml;
310 uint64_t s_copybasepa;
311 uint64_t t_copybasepa;
312 drmach_cr_err_t ecode;
313 void *earg;
314 } drmach_copy_rename_t;
315
316 /*
317 * The following global is read as a boolean value, non-zero is true.
318 * If zero, DR copy-rename and cpu poweron will not set the processor
319 * LPA settings (CBASE, CBND of Safari config register) to correspond
320 * to the current memory slice map. LPAs of processors present at boot
321 * will remain as programmed by POST. LPAs of processors on boards added
322 * by DR will remain NULL, as programmed by POST. This can be used to
323 * to override the per-board L1SSFLG_THIS_L1_NULL_PROC_LPA flag set by
324 * POST in the LDCD (and copied to the GDCD by SMS).
325 *
326 * drmach_reprogram_lpa and L1SSFLG_THIS_L1_NULL_PROC_LPA do not apply
327 * to Schizo device LPAs. These are always set by DR.
328 */
329 static int drmach_reprogram_lpa = 1;
330
331 /*
332 * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
333 * can fail to receive an XIR. To workaround this issue until a hardware
334 * fix is implemented, we will exclude the selection of these CPUs.
335 * Setting this to 0 will allow their selection again.
336 */
337 static int drmach_iocage_exclude_jaguar_port_zero = 1;
338
339 static int drmach_initialized;
340 static drmach_array_t *drmach_boards;
341
342 static int drmach_cpu_delay = 1000;
343 static int drmach_cpu_ntries = 50000;
344
345 static uint32_t drmach_slice_table[AXQ_MAX_EXP];
346 static kmutex_t drmach_slice_table_lock;
347
348 tte_t drmach_cpu_sram_tte[NCPU];
349 caddr_t drmach_cpu_sram_va;
350
351 /*
352 * Setting to non-zero will enable delay before all disconnect ops.
353 */
354 static int drmach_unclaim_delay_all;
355 /*
356 * Default delay is slightly greater than the max processor Safari timeout.
357 * This delay is intended to ensure the outstanding Safari activity has
358 * retired on this board prior to a board disconnect.
359 */
360 static clock_t drmach_unclaim_usec_delay = DRMACH_SAF_TOL_MAX + 10;
361
362 /*
363 * By default, DR of non-Panther procs is not allowed into a Panther
364 * domain with large page sizes enabled. Setting this to 0 will remove
365 * the restriction.
366 */
367 static int drmach_large_page_restriction = 1;
368
369 /*
370 * Used to pass updated LPA values to procs.
371 * Protocol is to clear the array before use.
372 */
373 volatile uchar_t *drmach_xt_mb;
374 volatile uint64_t drmach_xt_ready;
375 static kmutex_t drmach_xt_mb_lock;
376 static int drmach_xt_mb_size;
377
378 uint64_t drmach_bus_sync_list[18 * 4 * 4 + 1];
379 static kmutex_t drmach_bus_sync_lock;
380
381 static void drmach_fini(void);
382
383 static sbd_error_t *drmach_device_new(drmach_node_t *,
384 drmach_board_t *, int, drmachid_t *);
385 static sbd_error_t *drmach_cpu_new(drmach_device_t *, drmachid_t *);
386 static sbd_error_t *drmach_mem_new(drmach_device_t *, drmachid_t *);
387 static sbd_error_t *drmach_pci_new(drmach_device_t *, drmachid_t *);
388 static sbd_error_t *drmach_io_new(drmach_device_t *, drmachid_t *);
389
390 static sbd_error_t *drmach_board_release(drmachid_t);
391 static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
392
393 static void drmach_cpu_dispose(drmachid_t);
394 static sbd_error_t *drmach_cpu_release(drmachid_t);
395 static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
396
397 static void drmach_mem_dispose(drmachid_t);
398 static sbd_error_t *drmach_mem_release(drmachid_t);
399 static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
400
401 static dev_info_t *drmach_node_ddi_get_dip(drmach_node_t *np);
402 static int drmach_node_ddi_get_prop(drmach_node_t *np,
403 char *name, void *buf, int len);
404 static int drmach_node_ddi_get_proplen(drmach_node_t *np,
405 char *name, int *len);
406
407 static dev_info_t *drmach_node_obp_get_dip(drmach_node_t *np);
408 static int drmach_node_obp_get_prop(drmach_node_t *np,
409 char *name, void *buf, int len);
410 static int drmach_node_obp_get_proplen(drmach_node_t *np,
411 char *name, int *len);
412
413 static sbd_error_t *drmach_mbox_trans(uint8_t msgtype, int bnum,
414 caddr_t obufp, int olen,
415 caddr_t ibufp, int ilen);
416
417 sbd_error_t *drmach_io_post_attach(drmachid_t id);
418 sbd_error_t *drmach_io_post_release(drmachid_t id);
419
420 static sbd_error_t *drmach_iocage_setup(dr_testboard_req_t *,
421 drmach_device_t **dpp, cpu_flag_t *oflags);
422 static int drmach_iocage_cpu_return(drmach_device_t *dp,
423 cpu_flag_t oflags);
424 static sbd_error_t *drmach_iocage_mem_return(dr_testboard_reply_t *tbr);
425 void drmach_iocage_mem_scrub(uint64_t nbytes);
426
427 static sbd_error_t *drmach_i_status(drmachid_t id, drmach_status_t *stat);
428
429 static void drmach_slot1_lpa_set(drmach_board_t *bp);
430
431 static void drmach_cpu_read(uint64_t arg1, uint64_t arg2);
432 static int drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr);
433
434 static void drmach_bus_sync_list_update(void);
435 static void drmach_slice_table_update(drmach_board_t *, int);
436 static int drmach_portid2bnum(int);
437
438 static void drmach_msg_memslice_init(dr_memslice_t slice_arr[]);
439 static void drmach_msg_memregs_init(dr_memregs_t regs_arr[]);
440
441 static int drmach_panther_boards(void);
442
443 static int drmach_name2type_idx(char *);
444
445 #ifdef DEBUG
446
447 #define DRMACH_PR if (drmach_debug) printf
448 #define DRMACH_MEMLIST_DUMP if (drmach_debug) MEMLIST_DUMP
449 int drmach_debug = 0; /* set to non-zero to enable debug messages */
450 #else
451
452 #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf
453 #define DRMACH_MEMLIST_DUMP _NOTE(CONSTANTCONDITION) if (0) MEMLIST_DUMP
454 #endif /* DEBUG */
455
456 #define DRMACH_OBJ(id) ((drmach_common_t *)id)
457
458 #define DRMACH_IS_BOARD_ID(id) \
459 ((id != 0) && \
460 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
461
462 #define DRMACH_IS_CPU_ID(id) \
463 ((id != 0) && \
464 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
465
466 #define DRMACH_IS_MEM_ID(id) \
467 ((id != 0) && \
468 (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
469
470 #define DRMACH_IS_IO_ID(id) \
471 ((id != 0) && \
472 (DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
473
474 #define DRMACH_IS_DEVICE_ID(id) \
475 ((id != 0) && \
476 (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
477 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
478 DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
479
480 #define DRMACH_IS_ID(id) \
481 ((id != 0) && \
482 (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \
483 DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
484 DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
485 DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
486
487 #define DRMACH_INTERNAL_ERROR() \
488 drerr_new(1, ESTC_INTERNAL, drmach_ie_fmt, __LINE__)
489 static char *drmach_ie_fmt = "drmach.c %d";
490
491 static struct {
492 const char *name;
493 const char *type;
494 sbd_error_t *(*new)(drmach_device_t *, drmachid_t *);
495 } drmach_name2type[] = {
496 {"cmp", DRMACH_DEVTYPE_CMP, NULL },
497 {"cpu", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
498 {"SUNW,UltraSPARC-III", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
499 {"SUNW,UltraSPARC-III+", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
500 {"memory-controller", DRMACH_DEVTYPE_MEM, drmach_mem_new },
501 {"pci", DRMACH_DEVTYPE_PCI, drmach_pci_new },
502 {"SUNW,wci", DRMACH_DEVTYPE_WCI, drmach_io_new },
503 };
504
505 /*
506 * drmach autoconfiguration data structures and interfaces
507 */
508
509 extern struct mod_ops mod_miscops;
510
511 static struct modlmisc modlmisc = {
512 &mod_miscops,
513 "Sun Fire 15000 DR"
514 };
515
516 static struct modlinkage modlinkage = {
517 MODREV_1,
518 (void *)&modlmisc,
519 NULL
520 };
521
522 /*
523 * drmach_boards_rwlock is used to synchronize read/write
524 * access to drmach_boards array between status and board lookup
525 * as READERS, and assign, and unassign threads as WRITERS.
526 */
527 static krwlock_t drmach_boards_rwlock;
528
529 static kmutex_t drmach_i_lock;
530 static kmutex_t drmach_iocage_lock;
531 static kcondvar_t drmach_iocage_cv;
532 static int drmach_iocage_is_busy = 0;
533 uint64_t drmach_iocage_paddr;
534 static caddr_t drmach_iocage_vaddr;
535 static int drmach_iocage_size = 0;
536 static int drmach_is_cheetah = -1;
537
538 int
_init(void)539 _init(void)
540 {
541 int err;
542
543 mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL);
544 rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
545 drmach_xt_mb_size = NCPU * sizeof (uchar_t);
546 drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena,
547 drmach_xt_mb_size, VM_SLEEP);
548 bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
549 if ((err = mod_install(&modlinkage)) != 0) {
550 mutex_destroy(&drmach_i_lock);
551 rw_destroy(&drmach_boards_rwlock);
552 vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
553 drmach_xt_mb_size);
554 }
555
556 return (err);
557 }
558
559 int
_fini(void)560 _fini(void)
561 {
562 int err;
563
564 if ((err = mod_remove(&modlinkage)) == 0)
565 drmach_fini();
566
567 return (err);
568 }
569
570 int
_info(struct modinfo * modinfop)571 _info(struct modinfo *modinfop)
572 {
573 return (mod_info(&modlinkage, modinfop));
574 }
575
576 /*
577 * drmach_node_* routines serve the purpose of separating the
578 * rest of the code from the device tree and OBP. This is necessary
579 * because of In-Kernel-Probing. Devices probed after stod, are probed
580 * by the in-kernel-prober, not OBP. These devices, therefore, do not
581 * have dnode ids.
582 */
583
584 static int
drmach_node_obp_get_parent(drmach_node_t * np,drmach_node_t * pp)585 drmach_node_obp_get_parent(drmach_node_t *np, drmach_node_t *pp)
586 {
587 pnode_t nodeid;
588 static char *fn = "drmach_node_obp_get_parent";
589
590 nodeid = np->get_dnode(np);
591 if (nodeid == OBP_NONODE) {
592 cmn_err(CE_WARN, "%s: invalid dnode", fn);
593 return (-1);
594 }
595
596 bcopy(np, pp, sizeof (drmach_node_t));
597
598 pp->here = (void *)(uintptr_t)prom_parentnode(nodeid);
599 if (pp->here == OBP_NONODE) {
600 cmn_err(CE_WARN, "%s: invalid parent dnode", fn);
601 return (-1);
602 }
603
604 return (0);
605 }
606
607 static pnode_t
drmach_node_obp_get_dnode(drmach_node_t * np)608 drmach_node_obp_get_dnode(drmach_node_t *np)
609 {
610 return ((pnode_t)(uintptr_t)np->here);
611 }
612
613 typedef struct {
614 drmach_node_walk_args_t *nwargs;
615 int (*cb)(drmach_node_walk_args_t *args);
616 int err;
617 } drmach_node_ddi_walk_args_t;
618
619 int
drmach_node_ddi_walk_cb(dev_info_t * dip,void * arg)620 drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
621 {
622 drmach_node_ddi_walk_args_t *nargs;
623
624 nargs = (drmach_node_ddi_walk_args_t *)arg;
625
626 /*
627 * dip doesn't have to be held here as we are called
628 * from ddi_walk_devs() which holds the dip.
629 */
630 nargs->nwargs->node->here = (void *)dip;
631
632 nargs->err = nargs->cb(nargs->nwargs);
633
634 /*
635 * Set "here" to NULL so that unheld dip is not accessible
636 * outside ddi_walk_devs()
637 */
638 nargs->nwargs->node->here = NULL;
639
640 if (nargs->err)
641 return (DDI_WALK_TERMINATE);
642 else
643 return (DDI_WALK_CONTINUE);
644 }
645
646 static int
drmach_node_ddi_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))647 drmach_node_ddi_walk(drmach_node_t *np, void *data,
648 int (*cb)(drmach_node_walk_args_t *args))
649 {
650 drmach_node_walk_args_t args;
651 drmach_node_ddi_walk_args_t nargs;
652
653 /* initialized args structure for callback */
654 args.node = np;
655 args.data = data;
656
657 nargs.nwargs = &args;
658 nargs.cb = cb;
659 nargs.err = 0;
660
661 /*
662 * Root node doesn't have to be held in any way.
663 */
664 ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
665
666 return (nargs.err);
667 }
668
669 static int
drmach_node_obp_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))670 drmach_node_obp_walk(drmach_node_t *np, void *data,
671 int (*cb)(drmach_node_walk_args_t *args))
672 {
673 pnode_t nodeid;
674 int rv;
675 drmach_node_walk_args_t args;
676
677 /* initialized args structure for callback */
678 args.node = np;
679 args.data = data;
680
681 nodeid = prom_childnode(prom_rootnode());
682
683 /* save our new position within the tree */
684 np->here = (void *)(uintptr_t)nodeid;
685
686 rv = 0;
687 while (nodeid != OBP_NONODE) {
688
689 pnode_t child;
690
691 rv = (*cb)(&args);
692 if (rv)
693 break;
694
695 child = prom_childnode(nodeid);
696 np->here = (void *)(uintptr_t)child;
697
698 while (child != OBP_NONODE) {
699 rv = (*cb)(&args);
700 if (rv)
701 break;
702
703 child = prom_nextnode(child);
704 np->here = (void *)(uintptr_t)child;
705 }
706
707 nodeid = prom_nextnode(nodeid);
708
709 /* save our new position within the tree */
710 np->here = (void *)(uintptr_t)nodeid;
711 }
712
713 return (rv);
714 }
715
716 static int
drmach_node_ddi_get_parent(drmach_node_t * np,drmach_node_t * pp)717 drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
718 {
719 dev_info_t *ndip;
720 static char *fn = "drmach_node_ddi_get_parent";
721
722 ndip = np->n_getdip(np);
723 if (ndip == NULL) {
724 cmn_err(CE_WARN, "%s: NULL dip", fn);
725 return (-1);
726 }
727
728 bcopy(np, pp, sizeof (drmach_node_t));
729
730 pp->here = (void *)ddi_get_parent(ndip);
731 if (pp->here == NULL) {
732 cmn_err(CE_WARN, "%s: NULL parent dip", fn);
733 return (-1);
734 }
735
736 return (0);
737 }
738
739 /*ARGSUSED*/
740 static pnode_t
drmach_node_ddi_get_dnode(drmach_node_t * np)741 drmach_node_ddi_get_dnode(drmach_node_t *np)
742 {
743 return ((pnode_t)NULL);
744 }
745
746 static drmach_node_t *
drmach_node_new(void)747 drmach_node_new(void)
748 {
749 drmach_node_t *np;
750
751 np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
752
753 if (drmach_initialized) {
754 np->get_dnode = drmach_node_ddi_get_dnode;
755 np->walk = drmach_node_ddi_walk;
756 np->n_getdip = drmach_node_ddi_get_dip;
757 np->n_getproplen = drmach_node_ddi_get_proplen;
758 np->n_getprop = drmach_node_ddi_get_prop;
759 np->get_parent = drmach_node_ddi_get_parent;
760 } else {
761 np->get_dnode = drmach_node_obp_get_dnode;
762 np->walk = drmach_node_obp_walk;
763 np->n_getdip = drmach_node_obp_get_dip;
764 np->n_getproplen = drmach_node_obp_get_proplen;
765 np->n_getprop = drmach_node_obp_get_prop;
766 np->get_parent = drmach_node_obp_get_parent;
767 }
768
769 return (np);
770 }
771
772 static void
drmach_node_dispose(drmach_node_t * np)773 drmach_node_dispose(drmach_node_t *np)
774 {
775 kmem_free(np, sizeof (*np));
776 }
777
778 /*
779 * Check if a CPU node is part of a CMP.
780 */
781 static int
drmach_is_cmp_child(dev_info_t * dip)782 drmach_is_cmp_child(dev_info_t *dip)
783 {
784 dev_info_t *pdip;
785
786 if (strcmp(ddi_node_name(dip), DRMACH_CPU_NAMEPROP) != 0) {
787 return (0);
788 }
789
790 pdip = ddi_get_parent(dip);
791
792 ASSERT(pdip);
793
794 if (strcmp(ddi_node_name(pdip), DRMACH_CMP_NAMEPROP) == 0) {
795 return (1);
796 }
797
798 return (0);
799 }
800
801 static dev_info_t *
drmach_node_obp_get_dip(drmach_node_t * np)802 drmach_node_obp_get_dip(drmach_node_t *np)
803 {
804 pnode_t nodeid;
805 dev_info_t *dip;
806
807 nodeid = np->get_dnode(np);
808 if (nodeid == OBP_NONODE)
809 return (NULL);
810
811 dip = e_ddi_nodeid_to_dip(nodeid);
812 if (dip) {
813 /*
814 * The branch rooted at dip will have been previously
815 * held, or it will be the child of a CMP. In either
816 * case, the hold acquired in e_ddi_nodeid_to_dip()
817 * is not needed.
818 */
819 ddi_release_devi(dip);
820 ASSERT(drmach_is_cmp_child(dip) || e_ddi_branch_held(dip));
821 }
822
823 return (dip);
824 }
825
826 static dev_info_t *
drmach_node_ddi_get_dip(drmach_node_t * np)827 drmach_node_ddi_get_dip(drmach_node_t *np)
828 {
829 return ((dev_info_t *)np->here);
830 }
831
832 static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))833 drmach_node_walk(drmach_node_t *np, void *param,
834 int (*cb)(drmach_node_walk_args_t *args))
835 {
836 return (np->walk(np, param, cb));
837 }
838
839 static int
drmach_node_ddi_get_prop(drmach_node_t * np,char * name,void * buf,int len)840 drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
841 {
842 int rv = 0;
843 dev_info_t *ndip;
844 static char *fn = "drmach_node_ddi_get_prop";
845
846 ndip = np->n_getdip(np);
847 if (ndip == NULL) {
848 cmn_err(CE_WARN, "%s: NULL dip", fn);
849 rv = -1;
850 } else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
851 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, name,
852 (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
853 rv = -1;
854 }
855
856 return (rv);
857 }
858
859 /* ARGSUSED */
860 static int
drmach_node_obp_get_prop(drmach_node_t * np,char * name,void * buf,int len)861 drmach_node_obp_get_prop(drmach_node_t *np, char *name, void *buf, int len)
862 {
863 int rv = 0;
864 pnode_t nodeid;
865 static char *fn = "drmach_node_obp_get_prop";
866
867 nodeid = np->get_dnode(np);
868 if (nodeid == OBP_NONODE) {
869 cmn_err(CE_WARN, "%s: invalid dnode", fn);
870 rv = -1;
871 } else if (prom_getproplen(nodeid, (caddr_t)name) < 0) {
872 rv = -1;
873 } else {
874 (void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf);
875 }
876
877 return (rv);
878 }
879
880 static int
drmach_node_ddi_get_proplen(drmach_node_t * np,char * name,int * len)881 drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
882 {
883 int rv = 0;
884 dev_info_t *ndip;
885
886 ndip = np->n_getdip(np);
887 if (ndip == NULL) {
888 rv = -1;
889 } else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS,
890 name, len) != DDI_PROP_SUCCESS) {
891 rv = -1;
892 }
893
894 return (rv);
895 }
896
897 static int
drmach_node_obp_get_proplen(drmach_node_t * np,char * name,int * len)898 drmach_node_obp_get_proplen(drmach_node_t *np, char *name, int *len)
899 {
900 pnode_t nodeid;
901 int rv;
902
903 nodeid = np->get_dnode(np);
904 if (nodeid == OBP_NONODE)
905 rv = -1;
906 else {
907 *len = prom_getproplen(nodeid, (caddr_t)name);
908 rv = (*len < 0 ? -1 : 0);
909 }
910
911 return (rv);
912 }
913
914 static drmachid_t
drmach_node_dup(drmach_node_t * np)915 drmach_node_dup(drmach_node_t *np)
916 {
917 drmach_node_t *dup;
918
919 dup = drmach_node_new();
920 dup->here = np->here;
921 dup->get_dnode = np->get_dnode;
922 dup->walk = np->walk;
923 dup->n_getdip = np->n_getdip;
924 dup->n_getproplen = np->n_getproplen;
925 dup->n_getprop = np->n_getprop;
926 dup->get_parent = np->get_parent;
927
928 return (dup);
929 }
930
931 /*
932 * drmach_array provides convenient array construction, access,
933 * bounds checking and array destruction logic.
934 */
935
936 static drmach_array_t *
drmach_array_new(int min_index,int max_index)937 drmach_array_new(int min_index, int max_index)
938 {
939 drmach_array_t *arr;
940
941 arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
942
943 arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
944 if (arr->arr_sz > 0) {
945 arr->min_index = min_index;
946 arr->max_index = max_index;
947
948 arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
949 return (arr);
950 } else {
951 kmem_free(arr, sizeof (*arr));
952 return (0);
953 }
954 }
955
956 static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)957 drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
958 {
959 if (idx < arr->min_index || idx > arr->max_index)
960 return (-1);
961 else {
962 arr->arr[idx - arr->min_index] = val;
963 return (0);
964 }
965 /*NOTREACHED*/
966 }
967
968 static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)969 drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
970 {
971 if (idx < arr->min_index || idx > arr->max_index)
972 return (-1);
973 else {
974 *val = arr->arr[idx - arr->min_index];
975 return (0);
976 }
977 /*NOTREACHED*/
978 }
979
980 static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)981 drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
982 {
983 int rv;
984
985 *idx = arr->min_index;
986 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
987 *idx += 1;
988
989 return (rv);
990 }
991
992 static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)993 drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
994 {
995 int rv;
996
997 *idx += 1;
998 while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
999 *idx += 1;
1000
1001 return (rv);
1002 }
1003
1004 static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))1005 drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
1006 {
1007 drmachid_t val;
1008 int idx;
1009 int rv;
1010
1011 rv = drmach_array_first(arr, &idx, &val);
1012 while (rv == 0) {
1013 (*disposer)(val);
1014
1015 /* clear the array entry */
1016 rv = drmach_array_set(arr, idx, NULL);
1017 ASSERT(rv == 0);
1018
1019 rv = drmach_array_next(arr, &idx, &val);
1020 }
1021
1022 kmem_free(arr->arr, arr->arr_sz);
1023 kmem_free(arr, sizeof (*arr));
1024 }
1025
1026
1027 static gdcd_t *
drmach_gdcd_new()1028 drmach_gdcd_new()
1029 {
1030 gdcd_t *gdcd;
1031
1032 gdcd = kmem_zalloc(sizeof (gdcd_t), KM_SLEEP);
1033
1034 /* read the gdcd, bail if magic or ver #s are not what is expected */
1035 if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) {
1036 bail:
1037 kmem_free(gdcd, sizeof (gdcd_t));
1038 return (NULL);
1039 } else if (gdcd->h.dcd_magic != GDCD_MAGIC) {
1040 goto bail;
1041 } else if (gdcd->h.dcd_version != DCD_VERSION) {
1042 goto bail;
1043 }
1044
1045 return (gdcd);
1046 }
1047
1048 static void
drmach_gdcd_dispose(gdcd_t * gdcd)1049 drmach_gdcd_dispose(gdcd_t *gdcd)
1050 {
1051 kmem_free(gdcd, sizeof (gdcd_t));
1052 }
1053
1054 /*ARGSUSED*/
1055 sbd_error_t *
drmach_configure(drmachid_t id,int flags)1056 drmach_configure(drmachid_t id, int flags)
1057 {
1058 drmach_device_t *dp;
1059 dev_info_t *rdip;
1060 sbd_error_t *err = NULL;
1061
1062 /*
1063 * On Starcat, there is no CPU driver, so it is
1064 * not necessary to configure any CPU nodes.
1065 */
1066 if (DRMACH_IS_CPU_ID(id)) {
1067 return (NULL);
1068 }
1069
1070 for (; id; ) {
1071 dev_info_t *fdip = NULL;
1072
1073 if (!DRMACH_IS_DEVICE_ID(id))
1074 return (drerr_new(0, ESTC_INAPPROP, NULL));
1075 dp = id;
1076
1077 rdip = dp->node->n_getdip(dp->node);
1078
1079 /*
1080 * We held this branch earlier, so at a minimum its
1081 * root should still be present in the device tree.
1082 */
1083 ASSERT(rdip);
1084
1085 DRMACH_PR("drmach_configure: configuring DDI branch");
1086
1087 ASSERT(e_ddi_branch_held(rdip));
1088 if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
1089 if (err == NULL) {
1090 /*
1091 * Record first failure but don't stop
1092 */
1093 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1094 dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
1095
1096 (void) ddi_pathname(dip, path);
1097 err = drerr_new(1, ESTC_DRVFAIL, path);
1098
1099 kmem_free(path, MAXPATHLEN);
1100 }
1101
1102 /*
1103 * If non-NULL, fdip is returned held and must be
1104 * released.
1105 */
1106 if (fdip != NULL) {
1107 ddi_release_devi(fdip);
1108 }
1109 }
1110
1111 if (DRMACH_IS_MEM_ID(id)) {
1112 drmach_mem_t *mp = id;
1113 id = mp->next;
1114 } else {
1115 id = NULL;
1116 }
1117 }
1118
1119 return (err);
1120 }
1121
1122 static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,int portid,drmachid_t * idp)1123 drmach_device_new(drmach_node_t *node,
1124 drmach_board_t *bp, int portid, drmachid_t *idp)
1125 {
1126 int i, rv, device_id, unum;
1127 char name[OBP_MAXDRVNAME];
1128 drmach_device_t proto;
1129
1130 rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
1131 if (rv) {
1132 sbd_error_t *err;
1133
1134 /* every node is expected to have a name */
1135 err = drerr_new(1, ESTC_GETPROP,
1136 "dip: 0x%p: property %s",
1137 node->n_getdip(node), OBP_NAME);
1138
1139 return (err);
1140 }
1141
1142 i = drmach_name2type_idx(name);
1143
1144 if (i < 0 || strcmp(name, "cmp") == 0) {
1145 /*
1146 * Not a node of interest to dr - including "cmp",
1147 * but it is in drmach_name2type[], which lets gptwocfg
1148 * driver to check if node is OBP created.
1149 */
1150 *idp = (drmachid_t)0;
1151 return (NULL);
1152 }
1153
1154 /*
1155 * Derive a best-guess unit number from the portid value.
1156 * Some drmach_*_new constructors (drmach_pci_new, for example)
1157 * will overwrite the prototype unum value with one that is more
1158 * appropriate for the device.
1159 */
1160 device_id = portid & 0x1f;
1161 if (device_id < 4)
1162 unum = device_id;
1163 else if (device_id == 8) {
1164 unum = 0;
1165 } else if (device_id == 9) {
1166 unum = 1;
1167 } else if (device_id == 0x1c) {
1168 unum = 0;
1169 } else if (device_id == 0x1d) {
1170 unum = 1;
1171 } else {
1172 return (DRMACH_INTERNAL_ERROR());
1173 }
1174
1175 bzero(&proto, sizeof (proto));
1176 proto.type = drmach_name2type[i].type;
1177 proto.bp = bp;
1178 proto.node = node;
1179 proto.portid = portid;
1180 proto.unum = unum;
1181
1182 return (drmach_name2type[i].new(&proto, idp));
1183 }
1184
1185 static void
drmach_device_dispose(drmachid_t id)1186 drmach_device_dispose(drmachid_t id)
1187 {
1188 drmach_device_t *self = id;
1189
1190 self->cm.dispose(id);
1191 }
1192
1193 static drmach_board_t *
drmach_board_new(int bnum)1194 drmach_board_new(int bnum)
1195 {
1196 drmach_board_t *bp;
1197
1198 bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
1199
1200 bp->cm.isa = (void *)drmach_board_new;
1201 bp->cm.release = drmach_board_release;
1202 bp->cm.status = drmach_board_status;
1203
1204 (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
1205
1206 bp->bnum = bnum;
1207 bp->devices = NULL;
1208 bp->tree = drmach_node_new();
1209
1210 (void) drmach_array_set(drmach_boards, bnum, bp);
1211 return (bp);
1212 }
1213
1214 static void
drmach_board_dispose(drmachid_t id)1215 drmach_board_dispose(drmachid_t id)
1216 {
1217 drmach_board_t *bp;
1218
1219 ASSERT(DRMACH_IS_BOARD_ID(id));
1220 bp = id;
1221
1222 if (bp->tree)
1223 drmach_node_dispose(bp->tree);
1224
1225 if (bp->devices)
1226 drmach_array_dispose(bp->devices, drmach_device_dispose);
1227
1228 kmem_free(bp, sizeof (*bp));
1229 }
1230
1231 static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)1232 drmach_board_status(drmachid_t id, drmach_status_t *stat)
1233 {
1234 sbd_error_t *err = NULL;
1235 drmach_board_t *bp;
1236 caddr_t obufp;
1237 dr_showboard_t shb;
1238
1239 if (!DRMACH_IS_BOARD_ID(id))
1240 return (drerr_new(0, ESTC_INAPPROP, NULL));
1241
1242 bp = id;
1243
1244 /*
1245 * we need to know if the board's connected before
1246 * issuing a showboard message. If it's connected, we just
1247 * reply with status composed of cached info
1248 */
1249
1250 if (!bp->connected) {
1251 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
1252 err = drmach_mbox_trans(DRMSG_SHOWBOARD, bp->bnum, obufp,
1253 sizeof (dr_proto_hdr_t), (caddr_t)&shb,
1254 sizeof (dr_showboard_t));
1255
1256 kmem_free(obufp, sizeof (dr_proto_hdr_t));
1257 if (err)
1258 return (err);
1259
1260 bp->connected = (shb.bd_assigned && shb.bd_active);
1261 (void) strncpy(bp->type, shb.board_type, sizeof (bp->type));
1262 stat->assigned = bp->assigned = shb.bd_assigned;
1263 stat->powered = bp->powered = shb.power_on;
1264 stat->empty = bp->empty = shb.slot_empty;
1265
1266 switch (shb.test_status) {
1267 case DR_TEST_STATUS_UNKNOWN:
1268 case DR_TEST_STATUS_IPOST:
1269 case DR_TEST_STATUS_ABORTED:
1270 stat->cond = bp->cond = SBD_COND_UNKNOWN;
1271 break;
1272 case DR_TEST_STATUS_PASSED:
1273 stat->cond = bp->cond = SBD_COND_OK;
1274 break;
1275 case DR_TEST_STATUS_FAILED:
1276 stat->cond = bp->cond = SBD_COND_FAILED;
1277 break;
1278 default:
1279 stat->cond = bp->cond = SBD_COND_UNKNOWN;
1280 DRMACH_PR("Unknown test status=0x%x from SC\n",
1281 shb.test_status);
1282 break;
1283
1284 }
1285
1286 (void) strncpy(stat->type, shb.board_type, sizeof (stat->type));
1287 (void) snprintf(stat->info, sizeof (stat->info),
1288 "Test Level=%d", shb.test_level);
1289 } else {
1290 stat->assigned = bp->assigned;
1291 stat->powered = bp->powered;
1292 stat->empty = bp->empty;
1293 stat->cond = bp->cond;
1294 (void) strncpy(stat->type, bp->type, sizeof (stat->type));
1295 }
1296
1297 stat->busy = 0; /* assume not busy */
1298 stat->configured = 0; /* assume not configured */
1299 if (bp->devices) {
1300 int rv;
1301 int d_idx;
1302 drmachid_t d_id;
1303
1304 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
1305 while (rv == 0) {
1306 drmach_status_t d_stat;
1307
1308 err = drmach_i_status(d_id, &d_stat);
1309 if (err)
1310 break;
1311
1312 stat->busy |= d_stat.busy;
1313 stat->configured |= d_stat.configured;
1314
1315 rv = drmach_array_next(bp->devices, &d_idx, &d_id);
1316 }
1317 }
1318
1319 return (err);
1320 }
1321
1322 typedef struct drmach_msglist {
1323 kcondvar_t s_cv; /* condvar for sending msg */
1324 kmutex_t s_lock; /* mutex for sending */
1325 kcondvar_t g_cv; /* condvar for getting reply */
1326 kmutex_t g_lock; /* mutex for getting reply */
1327 struct drmach_msglist *prev; /* link to previous entry */
1328 struct drmach_msglist *next; /* link to next entry */
1329 struct drmach_msglist *link; /* link to related entry */
1330 caddr_t o_buf; /* address of output buffer */
1331 caddr_t i_buf; /* address of input buffer */
1332 uint32_t o_buflen; /* output buffer length */
1333 uint32_t i_buflen; /* input buffer length */
1334 uint32_t msgid; /* message identifier */
1335 int o_nretry; /* number of sending retries */
1336 int f_error; /* mailbox framework error */
1337 uint8_t e_code; /* error code returned by SC */
1338 uint8_t p_flag :1, /* successfully putmsg */
1339 m_reply :1, /* msg reply received */
1340 unused :6;
1341 } drmach_msglist_t;
1342
1343 kmutex_t drmach_g_mbox_mutex; /* mutex for mailbox globals */
1344 kmutex_t drmach_ri_mbox_mutex; /* mutex for mailbox reinit */
1345 kmutex_t drmach_msglist_mutex; /* mutex for message list */
1346 drmach_msglist_t *drmach_msglist_first; /* first entry in msg list */
1347 drmach_msglist_t *drmach_msglist_last; /* last entry in msg list */
1348 uint32_t drmach_msgid; /* current message id */
1349 kthread_t *drmach_getmsg_thread; /* ptr to getmsg thread */
1350 volatile int drmach_getmsg_thread_run; /* run flag for getmsg thr */
1351 kmutex_t drmach_sendmsg_mutex; /* mutex for sendmsg cv */
1352 kcondvar_t drmach_sendmsg_cv; /* signaled to send new msg */
1353 kthread_t *drmach_sendmsg_thread; /* ptr to sendmsg thread */
1354 volatile int drmach_sendmsg_thread_run; /* run flag for sendmsg */
1355 int drmach_mbox_istate; /* mailbox init state */
1356 int drmach_mbox_iflag; /* set if init'd with SC */
1357 int drmach_mbox_ipending; /* set if reinit scheduled */
1358
1359 /*
1360 * Timeout values (in seconds) used when waiting for replies (from the SC) to
1361 * requests that we sent. Since we only receive boardevent messages, and they
1362 * are events rather than replies, there is no boardevent timeout.
1363 */
1364 int drmach_to_mbxinit = 60; /* 1 minute */
1365 int drmach_to_assign = 60; /* 1 minute */
1366 int drmach_to_unassign = 60; /* 1 minute */
1367 int drmach_to_claim = 3600; /* 1 hour */
1368 int drmach_to_unclaim = 3600; /* 1 hour */
1369 int drmach_to_poweron = 480; /* 8 minutes */
1370 int drmach_to_poweroff = 480; /* 8 minutes */
1371 int drmach_to_testboard = 43200; /* 12 hours */
1372 int drmach_to_aborttest = 180; /* 3 minutes */
1373 int drmach_to_showboard = 180; /* 3 minutes */
1374 int drmach_to_unconfig = 180; /* 3 minutes */
1375
1376 /*
1377 * Delay (in seconds) used after receiving a non-transient error indication from
1378 * an mboxsc_getmsg call in the thread that loops waiting for incoming messages.
1379 */
1380 int drmach_mbxerr_delay = 15; /* 15 seconds */
1381
1382 /*
1383 * Timeout values (in milliseconds) for mboxsc_putmsg and mboxsc_getmsg calls.
1384 */
1385 clock_t drmach_to_putmsg; /* set in drmach_mbox_init */
1386 clock_t drmach_to_getmsg = 31000; /* 31 seconds */
1387
1388 /*
1389 * Normally, drmach_to_putmsg is set dynamically during initialization in
1390 * drmach_mbox_init. This has the potentially undesirable side effect of
1391 * clobbering any value that might have been set in /etc/system. To prevent
1392 * dynamic setting of drmach_to_putmsg (thereby allowing it to be tuned in
1393 * /etc/system), set drmach_use_tuned_putmsg_to to 1.
1394 */
1395 int drmach_use_tuned_putmsg_to = 0;
1396
1397
1398 /* maximum conceivable message size for future mailbox protocol versions */
1399 #define DRMACH_MAX_MBOX_MSG_SIZE 4096
1400
1401 /*ARGSUSED*/
1402 void
drmach_mbox_prmsg(dr_mbox_msg_t * mbp,int dir)1403 drmach_mbox_prmsg(dr_mbox_msg_t *mbp, int dir)
1404 {
1405 int i, j;
1406 dr_memregs_t *memregs;
1407 dr_proto_hdr_t *php = &mbp->p_hdr;
1408 dr_msg_t *mp = &mbp->msgdata;
1409
1410 #ifdef DEBUG
1411 switch (php->command) {
1412 case DRMSG_BOARDEVENT:
1413 if (dir) {
1414 DRMACH_PR("ERROR!! outgoing BOARDEVENT\n");
1415 } else {
1416 DRMACH_PR("BOARDEVENT received:\n");
1417 DRMACH_PR("init=%d ins=%d rem=%d asgn=%d\n",
1418 mp->dm_be.initialized,
1419 mp->dm_be.board_insertion,
1420 mp->dm_be.board_removal,
1421 mp->dm_be.slot_assign);
1422 DRMACH_PR("unasgn=%d avail=%d unavail=%d\n",
1423 mp->dm_be.slot_unassign,
1424 mp->dm_be.slot_avail,
1425 mp->dm_be.slot_unavail);
1426 }
1427 break;
1428 case DRMSG_MBOX_INIT:
1429 if (dir) {
1430 DRMACH_PR("MBOX_INIT Request:\n");
1431 } else {
1432 DRMACH_PR("MBOX_INIT Reply:\n");
1433 }
1434 break;
1435 case DRMSG_ASSIGN:
1436 if (dir) {
1437 DRMACH_PR("ASSIGN Request:\n");
1438 } else {
1439 DRMACH_PR("ASSIGN Reply:\n");
1440 }
1441 break;
1442 case DRMSG_UNASSIGN:
1443 if (dir) {
1444 DRMACH_PR("UNASSIGN Request:\n");
1445 } else {
1446 DRMACH_PR("UNASSIGN Reply:\n");
1447 }
1448 break;
1449 case DRMSG_CLAIM:
1450 if (!dir) {
1451 DRMACH_PR("CLAIM Reply:\n");
1452 break;
1453 }
1454
1455 DRMACH_PR("CLAIM Request:\n");
1456 for (i = 0; i < 18; ++i) {
1457 DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
1458 mp->dm_cr.mem_slice[i].valid,
1459 mp->dm_cr.mem_slice[i].slice);
1460 memregs = &(mp->dm_cr.mem_regs[i]);
1461 for (j = 0; j < S0_LPORT_COUNT; j++) {
1462 DRMACH_PR(" MC %2d: "
1463 "MADR[%d] = 0x%lx, "
1464 "MADR[%d] = 0x%lx\n", j,
1465 0, DRMACH_MCREG_TO_U64(
1466 memregs->madr[j][0]),
1467 1, DRMACH_MCREG_TO_U64(
1468 memregs->madr[j][1]));
1469 DRMACH_PR(" : "
1470 "MADR[%d] = 0x%lx, "
1471 "MADR[%d] = 0x%lx\n",
1472 2, DRMACH_MCREG_TO_U64(
1473 memregs->madr[j][2]),
1474 3, DRMACH_MCREG_TO_U64(
1475 memregs->madr[j][3]));
1476 }
1477 }
1478 break;
1479 case DRMSG_UNCLAIM:
1480 if (!dir) {
1481 DRMACH_PR("UNCLAIM Reply:\n");
1482 break;
1483 }
1484
1485 DRMACH_PR("UNCLAIM Request:\n");
1486 for (i = 0; i < 18; ++i) {
1487 DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
1488 mp->dm_ur.mem_slice[i].valid,
1489 mp->dm_ur.mem_slice[i].slice);
1490 memregs = &(mp->dm_ur.mem_regs[i]);
1491 for (j = 0; j < S0_LPORT_COUNT; j++) {
1492 DRMACH_PR(" MC %2d: "
1493 "MADR[%d] = 0x%lx, "
1494 "MADR[%d] = 0x%lx\n", j,
1495 0, DRMACH_MCREG_TO_U64(
1496 memregs->madr[j][0]),
1497 1, DRMACH_MCREG_TO_U64(
1498 memregs->madr[j][1]));
1499 DRMACH_PR(" : "
1500 "MADR[%d] = 0x%lx, "
1501 "MADR[%d] = 0x%lx\n",
1502 2, DRMACH_MCREG_TO_U64(
1503 memregs->madr[j][2]),
1504 3, DRMACH_MCREG_TO_U64(
1505 memregs->madr[j][3]));
1506 }
1507 }
1508 DRMACH_PR(" mem_clear=%d\n", mp->dm_ur.mem_clear);
1509 break;
1510 case DRMSG_UNCONFIG:
1511 if (!dir) {
1512 DRMACH_PR("UNCONFIG Reply:\n");
1513 break;
1514 }
1515
1516 DRMACH_PR("UNCONFIG Request:\n");
1517 for (i = 0; i < 18; ++i) {
1518 DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
1519 mp->dm_uc.mem_slice[i].valid,
1520 mp->dm_uc.mem_slice[i].slice);
1521 memregs = &(mp->dm_uc.mem_regs[i]);
1522 for (j = 0; j < S0_LPORT_COUNT; j++) {
1523 DRMACH_PR(" MC %2d: "
1524 "MADR[%d] = 0x%lx, "
1525 "MADR[%d] = 0x%lx\n", j,
1526 0, DRMACH_MCREG_TO_U64(
1527 memregs->madr[j][0]),
1528 1, DRMACH_MCREG_TO_U64(
1529 memregs->madr[j][1]));
1530 DRMACH_PR(" : "
1531 "MADR[%d] = 0x%lx, "
1532 "MADR[%d] = 0x%lx\n",
1533 2, DRMACH_MCREG_TO_U64(
1534 memregs->madr[j][2]),
1535 3, DRMACH_MCREG_TO_U64(
1536 memregs->madr[j][3]));
1537 }
1538 }
1539 break;
1540 case DRMSG_POWERON:
1541 if (dir) {
1542 DRMACH_PR("POWERON Request:\n");
1543 } else {
1544 DRMACH_PR("POWERON Reply:\n");
1545 }
1546 break;
1547 case DRMSG_POWEROFF:
1548 if (dir) {
1549 DRMACH_PR("POWEROFF Request:\n");
1550 } else {
1551 DRMACH_PR("POWEROFF Reply:\n");
1552 }
1553 break;
1554 case DRMSG_TESTBOARD:
1555 if (dir) {
1556 DRMACH_PR("TESTBOARD Request:\n");
1557 DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
1558 mp->dm_tb.memaddrhi,
1559 mp->dm_tb.memaddrlo);
1560 DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
1561 mp->dm_tb.memlen, mp->dm_tb.cpu_portid);
1562 DRMACH_PR("\tforce=0x%x imm=0x%x\n",
1563 mp->dm_tb.force, mp->dm_tb.immediate);
1564 } else {
1565 DRMACH_PR("TESTBOARD Reply:\n");
1566 DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
1567 mp->dm_tr.memaddrhi,
1568 mp->dm_tr.memaddrlo);
1569 DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
1570 mp->dm_tr.memlen, mp->dm_tr.cpu_portid);
1571 DRMACH_PR("\trecovered=0x%x test status=0x%x\n",
1572 mp->dm_tr.cpu_recovered,
1573 mp->dm_tr.test_status);
1574
1575 }
1576 break;
1577 case DRMSG_ABORT_TEST:
1578 if (dir) {
1579 DRMACH_PR("ABORT_TEST Request:\n");
1580 } else {
1581 DRMACH_PR("ABORT_TEST Reply:\n");
1582 }
1583
1584 DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
1585 mp->dm_ta.memaddrhi,
1586 mp->dm_ta.memaddrlo);
1587 DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
1588 mp->dm_ta.memlen, mp->dm_ta.cpu_portid);
1589 break;
1590 case DRMSG_SHOWBOARD:
1591 if (dir) {
1592 DRMACH_PR("SHOWBOARD Request:\n");
1593 } else {
1594 DRMACH_PR("SHOWBOARD Reply:\n");
1595
1596 DRMACH_PR(": empty=%d power=%d assigned=%d",
1597 mp->dm_sb.slot_empty,
1598 mp->dm_sb.power_on,
1599 mp->dm_sb.bd_assigned);
1600 DRMACH_PR(": active=%d t_status=%d t_level=%d ",
1601 mp->dm_sb.bd_active,
1602 mp->dm_sb.test_status,
1603 mp->dm_sb.test_level);
1604 DRMACH_PR(": type=%s ", mp->dm_sb.board_type);
1605 }
1606 break;
1607 default:
1608 DRMACH_PR("Unknown message type\n");
1609 break;
1610 }
1611
1612 DRMACH_PR("dr hdr:\n\tid=0x%x vers=0x%x cmd=0x%x exp=0x%x slot=0x%x\n",
1613 php->message_id, php->drproto_version, php->command,
1614 php->expbrd, php->slot);
1615 #endif
1616 DRMACH_PR("\treply_status=0x%x error_code=0x%x\n", php->reply_status,
1617 php->error_code);
1618 }
1619
1620 /*
1621 * Callback function passed to taskq_dispatch when a mailbox reinitialization
1622 * handshake needs to be scheduled. The handshake can't be performed by the
1623 * thread that determines it is needed, in most cases, so this function is
1624 * dispatched on the system-wide taskq pool of threads. Failure is reported but
1625 * otherwise ignored, since any situation that requires a mailbox initialization
1626 * handshake will continue to request the handshake until it succeeds.
1627 */
1628 static void
drmach_mbox_reinit(void * unused)1629 drmach_mbox_reinit(void *unused)
1630 {
1631 _NOTE(ARGUNUSED(unused))
1632
1633 caddr_t obufp = NULL;
1634 sbd_error_t *serr = NULL;
1635
1636 DRMACH_PR("scheduled mailbox reinit running\n");
1637
1638 mutex_enter(&drmach_ri_mbox_mutex);
1639 mutex_enter(&drmach_g_mbox_mutex);
1640 if (drmach_mbox_iflag == 0) {
1641 /* need to initialize the mailbox */
1642 mutex_exit(&drmach_g_mbox_mutex);
1643
1644 cmn_err(CE_NOTE, "!reinitializing DR mailbox");
1645 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
1646 serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
1647 sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
1648 kmem_free(obufp, sizeof (dr_proto_hdr_t));
1649
1650 if (serr) {
1651 cmn_err(CE_WARN,
1652 "mbox_init: MBOX_INIT failed ecode=0x%x",
1653 serr->e_code);
1654 sbd_err_clear(&serr);
1655 }
1656 mutex_enter(&drmach_g_mbox_mutex);
1657 if (!serr) {
1658 drmach_mbox_iflag = 1;
1659 }
1660 }
1661 drmach_mbox_ipending = 0;
1662 mutex_exit(&drmach_g_mbox_mutex);
1663 mutex_exit(&drmach_ri_mbox_mutex);
1664 }
1665
1666 /*
1667 * To ensure sufficient compatibility with future versions of the DR mailbox
1668 * protocol, we use a buffer that is large enough to receive the largest message
1669 * that could possibly be sent to us. However, since that ends up being fairly
1670 * large, allocating it on the stack is a bad idea. Fortunately, this function
1671 * does not need to be MT-safe since it is only invoked by the mailbox
1672 * framework, which will never invoke it multiple times concurrently. Since
1673 * that is the case, we can use a static buffer.
1674 */
1675 void
drmach_mbox_event(void)1676 drmach_mbox_event(void)
1677 {
1678 static uint8_t buf[DRMACH_MAX_MBOX_MSG_SIZE];
1679 dr_mbox_msg_t *msg = (dr_mbox_msg_t *)buf;
1680 int err;
1681 uint32_t type = MBOXSC_MSG_EVENT;
1682 uint32_t command = DRMSG_BOARDEVENT;
1683 uint64_t transid = 0;
1684 uint32_t length = DRMACH_MAX_MBOX_MSG_SIZE;
1685 char *hint = "";
1686 int logsys = 0;
1687
1688 do {
1689 err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
1690 &length, (void *)msg, 0);
1691 } while (err == EAGAIN);
1692
1693 /* don't try to interpret anything with the wrong version number */
1694 if ((err == 0) && (msg->p_hdr.drproto_version != DRMBX_VERSION)) {
1695 cmn_err(CE_WARN, "mailbox version mismatch 0x%x vs 0x%x",
1696 msg->p_hdr.drproto_version, DRMBX_VERSION);
1697 mutex_enter(&drmach_g_mbox_mutex);
1698 drmach_mbox_iflag = 0;
1699 /* schedule a reinit handshake if one isn't pending */
1700 if (!drmach_mbox_ipending) {
1701 if (taskq_dispatch(system_taskq, drmach_mbox_reinit,
1702 NULL, TQ_NOSLEEP) != NULL) {
1703 drmach_mbox_ipending = 1;
1704 } else {
1705 cmn_err(CE_WARN,
1706 "failed to schedule mailbox reinit");
1707 }
1708 }
1709 mutex_exit(&drmach_g_mbox_mutex);
1710 return;
1711 }
1712
1713 if ((err != 0) || (msg->p_hdr.reply_status != DRMSG_REPLY_OK)) {
1714 cmn_err(CE_WARN,
1715 "Unsolicited mboxsc_getmsg failed: err=0x%x code=0x%x",
1716 err, msg->p_hdr.error_code);
1717 } else {
1718 dr_boardevent_t *be;
1719 be = (dr_boardevent_t *)&msg->msgdata;
1720
1721 /* check for initialization event */
1722 if (be->initialized) {
1723 mutex_enter(&drmach_g_mbox_mutex);
1724 drmach_mbox_iflag = 0;
1725 /* schedule a reinit handshake if one isn't pending */
1726 if (!drmach_mbox_ipending) {
1727 if (taskq_dispatch(system_taskq,
1728 drmach_mbox_reinit, NULL, TQ_NOSLEEP)
1729 != NULL) {
1730 drmach_mbox_ipending = 1;
1731 } else {
1732 cmn_err(CE_WARN, "failed to schedule "
1733 "mailbox reinit");
1734 }
1735 }
1736 mutex_exit(&drmach_g_mbox_mutex);
1737 cmn_err(CE_NOTE, "!Mailbox Init event received");
1738 }
1739
1740 /* anything else will be a log_sysevent call */
1741
1742 if (be->board_insertion) {
1743 DRMACH_PR("Board Insertion event received");
1744 hint = DR_HINT_INSERT;
1745 logsys++;
1746 }
1747 if (be->board_removal) {
1748 DRMACH_PR("Board Removal event received");
1749 hint = DR_HINT_REMOVE;
1750 logsys++;
1751 }
1752 if (be->slot_assign) {
1753 DRMACH_PR("Slot Assign event received");
1754 logsys++;
1755 }
1756 if (be->slot_unassign) {
1757 DRMACH_PR("Slot Unassign event received");
1758 logsys++;
1759 }
1760 if (be->slot_avail) {
1761 DRMACH_PR("Slot Available event received");
1762 logsys++;
1763 }
1764 if (be->slot_unavail) {
1765 DRMACH_PR("Slot Unavailable event received");
1766 logsys++;
1767 }
1768 if (be->power_on) {
1769 DRMACH_PR("Power ON event received");
1770 logsys++;
1771 }
1772 if (be->power_off) {
1773 DRMACH_PR("Power OFF event received");
1774 logsys++;
1775 }
1776
1777 if (logsys)
1778 (void) drmach_log_sysevent(
1779 DRMACH_EXPSLOT2BNUM(msg->p_hdr.expbrd,
1780 msg->p_hdr.slot), hint, SE_NOSLEEP, 1);
1781 }
1782 }
1783
1784 static uint32_t
drmach_get_msgid()1785 drmach_get_msgid()
1786 {
1787 uint32_t rv;
1788 mutex_enter(&drmach_msglist_mutex);
1789 if (!(++drmach_msgid))
1790 ++drmach_msgid;
1791 rv = drmach_msgid;
1792 mutex_exit(&drmach_msglist_mutex);
1793 return (rv);
1794 }
1795
1796 /*
1797 * unlink an entry from the message transaction list
1798 *
1799 * caller must hold drmach_msglist_mutex
1800 */
1801 void
drmach_msglist_unlink(drmach_msglist_t * entry)1802 drmach_msglist_unlink(drmach_msglist_t *entry)
1803 {
1804 ASSERT(mutex_owned(&drmach_msglist_mutex));
1805 if (entry->prev) {
1806 entry->prev->next = entry->next;
1807 if (entry->next)
1808 entry->next->prev = entry->prev;
1809 } else {
1810 drmach_msglist_first = entry->next;
1811 if (entry->next)
1812 entry->next->prev = NULL;
1813 }
1814 if (entry == drmach_msglist_last) {
1815 drmach_msglist_last = entry->prev;
1816 }
1817 }
1818
1819 void
drmach_msglist_link(drmach_msglist_t * entry)1820 drmach_msglist_link(drmach_msglist_t *entry)
1821 {
1822 mutex_enter(&drmach_msglist_mutex);
1823 if (drmach_msglist_last) {
1824 entry->prev = drmach_msglist_last;
1825 drmach_msglist_last->next = entry;
1826 drmach_msglist_last = entry;
1827 } else {
1828 drmach_msglist_last = drmach_msglist_first = entry;
1829 }
1830 mutex_exit(&drmach_msglist_mutex);
1831 }
1832
1833 void
drmach_mbox_getmsg()1834 drmach_mbox_getmsg()
1835 {
1836 int err;
1837 register int msgid;
1838 static uint8_t buf[DRMACH_MAX_MBOX_MSG_SIZE];
1839 dr_mbox_msg_t *msg = (dr_mbox_msg_t *)buf;
1840 dr_proto_hdr_t *php;
1841 drmach_msglist_t *found, *entry;
1842 uint32_t type = MBOXSC_MSG_REPLY;
1843 uint32_t command;
1844 uint64_t transid;
1845 uint32_t length;
1846
1847 php = &msg->p_hdr;
1848
1849 while (drmach_getmsg_thread_run != 0) {
1850 /* get a reply message */
1851 command = 0;
1852 transid = 0;
1853 length = DRMACH_MAX_MBOX_MSG_SIZE;
1854 err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
1855 &length, (void *)msg, drmach_to_getmsg);
1856
1857 if (err) {
1858 /*
1859 * If mboxsc_getmsg returns ETIMEDOUT or EAGAIN, then
1860 * the "error" is really just a normal, transient
1861 * condition and we can retry the operation right away.
1862 * Any other error suggests a more serious problem,
1863 * ranging from a message being too big for our buffer
1864 * (EMSGSIZE) to total failure of the mailbox layer.
1865 * This second class of errors is much less "transient",
1866 * so rather than retrying over and over (and getting
1867 * the same error over and over) as fast as we can,
1868 * we'll sleep for a while before retrying.
1869 */
1870 if ((err != ETIMEDOUT) && (err != EAGAIN)) {
1871 cmn_err(CE_WARN,
1872 "mboxsc_getmsg failed, err=0x%x", err);
1873 delay(drmach_mbxerr_delay * hz);
1874 }
1875 continue;
1876 }
1877
1878 drmach_mbox_prmsg(msg, 0);
1879
1880 if (php->drproto_version != DRMBX_VERSION) {
1881 cmn_err(CE_WARN,
1882 "mailbox version mismatch 0x%x vs 0x%x",
1883 php->drproto_version, DRMBX_VERSION);
1884
1885 mutex_enter(&drmach_g_mbox_mutex);
1886 drmach_mbox_iflag = 0;
1887 /* schedule a reinit handshake if one isn't pending */
1888 if (!drmach_mbox_ipending) {
1889 if (taskq_dispatch(system_taskq,
1890 drmach_mbox_reinit, NULL, TQ_NOSLEEP)
1891 != NULL) {
1892 drmach_mbox_ipending = 1;
1893 } else {
1894 cmn_err(CE_WARN, "failed to schedule "
1895 "mailbox reinit");
1896 }
1897 }
1898 mutex_exit(&drmach_g_mbox_mutex);
1899
1900 continue;
1901 }
1902
1903 msgid = php->message_id;
1904 found = NULL;
1905 mutex_enter(&drmach_msglist_mutex);
1906 entry = drmach_msglist_first;
1907 while (entry != NULL) {
1908 if (entry->msgid == msgid) {
1909 found = entry;
1910 drmach_msglist_unlink(entry);
1911 entry = NULL;
1912 } else
1913 entry = entry->next;
1914 }
1915
1916 if (found) {
1917 mutex_enter(&found->g_lock);
1918
1919 found->e_code = php->error_code;
1920 if (found->i_buflen > 0)
1921 bcopy((caddr_t)&msg->msgdata, found->i_buf,
1922 found->i_buflen);
1923 found->m_reply = 1;
1924
1925 cv_signal(&found->g_cv);
1926 mutex_exit(&found->g_lock);
1927 } else {
1928 cmn_err(CE_WARN, "!mbox_getmsg: no match for id 0x%x",
1929 msgid);
1930 cmn_err(CE_WARN, "! cmd = 0x%x, exb = %d, slot = %d",
1931 php->command, php->expbrd, php->slot);
1932 }
1933
1934 mutex_exit(&drmach_msglist_mutex);
1935 }
1936 cmn_err(CE_WARN, "mbox_getmsg: exiting");
1937 mutex_enter(&drmach_msglist_mutex);
1938 entry = drmach_msglist_first;
1939 while (entry != NULL) {
1940 if (entry->p_flag == 1) {
1941 entry->f_error = -1;
1942 mutex_enter(&entry->g_lock);
1943 cv_signal(&entry->g_cv);
1944 mutex_exit(&entry->g_lock);
1945 drmach_msglist_unlink(entry);
1946 }
1947 entry = entry->next;
1948 }
1949 mutex_exit(&drmach_msglist_mutex);
1950 drmach_getmsg_thread_run = -1;
1951 thread_exit();
1952 }
1953
1954 void
drmach_mbox_sendmsg()1955 drmach_mbox_sendmsg()
1956 {
1957 int err, retry;
1958 drmach_msglist_t *entry;
1959 dr_mbox_msg_t *mp;
1960 dr_proto_hdr_t *php;
1961
1962 while (drmach_sendmsg_thread_run != 0) {
1963 /*
1964 * Search through the list to find entries awaiting
1965 * transmission to the SC
1966 */
1967 mutex_enter(&drmach_msglist_mutex);
1968 entry = drmach_msglist_first;
1969 retry = 0;
1970 while (entry != NULL) {
1971 if (entry->p_flag == 1) {
1972 entry = entry->next;
1973 continue;
1974 }
1975
1976 mutex_exit(&drmach_msglist_mutex);
1977
1978 if (!retry)
1979 mutex_enter(&entry->s_lock);
1980 mp = (dr_mbox_msg_t *)entry->o_buf;
1981 php = &mp->p_hdr;
1982
1983 drmach_mbox_prmsg(mp, 1);
1984
1985 err = mboxsc_putmsg(KEY_DRSC, MBOXSC_MSG_REQUEST,
1986 php->command, NULL, entry->o_buflen, (void *)mp,
1987 drmach_to_putmsg);
1988
1989 if (err) {
1990 switch (err) {
1991
1992 case EAGAIN:
1993 case EBUSY:
1994 ++retry;
1995 mutex_enter(&drmach_msglist_mutex);
1996 continue;
1997
1998 case ETIMEDOUT:
1999 if (--entry->o_nretry <= 0) {
2000 mutex_enter(
2001 &drmach_msglist_mutex);
2002 drmach_msglist_unlink(entry);
2003 mutex_exit(
2004 &drmach_msglist_mutex);
2005 entry->f_error = err;
2006 entry->p_flag = 1;
2007 cv_signal(&entry->s_cv);
2008 } else {
2009 ++retry;
2010 mutex_enter(
2011 &drmach_msglist_mutex);
2012 continue;
2013 }
2014 break;
2015 default:
2016 mutex_enter(&drmach_msglist_mutex);
2017 drmach_msglist_unlink(entry);
2018 mutex_exit(&drmach_msglist_mutex);
2019 entry->f_error = err;
2020 entry->p_flag = 1;
2021 cv_signal(&entry->s_cv);
2022 break;
2023 }
2024 } else {
2025 entry->p_flag = 1;
2026 cv_signal(&entry->s_cv);
2027 }
2028
2029 mutex_exit(&entry->s_lock);
2030 retry = 0;
2031 mutex_enter(&drmach_msglist_mutex);
2032 entry = drmach_msglist_first;
2033 }
2034 mutex_exit(&drmach_msglist_mutex);
2035
2036 mutex_enter(&drmach_sendmsg_mutex);
2037 (void) cv_reltimedwait(&drmach_sendmsg_cv,
2038 &drmach_sendmsg_mutex, (5 * hz), TR_CLOCK_TICK);
2039 mutex_exit(&drmach_sendmsg_mutex);
2040 }
2041 cmn_err(CE_WARN, "mbox_sendmsg: exiting");
2042 mutex_enter(&drmach_msglist_mutex);
2043 entry = drmach_msglist_first;
2044 while (entry != NULL) {
2045 if (entry->p_flag == 0) {
2046 entry->f_error = -1;
2047 mutex_enter(&entry->s_lock);
2048 cv_signal(&entry->s_cv);
2049 mutex_exit(&entry->s_lock);
2050 drmach_msglist_unlink(entry);
2051 }
2052 entry = entry->next;
2053 }
2054 mutex_exit(&drmach_msglist_mutex);
2055 cv_destroy(&drmach_sendmsg_cv);
2056 mutex_destroy(&drmach_sendmsg_mutex);
2057
2058 drmach_sendmsg_thread_run = -1;
2059 thread_exit();
2060 }
2061
2062 void
drmach_msglist_destroy(drmach_msglist_t * listp)2063 drmach_msglist_destroy(drmach_msglist_t *listp)
2064 {
2065 if (listp != NULL) {
2066 drmach_msglist_t *entry;
2067
2068 mutex_enter(&drmach_msglist_mutex);
2069 entry = drmach_msglist_first;
2070 while (entry) {
2071 if (listp == entry) {
2072 drmach_msglist_unlink(listp);
2073 entry = NULL;
2074 } else
2075 entry = entry->next;
2076 }
2077
2078 mutex_destroy(&listp->s_lock);
2079 cv_destroy(&listp->s_cv);
2080 mutex_destroy(&listp->g_lock);
2081 cv_destroy(&listp->g_cv);
2082 kmem_free(listp, sizeof (drmach_msglist_t));
2083
2084 mutex_exit(&drmach_msglist_mutex);
2085 }
2086 }
2087
2088 static drmach_msglist_t *
drmach_msglist_new(caddr_t ibufp,uint32_t ilen,dr_proto_hdr_t * hdrp,uint32_t olen,int nrtry)2089 drmach_msglist_new(caddr_t ibufp, uint32_t ilen, dr_proto_hdr_t *hdrp,
2090 uint32_t olen, int nrtry)
2091 {
2092 drmach_msglist_t *listp;
2093
2094 listp = kmem_zalloc(sizeof (drmach_msglist_t), KM_SLEEP);
2095 mutex_init(&listp->s_lock, NULL, MUTEX_DRIVER, NULL);
2096 cv_init(&listp->s_cv, NULL, CV_DRIVER, NULL);
2097 mutex_init(&listp->g_lock, NULL, MUTEX_DRIVER, NULL);
2098 cv_init(&listp->g_cv, NULL, CV_DRIVER, NULL);
2099 listp->o_buf = (caddr_t)hdrp;
2100 listp->o_buflen = olen;
2101 listp->i_buf = ibufp;
2102 listp->i_buflen = ilen;
2103 listp->o_nretry = nrtry;
2104 listp->msgid = hdrp->message_id;
2105
2106 return (listp);
2107 }
2108
2109 static drmach_msglist_t *
drmach_mbox_req_rply(dr_proto_hdr_t * hdrp,uint32_t olen,caddr_t ibufp,uint32_t ilen,int timeout,int nrtry,int nosig,drmach_msglist_t * link)2110 drmach_mbox_req_rply(dr_proto_hdr_t *hdrp, uint32_t olen, caddr_t ibufp,
2111 uint32_t ilen, int timeout, int nrtry, int nosig,
2112 drmach_msglist_t *link)
2113 {
2114 int crv;
2115 drmach_msglist_t *listp;
2116 clock_t to_val;
2117 dr_proto_hdr_t *php;
2118
2119 /* setup transaction list entry */
2120 listp = drmach_msglist_new(ibufp, ilen, hdrp, olen, nrtry);
2121
2122 /* send mailbox message, await reply */
2123 mutex_enter(&listp->s_lock);
2124 mutex_enter(&listp->g_lock);
2125
2126 listp->link = link;
2127 drmach_msglist_link(listp);
2128
2129 mutex_enter(&drmach_sendmsg_mutex);
2130 cv_signal(&drmach_sendmsg_cv);
2131 mutex_exit(&drmach_sendmsg_mutex);
2132
2133 while (listp->p_flag == 0) {
2134 cv_wait(&listp->s_cv, &listp->s_lock);
2135 }
2136
2137 to_val = ddi_get_lbolt() + (timeout * hz);
2138
2139 if (listp->f_error) {
2140 listp->p_flag = 0;
2141 cmn_err(CE_WARN, "!mboxsc_putmsg failed: 0x%x", listp->f_error);
2142 php = (dr_proto_hdr_t *)listp->o_buf;
2143 cmn_err(CE_WARN, "! cmd = 0x%x, exb = %d, slot = %d",
2144 php->command, php->expbrd, php->slot);
2145 } else {
2146 while (listp->m_reply == 0 && listp->f_error == 0) {
2147 if (nosig)
2148 crv = cv_timedwait(&listp->g_cv, &listp->g_lock,
2149 to_val);
2150 else
2151 crv = cv_timedwait_sig(&listp->g_cv,
2152 &listp->g_lock, to_val);
2153 switch (crv) {
2154 case -1: /* timed out */
2155 cmn_err(CE_WARN,
2156 "!msgid=0x%x reply timed out",
2157 hdrp->message_id);
2158 php = (dr_proto_hdr_t *)listp->o_buf;
2159 cmn_err(CE_WARN, "! cmd = 0x%x, "
2160 "exb = %d, slot = %d", php->command,
2161 php->expbrd, php->slot);
2162 listp->f_error = ETIMEDOUT;
2163 break;
2164 case 0: /* signal received */
2165 cmn_err(CE_WARN,
2166 "operation interrupted by signal");
2167 listp->f_error = EINTR;
2168 break;
2169 default:
2170 break;
2171 }
2172 }
2173
2174 /*
2175 * If link is set for this entry, check to see if
2176 * the linked entry has been replied to. If not,
2177 * wait for the response.
2178 * Currently, this is only used for ABORT_TEST functionality,
2179 * wherein a check is made for the TESTBOARD reply when
2180 * the ABORT_TEST reply is received.
2181 */
2182
2183 if (link) {
2184 mutex_enter(&link->g_lock);
2185 /*
2186 * If the reply to the linked entry hasn't been
2187 * received, clear the existing link->f_error,
2188 * and await the reply.
2189 */
2190 if (link->m_reply == 0) {
2191 link->f_error = 0;
2192 }
2193 to_val = ddi_get_lbolt() + (timeout * hz);
2194 while (link->m_reply == 0 && link->f_error == 0) {
2195 crv = cv_timedwait(&link->g_cv, &link->g_lock,
2196 to_val);
2197 switch (crv) {
2198 case -1: /* timed out */
2199 cmn_err(CE_NOTE,
2200 "!link msgid=0x%x reply timed out",
2201 link->msgid);
2202 link->f_error = ETIMEDOUT;
2203 break;
2204 default:
2205 break;
2206 }
2207 }
2208 mutex_exit(&link->g_lock);
2209 }
2210 }
2211 mutex_exit(&listp->g_lock);
2212 mutex_exit(&listp->s_lock);
2213 return (listp);
2214 }
2215
2216 static sbd_error_t *
drmach_mbx2sbderr(drmach_msglist_t * mlp)2217 drmach_mbx2sbderr(drmach_msglist_t *mlp)
2218 {
2219 char a_pnt[MAXNAMELEN];
2220 dr_proto_hdr_t *php;
2221 int bnum;
2222
2223 if (mlp->f_error) {
2224 /*
2225 * If framework failure is due to signal, return "no error"
2226 * error.
2227 */
2228 if (mlp->f_error == EINTR)
2229 return (drerr_new(0, ESTC_NONE, NULL));
2230
2231 mutex_enter(&drmach_g_mbox_mutex);
2232 drmach_mbox_iflag = 0;
2233 mutex_exit(&drmach_g_mbox_mutex);
2234 if (!mlp->p_flag)
2235 return (drerr_new(1, ESTC_MBXRQST, NULL));
2236 else
2237 return (drerr_new(1, ESTC_MBXRPLY, NULL));
2238 }
2239 php = (dr_proto_hdr_t *)mlp->o_buf;
2240 bnum = 2 * php->expbrd + php->slot;
2241 a_pnt[0] = '\0';
2242 (void) drmach_board_name(bnum, a_pnt, MAXNAMELEN);
2243
2244 switch (mlp->e_code) {
2245 case 0:
2246 return (NULL);
2247 case DRERR_NOACL:
2248 return (drerr_new(0, ESTC_NOACL, "%s", a_pnt));
2249 case DRERR_NOT_ASSIGNED:
2250 return (drerr_new(0, ESTC_NOT_ASSIGNED, "%s", a_pnt));
2251 case DRERR_NOT_ACTIVE:
2252 return (drerr_new(0, ESTC_NOT_ACTIVE, "%s", a_pnt));
2253 case DRERR_EMPTY_SLOT:
2254 return (drerr_new(0, ESTC_EMPTY_SLOT, "%s", a_pnt));
2255 case DRERR_POWER_OFF:
2256 return (drerr_new(0, ESTC_POWER_OFF, "%s", a_pnt));
2257 case DRERR_TEST_IN_PROGRESS:
2258 return (drerr_new(0, ESTC_TEST_IN_PROGRESS, "%s",
2259 a_pnt));
2260 case DRERR_TESTING_BUSY:
2261 return (drerr_new(0, ESTC_TESTING_BUSY, "%s", a_pnt));
2262 case DRERR_TEST_REQUIRED:
2263 return (drerr_new(0, ESTC_TEST_REQUIRED, "%s", a_pnt));
2264 case DRERR_UNAVAILABLE:
2265 return (drerr_new(0, ESTC_UNAVAILABLE, "%s", a_pnt));
2266 case DRERR_RECOVERABLE:
2267 return (drerr_new(0, ESTC_SMS_ERR_RECOVERABLE, "%s",
2268 a_pnt));
2269 case DRERR_UNRECOVERABLE:
2270 return (drerr_new(1, ESTC_SMS_ERR_UNRECOVERABLE, "%s",
2271 a_pnt));
2272 default:
2273 return (drerr_new(1, ESTC_MBOX_UNKNOWN, NULL));
2274 }
2275 }
2276
2277 static sbd_error_t *
drmach_mbox_trans(uint8_t msgtype,int bnum,caddr_t obufp,int olen,caddr_t ibufp,int ilen)2278 drmach_mbox_trans(uint8_t msgtype, int bnum, caddr_t obufp, int olen,
2279 caddr_t ibufp, int ilen)
2280 {
2281 int timeout = 0;
2282 int ntries = 0;
2283 int nosignals = 0;
2284 dr_proto_hdr_t *hdrp;
2285 drmach_msglist_t *mlp;
2286 sbd_error_t *err = NULL;
2287
2288 if (msgtype != DRMSG_MBOX_INIT) {
2289 mutex_enter(&drmach_ri_mbox_mutex);
2290 mutex_enter(&drmach_g_mbox_mutex);
2291 if (drmach_mbox_iflag == 0) {
2292 /* need to initialize the mailbox */
2293 dr_proto_hdr_t imsg;
2294
2295 mutex_exit(&drmach_g_mbox_mutex);
2296
2297 imsg.command = DRMSG_MBOX_INIT;
2298
2299 imsg.message_id = drmach_get_msgid();
2300 imsg.drproto_version = DRMBX_VERSION;
2301 imsg.expbrd = 0;
2302 imsg.slot = 0;
2303
2304 cmn_err(CE_WARN, "!reinitializing DR mailbox");
2305 mlp = drmach_mbox_req_rply(&imsg, sizeof (imsg), 0, 0,
2306 10, 5, 0, NULL);
2307 err = drmach_mbx2sbderr(mlp);
2308 /*
2309 * If framework failure incoming is encountered on
2310 * the MBOX_INIT [timeout on SMS reply], the error
2311 * type must be changed before returning to caller.
2312 * This is to prevent drmach_board_connect() and
2313 * drmach_board_disconnect() from marking boards
2314 * UNUSABLE based on MBOX_INIT failures.
2315 */
2316 if ((err != NULL) && (err->e_code == ESTC_MBXRPLY)) {
2317 cmn_err(CE_WARN,
2318 "!Changed mbox incoming to outgoing"
2319 " failure on reinit");
2320 sbd_err_clear(&err);
2321 err = drerr_new(0, ESTC_MBXRQST, NULL);
2322 }
2323 drmach_msglist_destroy(mlp);
2324 if (err) {
2325 mutex_exit(&drmach_ri_mbox_mutex);
2326 return (err);
2327 }
2328 mutex_enter(&drmach_g_mbox_mutex);
2329 drmach_mbox_iflag = 1;
2330 }
2331 mutex_exit(&drmach_g_mbox_mutex);
2332 mutex_exit(&drmach_ri_mbox_mutex);
2333 }
2334
2335 hdrp = (dr_proto_hdr_t *)obufp;
2336
2337 /* setup outgoing mailbox header */
2338 hdrp->command = msgtype;
2339 hdrp->message_id = drmach_get_msgid();
2340 hdrp->drproto_version = DRMBX_VERSION;
2341 hdrp->expbrd = DRMACH_BNUM2EXP(bnum);
2342 hdrp->slot = DRMACH_BNUM2SLOT(bnum);
2343
2344 switch (msgtype) {
2345
2346 case DRMSG_MBOX_INIT:
2347 timeout = drmach_to_mbxinit;
2348 ntries = 1;
2349 nosignals = 0;
2350 break;
2351
2352 case DRMSG_ASSIGN:
2353 timeout = drmach_to_assign;
2354 ntries = 1;
2355 nosignals = 0;
2356 break;
2357
2358 case DRMSG_UNASSIGN:
2359 timeout = drmach_to_unassign;
2360 ntries = 1;
2361 nosignals = 0;
2362 break;
2363
2364 case DRMSG_POWERON:
2365 timeout = drmach_to_poweron;
2366 ntries = 1;
2367 nosignals = 0;
2368 break;
2369
2370 case DRMSG_POWEROFF:
2371 timeout = drmach_to_poweroff;
2372 ntries = 1;
2373 nosignals = 0;
2374 break;
2375
2376 case DRMSG_SHOWBOARD:
2377 timeout = drmach_to_showboard;
2378 ntries = 1;
2379 nosignals = 0;
2380 break;
2381
2382 case DRMSG_CLAIM:
2383 timeout = drmach_to_claim;
2384 ntries = 1;
2385 nosignals = 1;
2386 break;
2387
2388 case DRMSG_UNCLAIM:
2389 timeout = drmach_to_unclaim;
2390 ntries = 1;
2391 nosignals = 1;
2392 break;
2393
2394 case DRMSG_UNCONFIG:
2395 timeout = drmach_to_unconfig;
2396 ntries = 1;
2397 nosignals = 0;
2398 break;
2399
2400 case DRMSG_TESTBOARD:
2401 timeout = drmach_to_testboard;
2402 ntries = 1;
2403 nosignals = 0;
2404 break;
2405
2406 default:
2407 cmn_err(CE_WARN, "Unknown outgoing message type 0x%x",
2408 msgtype);
2409 err = DRMACH_INTERNAL_ERROR();
2410 break;
2411 }
2412
2413 if (err == NULL) {
2414 mlp = drmach_mbox_req_rply(hdrp, olen, ibufp, ilen, timeout,
2415 ntries, nosignals, NULL);
2416 err = drmach_mbx2sbderr(mlp);
2417
2418 /*
2419 * For DRMSG_TESTBOARD attempts which have timed out, or
2420 * been aborted due to a signal received after mboxsc_putmsg()
2421 * has succeeded in sending the message, a DRMSG_ABORT_TEST
2422 * must be sent.
2423 */
2424 if ((msgtype == DRMSG_TESTBOARD) && (err != NULL) &&
2425 ((mlp->f_error == EINTR) || ((mlp->f_error == ETIMEDOUT) &&
2426 (mlp->p_flag != 0)))) {
2427 drmach_msglist_t *abmlp;
2428 dr_abort_test_t abibuf;
2429
2430 hdrp->command = DRMSG_ABORT_TEST;
2431 hdrp->message_id = drmach_get_msgid();
2432 abmlp = drmach_mbox_req_rply(hdrp,
2433 sizeof (dr_abort_test_t), (caddr_t)&abibuf,
2434 sizeof (abibuf), drmach_to_aborttest, 5, 1, mlp);
2435 cmn_err(CE_WARN, "test aborted");
2436 drmach_msglist_destroy(abmlp);
2437 }
2438
2439 drmach_msglist_destroy(mlp);
2440 }
2441
2442 return (err);
2443 }
2444
2445 static int
drmach_mbox_init()2446 drmach_mbox_init()
2447 {
2448 int err;
2449 caddr_t obufp;
2450 sbd_error_t *serr = NULL;
2451 mboxsc_timeout_range_t mbxtoz;
2452
2453 drmach_mbox_istate = 0;
2454 /* register the outgoing mailbox */
2455 if ((err = mboxsc_init(KEY_DRSC, MBOXSC_MBOX_OUT,
2456 NULL)) != 0) {
2457 cmn_err(CE_WARN, "DR - SC mboxsc_init failed: 0x%x", err);
2458 return (-1);
2459 }
2460 drmach_mbox_istate = 1;
2461
2462 /* setup the mboxsc_putmsg timeout value */
2463 if (drmach_use_tuned_putmsg_to) {
2464 cmn_err(CE_NOTE, "!using tuned drmach_to_putmsg = 0x%lx\n",
2465 drmach_to_putmsg);
2466 } else {
2467 if ((err = mboxsc_ctrl(KEY_DRSC,
2468 MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE, &mbxtoz)) != 0) {
2469 cmn_err(CE_WARN, "mboxsc_ctrl failed: 0x%x", err);
2470 drmach_to_putmsg = 60000;
2471 } else {
2472 drmach_to_putmsg = mboxsc_putmsg_def_timeout() * 6;
2473 DRMACH_PR("putmsg range is 0x%lx - 0x%lx value"
2474 " is 0x%lx\n", mbxtoz.min_timeout,
2475 mbxtoz.max_timeout, drmach_to_putmsg);
2476 }
2477 }
2478
2479 /* register the incoming mailbox */
2480 if ((err = mboxsc_init(KEY_SCDR, MBOXSC_MBOX_IN,
2481 drmach_mbox_event)) != 0) {
2482 cmn_err(CE_WARN, "SC - DR mboxsc_init failed: 0x%x", err);
2483 return (-1);
2484 }
2485 drmach_mbox_istate = 2;
2486
2487 /* initialize mutex for mailbox globals */
2488 mutex_init(&drmach_g_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
2489
2490 /* initialize mutex for mailbox re-init */
2491 mutex_init(&drmach_ri_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
2492
2493 /* initialize mailbox message list elements */
2494 drmach_msglist_first = drmach_msglist_last = NULL;
2495 mutex_init(&drmach_msglist_mutex, NULL, MUTEX_DRIVER, NULL);
2496
2497 mutex_init(&drmach_sendmsg_mutex, NULL, MUTEX_DRIVER, NULL);
2498 cv_init(&drmach_sendmsg_cv, NULL, CV_DRIVER, NULL);
2499
2500 drmach_mbox_istate = 3;
2501
2502 /* start mailbox sendmsg thread */
2503 drmach_sendmsg_thread_run = 1;
2504 if (drmach_sendmsg_thread == NULL)
2505 drmach_sendmsg_thread = thread_create(NULL, 0,
2506 (void (*)())drmach_mbox_sendmsg, NULL, 0, &p0,
2507 TS_RUN, minclsyspri);
2508
2509 /* start mailbox getmsg thread */
2510 drmach_getmsg_thread_run = 1;
2511 if (drmach_getmsg_thread == NULL)
2512 drmach_getmsg_thread = thread_create(NULL, 0,
2513 (void (*)())drmach_mbox_getmsg, NULL, 0, &p0,
2514 TS_RUN, minclsyspri);
2515
2516 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
2517 serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
2518 sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
2519 kmem_free(obufp, sizeof (dr_proto_hdr_t));
2520 if (serr) {
2521 cmn_err(CE_WARN, "mbox_init: MBOX_INIT failed ecode=0x%x",
2522 serr->e_code);
2523 sbd_err_clear(&serr);
2524 return (-1);
2525 }
2526 mutex_enter(&drmach_g_mbox_mutex);
2527 drmach_mbox_iflag = 1;
2528 drmach_mbox_ipending = 0;
2529 mutex_exit(&drmach_g_mbox_mutex);
2530
2531 return (0);
2532 }
2533
2534 static int
drmach_mbox_fini()2535 drmach_mbox_fini()
2536 {
2537 int err, rv = 0;
2538
2539 if (drmach_mbox_istate > 2) {
2540 drmach_getmsg_thread_run = 0;
2541 drmach_sendmsg_thread_run = 0;
2542 cmn_err(CE_WARN,
2543 "drmach_mbox_fini: waiting for mbox threads...");
2544 while ((drmach_getmsg_thread_run == 0) ||
2545 (drmach_sendmsg_thread_run == 0)) {
2546 continue;
2547 }
2548 cmn_err(CE_WARN, "drmach_mbox_fini: mbox threads done.");
2549 mutex_destroy(&drmach_msglist_mutex);
2550
2551 }
2552 if (drmach_mbox_istate) {
2553 /* de-register the outgoing mailbox */
2554 if ((err = mboxsc_fini(KEY_DRSC)) != 0) {
2555 cmn_err(CE_WARN, "DR - SC mboxsc_fini failed: 0x%x",
2556 err);
2557 rv = -1;
2558 }
2559 }
2560 if (drmach_mbox_istate > 1) {
2561 /* de-register the incoming mailbox */
2562 if ((err = mboxsc_fini(KEY_SCDR)) != 0) {
2563 cmn_err(CE_WARN, "SC - DR mboxsc_fini failed: 0x%x",
2564 err);
2565 rv = -1;
2566 }
2567 }
2568 mutex_destroy(&drmach_g_mbox_mutex);
2569 mutex_destroy(&drmach_ri_mbox_mutex);
2570 return (rv);
2571 }
2572
2573 static int
drmach_portid2bnum(int portid)2574 drmach_portid2bnum(int portid)
2575 {
2576 int slot;
2577
2578 switch (portid & 0x1f) {
2579 case 0: case 1: case 2: case 3: /* cpu/wci devices */
2580 case 0x1e: /* slot 0 axq registers */
2581 slot = 0;
2582 break;
2583
2584 case 8: case 9: /* cpu devices */
2585 case 0x1c: case 0x1d: /* schizo/wci devices */
2586 case 0x1f: /* slot 1 axq registers */
2587 slot = 1;
2588 break;
2589
2590 default:
2591 ASSERT(0); /* catch in debug kernels */
2592 }
2593
2594 return (((portid >> 4) & 0x7e) | slot);
2595 }
2596
2597 extern int axq_suspend_iopause;
2598
2599 static int
hold_rele_branch(dev_info_t * rdip,void * arg)2600 hold_rele_branch(dev_info_t *rdip, void *arg)
2601 {
2602 int i;
2603 int *holdp = (int *)arg;
2604 char *name = ddi_node_name(rdip);
2605
2606 /*
2607 * For Starcat, we must be children of the root devinfo node
2608 */
2609 ASSERT(ddi_get_parent(rdip) == ddi_root_node());
2610
2611 i = drmach_name2type_idx(name);
2612
2613 /*
2614 * Only children of the root devinfo node need to be
2615 * held/released since they are the only valid targets
2616 * of tree operations. This corresponds to the node types
2617 * listed in the drmach_name2type array.
2618 */
2619 if (i < 0) {
2620 /* Not of interest to us */
2621 return (DDI_WALK_PRUNECHILD);
2622 }
2623
2624 if (*holdp) {
2625 ASSERT(!e_ddi_branch_held(rdip));
2626 e_ddi_branch_hold(rdip);
2627 } else {
2628 ASSERT(e_ddi_branch_held(rdip));
2629 e_ddi_branch_rele(rdip);
2630 }
2631
2632 return (DDI_WALK_PRUNECHILD);
2633 }
2634
2635 static int
drmach_init(void)2636 drmach_init(void)
2637 {
2638 pnode_t nodeid;
2639 gdcd_t *gdcd;
2640 int bnum;
2641 dev_info_t *rdip;
2642 int hold, circ;
2643
2644 mutex_enter(&drmach_i_lock);
2645 if (drmach_initialized) {
2646 mutex_exit(&drmach_i_lock);
2647 return (0);
2648 }
2649
2650 gdcd = drmach_gdcd_new();
2651 if (gdcd == NULL) {
2652 mutex_exit(&drmach_i_lock);
2653 cmn_err(CE_WARN, "drmach_init: failed to access GDCD\n");
2654 return (-1);
2655 }
2656
2657 drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
2658
2659 nodeid = prom_childnode(prom_rootnode());
2660 do {
2661 int len;
2662 int portid;
2663 drmachid_t id;
2664
2665 len = prom_getproplen(nodeid, "portid");
2666 if (len != sizeof (portid))
2667 continue;
2668
2669 portid = -1;
2670 (void) prom_getprop(nodeid, "portid", (caddr_t)&portid);
2671 if (portid == -1)
2672 continue;
2673
2674 bnum = drmach_portid2bnum(portid);
2675
2676 if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
2677 /* portid translated to an invalid board number */
2678 cmn_err(CE_WARN, "OBP node 0x%x has"
2679 " invalid property value, %s=%u",
2680 nodeid, "portid", portid);
2681
2682 /* clean up */
2683 drmach_array_dispose(drmach_boards,
2684 drmach_board_dispose);
2685 drmach_gdcd_dispose(gdcd);
2686 mutex_exit(&drmach_i_lock);
2687 return (-1);
2688 } else if (id == NULL) {
2689 drmach_board_t *bp;
2690 l1_slot_stat_t *dcd;
2691 int exp, slot;
2692
2693 bp = drmach_board_new(bnum);
2694 bp->assigned = !drmach_initialized;
2695 bp->powered = !drmach_initialized;
2696
2697 exp = DRMACH_BNUM2EXP(bnum);
2698 slot = DRMACH_BNUM2SLOT(bnum);
2699 dcd = &gdcd->dcd_slot[exp][slot];
2700 bp->stardrb_offset =
2701 dcd->l1ss_cpu_drblock_xwd_offset << 3;
2702 DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
2703 bp->stardrb_offset);
2704
2705 if (gdcd->dcd_slot[exp][slot].l1ss_flags &
2706 L1SSFLG_THIS_L1_NULL_PROC_LPA) {
2707 bp->flags |= DRMACH_NULL_PROC_LPA;
2708 DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
2709 }
2710 }
2711 } while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE);
2712
2713 drmach_cpu_sram_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
2714
2715 if (gdcd->dcd_testcage_log2_mbytes_size != DCD_DR_TESTCAGE_DISABLED) {
2716 ASSERT(gdcd->dcd_testcage_log2_mbytes_size ==
2717 gdcd->dcd_testcage_log2_mbytes_align);
2718 drmach_iocage_paddr =
2719 (uint64_t)gdcd->dcd_testcage_mbyte_PA << 20;
2720 drmach_iocage_size =
2721 1 << (gdcd->dcd_testcage_log2_mbytes_size + 20);
2722
2723 drmach_iocage_vaddr = (caddr_t)vmem_alloc(heap_arena,
2724 drmach_iocage_size, VM_SLEEP);
2725 hat_devload(kas.a_hat, drmach_iocage_vaddr, drmach_iocage_size,
2726 mmu_btop(drmach_iocage_paddr),
2727 PROT_READ | PROT_WRITE,
2728 HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
2729
2730 DRMACH_PR("gdcd size=0x%x align=0x%x PA=0x%x\n",
2731 gdcd->dcd_testcage_log2_mbytes_size,
2732 gdcd->dcd_testcage_log2_mbytes_align,
2733 gdcd->dcd_testcage_mbyte_PA);
2734 DRMACH_PR("drmach size=0x%x PA=0x%lx VA=0x%p\n",
2735 drmach_iocage_size, drmach_iocage_paddr,
2736 (void *)drmach_iocage_vaddr);
2737 }
2738
2739 if (drmach_iocage_size == 0) {
2740 drmach_array_dispose(drmach_boards, drmach_board_dispose);
2741 drmach_boards = NULL;
2742 vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
2743 drmach_gdcd_dispose(gdcd);
2744 mutex_exit(&drmach_i_lock);
2745 cmn_err(CE_WARN, "drmach_init: iocage not available\n");
2746 return (-1);
2747 }
2748
2749 drmach_gdcd_dispose(gdcd);
2750
2751 mutex_init(&drmach_iocage_lock, NULL, MUTEX_DRIVER, NULL);
2752 cv_init(&drmach_iocage_cv, NULL, CV_DRIVER, NULL);
2753 mutex_init(&drmach_xt_mb_lock, NULL, MUTEX_DRIVER, NULL);
2754 mutex_init(&drmach_bus_sync_lock, NULL, MUTEX_DRIVER, NULL);
2755 mutex_init(&drmach_slice_table_lock, NULL, MUTEX_DRIVER, NULL);
2756
2757 mutex_enter(&cpu_lock);
2758 mutex_enter(&drmach_iocage_lock);
2759 ASSERT(drmach_iocage_is_busy == 0);
2760 drmach_iocage_is_busy = 1;
2761 drmach_iocage_mem_scrub(drmach_iocage_size);
2762 drmach_iocage_is_busy = 0;
2763 cv_signal(&drmach_iocage_cv);
2764 mutex_exit(&drmach_iocage_lock);
2765 mutex_exit(&cpu_lock);
2766
2767
2768 if (drmach_mbox_init() == -1) {
2769 cmn_err(CE_WARN, "DR - SC mailbox initialization Failed");
2770 }
2771
2772 /*
2773 * Walk immediate children of devinfo root node and hold
2774 * all devinfo branches of interest.
2775 */
2776 hold = 1;
2777 rdip = ddi_root_node();
2778
2779 ndi_devi_enter(rdip, &circ);
2780 ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
2781 ndi_devi_exit(rdip, circ);
2782
2783 drmach_initialized = 1;
2784
2785 /*
2786 * To avoid a circular patch dependency between DR and AXQ, the AXQ
2787 * rev introducing the axq_iopause_*_all interfaces should not regress
2788 * when installed without the DR rev using those interfaces. The default
2789 * is for iopause to be enabled/disabled during axq suspend/resume. By
2790 * setting the following axq flag to zero, axq will not enable iopause
2791 * during suspend/resume, instead DR will call the axq_iopause_*_all
2792 * interfaces during drmach_copy_rename.
2793 */
2794 axq_suspend_iopause = 0;
2795
2796 mutex_exit(&drmach_i_lock);
2797
2798 return (0);
2799 }
2800
2801 static void
drmach_fini(void)2802 drmach_fini(void)
2803 {
2804 dev_info_t *rdip;
2805 int hold, circ;
2806
2807 if (drmach_initialized) {
2808 rw_enter(&drmach_boards_rwlock, RW_WRITER);
2809 drmach_array_dispose(drmach_boards, drmach_board_dispose);
2810 drmach_boards = NULL;
2811 rw_exit(&drmach_boards_rwlock);
2812
2813 mutex_destroy(&drmach_slice_table_lock);
2814 mutex_destroy(&drmach_xt_mb_lock);
2815 mutex_destroy(&drmach_bus_sync_lock);
2816 cv_destroy(&drmach_iocage_cv);
2817 mutex_destroy(&drmach_iocage_lock);
2818
2819 vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
2820
2821 /*
2822 * Walk immediate children of the root devinfo node
2823 * releasing holds acquired on branches in drmach_init()
2824 */
2825 hold = 0;
2826 rdip = ddi_root_node();
2827
2828 ndi_devi_enter(rdip, &circ);
2829 ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
2830 ndi_devi_exit(rdip, circ);
2831
2832 drmach_initialized = 0;
2833 }
2834
2835 (void) drmach_mbox_fini();
2836 if (drmach_xt_mb != NULL) {
2837 vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
2838 drmach_xt_mb_size);
2839 }
2840 rw_destroy(&drmach_boards_rwlock);
2841 mutex_destroy(&drmach_i_lock);
2842 }
2843
2844 static void
drmach_mem_read_madr(drmach_mem_t * mp,int bank,uint64_t * madr)2845 drmach_mem_read_madr(drmach_mem_t *mp, int bank, uint64_t *madr)
2846 {
2847 kpreempt_disable();
2848
2849 /* get register address, read madr value */
2850 if (STARCAT_CPUID_TO_PORTID(CPU->cpu_id) == mp->dev.portid) {
2851 *madr = lddmcdecode(DRMACH_MC_ASI_ADDR(mp, bank));
2852 } else {
2853 *madr = lddphysio(DRMACH_MC_ADDR(mp, bank));
2854 }
2855
2856 kpreempt_enable();
2857 }
2858
2859
2860 static uint64_t *
drmach_prep_mc_rename(uint64_t * p,int local,drmach_mem_t * mp,uint64_t current_basepa,uint64_t new_basepa)2861 drmach_prep_mc_rename(uint64_t *p, int local,
2862 drmach_mem_t *mp, uint64_t current_basepa, uint64_t new_basepa)
2863 {
2864 int bank;
2865
2866 for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
2867 uint64_t madr, bank_offset;
2868
2869 /* fetch mc's bank madr register value */
2870 drmach_mem_read_madr(mp, bank, &madr);
2871 if (madr & DRMACH_MC_VALID_MASK) {
2872 uint64_t bankpa;
2873
2874 bank_offset = (DRMACH_MC_UM_TO_PA(madr) |
2875 DRMACH_MC_LM_TO_PA(madr)) - current_basepa;
2876 bankpa = new_basepa + bank_offset;
2877
2878 /* encode new base pa into madr */
2879 madr &= ~DRMACH_MC_UM_MASK;
2880 madr |= DRMACH_MC_PA_TO_UM(bankpa);
2881 madr &= ~DRMACH_MC_LM_MASK;
2882 madr |= DRMACH_MC_PA_TO_LM(bankpa);
2883
2884 if (local)
2885 *p++ = DRMACH_MC_ASI_ADDR(mp, bank);
2886 else
2887 *p++ = DRMACH_MC_ADDR(mp, bank);
2888
2889 *p++ = madr;
2890 }
2891 }
2892
2893 return (p);
2894 }
2895
2896 static uint64_t *
drmach_prep_schizo_script(uint64_t * p,drmach_mem_t * mp,uint64_t new_basepa)2897 drmach_prep_schizo_script(uint64_t *p, drmach_mem_t *mp, uint64_t new_basepa)
2898 {
2899 drmach_board_t *bp;
2900 int rv;
2901 int idx;
2902 drmachid_t id;
2903 uint64_t last_scsr_pa = 0;
2904
2905 /* memory is always in slot 0 */
2906 ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
2907
2908 /* look up slot 1 board on same expander */
2909 idx = DRMACH_EXPSLOT2BNUM(DRMACH_BNUM2EXP(mp->dev.bp->bnum), 1);
2910 rv = drmach_array_get(drmach_boards, idx, &id);
2911 bp = id; /* bp will be NULL if board not found */
2912
2913 /* look up should never be out of bounds */
2914 ASSERT(rv == 0);
2915
2916 /* nothing to do when board is not found or has no devices */
2917 if (rv == -1 || bp == NULL || bp->devices == NULL)
2918 return (p);
2919
2920 rv = drmach_array_first(bp->devices, &idx, &id);
2921 while (rv == 0) {
2922 if (DRMACH_IS_IO_ID(id)) {
2923 drmach_io_t *io = id;
2924
2925 /*
2926 * Skip all non-Schizo IO devices (only IO nodes
2927 * that are Schizo devices have non-zero scsr_pa).
2928 * Filter out "other" leaf to avoid writing to the
2929 * same Schizo Control/Status Register twice.
2930 */
2931 if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
2932 uint64_t scsr;
2933
2934 scsr = lddphysio(io->scsr_pa);
2935 scsr &= ~(DRMACH_LPA_BASE_MASK |
2936 DRMACH_LPA_BND_MASK);
2937 scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
2938 scsr |= DRMACH_PA_TO_LPA_BND(
2939 new_basepa + DRMACH_MEM_SLICE_SIZE);
2940
2941 *p++ = io->scsr_pa;
2942 *p++ = scsr;
2943
2944 last_scsr_pa = io->scsr_pa;
2945 }
2946 }
2947 rv = drmach_array_next(bp->devices, &idx, &id);
2948 }
2949
2950 return (p);
2951 }
2952
2953 /*
2954 * For Panther MCs, append the MC idle reg address and drmach_mem_t pointer.
2955 * The latter is returned when drmach_rename fails to idle a Panther MC and
2956 * is used to identify the MC for error reporting.
2957 */
2958 static uint64_t *
drmach_prep_pn_mc_idle(uint64_t * p,drmach_mem_t * mp,int local)2959 drmach_prep_pn_mc_idle(uint64_t *p, drmach_mem_t *mp, int local)
2960 {
2961 /* only slot 0 has memory */
2962 ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
2963 ASSERT(IS_PANTHER(mp->dev.bp->cpu_impl));
2964
2965 for (mp = mp->dev.bp->mem; mp != NULL; mp = mp->next) {
2966 ASSERT(DRMACH_IS_MEM_ID(mp));
2967
2968 if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
2969 if (local) {
2970 *p++ = ASI_EMU_ACT_STATUS_VA; /* local ASI */
2971 *p++ = (uintptr_t)mp;
2972 }
2973 } else if (!local) {
2974 *p++ = DRMACH_EMU_ACT_STATUS_ADDR(mp); /* PIO */
2975 *p++ = (uintptr_t)mp;
2976 }
2977 }
2978
2979 return (p);
2980 }
2981
2982 static sbd_error_t *
drmach_prep_rename_script(drmach_mem_t * s_mp,drmach_mem_t * t_mp,uint64_t t_slice_offset,caddr_t buf,int buflen)2983 drmach_prep_rename_script(drmach_mem_t *s_mp, drmach_mem_t *t_mp,
2984 uint64_t t_slice_offset, caddr_t buf, int buflen)
2985 {
2986 _NOTE(ARGUNUSED(buflen))
2987
2988 uint64_t *p = (uint64_t *)buf, *q;
2989 sbd_error_t *err;
2990 int rv;
2991 drmach_mem_t *mp, *skip_mp;
2992 uint64_t s_basepa, t_basepa;
2993 uint64_t s_new_basepa, t_new_basepa;
2994
2995 /* verify supplied buffer space is adequate */
2996 ASSERT(buflen >=
2997 /* addr for all possible MC banks */
2998 (sizeof (uint64_t) * 4 * 4 * 18) +
2999 /* list section terminator */
3000 (sizeof (uint64_t) * 1) +
3001 /* addr/id tuple for local Panther MC idle reg */
3002 (sizeof (uint64_t) * 2) +
3003 /* list section terminator */
3004 (sizeof (uint64_t) * 1) +
3005 /* addr/id tuple for 2 boards with 4 Panther MC idle regs */
3006 (sizeof (uint64_t) * 2 * 2 * 4) +
3007 /* list section terminator */
3008 (sizeof (uint64_t) * 1) +
3009 /* addr/val tuple for 1 proc with 4 MC banks */
3010 (sizeof (uint64_t) * 2 * 4) +
3011 /* list section terminator */
3012 (sizeof (uint64_t) * 1) +
3013 /* addr/val tuple for 2 boards w/ 2 schizos each */
3014 (sizeof (uint64_t) * 2 * 2 * 2) +
3015 /* addr/val tuple for 2 boards w/ 16 MC banks each */
3016 (sizeof (uint64_t) * 2 * 2 * 16) +
3017 /* list section terminator */
3018 (sizeof (uint64_t) * 1) +
3019 /* addr/val tuple for 18 AXQs w/ two slots each */
3020 (sizeof (uint64_t) * 2 * 2 * 18) +
3021 /* list section terminator */
3022 (sizeof (uint64_t) * 1) +
3023 /* list terminator */
3024 (sizeof (uint64_t) * 1));
3025
3026 /* copy bank list to rename script */
3027 mutex_enter(&drmach_bus_sync_lock);
3028 for (q = drmach_bus_sync_list; *q; q++, p++)
3029 *p = *q;
3030 mutex_exit(&drmach_bus_sync_lock);
3031
3032 /* list section terminator */
3033 *p++ = 0;
3034
3035 /*
3036 * Write idle script for MC on this processor. A script will be
3037 * produced only if this is a Panther processor on the source or
3038 * target board.
3039 */
3040 if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
3041 p = drmach_prep_pn_mc_idle(p, s_mp, 1);
3042
3043 if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
3044 p = drmach_prep_pn_mc_idle(p, t_mp, 1);
3045
3046 /* list section terminator */
3047 *p++ = 0;
3048
3049 /*
3050 * Write idle script for all other MCs on source and target
3051 * Panther boards.
3052 */
3053 if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
3054 p = drmach_prep_pn_mc_idle(p, s_mp, 0);
3055
3056 if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
3057 p = drmach_prep_pn_mc_idle(p, t_mp, 0);
3058
3059 /* list section terminator */
3060 *p++ = 0;
3061
3062 /*
3063 * Step 1: Write source base address to target MC
3064 * with present bit off.
3065 * Step 2: Now rewrite target reg with present bit on.
3066 */
3067 err = drmach_mem_get_base_physaddr(s_mp, &s_basepa);
3068 ASSERT(err == NULL);
3069 err = drmach_mem_get_base_physaddr(t_mp, &t_basepa);
3070 ASSERT(err == NULL);
3071
3072 /* exchange base pa. include slice offset in new target base pa */
3073 s_new_basepa = t_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1);
3074 t_new_basepa = (s_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1)) +
3075 t_slice_offset;
3076
3077 DRMACH_PR("s_new_basepa 0x%lx\n", s_new_basepa);
3078 DRMACH_PR("t_new_basepa 0x%lx\n", t_new_basepa);
3079
3080 DRMACH_PR("preparing MC MADR rename script (master is CPU%d):\n",
3081 CPU->cpu_id);
3082
3083 /*
3084 * Write rename script for MC on this processor. A script will
3085 * be produced only if this processor is on the source or target
3086 * board.
3087 */
3088
3089 skip_mp = NULL;
3090 mp = s_mp->dev.bp->mem;
3091 while (mp != NULL && skip_mp == NULL) {
3092 if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
3093 skip_mp = mp;
3094 p = drmach_prep_mc_rename(p, 1, mp, s_basepa,
3095 s_new_basepa);
3096 }
3097
3098 mp = mp->next;
3099 }
3100
3101 mp = t_mp->dev.bp->mem;
3102 while (mp != NULL && skip_mp == NULL) {
3103 if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
3104 skip_mp = mp;
3105 p = drmach_prep_mc_rename(p, 1, mp, t_basepa,
3106 t_new_basepa);
3107 }
3108
3109 mp = mp->next;
3110 }
3111
3112 /* list section terminator */
3113 *p++ = 0;
3114
3115 /*
3116 * Write rename script for all other MCs on source and target
3117 * boards.
3118 */
3119
3120 for (mp = s_mp->dev.bp->mem; mp; mp = mp->next) {
3121 if (mp == skip_mp)
3122 continue;
3123 p = drmach_prep_mc_rename(p, 0, mp, s_basepa, s_new_basepa);
3124 }
3125
3126 for (mp = t_mp->dev.bp->mem; mp; mp = mp->next) {
3127 if (mp == skip_mp)
3128 continue;
3129 p = drmach_prep_mc_rename(p, 0, mp, t_basepa, t_new_basepa);
3130 }
3131
3132 /* Write rename script for Schizo LPA_BASE/LPA_BND */
3133 p = drmach_prep_schizo_script(p, s_mp, s_new_basepa);
3134 p = drmach_prep_schizo_script(p, t_mp, t_new_basepa);
3135
3136 /* list section terminator */
3137 *p++ = 0;
3138
3139 DRMACH_PR("preparing AXQ CASM rename script (EXP%d <> EXP%d):\n",
3140 DRMACH_BNUM2EXP(s_mp->dev.bp->bnum),
3141 DRMACH_BNUM2EXP(t_mp->dev.bp->bnum));
3142
3143 rv = axq_do_casm_rename_script(&p,
3144 DRMACH_PA_TO_SLICE(s_new_basepa),
3145 DRMACH_PA_TO_SLICE(t_new_basepa));
3146 if (rv == DDI_FAILURE)
3147 return (DRMACH_INTERNAL_ERROR());
3148
3149 /* list section & final terminator */
3150 *p++ = 0;
3151 *p++ = 0;
3152
3153 #ifdef DEBUG
3154 {
3155 uint64_t *q = (uint64_t *)buf;
3156
3157 /* paranoia */
3158 ASSERT((caddr_t)p <= buf + buflen);
3159
3160 DRMACH_PR("MC bank base pa list:\n");
3161 while (*q) {
3162 uint64_t a = *q++;
3163
3164 DRMACH_PR("0x%lx\n", a);
3165 }
3166
3167 /* skip terminator */
3168 q += 1;
3169
3170 DRMACH_PR("local Panther MC idle reg (via ASI 0x4a):\n");
3171 while (*q) {
3172 DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
3173 q += 2;
3174 }
3175
3176 /* skip terminator */
3177 q += 1;
3178
3179 DRMACH_PR("non-local Panther MC idle reg (via ASI 0x15):\n");
3180 while (*q) {
3181 DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
3182 q += 2;
3183 }
3184
3185 /* skip terminator */
3186 q += 1;
3187
3188 DRMACH_PR("MC reprogramming script (via ASI 0x72):\n");
3189 while (*q) {
3190 uint64_t r = *q++; /* register address */
3191 uint64_t v = *q++; /* new register value */
3192
3193 DRMACH_PR("0x%lx = 0x%lx, basepa 0x%lx\n",
3194 r, v, (long)(DRMACH_MC_UM_TO_PA(v)|
3195 DRMACH_MC_LM_TO_PA(v)));
3196 }
3197
3198 /* skip terminator */
3199 q += 1;
3200
3201 DRMACH_PR("MC/SCHIZO reprogramming script:\n");
3202 while (*q) {
3203 DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
3204 q += 2;
3205 }
3206
3207 /* skip terminator */
3208 q += 1;
3209
3210 DRMACH_PR("AXQ reprogramming script:\n");
3211 while (*q) {
3212 DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
3213 q += 2;
3214 }
3215
3216 /* verify final terminator is present */
3217 ASSERT(*(q + 1) == 0);
3218
3219 DRMACH_PR("copy-rename script 0x%p, len %d\n",
3220 (void *)buf, (int)((intptr_t)p - (intptr_t)buf));
3221
3222 if (drmach_debug)
3223 DELAY(10000000);
3224 }
3225 #endif
3226
3227 return (NULL);
3228 }
3229
3230 static void
drmach_prep_xt_mb_for_slice_update(drmach_board_t * bp,uchar_t slice)3231 drmach_prep_xt_mb_for_slice_update(drmach_board_t *bp, uchar_t slice)
3232 {
3233 int rv;
3234
3235 ASSERT(MUTEX_HELD(&drmach_xt_mb_lock));
3236
3237 if (bp->devices) {
3238 int d_idx;
3239 drmachid_t d_id;
3240
3241 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
3242 while (rv == 0) {
3243 if (DRMACH_IS_CPU_ID(d_id)) {
3244 drmach_cpu_t *cp = d_id;
3245 processorid_t cpuid = cp->cpuid;
3246
3247 mutex_enter(&cpu_lock);
3248 if (cpu[cpuid] && cpu[cpuid]->cpu_flags)
3249 drmach_xt_mb[cpuid] = 0x80 | slice;
3250 mutex_exit(&cpu_lock);
3251 }
3252 rv = drmach_array_next(bp->devices, &d_idx, &d_id);
3253 }
3254 }
3255 if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
3256 drmach_board_t *s1bp = NULL;
3257
3258 rv = drmach_array_get(drmach_boards, bp->bnum + 1,
3259 (void *) &s1bp);
3260 if (rv == 0 && s1bp != NULL) {
3261 ASSERT(DRMACH_IS_BOARD_ID(s1bp));
3262 ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
3263 drmach_prep_xt_mb_for_slice_update(s1bp, slice);
3264 }
3265 }
3266 }
3267
3268 sbd_error_t *
drmach_copy_rename_init(drmachid_t t_id,uint64_t t_slice_offset,drmachid_t s_id,struct memlist * c_ml,drmachid_t * cr_id)3269 drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset,
3270 drmachid_t s_id, struct memlist *c_ml, drmachid_t *cr_id)
3271 {
3272 extern void drmach_rename(uint64_t *, uint_t *, uint64_t *);
3273 extern void drmach_rename_end(void);
3274
3275 drmach_mem_t *s_mp, *t_mp;
3276 struct memlist *x_ml;
3277 uint64_t off_mask, s_copybasepa, t_copybasepa, t_basepa;
3278 int len;
3279 caddr_t bp, wp;
3280 uint_t *p, *q;
3281 sbd_error_t *err;
3282 tte_t *tte;
3283 drmach_copy_rename_t *cr;
3284
3285 if (!DRMACH_IS_MEM_ID(s_id))
3286 return (drerr_new(0, ESTC_INAPPROP, NULL));
3287 if (!DRMACH_IS_MEM_ID(t_id))
3288 return (drerr_new(0, ESTC_INAPPROP, NULL));
3289 s_mp = s_id;
3290 t_mp = t_id;
3291
3292 /* get starting physical address of target memory */
3293 err = drmach_mem_get_base_physaddr(t_id, &t_basepa);
3294 if (err)
3295 return (err);
3296
3297 /* calculate slice offset mask from slice size */
3298 off_mask = DRMACH_MEM_SLICE_SIZE - 1;
3299
3300 /* calculate source and target base pa */
3301 s_copybasepa = c_ml->ml_address;
3302 t_copybasepa =
3303 t_basepa + ((c_ml->ml_address & off_mask) - t_slice_offset);
3304
3305 /* paranoia */
3306 ASSERT((c_ml->ml_address & off_mask) >= t_slice_offset);
3307
3308 /* adjust copy memlist addresses to be relative to copy base pa */
3309 x_ml = c_ml;
3310 while (x_ml != NULL) {
3311 x_ml->ml_address -= s_copybasepa;
3312 x_ml = x_ml->ml_next;
3313 }
3314
3315 #ifdef DEBUG
3316 {
3317 uint64_t s_basepa, s_size, t_size;
3318
3319 x_ml = c_ml;
3320 while (x_ml->ml_next != NULL)
3321 x_ml = x_ml->ml_next;
3322
3323 DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n",
3324 s_copybasepa,
3325 s_copybasepa + x_ml->ml_address + x_ml->ml_size);
3326
3327 DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n",
3328 t_copybasepa,
3329 t_copybasepa + x_ml->ml_address + x_ml->ml_size);
3330
3331 DRMACH_PR("copy memlist (relative to copy base pa):\n");
3332 DRMACH_MEMLIST_DUMP(c_ml);
3333
3334 err = drmach_mem_get_base_physaddr(s_id, &s_basepa);
3335 ASSERT(err == NULL);
3336
3337 err = drmach_mem_get_size(s_id, &s_size);
3338 ASSERT(err == NULL);
3339
3340 err = drmach_mem_get_size(t_id, &t_size);
3341 ASSERT(err == NULL);
3342
3343 DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n",
3344 s_basepa, s_size);
3345 DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n",
3346 t_basepa, t_size);
3347 }
3348 #endif /* DEBUG */
3349
3350 /* Map in appropriate cpu sram page */
3351 tte = &drmach_cpu_sram_tte[CPU->cpu_id];
3352 ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) &&
3353 TTE_IS_PRIVILEGED(tte) && TTE_IS_LOCKED(tte));
3354 sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
3355 sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
3356
3357 bp = wp = drmach_cpu_sram_va;
3358
3359 /* Make sure the rename routine will fit */
3360 len = (ptrdiff_t)drmach_rename_end - (ptrdiff_t)drmach_rename;
3361 ASSERT(wp + len < bp + PAGESIZE);
3362
3363 /* copy text. standard bcopy not designed to work in nc space */
3364 p = (uint_t *)wp;
3365 q = (uint_t *)drmach_rename;
3366 while (q < (uint_t *)drmach_rename_end)
3367 *p++ = *q++;
3368
3369 /* zero remainder. standard bzero not designed to work in nc space */
3370 while (p < (uint_t *)(bp + PAGESIZE))
3371 *p++ = 0;
3372
3373 DRMACH_PR("drmach_rename function 0x%p, len %d\n", (void *)wp, len);
3374 wp += (len + 15) & ~15;
3375
3376 err = drmach_prep_rename_script(s_mp, t_mp, t_slice_offset, wp,
3377 PAGESIZE - (wp - bp));
3378 if (err) {
3379 cleanup:
3380 xt_one(CPU->cpu_id, vtag_flushpage_tl1,
3381 (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
3382 return (err);
3383 }
3384
3385 /* disable and flush CDC */
3386 if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
3387 axq_cdc_enable_all(); /* paranoia */
3388 err = DRMACH_INTERNAL_ERROR();
3389 goto cleanup;
3390 }
3391
3392 /* mark both memory units busy */
3393 t_mp->dev.busy++;
3394 s_mp->dev.busy++;
3395
3396 cr = vmem_alloc(static_alloc_arena, sizeof (drmach_copy_rename_t),
3397 VM_SLEEP);
3398 cr->isa = (void *)drmach_copy_rename_init;
3399 cr->data = wp;
3400 cr->c_ml = c_ml;
3401 cr->s_mp = s_mp;
3402 cr->t_mp = t_mp;
3403 cr->s_copybasepa = s_copybasepa;
3404 cr->t_copybasepa = t_copybasepa;
3405 cr->ecode = DRMACH_CR_OK;
3406
3407 mutex_enter(&drmach_slice_table_lock);
3408
3409 mutex_enter(&drmach_xt_mb_lock);
3410 bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
3411
3412 if (DRMACH_L1_SET_LPA(s_mp->dev.bp) && drmach_reprogram_lpa) {
3413 drmach_prep_xt_mb_for_slice_update(s_mp->dev.bp,
3414 DRMACH_PA_TO_SLICE(t_copybasepa));
3415 }
3416 if (DRMACH_L1_SET_LPA(t_mp->dev.bp) && drmach_reprogram_lpa) {
3417 drmach_prep_xt_mb_for_slice_update(t_mp->dev.bp,
3418 DRMACH_PA_TO_SLICE(s_copybasepa));
3419 }
3420
3421 *cr_id = cr;
3422 return (NULL);
3423 }
3424
3425 int drmach_rename_count;
3426 int drmach_rename_ntries;
3427
3428 sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)3429 drmach_copy_rename_fini(drmachid_t id)
3430 {
3431 drmach_copy_rename_t *cr = id;
3432 sbd_error_t *err = NULL;
3433 dr_mbox_msg_t *obufp;
3434
3435 ASSERT(cr->isa == (void *)drmach_copy_rename_init);
3436
3437 axq_cdc_enable_all();
3438
3439 xt_one(CPU->cpu_id, vtag_flushpage_tl1,
3440 (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
3441
3442 switch (cr->ecode) {
3443 case DRMACH_CR_OK:
3444 break;
3445 case DRMACH_CR_MC_IDLE_ERR: {
3446 dev_info_t *dip = NULL;
3447 drmach_mem_t *mp = (drmach_mem_t *)cr->earg;
3448 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
3449
3450 ASSERT(DRMACH_IS_MEM_ID(mp));
3451
3452 err = drmach_get_dip(mp, &dip);
3453
3454 ASSERT(err == NULL);
3455 ASSERT(dip != NULL);
3456
3457 err = drerr_new(0, ESBD_MEMFAIL, NULL);
3458 (void) ddi_pathname(dip, path);
3459 cmn_err(CE_WARN, "failed to idle memory controller %s on %s: "
3460 "copy-rename aborted", path, mp->dev.bp->cm.name);
3461 kmem_free(path, MAXPATHLEN);
3462 break;
3463 }
3464 case DRMACH_CR_IOPAUSE_ERR:
3465 ASSERT((uintptr_t)cr->earg >= 0 &&
3466 (uintptr_t)cr->earg < AXQ_MAX_EXP);
3467
3468 err = drerr_new(0, ESBD_SUSPEND, "EX%d", (uintptr_t)cr->earg);
3469 cmn_err(CE_WARN, "failed to idle EX%ld AXQ slot1 activity prior"
3470 " to copy-rename", (uintptr_t)cr->earg);
3471 break;
3472 case DRMACH_CR_ONTRAP_ERR:
3473 err = drerr_new(0, ESBD_MEMFAIL, NULL);
3474 cmn_err(CE_WARN, "copy-rename aborted due to uncorrectable "
3475 "memory error");
3476 break;
3477 default:
3478 err = DRMACH_INTERNAL_ERROR();
3479 cmn_err(CE_WARN, "unknown copy-rename error code (%d)\n",
3480 cr->ecode);
3481 break;
3482 }
3483
3484 #ifdef DEBUG
3485 if ((DRMACH_L1_SET_LPA(cr->s_mp->dev.bp) ||
3486 DRMACH_L1_SET_LPA(cr->t_mp->dev.bp)) && drmach_reprogram_lpa) {
3487 int i;
3488 for (i = 0; i < NCPU; i++) {
3489 if (drmach_xt_mb[i])
3490 DRMACH_PR("cpu%d ignored drmach_xt_mb", i);
3491 }
3492 }
3493 #endif
3494 mutex_exit(&drmach_xt_mb_lock);
3495
3496 if (cr->c_ml != NULL)
3497 memlist_delete(cr->c_ml);
3498
3499 cr->t_mp->dev.busy--;
3500 cr->s_mp->dev.busy--;
3501
3502 if (err) {
3503 mutex_exit(&drmach_slice_table_lock);
3504 goto done;
3505 }
3506
3507 /* update casm shadow for target and source board */
3508 drmach_slice_table_update(cr->t_mp->dev.bp, 0);
3509 drmach_slice_table_update(cr->s_mp->dev.bp, 0);
3510 mutex_exit(&drmach_slice_table_lock);
3511
3512 mutex_enter(&drmach_bus_sync_lock);
3513 drmach_bus_sync_list_update();
3514 mutex_exit(&drmach_bus_sync_lock);
3515
3516 /*
3517 * Make a good-faith effort to notify the SC about the copy-rename, but
3518 * don't worry if it fails, since a subsequent claim/unconfig/unclaim
3519 * will duplicate the update.
3520 */
3521 obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
3522 mutex_enter(&drmach_slice_table_lock);
3523 drmach_msg_memslice_init(obufp->msgdata.dm_uc.mem_slice);
3524 drmach_msg_memregs_init(obufp->msgdata.dm_uc.mem_regs);
3525 mutex_exit(&drmach_slice_table_lock);
3526 (void) drmach_mbox_trans(DRMSG_UNCONFIG, cr->s_mp->dev.bp->bnum,
3527 (caddr_t)obufp, sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
3528 kmem_free(obufp, sizeof (dr_mbox_msg_t));
3529
3530 done:
3531 vmem_free(static_alloc_arena, cr, sizeof (drmach_copy_rename_t));
3532
3533 DRMACH_PR("waited %d out of %d tries for drmach_rename_wait on %d cpus",
3534 drmach_rename_ntries, drmach_cpu_ntries, drmach_rename_count);
3535
3536 return (err);
3537 }
3538
3539 int drmach_slow_copy = 0;
3540
3541 void
drmach_copy_rename(drmachid_t id)3542 drmach_copy_rename(drmachid_t id)
3543 {
3544 extern uint_t getpstate(void);
3545 extern void setpstate(uint_t);
3546
3547 extern xcfunc_t drmach_rename_wait;
3548 extern xcfunc_t drmach_rename_done;
3549 extern xcfunc_t drmach_rename_abort;
3550
3551 drmach_copy_rename_t *cr = id;
3552 uint64_t neer;
3553 struct memlist *ml;
3554 int i, count;
3555 int csize, lnsize;
3556 uint64_t caddr;
3557 cpuset_t cpuset;
3558 uint_t pstate;
3559 uint32_t exp = 0;
3560 on_trap_data_t otd;
3561 xcfunc_t *drmach_end_wait_xcall = drmach_rename_done;
3562
3563 ASSERT(cr->isa == (void *)drmach_copy_rename_init);
3564 ASSERT(MUTEX_HELD(&cpu_lock));
3565 ASSERT(cr->ecode == DRMACH_CR_OK);
3566
3567 /*
3568 * Prevent slot1 IO from accessing Safari memory bus.
3569 */
3570 if (axq_iopause_enable_all(&exp) != DDI_SUCCESS) {
3571 ASSERT(exp >= 0 && exp < AXQ_MAX_EXP);
3572 cr->ecode = DRMACH_CR_IOPAUSE_ERR;
3573 cr->earg = (void *)(uintptr_t)exp;
3574 return;
3575 }
3576
3577 cpuset = cpu_ready_set;
3578 CPUSET_DEL(cpuset, CPU->cpu_id);
3579 count = ncpus - 1;
3580 drmach_rename_count = count; /* for debug */
3581
3582 drmach_xt_ready = 0;
3583 xt_some(cpuset, drmach_rename_wait, NULL, NULL);
3584
3585 for (i = 0; i < drmach_cpu_ntries; i++) {
3586 if (drmach_xt_ready == count)
3587 break;
3588 DELAY(drmach_cpu_delay);
3589 }
3590
3591 drmach_rename_ntries = i; /* for debug */
3592
3593 drmach_xt_ready = 0; /* steal the line back */
3594 for (i = 0; i < NCPU; i++) /* steal the line back, preserve data */
3595 drmach_xt_mb[i] = drmach_xt_mb[i];
3596
3597 caddr = drmach_iocage_paddr;
3598 csize = cpunodes[CPU->cpu_id].ecache_size;
3599 lnsize = cpunodes[CPU->cpu_id].ecache_linesize;
3600
3601 /* disable CE reporting */
3602 neer = get_error_enable();
3603 set_error_enable(neer & ~EN_REG_CEEN);
3604
3605 /* disable interrupts (paranoia) */
3606 pstate = getpstate();
3607 setpstate(pstate & ~PSTATE_IE);
3608
3609 /*
3610 * Execute copy-rename under on_trap to protect against a panic due
3611 * to an uncorrectable error. Instead, DR will abort the copy-rename
3612 * operation and rely on the OS to do the error reporting.
3613 *
3614 * In general, trap handling on any cpu once the copy begins
3615 * can result in an inconsistent memory image on the target.
3616 */
3617 if (on_trap(&otd, OT_DATA_EC)) {
3618 cr->ecode = DRMACH_CR_ONTRAP_ERR;
3619 goto copy_rename_end;
3620 }
3621
3622 /*
3623 * DO COPY.
3624 */
3625 for (ml = cr->c_ml; ml; ml = ml->ml_next) {
3626 uint64_t s_pa, t_pa;
3627 uint64_t nbytes;
3628
3629 s_pa = cr->s_copybasepa + ml->ml_address;
3630 t_pa = cr->t_copybasepa + ml->ml_address;
3631 nbytes = ml->ml_size;
3632
3633 while (nbytes != 0ull) {
3634 /* copy 32 bytes at src_pa to dst_pa */
3635 bcopy32_il(s_pa, t_pa);
3636
3637 /* increment by 32 bytes */
3638 s_pa += (4 * sizeof (uint64_t));
3639 t_pa += (4 * sizeof (uint64_t));
3640
3641 /* decrement by 32 bytes */
3642 nbytes -= (4 * sizeof (uint64_t));
3643
3644 if (drmach_slow_copy) { /* for debug */
3645 uint64_t i = 13 * 50;
3646 while (i--)
3647 ;
3648 }
3649 }
3650 }
3651
3652 /*
3653 * XXX CHEETAH SUPPORT
3654 * For cheetah, we need to grab the iocage lock since iocage
3655 * memory is used for e$ flush.
3656 *
3657 * NOTE: This code block is dangerous at this point in the
3658 * copy-rename operation. It modifies memory after the copy
3659 * has taken place which means that any persistent state will
3660 * be abandoned after the rename operation. The code is also
3661 * performing thread synchronization at a time when all but
3662 * one processors are paused. This is a potential deadlock
3663 * situation.
3664 *
3665 * This code block must be moved to drmach_copy_rename_init.
3666 */
3667 if (drmach_is_cheetah) {
3668 mutex_enter(&drmach_iocage_lock);
3669 while (drmach_iocage_is_busy)
3670 cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
3671 drmach_iocage_is_busy = 1;
3672 drmach_iocage_mem_scrub(ecache_size * 2);
3673 mutex_exit(&drmach_iocage_lock);
3674 }
3675
3676 /*
3677 * bcopy32_il is implemented as a series of ldxa/stxa via
3678 * ASI_MEM instructions. Following the copy loop, the E$
3679 * of the master (this) processor will have lines in state
3680 * O that correspond to lines of home memory in state gI.
3681 * An E$ flush is necessary to commit these lines before
3682 * proceeding with the rename operation.
3683 *
3684 * Flushing the E$ will automatically flush the W$, but
3685 * the D$ and I$ must be flushed separately and explicitly.
3686 */
3687 flush_ecache_il(caddr, csize, lnsize); /* inline version */
3688
3689 /*
3690 * Each line of home memory is now in state gM, except in
3691 * the case of a cheetah processor when the E$ flush area
3692 * is included within the copied region. In such a case,
3693 * the lines of home memory for the upper half of the
3694 * flush area are in state gS.
3695 *
3696 * Each line of target memory is in state gM.
3697 *
3698 * Each line of this processor's E$ is in state I, except
3699 * those of a cheetah processor. All lines of a cheetah
3700 * processor's E$ are in state S and correspond to the lines
3701 * in upper half of the E$ flush area.
3702 *
3703 * It is vital at this point that none of the lines in the
3704 * home or target memories are in state gI and that none
3705 * of the lines in this processor's E$ are in state O or Os.
3706 * A single instance of such a condition will cause loss of
3707 * coherency following the rename operation.
3708 */
3709
3710 /*
3711 * Rename
3712 */
3713 (*(void(*)())drmach_cpu_sram_va)(cr->data, &cr->ecode, &cr->earg);
3714
3715 /*
3716 * Rename operation complete. The physical address space
3717 * of the home and target memories have been swapped, the
3718 * routing data in the respective CASM entries have been
3719 * swapped, and LPA settings in the processor and schizo
3720 * devices have been reprogrammed accordingly.
3721 *
3722 * In the case of a cheetah processor, the E$ remains
3723 * populated with lines in state S that correspond to the
3724 * lines in the former home memory. Now that the physical
3725 * addresses have been swapped, these E$ lines correspond
3726 * to lines in the new home memory which are in state gM.
3727 * This combination is invalid. An additional E$ flush is
3728 * necessary to restore coherency. The E$ flush will cause
3729 * the lines of the new home memory for the flush region
3730 * to transition from state gM to gS. The former home memory
3731 * remains unmodified. This additional E$ flush has no effect
3732 * on a cheetah+ processor.
3733 */
3734 flush_ecache_il(caddr, csize, lnsize); /* inline version */
3735
3736 /*
3737 * The D$ and I$ must be flushed to ensure that coherency is
3738 * maintained. Any line in a cache that is in the valid
3739 * state has its corresponding line of the new home memory
3740 * in the gM state. This is an invalid condition. When the
3741 * flushes are complete the cache line states will be
3742 * resynchronized with those in the new home memory.
3743 */
3744 flush_icache_il(); /* inline version */
3745 flush_dcache_il(); /* inline version */
3746 flush_pcache_il(); /* inline version */
3747
3748 copy_rename_end:
3749
3750 no_trap();
3751
3752 /* enable interrupts */
3753 setpstate(pstate);
3754
3755 /* enable CE reporting */
3756 set_error_enable(neer);
3757
3758 if (cr->ecode != DRMACH_CR_OK)
3759 drmach_end_wait_xcall = drmach_rename_abort;
3760
3761 /*
3762 * XXX CHEETAH SUPPORT
3763 */
3764 if (drmach_is_cheetah) {
3765 mutex_enter(&drmach_iocage_lock);
3766 drmach_iocage_mem_scrub(ecache_size * 2);
3767 drmach_iocage_is_busy = 0;
3768 cv_signal(&drmach_iocage_cv);
3769 mutex_exit(&drmach_iocage_lock);
3770 }
3771
3772 axq_iopause_disable_all();
3773
3774 xt_some(cpuset, drmach_end_wait_xcall, NULL, NULL);
3775 }
3776
3777 static void drmach_io_dispose(drmachid_t);
3778 static sbd_error_t *drmach_io_release(drmachid_t);
3779 static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
3780
3781 static sbd_error_t *
drmach_pci_new(drmach_device_t * proto,drmachid_t * idp)3782 drmach_pci_new(drmach_device_t *proto, drmachid_t *idp)
3783 {
3784 drmach_node_t *node = proto->node;
3785 sbd_error_t *err;
3786 drmach_reg_t regs[3];
3787 int rv;
3788 int len = 0;
3789
3790 rv = node->n_getproplen(node, "reg", &len);
3791 if (rv != 0 || len != sizeof (regs)) {
3792 sbd_error_t *err;
3793
3794 /* pci nodes are expected to have regs */
3795 err = drerr_new(1, ESTC_GETPROP,
3796 "Device Node 0x%x: property %s",
3797 (uint_t)node->get_dnode(node), "reg");
3798 return (err);
3799 }
3800
3801 rv = node->n_getprop(node, "reg", (void *)regs, sizeof (regs));
3802 if (rv) {
3803 sbd_error_t *err;
3804
3805 err = drerr_new(1, ESTC_GETPROP,
3806 "Device Node 0x%x: property %s",
3807 (uint_t)node->get_dnode(node), "reg");
3808
3809 return (err);
3810 }
3811
3812 /*
3813 * Fix up unit number so that Leaf A has a lower unit number
3814 * than Leaf B.
3815 */
3816 if ((proto->portid % 2) != 0) {
3817 if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
3818 proto->unum = 0;
3819 else
3820 proto->unum = 1;
3821 } else {
3822 if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
3823 proto->unum = 2;
3824 else
3825 proto->unum = 3;
3826 }
3827
3828 err = drmach_io_new(proto, idp);
3829 if (err == NULL) {
3830 drmach_io_t *self = *idp;
3831
3832 /* reassemble 64-bit base address */
3833 self->scsr_pa = (uint64_t)regs[1].reg_addr_hi << 32;
3834 self->scsr_pa |= (uint64_t)regs[1].reg_addr_lo;
3835 }
3836
3837 return (err);
3838 }
3839
3840 static sbd_error_t *
drmach_io_new(drmach_device_t * proto,drmachid_t * idp)3841 drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
3842 {
3843 drmach_io_t *ip;
3844
3845 ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
3846 bcopy(proto, &ip->dev, sizeof (ip->dev));
3847 ip->dev.node = drmach_node_dup(proto->node);
3848 ip->dev.cm.isa = (void *)drmach_io_new;
3849 ip->dev.cm.dispose = drmach_io_dispose;
3850 ip->dev.cm.release = drmach_io_release;
3851 ip->dev.cm.status = drmach_io_status;
3852
3853 (void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
3854 ip->dev.type, ip->dev.unum);
3855
3856 *idp = (drmachid_t)ip;
3857 return (NULL);
3858 }
3859
3860 static void
drmach_io_dispose(drmachid_t id)3861 drmach_io_dispose(drmachid_t id)
3862 {
3863 drmach_io_t *self;
3864
3865 ASSERT(DRMACH_IS_IO_ID(id));
3866
3867 self = id;
3868 if (self->dev.node)
3869 drmach_node_dispose(self->dev.node);
3870
3871 kmem_free(self, sizeof (*self));
3872 }
3873
3874 /*ARGSUSED*/
3875 sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)3876 drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
3877 {
3878 drmach_board_t *bp = (drmach_board_t *)id;
3879 sbd_error_t *err = NULL;
3880
3881 if (id && DRMACH_IS_BOARD_ID(id)) {
3882 switch (cmd) {
3883 case SBD_CMD_TEST:
3884 case SBD_CMD_STATUS:
3885 case SBD_CMD_GETNCM:
3886 break;
3887 case SBD_CMD_CONNECT:
3888 if (bp->connected)
3889 err = drerr_new(0, ESBD_STATE, NULL);
3890
3891 if (bp->cond == SBD_COND_UNUSABLE)
3892 err = drerr_new(0,
3893 ESBD_FATAL_STATE, NULL);
3894 break;
3895 case SBD_CMD_DISCONNECT:
3896 if (!bp->connected)
3897 err = drerr_new(0, ESBD_STATE, NULL);
3898
3899 if (bp->cond == SBD_COND_UNUSABLE)
3900 err = drerr_new(0,
3901 ESBD_FATAL_STATE, NULL);
3902 break;
3903 default:
3904 if (bp->cond == SBD_COND_UNUSABLE)
3905 err = drerr_new(0,
3906 ESBD_FATAL_STATE, NULL);
3907 break;
3908
3909 }
3910 }
3911
3912 return (err);
3913 }
3914
3915 /*ARGSUSED*/
3916 sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)3917 drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
3918 {
3919 return (NULL);
3920 }
3921
3922 sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)3923 drmach_board_assign(int bnum, drmachid_t *id)
3924 {
3925 sbd_error_t *err = NULL;
3926 caddr_t obufp;
3927
3928 if (!drmach_initialized && drmach_init() == -1) {
3929 err = DRMACH_INTERNAL_ERROR();
3930 }
3931
3932 rw_enter(&drmach_boards_rwlock, RW_WRITER);
3933
3934 if (!err) {
3935 if (drmach_array_get(drmach_boards, bnum, id) == -1) {
3936 err = drerr_new(0, ESTC_BNUM, "%d", bnum);
3937 } else {
3938 drmach_board_t *bp;
3939
3940 if (*id)
3941 rw_downgrade(&drmach_boards_rwlock);
3942
3943 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
3944 err = drmach_mbox_trans(DRMSG_ASSIGN, bnum, obufp,
3945 sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
3946 kmem_free(obufp, sizeof (dr_proto_hdr_t));
3947
3948 if (!err) {
3949 bp = *id;
3950 if (!*id)
3951 bp = *id =
3952 (drmachid_t)drmach_board_new(bnum);
3953 bp->assigned = 1;
3954 }
3955 }
3956 }
3957 rw_exit(&drmach_boards_rwlock);
3958 return (err);
3959 }
3960
3961 static uint_t
drmach_board_non_panther_cpus(gdcd_t * gdcd,uint_t exp,uint_t slot)3962 drmach_board_non_panther_cpus(gdcd_t *gdcd, uint_t exp, uint_t slot)
3963 {
3964 uint_t port, port_start, port_end;
3965 uint_t non_panther_cpus = 0;
3966 uint_t impl;
3967
3968 ASSERT(gdcd != NULL);
3969
3970 /*
3971 * Determine PRD port indices based on slot location.
3972 */
3973 switch (slot) {
3974 case 0:
3975 port_start = 0;
3976 port_end = 3;
3977 break;
3978 case 1:
3979 port_start = 4;
3980 port_end = 5;
3981 break;
3982 default:
3983 ASSERT(0);
3984 /* check all */
3985 port_start = 0;
3986 port_end = 5;
3987 break;
3988 }
3989
3990 for (port = port_start; port <= port_end; port++) {
3991 if (gdcd->dcd_prd[exp][port].prd_ptype == SAFPTYPE_CPU &&
3992 RSV_GOOD(gdcd->dcd_prd[exp][port].prd_prsv)) {
3993 /*
3994 * This Safari port passed POST and represents a
3995 * cpu, so check the implementation.
3996 */
3997 impl = (gdcd->dcd_prd[exp][port].prd_ver_reg >> 32)
3998 & 0xffff;
3999
4000 switch (impl) {
4001 case CHEETAH_IMPL:
4002 case CHEETAH_PLUS_IMPL:
4003 case JAGUAR_IMPL:
4004 non_panther_cpus++;
4005 break;
4006 case PANTHER_IMPL:
4007 break;
4008 default:
4009 ASSERT(0);
4010 non_panther_cpus++;
4011 break;
4012 }
4013 }
4014 }
4015
4016 DRMACH_PR("drmach_board_non_panther_cpus: exp=%d, slot=%d, "
4017 "non_panther_cpus=%d", exp, slot, non_panther_cpus);
4018
4019 return (non_panther_cpus);
4020 }
4021
4022 sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)4023 drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
4024 {
4025 _NOTE(ARGUNUSED(opts))
4026
4027 drmach_board_t *bp = (drmach_board_t *)id;
4028 sbd_error_t *err;
4029 dr_mbox_msg_t *obufp;
4030 gdcd_t *gdcd = NULL;
4031 uint_t exp, slot;
4032 sc_gptwocfg_cookie_t scc;
4033 int panther_pages_enabled;
4034
4035 if (!DRMACH_IS_BOARD_ID(id))
4036 return (drerr_new(0, ESTC_INAPPROP, NULL));
4037
4038 /*
4039 * Build the casm info portion of the CLAIM message.
4040 */
4041 obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4042 mutex_enter(&drmach_slice_table_lock);
4043 drmach_msg_memslice_init(obufp->msgdata.dm_cr.mem_slice);
4044 drmach_msg_memregs_init(obufp->msgdata.dm_cr.mem_regs);
4045 mutex_exit(&drmach_slice_table_lock);
4046 err = drmach_mbox_trans(DRMSG_CLAIM, bp->bnum, (caddr_t)obufp,
4047 sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
4048 kmem_free(obufp, sizeof (dr_mbox_msg_t));
4049
4050 if (err) {
4051 /*
4052 * if mailbox timeout or unrecoverable error from SC,
4053 * board cannot be touched. Mark the status as
4054 * unusable.
4055 */
4056 if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
4057 (err->e_code == ESTC_MBXRPLY))
4058 bp->cond = SBD_COND_UNUSABLE;
4059 return (err);
4060 }
4061
4062 gdcd = drmach_gdcd_new();
4063 if (gdcd == NULL) {
4064 cmn_err(CE_WARN, "failed to read GDCD info for %s\n",
4065 bp->cm.name);
4066 return (DRMACH_INTERNAL_ERROR());
4067 }
4068
4069 /*
4070 * Read CPU SRAM DR buffer offset from GDCD.
4071 */
4072 exp = DRMACH_BNUM2EXP(bp->bnum);
4073 slot = DRMACH_BNUM2SLOT(bp->bnum);
4074 bp->stardrb_offset =
4075 gdcd->dcd_slot[exp][slot].l1ss_cpu_drblock_xwd_offset << 3;
4076 DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
4077 bp->stardrb_offset);
4078
4079 /*
4080 * Read board LPA setting from GDCD.
4081 */
4082 bp->flags &= ~DRMACH_NULL_PROC_LPA;
4083 if (gdcd->dcd_slot[exp][slot].l1ss_flags &
4084 L1SSFLG_THIS_L1_NULL_PROC_LPA) {
4085 bp->flags |= DRMACH_NULL_PROC_LPA;
4086 DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
4087 }
4088
4089 /*
4090 * XXX Until the Solaris large pages support heterogeneous cpu
4091 * domains, DR needs to prevent the addition of non-Panther cpus
4092 * to an all-Panther domain with large pages enabled.
4093 */
4094 panther_pages_enabled = (page_num_pagesizes() > DEFAULT_MMU_PAGE_SIZES);
4095 if (drmach_board_non_panther_cpus(gdcd, exp, slot) > 0 &&
4096 panther_pages_enabled && drmach_large_page_restriction) {
4097 cmn_err(CE_WARN, "Domain shutdown is required to add a non-"
4098 "UltraSPARC-IV+ board into an all UltraSPARC-IV+ domain");
4099 err = drerr_new(0, ESTC_SUPPORT, NULL);
4100 }
4101
4102 if (err == NULL) {
4103 /* do saf configurator stuff */
4104 DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
4105 scc = sc_probe_board(bp->bnum);
4106 if (scc == NULL)
4107 err = drerr_new(0, ESTC_PROBE, bp->cm.name);
4108 }
4109
4110 if (err) {
4111 /* flush CDC srams */
4112 if (axq_cdc_flush_all() != DDI_SUCCESS) {
4113 goto out;
4114 }
4115
4116 /*
4117 * Build the casm info portion of the UNCLAIM message.
4118 */
4119 obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4120 mutex_enter(&drmach_slice_table_lock);
4121 drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
4122 drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
4123 mutex_exit(&drmach_slice_table_lock);
4124 (void) drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum,
4125 (caddr_t)obufp, sizeof (dr_mbox_msg_t),
4126 (caddr_t)NULL, 0);
4127
4128 kmem_free(obufp, sizeof (dr_mbox_msg_t));
4129
4130 /*
4131 * we clear the connected flag just in case it would have
4132 * been set by a concurrent drmach_board_status() thread
4133 * before the UNCLAIM completed.
4134 */
4135 bp->connected = 0;
4136 goto out;
4137 }
4138
4139 /*
4140 * Now that the board has been successfully attached, obtain
4141 * platform-specific DIMM serial id information for the board.
4142 */
4143 if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
4144 plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE)) {
4145 (void) plat_request_mem_sids(DRMACH_BNUM2EXP(bp->bnum));
4146 }
4147
4148 out:
4149 if (gdcd != NULL)
4150 drmach_gdcd_dispose(gdcd);
4151
4152 return (err);
4153 }
4154
4155 static void
drmach_slice_table_update(drmach_board_t * bp,int invalidate)4156 drmach_slice_table_update(drmach_board_t *bp, int invalidate)
4157 {
4158 static char *axq_name = "address-extender-queue";
4159 static dev_info_t *axq_dip = NULL;
4160 static int axq_exp = -1;
4161 static int axq_slot;
4162 int e, s, slice;
4163
4164 ASSERT(MUTEX_HELD(&drmach_slice_table_lock));
4165
4166 e = DRMACH_BNUM2EXP(bp->bnum);
4167 if (invalidate) {
4168 ASSERT(DRMACH_BNUM2SLOT(bp->bnum) == 0);
4169
4170 /* invalidate cached casm value */
4171 drmach_slice_table[e] = 0;
4172
4173 /* invalidate cached axq info if for same exp */
4174 if (e == axq_exp && axq_dip) {
4175 ndi_rele_devi(axq_dip);
4176 axq_dip = NULL;
4177 }
4178 }
4179
4180 if (axq_dip == NULL || !i_ddi_devi_attached(axq_dip)) {
4181 int i, portid;
4182
4183 /* search for an attached slot0 axq instance */
4184 for (i = 0; i < AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP; i++) {
4185 if (axq_dip)
4186 ndi_rele_devi(axq_dip);
4187 axq_dip = ddi_find_devinfo(axq_name, i, 0);
4188 if (axq_dip && DDI_CF2(axq_dip)) {
4189 portid = ddi_getprop(DDI_DEV_T_ANY, axq_dip,
4190 DDI_PROP_DONTPASS, "portid", -1);
4191 if (portid == -1) {
4192 DRMACH_PR("cant get portid of axq "
4193 "instance %d\n", i);
4194 continue;
4195 }
4196
4197 axq_exp = (portid >> 5) & 0x1f;
4198 axq_slot = portid & 1;
4199
4200 if (invalidate && axq_exp == e)
4201 continue;
4202
4203 if (axq_slot == 0)
4204 break; /* found */
4205 }
4206 }
4207
4208 if (i == AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP) {
4209 if (axq_dip) {
4210 ndi_rele_devi(axq_dip);
4211 axq_dip = NULL;
4212 }
4213 DRMACH_PR("drmach_slice_table_update: failed to "
4214 "update axq dip\n");
4215 return;
4216 }
4217
4218 }
4219
4220 ASSERT(axq_dip);
4221 ASSERT(axq_slot == 0);
4222
4223 if (invalidate)
4224 return;
4225
4226 s = DRMACH_BNUM2SLOT(bp->bnum);
4227 DRMACH_PR("using AXQ casm %d.%d for slot%d.%d\n", axq_exp, axq_slot,
4228 e, s);
4229
4230 /* invalidate entry */
4231 drmach_slice_table[e] &= ~0x20;
4232
4233 /*
4234 * find a slice that routes to expander e. If no match
4235 * is found, drmach_slice_table[e] will remain invalid.
4236 *
4237 * The CASM is a routing table indexed by slice number.
4238 * Each element in the table contains permission bits,
4239 * a destination expander number and a valid bit. The
4240 * valid bit must true for the element to be meaningful.
4241 *
4242 * CASM entry structure
4243 * Bits 15..6 ignored
4244 * Bit 5 valid
4245 * Bits 0..4 expander number
4246 *
4247 * NOTE: the for loop is really enumerating the range of slices,
4248 * which is ALWAYS equal to the range of expanders. Hence,
4249 * AXQ_MAX_EXP is okay to use in this loop.
4250 */
4251 for (slice = 0; slice < AXQ_MAX_EXP; slice++) {
4252 uint32_t casm = axq_casm_read(axq_exp, axq_slot, slice);
4253
4254 if ((casm & 0x20) && (casm & 0x1f) == e)
4255 drmach_slice_table[e] = 0x20 | slice;
4256 }
4257 }
4258
4259 /*
4260 * Get base and bound PAs for slot 1 board lpa programming
4261 * If a cpu/mem board is present in the same expander, use slice
4262 * information corresponding to the CASM. Otherwise, set base and
4263 * bound PAs to 0.
4264 */
4265 static void
drmach_lpa_bb_get(drmach_board_t * s1bp,uint64_t * basep,uint64_t * boundp)4266 drmach_lpa_bb_get(drmach_board_t *s1bp, uint64_t *basep, uint64_t *boundp)
4267 {
4268 drmachid_t s0id;
4269
4270 ASSERT(mutex_owned(&drmach_slice_table_lock));
4271 ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
4272
4273 *basep = *boundp = 0;
4274 if (drmach_array_get(drmach_boards, s1bp->bnum - 1, &s0id) == 0 &&
4275 s0id != 0) {
4276
4277 uint32_t slice;
4278 if ((slice = drmach_slice_table[DRMACH_BNUM2EXP(s1bp->bnum)])
4279 & 0x20) {
4280 *basep = DRMACH_SLICE_TO_PA(slice & DRMACH_SLICE_MASK);
4281 *boundp = *basep + DRMACH_MEM_SLICE_SIZE;
4282 }
4283 }
4284 }
4285
4286
4287 /*
4288 * Reprogram slot 1 lpa's as required.
4289 * The purpose of this routine is maintain the LPA settings of the devices
4290 * in slot 1. To date we know Schizo and Cheetah are the only devices that
4291 * require this attention. The LPA setting must match the slice field in the
4292 * CASM element for the local expander. This field is guaranteed to be
4293 * programmed in accordance with the cacheable address space on the slot 0
4294 * board of the local expander. If no memory is present on the slot 0 board,
4295 * there is no cacheable address space and, hence, the CASM slice field will
4296 * be zero or its valid bit will be false (or both).
4297 */
4298
4299 static void
drmach_slot1_lpa_set(drmach_board_t * bp)4300 drmach_slot1_lpa_set(drmach_board_t *bp)
4301 {
4302 drmachid_t id;
4303 drmach_board_t *s1bp = NULL;
4304 int rv, idx, is_maxcat = 1;
4305 uint64_t last_scsr_pa = 0;
4306 uint64_t new_basepa, new_boundpa;
4307
4308 if (DRMACH_BNUM2SLOT(bp->bnum)) {
4309 s1bp = bp;
4310 if (s1bp->devices == NULL) {
4311 DRMACH_PR("drmach...lpa_set: slot1=%d not present",
4312 bp->bnum);
4313 return;
4314 }
4315 } else {
4316 rv = drmach_array_get(drmach_boards, bp->bnum + 1, &id);
4317 /* nothing to do when board is not found or has no devices */
4318 s1bp = id;
4319 if (rv == -1 || s1bp == NULL || s1bp->devices == NULL) {
4320 DRMACH_PR("drmach...lpa_set: slot1=%d not present",
4321 bp->bnum + 1);
4322 return;
4323 }
4324 ASSERT(DRMACH_IS_BOARD_ID(id));
4325 }
4326 mutex_enter(&drmach_slice_table_lock);
4327 drmach_lpa_bb_get(s1bp, &new_basepa, &new_boundpa);
4328 DRMACH_PR("drmach_...lpa_set: bnum=%d base=0x%lx bound=0x%lx\n",
4329 s1bp->bnum, new_basepa, new_boundpa);
4330
4331 rv = drmach_array_first(s1bp->devices, &idx, &id);
4332 while (rv == 0) {
4333 if (DRMACH_IS_IO_ID(id)) {
4334 drmach_io_t *io = id;
4335
4336 is_maxcat = 0;
4337
4338 /*
4339 * Skip all non-Schizo IO devices (only IO nodes
4340 * that are Schizo devices have non-zero scsr_pa).
4341 * Filter out "other" leaf to avoid writing to the
4342 * same Schizo Control/Status Register twice.
4343 */
4344 if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
4345 uint64_t scsr;
4346
4347 scsr = lddphysio(io->scsr_pa);
4348 DRMACH_PR("drmach...lpa_set: old scsr=0x%lx\n",
4349 scsr);
4350 scsr &= ~(DRMACH_LPA_BASE_MASK |
4351 DRMACH_LPA_BND_MASK);
4352 scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
4353 scsr |= DRMACH_PA_TO_LPA_BND(new_boundpa);
4354
4355 stdphysio(io->scsr_pa, scsr);
4356 DRMACH_PR("drmach...lpa_set: new scsr=0x%lx\n",
4357 scsr);
4358
4359 last_scsr_pa = io->scsr_pa;
4360 }
4361 }
4362 rv = drmach_array_next(s1bp->devices, &idx, &id);
4363 }
4364
4365 if (is_maxcat && DRMACH_L1_SET_LPA(s1bp) && drmach_reprogram_lpa) {
4366 extern xcfunc_t drmach_set_lpa;
4367
4368 DRMACH_PR("reprogramming maxcat lpa's");
4369
4370 mutex_enter(&cpu_lock);
4371 rv = drmach_array_first(s1bp->devices, &idx, &id);
4372 while (rv == 0 && id != NULL) {
4373 if (DRMACH_IS_CPU_ID(id)) {
4374 int ntries;
4375 processorid_t cpuid;
4376
4377 cpuid = ((drmach_cpu_t *)id)->cpuid;
4378
4379 /*
4380 * Check for unconfigured or powered-off
4381 * MCPUs. If CPU_READY flag is clear, the
4382 * MCPU cannot be xcalled.
4383 */
4384 if ((cpu[cpuid] == NULL) ||
4385 (cpu[cpuid]->cpu_flags &
4386 CPU_READY) == 0) {
4387
4388 rv = drmach_array_next(s1bp->devices,
4389 &idx, &id);
4390 continue;
4391 }
4392
4393 /*
4394 * XXX CHEETAH SUPPORT
4395 * for cheetah, we need to clear iocage
4396 * memory since it will be used for e$ flush
4397 * in drmach_set_lpa.
4398 */
4399 if (drmach_is_cheetah) {
4400 mutex_enter(&drmach_iocage_lock);
4401 while (drmach_iocage_is_busy)
4402 cv_wait(&drmach_iocage_cv,
4403 &drmach_iocage_lock);
4404 drmach_iocage_is_busy = 1;
4405 drmach_iocage_mem_scrub(ecache_size *
4406 2);
4407 mutex_exit(&drmach_iocage_lock);
4408 }
4409
4410 /*
4411 * drmach_slice_table[*]
4412 * bit 5 valid
4413 * bit 0:4 slice number
4414 *
4415 * drmach_xt_mb[*] format for drmach_set_lpa
4416 * bit 7 valid
4417 * bit 6 set null LPA
4418 * (overrides bits 0:4)
4419 * bit 0:4 slice number
4420 *
4421 * drmach_set_lpa derives processor CBASE and
4422 * CBND from bits 6 and 0:4 of drmach_xt_mb.
4423 * If bit 6 is set, then CBASE = CBND = 0.
4424 * Otherwise, CBASE = slice number;
4425 * CBND = slice number + 1.
4426 * No action is taken if bit 7 is zero.
4427 */
4428
4429 mutex_enter(&drmach_xt_mb_lock);
4430 bzero((void *)drmach_xt_mb,
4431 drmach_xt_mb_size);
4432
4433 if (new_basepa == 0 && new_boundpa == 0)
4434 drmach_xt_mb[cpuid] = 0x80 | 0x40;
4435 else
4436 drmach_xt_mb[cpuid] = 0x80 |
4437 DRMACH_PA_TO_SLICE(new_basepa);
4438
4439 drmach_xt_ready = 0;
4440
4441 xt_one(cpuid, drmach_set_lpa, NULL, NULL);
4442
4443 ntries = drmach_cpu_ntries;
4444 while (!drmach_xt_ready && ntries) {
4445 DELAY(drmach_cpu_delay);
4446 ntries--;
4447 }
4448 mutex_exit(&drmach_xt_mb_lock);
4449 drmach_xt_ready = 0;
4450
4451 /*
4452 * XXX CHEETAH SUPPORT
4453 * for cheetah, we need to clear iocage
4454 * memory since it was used for e$ flush
4455 * in performed drmach_set_lpa.
4456 */
4457 if (drmach_is_cheetah) {
4458 mutex_enter(&drmach_iocage_lock);
4459 drmach_iocage_mem_scrub(ecache_size *
4460 2);
4461 drmach_iocage_is_busy = 0;
4462 cv_signal(&drmach_iocage_cv);
4463 mutex_exit(&drmach_iocage_lock);
4464 }
4465 }
4466 rv = drmach_array_next(s1bp->devices, &idx, &id);
4467 }
4468 mutex_exit(&cpu_lock);
4469 }
4470 mutex_exit(&drmach_slice_table_lock);
4471 }
4472
4473 /*
4474 * Return the number of connected Panther boards in the domain.
4475 */
4476 static int
drmach_panther_boards(void)4477 drmach_panther_boards(void)
4478 {
4479 int rv;
4480 int b_idx;
4481 drmachid_t b_id;
4482 drmach_board_t *bp;
4483 int npanther = 0;
4484
4485 rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
4486 while (rv == 0) {
4487 ASSERT(DRMACH_IS_BOARD_ID(b_id));
4488 bp = b_id;
4489
4490 if (IS_PANTHER(bp->cpu_impl))
4491 npanther++;
4492
4493 rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
4494 }
4495
4496 return (npanther);
4497 }
4498
4499 /*ARGSUSED*/
4500 sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)4501 drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
4502 {
4503 drmach_board_t *bp;
4504 dr_mbox_msg_t *obufp;
4505 sbd_error_t *err = NULL;
4506
4507 sc_gptwocfg_cookie_t scc;
4508
4509 if (!DRMACH_IS_BOARD_ID(id))
4510 return (drerr_new(0, ESTC_INAPPROP, NULL));
4511 bp = id;
4512
4513 /*
4514 * Build the casm info portion of the UNCLAIM message.
4515 * This must be done prior to calling for saf configurator
4516 * deprobe, to ensure that the associated axq instance
4517 * is not detached.
4518 */
4519 obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4520 mutex_enter(&drmach_slice_table_lock);
4521 drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
4522
4523 /*
4524 * If disconnecting slot 0 board, update the casm slice table
4525 * info now, for use by drmach_slot1_lpa_set()
4526 */
4527 if (DRMACH_BNUM2SLOT(bp->bnum) == 0)
4528 drmach_slice_table_update(bp, 1);
4529
4530 drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
4531 mutex_exit(&drmach_slice_table_lock);
4532
4533 /*
4534 * Update LPA information for slot1 board
4535 */
4536 drmach_slot1_lpa_set(bp);
4537
4538 /* disable and flush CDC */
4539 if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
4540 axq_cdc_enable_all(); /* paranoia */
4541 err = DRMACH_INTERNAL_ERROR();
4542 }
4543
4544 /*
4545 * call saf configurator for deprobe
4546 * It's done now before sending an UNCLAIM message because
4547 * IKP will probe boards it doesn't know about <present at boot>
4548 * prior to unprobing them. If this happens after sending the
4549 * UNCLAIM, it will cause a dstop for domain transgression error.
4550 */
4551
4552 if (!err) {
4553 scc = sc_unprobe_board(bp->bnum);
4554 axq_cdc_enable_all();
4555 if (scc != NULL) {
4556 err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
4557 }
4558 }
4559
4560 /*
4561 * If disconnecting a board from a Panther domain, wait a fixed-
4562 * time delay for pending Safari transactions to complete on the
4563 * disconnecting board's processors. The bus sync list read used
4564 * in drmach_shutdown_asm to synchronize with outstanding Safari
4565 * transactions assumes no read-bypass-write mode for all memory
4566 * controllers. Since Panther supports read-bypass-write, a
4567 * delay is used that is slightly larger than the maximum Safari
4568 * timeout value in the Safari/Fireplane Config Reg.
4569 */
4570 if (drmach_panther_boards() > 0 || drmach_unclaim_delay_all) {
4571 clock_t stime = ddi_get_lbolt();
4572
4573 delay(drv_usectohz(drmach_unclaim_usec_delay));
4574
4575 stime = ddi_get_lbolt() - stime;
4576 DRMACH_PR("delayed %ld ticks (%ld secs) before disconnecting "
4577 "board %s from domain\n", stime, stime / hz, bp->cm.name);
4578 }
4579
4580 if (!err) {
4581 obufp->msgdata.dm_ur.mem_clear = 0;
4582
4583 err = drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum, (caddr_t)obufp,
4584 sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
4585
4586 if (err) {
4587 /*
4588 * if mailbox timeout or unrecoverable error from SC,
4589 * board cannot be touched. Mark the status as
4590 * unusable.
4591 */
4592 if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
4593 (err->e_code == ESTC_MBXRPLY))
4594 bp->cond = SBD_COND_UNUSABLE;
4595 else {
4596 DRMACH_PR("UNCLAIM failed for bnum=%d\n",
4597 bp->bnum);
4598 DRMACH_PR("calling sc_probe_board: bnum=%d\n",
4599 bp->bnum);
4600 scc = sc_probe_board(bp->bnum);
4601 if (scc == NULL) {
4602 cmn_err(CE_WARN,
4603 "sc_probe_board failed for bnum=%d",
4604 bp->bnum);
4605 } else {
4606 if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
4607 mutex_enter(
4608 &drmach_slice_table_lock);
4609 drmach_slice_table_update(bp,
4610 0);
4611 mutex_exit(
4612 &drmach_slice_table_lock);
4613 }
4614 drmach_slot1_lpa_set(bp);
4615 }
4616 }
4617 } else {
4618 bp->connected = 0;
4619 /*
4620 * Now that the board has been successfully detached,
4621 * discard platform-specific DIMM serial id information
4622 * for the board.
4623 */
4624 if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
4625 plat_ecc_capability_sc_get(
4626 PLAT_ECC_DIMM_SID_MESSAGE)) {
4627 (void) plat_discard_mem_sids(
4628 DRMACH_BNUM2EXP(bp->bnum));
4629 }
4630 }
4631 }
4632 kmem_free(obufp, sizeof (dr_mbox_msg_t));
4633
4634 return (err);
4635 }
4636
4637 static int
drmach_get_portid(drmach_node_t * np)4638 drmach_get_portid(drmach_node_t *np)
4639 {
4640 drmach_node_t pp;
4641 int portid;
4642 char type[OBP_MAXPROPNAME];
4643
4644 if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
4645 return (portid);
4646
4647 /*
4648 * Get the device_type property to see if we should
4649 * continue processing this node.
4650 */
4651 if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
4652 return (-1);
4653
4654 /*
4655 * If the device is a CPU without a 'portid' property,
4656 * it is a CMP core. For such cases, the parent node
4657 * has the portid.
4658 */
4659 if (strcmp(type, DRMACH_CPU_NAMEPROP) == 0) {
4660 if (np->get_parent(np, &pp) != 0)
4661 return (-1);
4662
4663 if (pp.n_getprop(&pp, "portid", &portid, sizeof (portid)) == 0)
4664 return (portid);
4665 }
4666
4667 return (-1);
4668 }
4669
4670 /*
4671 * This is a helper function to determine if a given
4672 * node should be considered for a dr operation according
4673 * to predefined dr type nodes and the node's name.
4674 * Formal Parameter : The name of a device node.
4675 * Return Value: -1, name does not map to a valid dr type.
4676 * A value greater or equal to 0, name is a valid dr type.
4677 */
4678 static int
drmach_name2type_idx(char * name)4679 drmach_name2type_idx(char *name)
4680 {
4681 int index, ntypes;
4682
4683 if (name == NULL)
4684 return (-1);
4685
4686 /*
4687 * Determine how many possible types are currently supported
4688 * for dr.
4689 */
4690 ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
4691
4692 /* Determine if the node's name correspond to a predefined type. */
4693 for (index = 0; index < ntypes; index++) {
4694 if (strcmp(drmach_name2type[index].name, name) == 0)
4695 /* The node is an allowed type for dr. */
4696 return (index);
4697 }
4698
4699 /*
4700 * If the name of the node does not map to any of the
4701 * types in the array drmach_name2type then the node is not of
4702 * interest to dr.
4703 */
4704 return (-1);
4705 }
4706
4707 static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)4708 drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
4709 {
4710 drmach_node_t *node = args->node;
4711 drmach_board_cb_data_t *data = args->data;
4712 drmach_board_t *obj = data->obj;
4713
4714 int rv, portid;
4715 drmachid_t id;
4716 drmach_device_t *device;
4717 char name[OBP_MAXDRVNAME];
4718
4719 portid = drmach_get_portid(node);
4720 if (portid == -1) {
4721 /*
4722 * if the node does not have a portid property, then
4723 * by that information alone it is known that drmach
4724 * is not interested in it.
4725 */
4726 return (0);
4727 }
4728 rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
4729
4730 /* The node must have a name */
4731 if (rv)
4732 return (0);
4733
4734 /*
4735 * Ignore devices whose portid do not map to this board,
4736 * or that their name property is not mapped to a valid
4737 * dr device name.
4738 */
4739 if ((drmach_portid2bnum(portid) != obj->bnum) ||
4740 (drmach_name2type_idx(name) < 0))
4741 return (0);
4742
4743 /*
4744 * Create a device data structure from this node data.
4745 * The call may yield nothing if the node is not of interest
4746 * to drmach.
4747 */
4748 data->err = drmach_device_new(node, obj, portid, &id);
4749 if (data->err)
4750 return (-1);
4751 else if (!id) {
4752 /*
4753 * drmach_device_new examined the node we passed in
4754 * and determined that it was either one not of
4755 * interest to drmach or the PIM dr layer.
4756 * So, it is skipped.
4757 */
4758 return (0);
4759 }
4760
4761 rv = drmach_array_set(obj->devices, data->ndevs++, id);
4762 if (rv) {
4763 data->err = DRMACH_INTERNAL_ERROR();
4764 return (-1);
4765 }
4766
4767 device = id;
4768
4769 #ifdef DEBUG
4770 DRMACH_PR("%d %s %d %p\n", portid, device->type, device->unum, id);
4771 if (DRMACH_IS_IO_ID(id))
4772 DRMACH_PR("ndevs = %d dip/node = %p", data->ndevs, node->here);
4773 #endif
4774
4775 data->err = (*data->found)(data->a, device->type, device->unum, id);
4776 return (data->err == NULL ? 0 : -1);
4777 }
4778
4779 sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))4780 drmach_board_find_devices(drmachid_t id, void *a,
4781 sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
4782 {
4783 drmach_board_t *bp = (drmach_board_t *)id;
4784 sbd_error_t *err;
4785 int max_devices;
4786 int rv;
4787 drmach_board_cb_data_t data;
4788
4789 if (!DRMACH_IS_BOARD_ID(id))
4790 return (drerr_new(0, ESTC_INAPPROP, NULL));
4791
4792 max_devices = plat_max_cpu_units_per_board();
4793 max_devices += plat_max_mem_units_per_board();
4794 max_devices += plat_max_io_units_per_board();
4795
4796 bp->devices = drmach_array_new(0, max_devices);
4797
4798 if (bp->tree == NULL)
4799 bp->tree = drmach_node_new();
4800
4801 data.obj = bp;
4802 data.ndevs = 0;
4803 data.found = found;
4804 data.a = a;
4805 data.err = NULL;
4806
4807 mutex_enter(&drmach_slice_table_lock);
4808 mutex_enter(&drmach_bus_sync_lock);
4809
4810 rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
4811
4812 drmach_slice_table_update(bp, 0);
4813 drmach_bus_sync_list_update();
4814
4815 mutex_exit(&drmach_bus_sync_lock);
4816 mutex_exit(&drmach_slice_table_lock);
4817
4818 if (rv == 0) {
4819 err = NULL;
4820 drmach_slot1_lpa_set(bp);
4821 } else {
4822 drmach_array_dispose(bp->devices, drmach_device_dispose);
4823 bp->devices = NULL;
4824
4825 if (data.err)
4826 err = data.err;
4827 else
4828 err = DRMACH_INTERNAL_ERROR();
4829 }
4830
4831 return (err);
4832 }
4833
4834 int
drmach_board_lookup(int bnum,drmachid_t * id)4835 drmach_board_lookup(int bnum, drmachid_t *id)
4836 {
4837 int rv = 0;
4838
4839 if (!drmach_initialized && drmach_init() == -1) {
4840 *id = 0;
4841 return (-1);
4842 }
4843 rw_enter(&drmach_boards_rwlock, RW_WRITER);
4844 if (drmach_array_get(drmach_boards, bnum, id)) {
4845 *id = 0;
4846 rv = -1;
4847 } else {
4848 caddr_t obufp;
4849 dr_showboard_t shb;
4850 sbd_error_t *err = NULL;
4851 drmach_board_t *bp;
4852
4853 bp = *id;
4854
4855 if (bp)
4856 rw_downgrade(&drmach_boards_rwlock);
4857
4858 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
4859 err = drmach_mbox_trans(DRMSG_SHOWBOARD, bnum, obufp,
4860 sizeof (dr_proto_hdr_t), (caddr_t)&shb,
4861 sizeof (dr_showboard_t));
4862 kmem_free(obufp, sizeof (dr_proto_hdr_t));
4863
4864 if (err) {
4865 if (err->e_code == ESTC_UNAVAILABLE) {
4866 *id = 0;
4867 rv = -1;
4868 }
4869 sbd_err_clear(&err);
4870 } else {
4871 if (!bp)
4872 bp = *id = (drmachid_t)drmach_board_new(bnum);
4873 bp->connected = (shb.bd_assigned && shb.bd_active);
4874 bp->empty = shb.slot_empty;
4875
4876 switch (shb.test_status) {
4877 case DR_TEST_STATUS_UNKNOWN:
4878 case DR_TEST_STATUS_IPOST:
4879 case DR_TEST_STATUS_ABORTED:
4880 bp->cond = SBD_COND_UNKNOWN;
4881 break;
4882 case DR_TEST_STATUS_PASSED:
4883 bp->cond = SBD_COND_OK;
4884 break;
4885 case DR_TEST_STATUS_FAILED:
4886 bp->cond = SBD_COND_FAILED;
4887 break;
4888 default:
4889 bp->cond = SBD_COND_UNKNOWN;
4890 DRMACH_PR("Unknown test status=0x%x from SC\n",
4891 shb.test_status);
4892 break;
4893 }
4894 (void) strncpy(bp->type, shb.board_type,
4895 sizeof (bp->type));
4896 bp->assigned = shb.bd_assigned;
4897 bp->powered = shb.power_on;
4898 }
4899 }
4900 rw_exit(&drmach_boards_rwlock);
4901 return (rv);
4902 }
4903
4904 sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)4905 drmach_board_name(int bnum, char *buf, int buflen)
4906 {
4907 (void) snprintf(buf, buflen, "%s%d", DRMACH_BNUM2SLOT(bnum) ?
4908 "IO" : "SB", DRMACH_BNUM2EXP(bnum));
4909
4910 return (NULL);
4911 }
4912
4913 sbd_error_t *
drmach_board_poweroff(drmachid_t id)4914 drmach_board_poweroff(drmachid_t id)
4915 {
4916 drmach_board_t *bp;
4917 sbd_error_t *err;
4918 drmach_status_t stat;
4919
4920 if (!DRMACH_IS_BOARD_ID(id))
4921 return (drerr_new(0, ESTC_INAPPROP, NULL));
4922 bp = id;
4923
4924 err = drmach_board_status(id, &stat);
4925 if (!err) {
4926 if (stat.configured || stat.busy)
4927 err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
4928 else {
4929 caddr_t obufp;
4930
4931 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
4932 err = drmach_mbox_trans(DRMSG_POWEROFF, bp->bnum, obufp,
4933 sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
4934 kmem_free(obufp, sizeof (dr_proto_hdr_t));
4935 if (!err)
4936 bp->powered = 0;
4937 }
4938 }
4939 return (err);
4940 }
4941
4942 sbd_error_t *
drmach_board_poweron(drmachid_t id)4943 drmach_board_poweron(drmachid_t id)
4944 {
4945 drmach_board_t *bp;
4946 caddr_t obufp;
4947 sbd_error_t *err;
4948
4949 if (!DRMACH_IS_BOARD_ID(id))
4950 return (drerr_new(0, ESTC_INAPPROP, NULL));
4951 bp = id;
4952
4953 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
4954 err = drmach_mbox_trans(DRMSG_POWERON, bp->bnum, obufp,
4955 sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
4956 if (!err)
4957 bp->powered = 1;
4958
4959 kmem_free(obufp, sizeof (dr_proto_hdr_t));
4960
4961 return (err);
4962 }
4963
4964 static sbd_error_t *
drmach_board_release(drmachid_t id)4965 drmach_board_release(drmachid_t id)
4966 {
4967 if (!DRMACH_IS_BOARD_ID(id))
4968 return (drerr_new(0, ESTC_INAPPROP, NULL));
4969 return (NULL);
4970 }
4971
4972 sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)4973 drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
4974 {
4975 drmach_board_t *bp;
4976 drmach_device_t *dp[MAX_CORES_PER_CMP];
4977 dr_mbox_msg_t *obufp;
4978 sbd_error_t *err;
4979 dr_testboard_reply_t tbr;
4980 int cpylen;
4981 char *copts;
4982 int is_io;
4983 cpu_flag_t oflags[MAX_CORES_PER_CMP];
4984
4985 if (!DRMACH_IS_BOARD_ID(id))
4986 return (drerr_new(0, ESTC_INAPPROP, NULL));
4987 bp = id;
4988
4989 /*
4990 * If the board is an I/O or MAXCAT board, setup I/O cage for
4991 * testing. Slot 1 indicates I/O or MAXCAT board.
4992 */
4993
4994 is_io = DRMACH_BNUM2SLOT(bp->bnum);
4995
4996 obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
4997
4998 if (force)
4999 obufp->msgdata.dm_tb.force = 1;
5000
5001 obufp->msgdata.dm_tb.immediate = 1;
5002
5003 if ((opts->size > 0) && ((copts = opts->copts) != NULL)) {
5004 cpylen = (opts->size > DR_HPOPTLEN ? DR_HPOPTLEN : opts->size);
5005 bcopy(copts, obufp->msgdata.dm_tb.hpost_opts, cpylen);
5006 }
5007
5008 if (is_io) {
5009 err = drmach_iocage_setup(&obufp->msgdata.dm_tb, dp, oflags);
5010
5011 if (err) {
5012 kmem_free(obufp, sizeof (dr_mbox_msg_t));
5013 return (err);
5014 }
5015 }
5016
5017 err = drmach_mbox_trans(DRMSG_TESTBOARD, bp->bnum, (caddr_t)obufp,
5018 sizeof (dr_mbox_msg_t), (caddr_t)&tbr, sizeof (tbr));
5019
5020 if (!err)
5021 bp->cond = SBD_COND_OK;
5022 else
5023 bp->cond = SBD_COND_UNKNOWN;
5024
5025 if ((!err) && (tbr.test_status != DR_TEST_STATUS_PASSED)) {
5026 /* examine test status */
5027 switch (tbr.test_status) {
5028 case DR_TEST_STATUS_IPOST:
5029 bp->cond = SBD_COND_UNKNOWN;
5030 err = drerr_new(0, ESTC_TEST_IN_PROGRESS, NULL);
5031 break;
5032 case DR_TEST_STATUS_UNKNOWN:
5033 bp->cond = SBD_COND_UNKNOWN;
5034 err = drerr_new(1,
5035 ESTC_TEST_STATUS_UNKNOWN, NULL);
5036 break;
5037 case DR_TEST_STATUS_FAILED:
5038 bp->cond = SBD_COND_FAILED;
5039 err = drerr_new(1, ESTC_TEST_FAILED, NULL);
5040 break;
5041 case DR_TEST_STATUS_ABORTED:
5042 bp->cond = SBD_COND_UNKNOWN;
5043 err = drerr_new(1, ESTC_TEST_ABORTED, NULL);
5044 break;
5045 default:
5046 bp->cond = SBD_COND_UNKNOWN;
5047 err = drerr_new(1, ESTC_TEST_RESULT_UNKNOWN,
5048 NULL);
5049 break;
5050 }
5051 }
5052
5053 /*
5054 * If I/O cage test was performed, check for availability of the
5055 * cpu used. If cpu has been returned, it's OK to proceed with
5056 * reconfiguring it for use.
5057 */
5058 if (is_io) {
5059 DRMACH_PR("drmach_board_test: tbr.cpu_recovered: %d",
5060 tbr.cpu_recovered);
5061 DRMACH_PR("drmach_board_test: port id: %d",
5062 tbr.cpu_portid);
5063
5064 /*
5065 * Check the cpu_recovered flag in the testboard reply, or
5066 * if the testboard request message was not sent to SMS due
5067 * to an mboxsc_putmsg() failure, it's OK to recover the
5068 * cpu since hpost hasn't touched it.
5069 */
5070 if ((tbr.cpu_recovered && tbr.cpu_portid ==
5071 obufp->msgdata.dm_tb.cpu_portid) ||
5072 ((err) && (err->e_code == ESTC_MBXRQST))) {
5073
5074 int i;
5075
5076 mutex_enter(&cpu_lock);
5077 for (i = 0; i < MAX_CORES_PER_CMP; i++) {
5078 if (dp[i] != NULL) {
5079 (void) drmach_iocage_cpu_return(dp[i],
5080 oflags[i]);
5081 }
5082 }
5083 mutex_exit(&cpu_lock);
5084 } else {
5085 cmn_err(CE_WARN, "Unable to recover port id %d "
5086 "after I/O cage test: cpu_recovered=%d, "
5087 "returned portid=%d",
5088 obufp->msgdata.dm_tb.cpu_portid,
5089 tbr.cpu_recovered, tbr.cpu_portid);
5090 }
5091 (void) drmach_iocage_mem_return(&tbr);
5092 }
5093 kmem_free(obufp, sizeof (dr_mbox_msg_t));
5094
5095 return (err);
5096 }
5097
5098 sbd_error_t *
drmach_board_unassign(drmachid_t id)5099 drmach_board_unassign(drmachid_t id)
5100 {
5101 drmach_board_t *bp;
5102 sbd_error_t *err;
5103 drmach_status_t stat;
5104 caddr_t obufp;
5105
5106 rw_enter(&drmach_boards_rwlock, RW_WRITER);
5107
5108 if (!DRMACH_IS_BOARD_ID(id)) {
5109 rw_exit(&drmach_boards_rwlock);
5110 return (drerr_new(0, ESTC_INAPPROP, NULL));
5111 }
5112 bp = id;
5113
5114 err = drmach_board_status(id, &stat);
5115 if (err) {
5116 rw_exit(&drmach_boards_rwlock);
5117 return (err);
5118 }
5119
5120 if (stat.configured || stat.busy) {
5121 err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
5122 } else {
5123
5124 obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
5125 err = drmach_mbox_trans(DRMSG_UNASSIGN, bp->bnum, obufp,
5126 sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
5127 kmem_free(obufp, sizeof (dr_proto_hdr_t));
5128 if (!err) {
5129 if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
5130 err = DRMACH_INTERNAL_ERROR();
5131 else
5132 drmach_board_dispose(bp);
5133 }
5134 }
5135 rw_exit(&drmach_boards_rwlock);
5136 return (err);
5137 }
5138
5139 static sbd_error_t *
drmach_read_reg_addr(drmach_device_t * dp,uint64_t * p)5140 drmach_read_reg_addr(drmach_device_t *dp, uint64_t *p)
5141 {
5142 int len;
5143 drmach_reg_t reg;
5144 drmach_node_t pp;
5145 drmach_node_t *np = dp->node;
5146
5147 /*
5148 * If the node does not have a portid property,
5149 * it represents a CMP device. For a CMP, the reg
5150 * property of the parent holds the information of
5151 * interest.
5152 */
5153 if (dp->node->n_getproplen(dp->node, "portid", &len) != 0) {
5154
5155 if (dp->node->get_parent(dp->node, &pp) != 0) {
5156 return (DRMACH_INTERNAL_ERROR());
5157 }
5158 np = &pp;
5159 }
5160
5161 if (np->n_getproplen(np, "reg", &len) != 0)
5162 return (DRMACH_INTERNAL_ERROR());
5163
5164 if (len != sizeof (reg))
5165 return (DRMACH_INTERNAL_ERROR());
5166
5167 if (np->n_getprop(np, "reg", ®, sizeof (reg)) != 0)
5168 return (DRMACH_INTERNAL_ERROR());
5169
5170 /* reassemble 64-bit base address */
5171 *p = ((uint64_t)reg.reg_addr_hi << 32) | reg.reg_addr_lo;
5172
5173 return (NULL);
5174 }
5175
5176 static void
drmach_cpu_read(uint64_t arg1,uint64_t arg2)5177 drmach_cpu_read(uint64_t arg1, uint64_t arg2)
5178 {
5179 uint64_t *saf_config_reg = (uint64_t *)arg1;
5180 uint_t *reg_read = (uint_t *)arg2;
5181
5182 *saf_config_reg = lddsafconfig();
5183 *reg_read = 0x1;
5184 }
5185
5186 /*
5187 * A return value of 1 indicates success and 0 indicates a failure
5188 */
5189 static int
drmach_cpu_read_scr(drmach_cpu_t * cp,uint64_t * scr)5190 drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr)
5191 {
5192
5193 int rv = 0x0;
5194
5195 *scr = 0x0;
5196
5197 /*
5198 * Confirm cpu was in ready set when xc was issued.
5199 * This is done by verifying rv which is
5200 * set to 0x1 when xc_one is successful.
5201 */
5202 xc_one(cp->dev.portid, (xcfunc_t *)drmach_cpu_read,
5203 (uint64_t)scr, (uint64_t)&rv);
5204
5205 return (rv);
5206
5207 }
5208
5209 static sbd_error_t *
drmach_cpu_read_cpuid(drmach_cpu_t * cp,processorid_t * cpuid)5210 drmach_cpu_read_cpuid(drmach_cpu_t *cp, processorid_t *cpuid)
5211 {
5212 drmach_node_t *np;
5213
5214 np = cp->dev.node;
5215
5216 /*
5217 * If a CPU does not have a portid property, it must
5218 * be a CMP device with a cpuid property.
5219 */
5220 if (np->n_getprop(np, "portid", cpuid, sizeof (*cpuid)) != 0) {
5221
5222 if (np->n_getprop(np, "cpuid", cpuid, sizeof (*cpuid)) != 0) {
5223 return (DRMACH_INTERNAL_ERROR());
5224 }
5225 }
5226
5227 return (NULL);
5228 }
5229
5230 /* Starcat CMP core id is bit 2 of the cpuid */
5231 #define DRMACH_COREID_MASK (1u << 2)
5232 #define DRMACH_CPUID2SRAM_IDX(id) \
5233 ((id & DRMACH_COREID_MASK) >> 1 | (id & 0x1))
5234
5235 static sbd_error_t *
drmach_cpu_new(drmach_device_t * proto,drmachid_t * idp)5236 drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
5237 {
5238 sbd_error_t *err;
5239 uint64_t scr_pa;
5240 drmach_cpu_t *cp = NULL;
5241 pfn_t pfn;
5242 uint64_t cpu_stardrb_offset, cpu_sram_pa;
5243 int idx;
5244 int impl;
5245 processorid_t cpuid;
5246
5247 err = drmach_read_reg_addr(proto, &scr_pa);
5248 if (err) {
5249 goto fail;
5250 }
5251
5252 cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
5253 bcopy(proto, &cp->dev, sizeof (cp->dev));
5254 cp->dev.node = drmach_node_dup(proto->node);
5255 cp->dev.cm.isa = (void *)drmach_cpu_new;
5256 cp->dev.cm.dispose = drmach_cpu_dispose;
5257 cp->dev.cm.release = drmach_cpu_release;
5258 cp->dev.cm.status = drmach_cpu_status;
5259 cp->scr_pa = scr_pa;
5260
5261 err = drmach_cpu_read_cpuid(cp, &cpuid);
5262 if (err) {
5263 goto fail;
5264 }
5265
5266 err = drmach_cpu_get_impl(cp, &impl);
5267 if (err) {
5268 goto fail;
5269 }
5270
5271 cp->cpuid = cpuid;
5272 cp->coreid = STARCAT_CPUID_TO_COREID(cp->cpuid);
5273 cp->dev.unum = STARCAT_CPUID_TO_AGENT(cp->cpuid);
5274
5275 /*
5276 * Init the board cpu type. Assumes all board cpus are the same type.
5277 */
5278 if (cp->dev.bp->cpu_impl == 0) {
5279 cp->dev.bp->cpu_impl = impl;
5280 }
5281 ASSERT(cp->dev.bp->cpu_impl == impl);
5282
5283 /*
5284 * XXX CHEETAH SUPPORT
5285 * determine if the domain uses Cheetah procs
5286 */
5287 if (drmach_is_cheetah < 0) {
5288 drmach_is_cheetah = IS_CHEETAH(impl);
5289 }
5290
5291 /*
5292 * Initialize TTE for mapping CPU SRAM STARDRB buffer.
5293 * The STARDRB buffer (16KB on Cheetah+ boards, 32KB on
5294 * Jaguar/Panther boards) is shared by all cpus in a Safari port
5295 * pair. Each cpu uses 8KB according to the following layout:
5296 *
5297 * Page 0: even numbered Cheetah+'s and Panther/Jaguar core 0's
5298 * Page 1: odd numbered Cheetah+'s and Panther/Jaguar core 0's
5299 * Page 2: even numbered Panther/Jaguar core 1's
5300 * Page 3: odd numbered Panther/Jaguar core 1's
5301 */
5302 idx = DRMACH_CPUID2SRAM_IDX(cp->cpuid);
5303 cpu_stardrb_offset = cp->dev.bp->stardrb_offset + (PAGESIZE * idx);
5304 cpu_sram_pa = DRMACH_CPU_SRAM_ADDR + cpu_stardrb_offset;
5305 pfn = cpu_sram_pa >> PAGESHIFT;
5306
5307 ASSERT(drmach_cpu_sram_tte[cp->cpuid].tte_inthi == 0 &&
5308 drmach_cpu_sram_tte[cp->cpuid].tte_intlo == 0);
5309 drmach_cpu_sram_tte[cp->cpuid].tte_inthi = TTE_PFN_INTHI(pfn) |
5310 TTE_VALID_INT | TTE_SZ_INT(TTE8K);
5311 drmach_cpu_sram_tte[cp->cpuid].tte_intlo = TTE_PFN_INTLO(pfn) |
5312 TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
5313
5314 DRMACH_PR("drmach_cpu_new: cpuid=%d, coreid=%d, stardrb_offset=0x%lx, "
5315 "cpu_sram_offset=0x%lx, idx=%d\n", cp->cpuid, cp->coreid,
5316 cp->dev.bp->stardrb_offset, cpu_stardrb_offset, idx);
5317
5318 (void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
5319 cp->dev.type, cp->dev.unum);
5320
5321 *idp = (drmachid_t)cp;
5322 return (NULL);
5323
5324 fail:
5325 if (cp) {
5326 drmach_node_dispose(cp->dev.node);
5327 kmem_free(cp, sizeof (*cp));
5328 }
5329
5330 *idp = (drmachid_t)0;
5331 return (err);
5332 }
5333
5334 static void
drmach_cpu_dispose(drmachid_t id)5335 drmach_cpu_dispose(drmachid_t id)
5336 {
5337 drmach_cpu_t *self;
5338 processorid_t cpuid;
5339
5340 ASSERT(DRMACH_IS_CPU_ID(id));
5341
5342 self = id;
5343 if (self->dev.node)
5344 drmach_node_dispose(self->dev.node);
5345
5346 cpuid = self->cpuid;
5347 ASSERT(TTE_IS_VALID(&drmach_cpu_sram_tte[cpuid]) &&
5348 TTE_IS_8K(&drmach_cpu_sram_tte[cpuid]) &&
5349 TTE_IS_PRIVILEGED(&drmach_cpu_sram_tte[cpuid]) &&
5350 TTE_IS_LOCKED(&drmach_cpu_sram_tte[cpuid]));
5351 drmach_cpu_sram_tte[cpuid].tte_inthi = 0;
5352 drmach_cpu_sram_tte[cpuid].tte_intlo = 0;
5353
5354 kmem_free(self, sizeof (*self));
5355 }
5356
5357 static int
drmach_cpu_start(struct cpu * cp)5358 drmach_cpu_start(struct cpu *cp)
5359 {
5360 extern xcfunc_t drmach_set_lpa;
5361 extern void restart_other_cpu(int);
5362 int cpuid = cp->cpu_id;
5363 int rv, bnum;
5364 drmach_board_t *bp;
5365
5366 ASSERT(MUTEX_HELD(&cpu_lock));
5367 ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
5368
5369 cp->cpu_flags &= ~CPU_POWEROFF;
5370
5371 /*
5372 * NOTE: restart_other_cpu pauses cpus during the
5373 * slave cpu start. This helps to quiesce the
5374 * bus traffic a bit which makes the tick sync
5375 * routine in the prom more robust.
5376 */
5377 DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
5378
5379 if (prom_hotaddcpu(cpuid) != 0) {
5380 cmn_err(CE_PANIC, "prom_hotaddcpu() for cpuid=%d failed.",
5381 cpuid);
5382 }
5383
5384 restart_other_cpu(cpuid);
5385
5386 bnum = drmach_portid2bnum(cpunodes[cpuid].portid);
5387 rv = drmach_array_get(drmach_boards, bnum, (drmachid_t)&bp);
5388 if (rv == -1 || bp == NULL) {
5389 DRMACH_PR("drmach_cpu_start: cannot read board info for "
5390 "cpuid=%d: rv=%d, bp=%p\n", cpuid, rv, (void *)bp);
5391 } else if (DRMACH_L1_SET_LPA(bp) && drmach_reprogram_lpa) {
5392 int exp;
5393 int ntries;
5394
5395 mutex_enter(&drmach_xt_mb_lock);
5396 mutex_enter(&drmach_slice_table_lock);
5397 bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
5398
5399 /*
5400 * drmach_slice_table[*]
5401 * bit 5 valid
5402 * bit 0:4 slice number
5403 *
5404 * drmach_xt_mb[*] format for drmach_set_lpa
5405 * bit 7 valid
5406 * bit 6 set null LPA (overrides bits 0:4)
5407 * bit 0:4 slice number
5408 *
5409 * drmach_set_lpa derives processor CBASE and CBND
5410 * from bits 6 and 0:4 of drmach_xt_mb. If bit 6 is
5411 * set, then CBASE = CBND = 0. Otherwise, CBASE = slice
5412 * number; CBND = slice number + 1.
5413 * No action is taken if bit 7 is zero.
5414 */
5415 exp = (cpuid >> 5) & 0x1f;
5416 if (drmach_slice_table[exp] & 0x20) {
5417 drmach_xt_mb[cpuid] = 0x80 |
5418 (drmach_slice_table[exp] & 0x1f);
5419 } else {
5420 drmach_xt_mb[cpuid] = 0x80 | 0x40;
5421 }
5422
5423 drmach_xt_ready = 0;
5424
5425 xt_one(cpuid, drmach_set_lpa, NULL, NULL);
5426
5427 ntries = drmach_cpu_ntries;
5428 while (!drmach_xt_ready && ntries) {
5429 DELAY(drmach_cpu_delay);
5430 ntries--;
5431 }
5432
5433 mutex_exit(&drmach_slice_table_lock);
5434 mutex_exit(&drmach_xt_mb_lock);
5435
5436 DRMACH_PR(
5437 "waited %d out of %d tries for drmach_set_lpa on cpu%d",
5438 drmach_cpu_ntries - ntries, drmach_cpu_ntries,
5439 cp->cpu_id);
5440 }
5441
5442 xt_one(cpuid, vtag_flushpage_tl1, (uint64_t)drmach_cpu_sram_va,
5443 (uint64_t)ksfmmup);
5444
5445 return (0);
5446 }
5447
5448 /*
5449 * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after
5450 * it has been offlined. The function of this routine is to get the cpu
5451 * spinning in a safe place. The requirement is that the system will not
5452 * reference anything on the detaching board (memory and i/o is detached
5453 * elsewhere) and that the CPU not reference anything on any other board
5454 * in the system. This isolation is required during and after the writes
5455 * to the domain masks to remove the board from the domain.
5456 *
5457 * To accomplish this isolation the following is done:
5458 * 1) Create a locked mapping to the STARDRB data buffer located
5459 * in this cpu's sram. There is one TTE per cpu, initialized in
5460 * drmach_cpu_new(). The cpuid is used to select which TTE to use.
5461 * Each Safari port pair shares the CPU SRAM on a Serengeti CPU/MEM
5462 * board. The STARDRB buffer is 16KB on Cheetah+ boards, 32KB on Jaguar
5463 * boards. Each STARDRB buffer is logically divided by DR into one
5464 * 8KB page per cpu (or Jaguar core).
5465 * 2) Copy the target function (drmach_shutdown_asm) into buffer.
5466 * 3) Jump to function now in the cpu sram.
5467 * Function will:
5468 * 3.1) Flush its Ecache (displacement).
5469 * 3.2) Flush its Dcache with HW mechanism.
5470 * 3.3) Flush its Icache with HW mechanism.
5471 * 3.4) Flush all valid and _unlocked_ D-TLB and I-TLB entries.
5472 * 3.5) Set LPA to NULL
5473 * 3.6) Clear xt_mb to signal completion. Note: cache line is
5474 * recovered by drmach_cpu_poweroff().
5475 * 4) Jump into an infinite loop.
5476 */
5477
5478 static void
drmach_cpu_stop_self(void)5479 drmach_cpu_stop_self(void)
5480 {
5481 extern void drmach_shutdown_asm(uint64_t, uint64_t, int, int, uint64_t);
5482 extern void drmach_shutdown_asm_end(void);
5483
5484 tte_t *tte;
5485 uint_t *p, *q;
5486 uint64_t stack_pointer;
5487
5488 ASSERT(((ptrdiff_t)drmach_shutdown_asm_end -
5489 (ptrdiff_t)drmach_shutdown_asm) < PAGESIZE);
5490
5491 tte = &drmach_cpu_sram_tte[CPU->cpu_id];
5492 ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) && TTE_IS_PRIVILEGED(tte) &&
5493 TTE_IS_LOCKED(tte));
5494 sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
5495 sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
5496
5497 /* copy text. standard bcopy not designed to work in nc space */
5498 p = (uint_t *)drmach_cpu_sram_va;
5499 q = (uint_t *)drmach_shutdown_asm;
5500 while (q < (uint_t *)drmach_shutdown_asm_end)
5501 *p++ = *q++;
5502
5503 /* zero to assist debug */
5504 q = (uint_t *)(drmach_cpu_sram_va + PAGESIZE);
5505 while (p < q)
5506 *p++ = 0;
5507
5508 /* a parking spot for the stack pointer */
5509 stack_pointer = (uint64_t)q;
5510
5511 /* call copy of drmach_shutdown_asm */
5512 (*(void (*)())drmach_cpu_sram_va)(
5513 stack_pointer,
5514 drmach_iocage_paddr,
5515 cpunodes[CPU->cpu_id].ecache_size,
5516 cpunodes[CPU->cpu_id].ecache_linesize,
5517 va_to_pa((void *)&drmach_xt_mb[CPU->cpu_id]));
5518 }
5519
5520 static void
drmach_cpu_shutdown_self(void)5521 drmach_cpu_shutdown_self(void)
5522 {
5523 cpu_t *cp = CPU;
5524 int cpuid = cp->cpu_id;
5525 extern void flush_windows(void);
5526
5527 flush_windows();
5528
5529 (void) spl8();
5530
5531 ASSERT(cp->cpu_intr_actv == 0);
5532 ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
5533 cp->cpu_thread == cp->cpu_startup_thread);
5534
5535 cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
5536
5537 drmach_cpu_stop_self();
5538
5539 cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid);
5540 }
5541
5542 static sbd_error_t *
drmach_cpu_release(drmachid_t id)5543 drmach_cpu_release(drmachid_t id)
5544 {
5545 drmach_cpu_t *cp;
5546 struct cpu *cpu;
5547 sbd_error_t *err;
5548
5549 if (!DRMACH_IS_CPU_ID(id))
5550 return (drerr_new(0, ESTC_INAPPROP, NULL));
5551 cp = id;
5552
5553 ASSERT(MUTEX_HELD(&cpu_lock));
5554
5555 cpu = cpu_get(cp->cpuid);
5556 if (cpu == NULL)
5557 err = DRMACH_INTERNAL_ERROR();
5558 else
5559 err = NULL;
5560
5561 return (err);
5562 }
5563
5564 static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)5565 drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
5566 {
5567 drmach_cpu_t *cp;
5568 drmach_device_t *dp;
5569
5570 ASSERT(DRMACH_IS_CPU_ID(id));
5571 cp = id;
5572 dp = &cp->dev;
5573
5574 stat->assigned = dp->bp->assigned;
5575 stat->powered = dp->bp->powered;
5576 mutex_enter(&cpu_lock);
5577 stat->configured = (cpu_get(cp->cpuid) != NULL);
5578 mutex_exit(&cpu_lock);
5579 stat->busy = dp->busy;
5580 (void) strncpy(stat->type, dp->type, sizeof (stat->type));
5581 stat->info[0] = '\0';
5582
5583 return (NULL);
5584 }
5585
5586 sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)5587 drmach_cpu_disconnect(drmachid_t id)
5588 {
5589 if (!DRMACH_IS_CPU_ID(id))
5590 return (drerr_new(0, ESTC_INAPPROP, NULL));
5591
5592 return (NULL);
5593 }
5594
5595 sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)5596 drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
5597 {
5598 drmach_cpu_t *cpu;
5599
5600 if (!DRMACH_IS_CPU_ID(id))
5601 return (drerr_new(0, ESTC_INAPPROP, NULL));
5602 cpu = id;
5603
5604 *cpuid = cpu->cpuid;
5605 return (NULL);
5606 }
5607
5608 sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)5609 drmach_cpu_get_impl(drmachid_t id, int *ip)
5610 {
5611 drmach_node_t *np;
5612 int impl;
5613
5614 if (!DRMACH_IS_CPU_ID(id))
5615 return (drerr_new(0, ESTC_INAPPROP, NULL));
5616
5617 np = ((drmach_device_t *)id)->node;
5618
5619 if (np->n_getprop(np, "implementation#", &impl, sizeof (impl)) == -1) {
5620 return (DRMACH_INTERNAL_ERROR());
5621 }
5622
5623 *ip = impl;
5624
5625 return (NULL);
5626 }
5627
5628 /*
5629 * Flush this cpu's ecache, then ensure all outstanding safari
5630 * transactions have retired.
5631 */
5632 void
drmach_cpu_flush_ecache_sync(void)5633 drmach_cpu_flush_ecache_sync(void)
5634 {
5635 uint64_t *p;
5636
5637 ASSERT(curthread->t_bound_cpu == CPU);
5638
5639 cpu_flush_ecache();
5640
5641 mutex_enter(&drmach_bus_sync_lock);
5642 for (p = drmach_bus_sync_list; *p; p++)
5643 (void) ldphys(*p);
5644 mutex_exit(&drmach_bus_sync_lock);
5645
5646 cpu_flush_ecache();
5647 }
5648
5649 sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)5650 drmach_get_dip(drmachid_t id, dev_info_t **dip)
5651 {
5652 drmach_device_t *dp;
5653
5654 if (!DRMACH_IS_DEVICE_ID(id))
5655 return (drerr_new(0, ESTC_INAPPROP, NULL));
5656 dp = id;
5657
5658 *dip = dp->node->n_getdip(dp->node);
5659 return (NULL);
5660 }
5661
5662 sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)5663 drmach_io_is_attached(drmachid_t id, int *yes)
5664 {
5665 drmach_device_t *dp;
5666 dev_info_t *dip;
5667 int state;
5668
5669 if (!DRMACH_IS_IO_ID(id))
5670 return (drerr_new(0, ESTC_INAPPROP, NULL));
5671 dp = id;
5672
5673 dip = dp->node->n_getdip(dp->node);
5674 if (dip == NULL) {
5675 *yes = 0;
5676 return (NULL);
5677 }
5678
5679 state = ddi_get_devstate(dip);
5680 *yes = i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP);
5681
5682 return (NULL);
5683 }
5684
5685 static int
drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t * dip)5686 drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t *dip)
5687 {
5688 char dtype[OBP_MAXPROPNAME];
5689 int portid;
5690 uint_t pci_csr_base;
5691 struct pci_phys_spec *regbuf = NULL;
5692 int rv, len;
5693
5694 ASSERT(dip != NULL);
5695 rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "device_type", &len);
5696 if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (dtype)))
5697 return (0);
5698
5699 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "device_type",
5700 (caddr_t)dtype, &len) == DDI_PROP_SUCCESS) {
5701
5702 if (strncmp(dtype, "pci", 3) == 0) {
5703
5704 /*
5705 * Get safari portid. All schizo/xmits 0
5706 * safari IDs end in 0x1C.
5707 */
5708 rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid",
5709 &len);
5710
5711 if ((rv != DDI_PROP_SUCCESS) ||
5712 (len > sizeof (portid)))
5713 return (0);
5714
5715 rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
5716 "portid", (caddr_t)&portid, &len);
5717
5718 if (rv != DDI_PROP_SUCCESS)
5719 return (0);
5720
5721 if ((portid & 0x1F) != 0x1C)
5722 return (0);
5723
5724 if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
5725 DDI_PROP_DONTPASS, "reg", (caddr_t)®buf,
5726 &len) == DDI_PROP_SUCCESS) {
5727
5728 pci_csr_base = regbuf[0].pci_phys_mid &
5729 PCI_CONF_ADDR_MASK;
5730 kmem_free(regbuf, len);
5731 /*
5732 * All PCI B-Leafs are at configspace 0x70.0000.
5733 */
5734 if (pci_csr_base == 0x700000)
5735 return (1);
5736 }
5737 }
5738 }
5739 return (0);
5740 }
5741
5742 #define SCHIZO_BINDING_NAME "pci108e,8001"
5743 #define XMITS_BINDING_NAME "pci108e,8002"
5744
5745 /*
5746 * Verify if the dip is an instance of MAN 'eri'.
5747 */
5748 static int
drmach_dip_is_man_eri(dev_info_t * dip)5749 drmach_dip_is_man_eri(dev_info_t *dip)
5750 {
5751 struct pci_phys_spec *regbuf = NULL;
5752 dev_info_t *parent_dip;
5753 char *name;
5754 uint_t pci_device;
5755 uint_t pci_function;
5756 int len;
5757
5758 if (dip == NULL)
5759 return (0);
5760 /*
5761 * Verify if the parent is schizo(xmits)0 and pci B leaf.
5762 */
5763 if (((parent_dip = ddi_get_parent(dip)) == NULL) ||
5764 ((name = ddi_binding_name(parent_dip)) == NULL))
5765 return (0);
5766 if (strcmp(name, SCHIZO_BINDING_NAME) != 0) {
5767 /*
5768 * This RIO could be on XMITS, so get the dip to
5769 * XMITS PCI Leaf.
5770 */
5771 if ((parent_dip = ddi_get_parent(parent_dip)) == NULL)
5772 return (0);
5773 if (((name = ddi_binding_name(parent_dip)) == NULL) ||
5774 (strcmp(name, XMITS_BINDING_NAME) != 0)) {
5775 return (0);
5776 }
5777 }
5778 if (!drmach_dip_is_schizo_xmits_0_pci_b(parent_dip))
5779 return (0);
5780 /*
5781 * Finally make sure it is the MAN eri.
5782 */
5783 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
5784 "reg", (caddr_t)®buf, &len) == DDI_PROP_SUCCESS) {
5785
5786 pci_device = PCI_REG_DEV_G(regbuf->pci_phys_hi);
5787 pci_function = PCI_REG_FUNC_G(regbuf->pci_phys_hi);
5788 kmem_free(regbuf, len);
5789
5790 /*
5791 * The network function of the RIO ASIC will always be
5792 * device 3 and function 1 ("network@3,1").
5793 */
5794 if ((pci_device == 3) && (pci_function == 1))
5795 return (1);
5796 }
5797 return (0);
5798 }
5799
5800 typedef struct {
5801 int iosram_inst;
5802 dev_info_t *eri_dip;
5803 int bnum;
5804 } drmach_io_inst_t;
5805
5806 int
drmach_board_find_io_insts(dev_info_t * dip,void * args)5807 drmach_board_find_io_insts(dev_info_t *dip, void *args)
5808 {
5809 drmach_io_inst_t *ios = (drmach_io_inst_t *)args;
5810
5811 int rv;
5812 int len;
5813 int portid;
5814 char name[OBP_MAXDRVNAME];
5815
5816 rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid", &len);
5817
5818 if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (portid))) {
5819 return (DDI_WALK_CONTINUE);
5820 }
5821
5822 rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
5823 "portid", (caddr_t)&portid, &len);
5824 if (rv != DDI_PROP_SUCCESS)
5825 return (DDI_WALK_CONTINUE);
5826
5827 /* ignore devices that are not on this board */
5828 if (drmach_portid2bnum(portid) != ios->bnum)
5829 return (DDI_WALK_CONTINUE);
5830
5831 if ((ios->iosram_inst < 0) || (ios->eri_dip == NULL)) {
5832 rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "name", &len);
5833 if (rv == DDI_PROP_SUCCESS) {
5834
5835 rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
5836 0, "name",
5837 (caddr_t)name, &len);
5838 if (rv != DDI_PROP_SUCCESS)
5839 return (DDI_WALK_CONTINUE);
5840
5841 if (strncmp("iosram", name, 6) == 0) {
5842 ios->iosram_inst = ddi_get_instance(dip);
5843 if (ios->eri_dip == NULL)
5844 return (DDI_WALK_CONTINUE);
5845 else
5846 return (DDI_WALK_TERMINATE);
5847 } else {
5848 if (drmach_dip_is_man_eri(dip)) {
5849 ASSERT(ios->eri_dip == NULL);
5850 ndi_hold_devi(dip);
5851 ios->eri_dip = dip;
5852 if (ios->iosram_inst < 0)
5853 return (DDI_WALK_CONTINUE);
5854 else
5855 return (DDI_WALK_TERMINATE);
5856 }
5857 }
5858 }
5859 }
5860 return (DDI_WALK_CONTINUE);
5861 }
5862
5863 sbd_error_t *
drmach_io_pre_release(drmachid_t id)5864 drmach_io_pre_release(drmachid_t id)
5865 {
5866 drmach_io_inst_t ios;
5867 drmach_board_t *bp;
5868 int rv = 0;
5869 sbd_error_t *err = NULL;
5870 drmach_device_t *dp;
5871 dev_info_t *rdip;
5872 int circ;
5873
5874 if (!DRMACH_IS_IO_ID(id))
5875 return (drerr_new(0, ESTC_INAPPROP, NULL));
5876 dp = id;
5877 bp = dp->bp;
5878
5879 rdip = dp->node->n_getdip(dp->node);
5880
5881 /* walk device tree to find iosram instance for the board */
5882 ios.iosram_inst = -1;
5883 ios.eri_dip = NULL;
5884 ios.bnum = bp->bnum;
5885
5886 ndi_devi_enter(rdip, &circ);
5887 ddi_walk_devs(ddi_get_child(rdip), drmach_board_find_io_insts,
5888 (void *)&ios);
5889
5890 DRMACH_PR("drmach_io_pre_release: bnum=%d iosram=%d eri=0x%p\n",
5891 ios.bnum, ios.iosram_inst, (void *)ios.eri_dip);
5892 ndi_devi_exit(rdip, circ);
5893
5894 if (ios.eri_dip) {
5895 /*
5896 * Release hold acquired in drmach_board_find_io_insts()
5897 */
5898 ndi_rele_devi(ios.eri_dip);
5899 }
5900 if (ios.iosram_inst >= 0) {
5901 /* call for tunnel switch */
5902 do {
5903 DRMACH_PR("calling iosram_switchfrom(%d)\n",
5904 ios.iosram_inst);
5905 rv = iosram_switchfrom(ios.iosram_inst);
5906 if (rv)
5907 DRMACH_PR("iosram_switchfrom returned %d\n",
5908 rv);
5909 } while (rv == EAGAIN);
5910
5911 if (rv)
5912 err = drerr_new(0, ESTC_IOSWITCH, NULL);
5913 }
5914 return (err);
5915 }
5916
5917 sbd_error_t *
drmach_io_unrelease(drmachid_t id)5918 drmach_io_unrelease(drmachid_t id)
5919 {
5920 dev_info_t *dip;
5921 sbd_error_t *err = NULL;
5922 drmach_device_t *dp;
5923
5924 if (!DRMACH_IS_IO_ID(id))
5925 return (drerr_new(0, ESTC_INAPPROP, NULL));
5926 dp = id;
5927
5928 dip = dp->node->n_getdip(dp->node);
5929
5930 if (dip == NULL)
5931 err = DRMACH_INTERNAL_ERROR();
5932 else {
5933 int (*func)(dev_info_t *dip);
5934
5935 func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach",
5936 0);
5937
5938 if (func) {
5939 drmach_io_inst_t ios;
5940 dev_info_t *pdip;
5941 int circ;
5942
5943 /*
5944 * Walk device tree to find rio dip for the board
5945 * Since we are not interested in iosram instance here,
5946 * initialize it to 0, so that the walk terminates as
5947 * soon as eri dip is found.
5948 */
5949 ios.iosram_inst = 0;
5950 ios.eri_dip = NULL;
5951 ios.bnum = dp->bp->bnum;
5952
5953 if (pdip = ddi_get_parent(dip)) {
5954 ndi_hold_devi(pdip);
5955 ndi_devi_enter(pdip, &circ);
5956 }
5957 /*
5958 * Root node doesn't have to be held in any way.
5959 */
5960 ddi_walk_devs(dip, drmach_board_find_io_insts,
5961 (void *)&ios);
5962
5963 if (pdip) {
5964 ndi_devi_exit(pdip, circ);
5965 ndi_rele_devi(pdip);
5966 }
5967
5968 DRMACH_PR("drmach_io_unrelease: bnum=%d eri=0x%p\n",
5969 ios.bnum, (void *)ios.eri_dip);
5970
5971 if (ios.eri_dip) {
5972 DRMACH_PR("calling man_dr_attach\n");
5973 if ((*func)(ios.eri_dip))
5974 err = drerr_new(0, ESTC_NWSWITCH, NULL);
5975 /*
5976 * Release hold acquired in
5977 * drmach_board_find_io_insts()
5978 */
5979 ndi_rele_devi(ios.eri_dip);
5980 }
5981 } else
5982 DRMACH_PR("man_dr_attach NOT present\n");
5983 }
5984 return (err);
5985 }
5986
5987 static sbd_error_t *
drmach_io_release(drmachid_t id)5988 drmach_io_release(drmachid_t id)
5989 {
5990 dev_info_t *dip;
5991 sbd_error_t *err = NULL;
5992 drmach_device_t *dp;
5993
5994 if (!DRMACH_IS_IO_ID(id))
5995 return (drerr_new(0, ESTC_INAPPROP, NULL));
5996 dp = id;
5997
5998 dip = dp->node->n_getdip(dp->node);
5999
6000 if (dip == NULL)
6001 err = DRMACH_INTERNAL_ERROR();
6002 else {
6003 int (*func)(dev_info_t *dip);
6004
6005 func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_detach",
6006 0);
6007
6008 if (func) {
6009 drmach_io_inst_t ios;
6010 dev_info_t *pdip;
6011 int circ;
6012
6013 /*
6014 * Walk device tree to find rio dip for the board
6015 * Since we are not interested in iosram instance here,
6016 * initialize it to 0, so that the walk terminates as
6017 * soon as eri dip is found.
6018 */
6019 ios.iosram_inst = 0;
6020 ios.eri_dip = NULL;
6021 ios.bnum = dp->bp->bnum;
6022
6023 if (pdip = ddi_get_parent(dip)) {
6024 ndi_hold_devi(pdip);
6025 ndi_devi_enter(pdip, &circ);
6026 }
6027 /*
6028 * Root node doesn't have to be held in any way.
6029 */
6030 ddi_walk_devs(dip, drmach_board_find_io_insts,
6031 (void *)&ios);
6032
6033 if (pdip) {
6034 ndi_devi_exit(pdip, circ);
6035 ndi_rele_devi(pdip);
6036 }
6037
6038 DRMACH_PR("drmach_io_release: bnum=%d eri=0x%p\n",
6039 ios.bnum, (void *)ios.eri_dip);
6040
6041 if (ios.eri_dip) {
6042 DRMACH_PR("calling man_dr_detach\n");
6043 if ((*func)(ios.eri_dip))
6044 err = drerr_new(0, ESTC_NWSWITCH, NULL);
6045 /*
6046 * Release hold acquired in
6047 * drmach_board_find_io_insts()
6048 */
6049 ndi_rele_devi(ios.eri_dip);
6050 }
6051 } else
6052 DRMACH_PR("man_dr_detach NOT present\n");
6053 }
6054 return (err);
6055 }
6056
6057 sbd_error_t *
drmach_io_post_release(drmachid_t id)6058 drmach_io_post_release(drmachid_t id)
6059 {
6060 char *path;
6061 dev_info_t *rdip;
6062 drmach_device_t *dp;
6063
6064 if (!DRMACH_IS_DEVICE_ID(id))
6065 return (drerr_new(0, ESTC_INAPPROP, NULL));
6066 dp = id;
6067
6068 rdip = dp->node->n_getdip(dp->node);
6069
6070 /*
6071 * Always called after drmach_unconfigure() which on Starcat
6072 * unconfigures the branch but doesn't remove it so the
6073 * dip must always exist.
6074 */
6075 ASSERT(rdip);
6076
6077 ASSERT(e_ddi_branch_held(rdip));
6078 #ifdef DEBUG
6079 path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
6080 (void) ddi_pathname(rdip, path);
6081 DRMACH_PR("post_release dip path is: %s\n", path);
6082 kmem_free(path, MAXPATHLEN);
6083 #endif
6084
6085 if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
6086 if (schpc_remove_pci(rdip)) {
6087 DRMACH_PR("schpc_remove_pci failed\n");
6088 return (drerr_new(0, ESBD_OFFLINE, NULL));
6089 } else {
6090 DRMACH_PR("schpc_remove_pci succeeded\n");
6091 }
6092 }
6093
6094 return (NULL);
6095 }
6096
6097 sbd_error_t *
drmach_io_post_attach(drmachid_t id)6098 drmach_io_post_attach(drmachid_t id)
6099 {
6100 int circ;
6101 dev_info_t *dip;
6102 dev_info_t *pdip;
6103 drmach_device_t *dp;
6104 drmach_io_inst_t ios;
6105
6106 if (!DRMACH_IS_DEVICE_ID(id))
6107 return (drerr_new(0, ESTC_INAPPROP, NULL));
6108 dp = id;
6109
6110 dip = dp->node->n_getdip(dp->node);
6111
6112 /*
6113 * We held the branch rooted at dip earlier, so at a minimum the
6114 * root i.e. dip must be present in the device tree.
6115 */
6116 ASSERT(dip);
6117
6118 if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
6119 if (schpc_add_pci(dip)) {
6120 DRMACH_PR("schpc_add_pci failed\n");
6121 } else {
6122 DRMACH_PR("schpc_add_pci succeeded\n");
6123 }
6124 }
6125
6126 /*
6127 * Walk device tree to find rio dip for the board
6128 * Since we are not interested in iosram instance here,
6129 * initialize it to 0, so that the walk terminates as
6130 * soon as eri dip is found.
6131 */
6132 ios.iosram_inst = 0;
6133 ios.eri_dip = NULL;
6134 ios.bnum = dp->bp->bnum;
6135
6136 if (pdip = ddi_get_parent(dip)) {
6137 ndi_hold_devi(pdip);
6138 ndi_devi_enter(pdip, &circ);
6139 }
6140 /*
6141 * Root node doesn't have to be held in any way.
6142 */
6143 ddi_walk_devs(dip, drmach_board_find_io_insts, (void *)&ios);
6144 if (pdip) {
6145 ndi_devi_exit(pdip, circ);
6146 ndi_rele_devi(pdip);
6147 }
6148
6149 DRMACH_PR("drmach_io_post_attach: bnum=%d eri=0x%p\n",
6150 ios.bnum, (void *)ios.eri_dip);
6151
6152 if (ios.eri_dip) {
6153 int (*func)(dev_info_t *dip);
6154
6155 func =
6156 (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach", 0);
6157
6158 if (func) {
6159 DRMACH_PR("calling man_dr_attach\n");
6160 (void) (*func)(ios.eri_dip);
6161 } else {
6162 DRMACH_PR("man_dr_attach NOT present\n");
6163 }
6164
6165 /*
6166 * Release hold acquired in drmach_board_find_io_insts()
6167 */
6168 ndi_rele_devi(ios.eri_dip);
6169
6170 }
6171
6172 return (NULL);
6173 }
6174
6175 static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)6176 drmach_io_status(drmachid_t id, drmach_status_t *stat)
6177 {
6178 drmach_device_t *dp;
6179 sbd_error_t *err;
6180 int configured;
6181
6182 ASSERT(DRMACH_IS_IO_ID(id));
6183 dp = id;
6184
6185 err = drmach_io_is_attached(id, &configured);
6186 if (err)
6187 return (err);
6188
6189 stat->assigned = dp->bp->assigned;
6190 stat->powered = dp->bp->powered;
6191 stat->configured = (configured != 0);
6192 stat->busy = dp->busy;
6193 (void) strncpy(stat->type, dp->type, sizeof (stat->type));
6194 stat->info[0] = '\0';
6195
6196 return (NULL);
6197 }
6198
6199 sbd_error_t *
drmach_mem_init_size(drmachid_t id)6200 drmach_mem_init_size(drmachid_t id)
6201 {
6202 drmach_mem_t *mp;
6203 sbd_error_t *err;
6204 gdcd_t *gdcd;
6205 mem_chunk_t *chunk;
6206 uint64_t chunks, pa, mask, sz;
6207
6208 if (!DRMACH_IS_MEM_ID(id))
6209 return (drerr_new(0, ESTC_INAPPROP, NULL));
6210 mp = id;
6211
6212 err = drmach_mem_get_base_physaddr(id, &pa);
6213 if (err)
6214 return (err);
6215
6216 mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
6217 pa &= mask;
6218
6219 gdcd = drmach_gdcd_new();
6220 if (gdcd == NULL)
6221 return (DRMACH_INTERNAL_ERROR());
6222
6223 sz = 0;
6224 chunk = gdcd->dcd_chunk_list.dcl_chunk;
6225 chunks = gdcd->dcd_chunk_list.dcl_chunks;
6226 while (chunks-- != 0) {
6227 if ((chunk->mc_base_pa & mask) == pa) {
6228 sz += chunk->mc_mbytes * 1048576;
6229 }
6230
6231 ++chunk;
6232 }
6233 mp->nbytes = sz;
6234
6235 drmach_gdcd_dispose(gdcd);
6236 return (NULL);
6237 }
6238
6239 /*
6240 * Hardware registers are organized into consecutively
6241 * addressed registers. The reg property's hi and lo fields
6242 * together describe the base address of the register set for
6243 * this memory-controller. Register descriptions and offsets
6244 * (from the base address) are as follows:
6245 *
6246 * Description Offset Size (bytes)
6247 * Memory Timing Control Register I 0x00 8
6248 * Memory Timing Control Register II 0x08 8
6249 * Memory Address Decoding Register I 0x10 8
6250 * Memory Address Decoding Register II 0x18 8
6251 * Memory Address Decoding Register III 0x20 8
6252 * Memory Address Decoding Register IV 0x28 8
6253 * Memory Address Control Register 0x30 8
6254 * Memory Timing Control Register III 0x38 8
6255 * Memory Timing Control Register IV 0x40 8
6256 * Memory Timing Control Register V 0x48 8 (Jaguar, Panther only)
6257 * EMU Activity Status Register 0x50 8 (Panther only)
6258 *
6259 * Only the Memory Address Decoding Register and EMU Activity Status
6260 * Register addresses are needed for DRMACH.
6261 */
6262 static sbd_error_t *
drmach_mem_new(drmach_device_t * proto,drmachid_t * idp)6263 drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
6264 {
6265 sbd_error_t *err;
6266 uint64_t madr_pa;
6267 drmach_mem_t *mp;
6268 int bank, count;
6269
6270 err = drmach_read_reg_addr(proto, &madr_pa);
6271 if (err)
6272 return (err);
6273
6274 mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
6275 bcopy(proto, &mp->dev, sizeof (mp->dev));
6276 mp->dev.node = drmach_node_dup(proto->node);
6277 mp->dev.cm.isa = (void *)drmach_mem_new;
6278 mp->dev.cm.dispose = drmach_mem_dispose;
6279 mp->dev.cm.release = drmach_mem_release;
6280 mp->dev.cm.status = drmach_mem_status;
6281 mp->madr_pa = madr_pa;
6282
6283 (void) snprintf(mp->dev.cm.name,
6284 sizeof (mp->dev.cm.name), "%s", mp->dev.type);
6285
6286 for (count = bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6287 uint64_t madr;
6288
6289 drmach_mem_read_madr(mp, bank, &madr);
6290 if (madr & DRMACH_MC_VALID_MASK) {
6291 count += 1;
6292 break;
6293 }
6294 }
6295
6296 /*
6297 * If none of the banks had their valid bit set, that means
6298 * post did not configure this MC to participate in the
6299 * domain. So, pretend this node does not exist by returning
6300 * a drmachid of zero.
6301 */
6302 if (count == 0) {
6303 /* drmach_mem_dispose frees board mem list */
6304 drmach_node_dispose(mp->dev.node);
6305 kmem_free(mp, sizeof (*mp));
6306 *idp = (drmachid_t)0;
6307 return (NULL);
6308 }
6309
6310 /*
6311 * Only one mem unit per board is exposed to the
6312 * PIM layer. The first mem unit encountered during
6313 * tree walk is used to represent all mem units on
6314 * the same board.
6315 */
6316 if (mp->dev.bp->mem == NULL) {
6317 /* start list of mem units on this board */
6318 mp->dev.bp->mem = mp;
6319
6320 /*
6321 * force unum to zero since this is the only mem unit
6322 * that will be visible to the PIM layer.
6323 */
6324 mp->dev.unum = 0;
6325
6326 /*
6327 * board memory size kept in this mem unit only
6328 */
6329 err = drmach_mem_init_size(mp);
6330 if (err) {
6331 mp->dev.bp->mem = NULL;
6332 /* drmach_mem_dispose frees board mem list */
6333 drmach_node_dispose(mp->dev.node);
6334 kmem_free(mp, sizeof (*mp));
6335 *idp = (drmachid_t)0;
6336 return (NULL);
6337 }
6338
6339 /*
6340 * allow this instance (the first encountered on this board)
6341 * to be visible to the PIM layer.
6342 */
6343 *idp = (drmachid_t)mp;
6344 } else {
6345 drmach_mem_t *lp;
6346
6347 /* hide this mem instance behind the first. */
6348 for (lp = mp->dev.bp->mem; lp->next; lp = lp->next)
6349 ;
6350 lp->next = mp;
6351
6352 /*
6353 * hide this instance from the caller.
6354 * See drmach_board_find_devices_cb() for details.
6355 */
6356 *idp = (drmachid_t)0;
6357 }
6358
6359 return (NULL);
6360 }
6361
6362 static void
drmach_mem_dispose(drmachid_t id)6363 drmach_mem_dispose(drmachid_t id)
6364 {
6365 drmach_mem_t *mp, *next;
6366 drmach_board_t *bp;
6367
6368 ASSERT(DRMACH_IS_MEM_ID(id));
6369
6370 mutex_enter(&drmach_bus_sync_lock);
6371
6372 mp = id;
6373 bp = mp->dev.bp;
6374
6375 do {
6376 if (mp->dev.node)
6377 drmach_node_dispose(mp->dev.node);
6378
6379 next = mp->next;
6380 kmem_free(mp, sizeof (*mp));
6381 mp = next;
6382 } while (mp);
6383
6384 bp->mem = NULL;
6385
6386 drmach_bus_sync_list_update();
6387 mutex_exit(&drmach_bus_sync_lock);
6388 }
6389
6390 sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)6391 drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
6392 {
6393 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
6394 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
6395 int rv;
6396
6397 ASSERT(size != 0);
6398
6399 if (!DRMACH_IS_MEM_ID(id))
6400 return (drerr_new(0, ESTC_INAPPROP, NULL));
6401
6402 rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
6403 if (rv == ENOMEM) {
6404 cmn_err(CE_WARN, "%lu megabytes not available"
6405 " to kernel cage", size >> 20);
6406 } else if (rv != 0) {
6407 /* catch this in debug kernels */
6408 ASSERT(0);
6409
6410 cmn_err(CE_WARN, "unexpected kcage_range_add"
6411 " return value %d", rv);
6412 }
6413
6414 return (NULL);
6415 }
6416
6417 sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)6418 drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
6419 {
6420 pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
6421 pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
6422 int rv;
6423
6424 if (!DRMACH_IS_MEM_ID(id))
6425 return (drerr_new(0, ESTC_INAPPROP, NULL));
6426
6427 if (size > 0) {
6428 rv = kcage_range_delete_post_mem_del(basepfn, npages);
6429 if (rv != 0) {
6430 cmn_err(CE_WARN,
6431 "unexpected kcage_range_delete_post_mem_del"
6432 " return value %d", rv);
6433 return (DRMACH_INTERNAL_ERROR());
6434 }
6435 }
6436
6437 return (NULL);
6438 }
6439
6440 sbd_error_t *
drmach_mem_disable(drmachid_t id)6441 drmach_mem_disable(drmachid_t id)
6442 {
6443 if (!DRMACH_IS_MEM_ID(id))
6444 return (drerr_new(0, ESTC_INAPPROP, NULL));
6445 else
6446 return (NULL);
6447 }
6448
6449 sbd_error_t *
drmach_mem_enable(drmachid_t id)6450 drmach_mem_enable(drmachid_t id)
6451 {
6452 if (!DRMACH_IS_MEM_ID(id))
6453 return (drerr_new(0, ESTC_INAPPROP, NULL));
6454 else
6455 return (NULL);
6456 }
6457
6458 sbd_error_t *
drmach_mem_get_alignment(drmachid_t id,uint64_t * mask)6459 drmach_mem_get_alignment(drmachid_t id, uint64_t *mask)
6460 {
6461 #define MB(mb) ((mb) * 1048576ull)
6462
6463 static struct {
6464 uint_t uk;
6465 uint64_t segsz;
6466 } uk2segsz[] = {
6467 { 0x003, MB(256) },
6468 { 0x007, MB(512) },
6469 { 0x00f, MB(1024) },
6470 { 0x01f, MB(2048) },
6471 { 0x03f, MB(4096) },
6472 { 0x07f, MB(8192) },
6473 { 0x0ff, MB(16384) },
6474 { 0x1ff, MB(32768) },
6475 { 0x3ff, MB(65536) },
6476 { 0x7ff, MB(131072) }
6477 };
6478 static int len = sizeof (uk2segsz) / sizeof (uk2segsz[0]);
6479
6480 #undef MB
6481
6482 uint64_t largest_sz = 0;
6483 drmach_mem_t *mp;
6484
6485 if (!DRMACH_IS_MEM_ID(id))
6486 return (drerr_new(0, ESTC_INAPPROP, NULL));
6487
6488 /* prime the result with a default value */
6489 *mask = (DRMACH_MEM_SLICE_SIZE - 1);
6490
6491 for (mp = id; mp; mp = mp->next) {
6492 int bank;
6493
6494 for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6495 int i;
6496 uint_t uk;
6497 uint64_t madr;
6498
6499 /* get register value, extract uk and normalize */
6500 drmach_mem_read_madr(mp, bank, &madr);
6501
6502 if (!(madr & DRMACH_MC_VALID_MASK))
6503 continue;
6504
6505 uk = DRMACH_MC_UK(madr);
6506
6507 /* match uk value */
6508 for (i = 0; i < len; i++)
6509 if (uk == uk2segsz[i].uk)
6510 break;
6511
6512 if (i < len) {
6513 uint64_t sz = uk2segsz[i].segsz;
6514
6515 /*
6516 * remember largest segment size,
6517 * update mask result
6518 */
6519 if (sz > largest_sz) {
6520 largest_sz = sz;
6521 *mask = sz - 1;
6522 }
6523 } else {
6524 /*
6525 * uk not in table, punt using
6526 * entire slice size. no longer any
6527 * reason to check other banks.
6528 */
6529 *mask = (DRMACH_MEM_SLICE_SIZE - 1);
6530 return (NULL);
6531 }
6532 }
6533 }
6534
6535 return (NULL);
6536 }
6537
6538 sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * base_addr)6539 drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *base_addr)
6540 {
6541 drmach_mem_t *mp;
6542
6543 if (!DRMACH_IS_MEM_ID(id))
6544 return (drerr_new(0, ESTC_INAPPROP, NULL));
6545
6546 *base_addr = (uint64_t)-1;
6547 for (mp = id; mp; mp = mp->next) {
6548 int bank;
6549
6550 for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6551 uint64_t addr, madr;
6552
6553 drmach_mem_read_madr(mp, bank, &madr);
6554 if (madr & DRMACH_MC_VALID_MASK) {
6555 addr = DRMACH_MC_UM_TO_PA(madr) |
6556 DRMACH_MC_LM_TO_PA(madr);
6557
6558 if (addr < *base_addr)
6559 *base_addr = addr;
6560 }
6561 }
6562 }
6563
6564 /* should not happen, but ... */
6565 if (*base_addr == (uint64_t)-1)
6566 return (DRMACH_INTERNAL_ERROR());
6567
6568 return (NULL);
6569 }
6570
6571 void
drmach_bus_sync_list_update(void)6572 drmach_bus_sync_list_update(void)
6573 {
6574 int rv, idx, cnt = 0;
6575 drmachid_t id;
6576
6577 ASSERT(MUTEX_HELD(&drmach_bus_sync_lock));
6578
6579 rv = drmach_array_first(drmach_boards, &idx, &id);
6580 while (rv == 0) {
6581 drmach_board_t *bp = id;
6582 drmach_mem_t *mp = bp->mem;
6583
6584 while (mp) {
6585 int bank;
6586
6587 for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
6588 uint64_t madr;
6589
6590 drmach_mem_read_madr(mp, bank, &madr);
6591 if (madr & DRMACH_MC_VALID_MASK) {
6592 uint64_t pa;
6593
6594 pa = DRMACH_MC_UM_TO_PA(madr);
6595 pa |= DRMACH_MC_LM_TO_PA(madr);
6596
6597 /*
6598 * The list is zero terminated.
6599 * Offset the pa by a doubleword
6600 * to avoid confusing a pa value of
6601 * of zero with the terminator.
6602 */
6603 pa += sizeof (uint64_t);
6604
6605 drmach_bus_sync_list[cnt++] = pa;
6606 }
6607 }
6608
6609 mp = mp->next;
6610 }
6611
6612 rv = drmach_array_next(drmach_boards, &idx, &id);
6613 }
6614
6615 drmach_bus_sync_list[cnt] = 0;
6616 }
6617
6618 sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)6619 drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
6620 {
6621 sbd_error_t *err;
6622 struct memlist *mlist;
6623 gdcd_t *gdcd;
6624 mem_chunk_t *chunk;
6625 uint64_t chunks, pa, mask;
6626
6627 err = drmach_mem_get_base_physaddr(id, &pa);
6628 if (err)
6629 return (err);
6630
6631 gdcd = drmach_gdcd_new();
6632 if (gdcd == NULL)
6633 return (DRMACH_INTERNAL_ERROR());
6634
6635 mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
6636 pa &= mask;
6637
6638 mlist = NULL;
6639 chunk = gdcd->dcd_chunk_list.dcl_chunk;
6640 chunks = gdcd->dcd_chunk_list.dcl_chunks;
6641 while (chunks-- != 0) {
6642 if ((chunk->mc_base_pa & mask) == pa) {
6643 mlist = memlist_add_span(mlist, chunk->mc_base_pa,
6644 chunk->mc_mbytes * 1048576);
6645 }
6646
6647 ++chunk;
6648 }
6649
6650 drmach_gdcd_dispose(gdcd);
6651
6652 #ifdef DEBUG
6653 DRMACH_PR("GDCD derived memlist:");
6654 memlist_dump(mlist);
6655 #endif
6656
6657 *ml = mlist;
6658 return (NULL);
6659 }
6660
6661 sbd_error_t *
drmach_mem_get_size(drmachid_t id,uint64_t * bytes)6662 drmach_mem_get_size(drmachid_t id, uint64_t *bytes)
6663 {
6664 drmach_mem_t *mp;
6665
6666 if (!DRMACH_IS_MEM_ID(id))
6667 return (drerr_new(0, ESTC_INAPPROP, NULL));
6668 mp = id;
6669
6670 ASSERT(mp->nbytes != 0);
6671 *bytes = mp->nbytes;
6672
6673 return (NULL);
6674 }
6675
6676 sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)6677 drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
6678 {
6679 sbd_error_t *err;
6680 drmach_device_t *mp;
6681
6682 if (!DRMACH_IS_MEM_ID(id))
6683 return (drerr_new(0, ESTC_INAPPROP, NULL));
6684 mp = id;
6685
6686 switch (DRMACH_BNUM2SLOT(mp->bp->bnum)) {
6687 case 0: *bytes = DRMACH_MEM_USABLE_SLICE_SIZE;
6688 err = NULL;
6689 break;
6690
6691 case 1: *bytes = 0;
6692 err = NULL;
6693 break;
6694
6695 default:
6696 err = DRMACH_INTERNAL_ERROR();
6697 break;
6698 }
6699
6700 return (err);
6701 }
6702
6703 processorid_t drmach_mem_cpu_affinity_nail;
6704
6705 processorid_t
drmach_mem_cpu_affinity(drmachid_t id)6706 drmach_mem_cpu_affinity(drmachid_t id)
6707 {
6708 drmach_device_t *mp;
6709 drmach_board_t *bp;
6710 processorid_t cpuid;
6711
6712 if (!DRMACH_IS_MEM_ID(id))
6713 return (CPU_CURRENT);
6714
6715 if (drmach_mem_cpu_affinity_nail) {
6716 cpuid = drmach_mem_cpu_affinity_nail;
6717
6718 if (cpuid < 0 || cpuid > NCPU)
6719 return (CPU_CURRENT);
6720
6721 mutex_enter(&cpu_lock);
6722 if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid]))
6723 cpuid = CPU_CURRENT;
6724 mutex_exit(&cpu_lock);
6725
6726 return (cpuid);
6727 }
6728
6729 /* try to choose a proc on the target board */
6730 mp = id;
6731 bp = mp->bp;
6732 if (bp->devices) {
6733 int rv;
6734 int d_idx;
6735 drmachid_t d_id;
6736
6737 rv = drmach_array_first(bp->devices, &d_idx, &d_id);
6738 while (rv == 0) {
6739 if (DRMACH_IS_CPU_ID(d_id)) {
6740 drmach_cpu_t *cp = d_id;
6741
6742 mutex_enter(&cpu_lock);
6743 cpuid = cp->cpuid;
6744 if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) {
6745 mutex_exit(&cpu_lock);
6746 return (cpuid);
6747 } else {
6748 mutex_exit(&cpu_lock);
6749 }
6750 }
6751
6752 rv = drmach_array_next(bp->devices, &d_idx, &d_id);
6753 }
6754 }
6755
6756 /* otherwise, this proc, wherever it is */
6757 return (CPU_CURRENT);
6758 }
6759
6760 static sbd_error_t *
drmach_mem_release(drmachid_t id)6761 drmach_mem_release(drmachid_t id)
6762 {
6763 if (!DRMACH_IS_MEM_ID(id))
6764 return (drerr_new(0, ESTC_INAPPROP, NULL));
6765 return (NULL);
6766 }
6767
6768 static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)6769 drmach_mem_status(drmachid_t id, drmach_status_t *stat)
6770 {
6771 drmach_mem_t *mp;
6772 sbd_error_t *err;
6773 uint64_t pa, slice_size;
6774 struct memlist *ml;
6775
6776 ASSERT(DRMACH_IS_MEM_ID(id));
6777 mp = id;
6778
6779 /* get starting physical address of target memory */
6780 err = drmach_mem_get_base_physaddr(id, &pa);
6781 if (err)
6782 return (err);
6783
6784 /* round down to slice boundary */
6785 slice_size = DRMACH_MEM_SLICE_SIZE;
6786 pa &= ~ (slice_size - 1);
6787
6788 /* stop at first span that is in slice */
6789 memlist_read_lock();
6790 for (ml = phys_install; ml; ml = ml->ml_next)
6791 if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
6792 break;
6793 memlist_read_unlock();
6794
6795 stat->assigned = mp->dev.bp->assigned;
6796 stat->powered = mp->dev.bp->powered;
6797 stat->configured = (ml != NULL);
6798 stat->busy = mp->dev.busy;
6799 (void) strncpy(stat->type, mp->dev.type, sizeof (stat->type));
6800 stat->info[0] = '\0';
6801
6802 return (NULL);
6803 }
6804
6805 sbd_error_t *
drmach_board_deprobe(drmachid_t id)6806 drmach_board_deprobe(drmachid_t id)
6807 {
6808 drmach_board_t *bp;
6809 sbd_error_t *err = NULL;
6810
6811 if (!DRMACH_IS_BOARD_ID(id))
6812 return (drerr_new(0, ESTC_INAPPROP, NULL));
6813 bp = id;
6814
6815 if (bp->tree) {
6816 drmach_node_dispose(bp->tree);
6817 bp->tree = NULL;
6818 }
6819 if (bp->devices) {
6820 drmach_array_dispose(bp->devices, drmach_device_dispose);
6821 bp->devices = NULL;
6822 bp->mem = NULL; /* TODO: still needed? */
6823 }
6824 return (err);
6825 }
6826
6827 /*ARGSUSED1*/
6828 static sbd_error_t *
drmach_pt_showlpa(drmachid_t id,drmach_opts_t * opts)6829 drmach_pt_showlpa(drmachid_t id, drmach_opts_t *opts)
6830 {
6831 drmach_device_t *dp;
6832 uint64_t val;
6833 int err = 1;
6834
6835 if (DRMACH_IS_CPU_ID(id)) {
6836 drmach_cpu_t *cp = id;
6837 if (drmach_cpu_read_scr(cp, &val))
6838 err = 0;
6839 } else if (DRMACH_IS_IO_ID(id) && ((drmach_io_t *)id)->scsr_pa != 0) {
6840 drmach_io_t *io = id;
6841 val = lddphysio(io->scsr_pa);
6842 err = 0;
6843 }
6844 if (err)
6845 return (drerr_new(0, ESTC_INAPPROP, NULL));
6846
6847 dp = id;
6848 uprintf("showlpa %s::%s portid %d, base pa %lx, bound pa %lx\n",
6849 dp->bp->cm.name,
6850 dp->cm.name,
6851 dp->portid,
6852 (long)(DRMACH_LPA_BASE_TO_PA(val)),
6853 (long)(DRMACH_LPA_BND_TO_PA(val)));
6854
6855 return (NULL);
6856 }
6857
6858 /*ARGSUSED*/
6859 static sbd_error_t *
drmach_pt_ikprobe(drmachid_t id,drmach_opts_t * opts)6860 drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
6861 {
6862 drmach_board_t *bp = (drmach_board_t *)id;
6863 sbd_error_t *err;
6864 sc_gptwocfg_cookie_t scc;
6865
6866 if (!DRMACH_IS_BOARD_ID(id))
6867 return (drerr_new(0, ESTC_INAPPROP, NULL));
6868
6869 /* do saf configurator stuff */
6870 DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
6871 scc = sc_probe_board(bp->bnum);
6872 if (scc == NULL) {
6873 err = drerr_new(0, ESTC_PROBE, bp->cm.name);
6874 return (err);
6875 }
6876
6877 return (err);
6878 }
6879
6880 /*ARGSUSED*/
6881 static sbd_error_t *
drmach_pt_ikdeprobe(drmachid_t id,drmach_opts_t * opts)6882 drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
6883 {
6884 drmach_board_t *bp;
6885 sbd_error_t *err = NULL;
6886 sc_gptwocfg_cookie_t scc;
6887
6888 if (!DRMACH_IS_BOARD_ID(id))
6889 return (drerr_new(0, ESTC_INAPPROP, NULL));
6890 bp = id;
6891
6892 cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
6893 scc = sc_unprobe_board(bp->bnum);
6894 if (scc != NULL) {
6895 err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
6896 }
6897
6898 if (err == NULL)
6899 err = drmach_board_deprobe(id);
6900
6901 return (err);
6902 }
6903
6904 static sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)6905 drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
6906 {
6907 _NOTE(ARGUNUSED(id))
6908 _NOTE(ARGUNUSED(opts))
6909
6910 struct memlist *ml;
6911 uint64_t src_pa;
6912 uint64_t dst_pa;
6913 uint64_t dst;
6914
6915 dst_pa = va_to_pa(&dst);
6916
6917 memlist_read_lock();
6918 for (ml = phys_install; ml; ml = ml->ml_next) {
6919 uint64_t nbytes;
6920
6921 src_pa = ml->ml_address;
6922 nbytes = ml->ml_size;
6923
6924 while (nbytes != 0ull) {
6925
6926 /* copy 32 bytes at src_pa to dst_pa */
6927 bcopy32_il(src_pa, dst_pa);
6928
6929 /* increment by 32 bytes */
6930 src_pa += (4 * sizeof (uint64_t));
6931
6932 /* decrement by 32 bytes */
6933 nbytes -= (4 * sizeof (uint64_t));
6934 }
6935 }
6936 memlist_read_unlock();
6937
6938 return (NULL);
6939 }
6940
6941 static sbd_error_t *
drmach_pt_recovercpu(drmachid_t id,drmach_opts_t * opts)6942 drmach_pt_recovercpu(drmachid_t id, drmach_opts_t *opts)
6943 {
6944 _NOTE(ARGUNUSED(opts))
6945
6946 drmach_cpu_t *cp;
6947
6948 if (!DRMACH_IS_CPU_ID(id))
6949 return (drerr_new(0, ESTC_INAPPROP, NULL));
6950 cp = id;
6951
6952 mutex_enter(&cpu_lock);
6953 (void) drmach_iocage_cpu_return(&(cp->dev),
6954 CPU_ENABLE | CPU_EXISTS | CPU_READY | CPU_RUNNING);
6955 mutex_exit(&cpu_lock);
6956
6957 return (NULL);
6958 }
6959
6960 /*
6961 * Starcat DR passthrus are for debugging purposes only.
6962 */
6963 static struct {
6964 const char *name;
6965 sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts);
6966 } drmach_pt_arr[] = {
6967 { "showlpa", drmach_pt_showlpa },
6968 { "ikprobe", drmach_pt_ikprobe },
6969 { "ikdeprobe", drmach_pt_ikdeprobe },
6970 { "readmem", drmach_pt_readmem },
6971 { "recovercpu", drmach_pt_recovercpu },
6972
6973 /* the following line must always be last */
6974 { NULL, NULL }
6975 };
6976
6977 /*ARGSUSED*/
6978 sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)6979 drmach_passthru(drmachid_t id, drmach_opts_t *opts)
6980 {
6981 int i;
6982 sbd_error_t *err;
6983
6984 i = 0;
6985 while (drmach_pt_arr[i].name != NULL) {
6986 int len = strlen(drmach_pt_arr[i].name);
6987
6988 if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
6989 break;
6990
6991 i += 1;
6992 }
6993
6994 if (drmach_pt_arr[i].name == NULL)
6995 err = drerr_new(0, ESTC_UNKPTCMD, opts->copts);
6996 else
6997 err = (*drmach_pt_arr[i].handler)(id, opts);
6998
6999 return (err);
7000 }
7001
7002 sbd_error_t *
drmach_release(drmachid_t id)7003 drmach_release(drmachid_t id)
7004 {
7005 drmach_common_t *cp;
7006
7007 if (!DRMACH_IS_DEVICE_ID(id))
7008 return (drerr_new(0, ESTC_INAPPROP, NULL));
7009 cp = id;
7010
7011 return (cp->release(id));
7012 }
7013
7014 sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)7015 drmach_status(drmachid_t id, drmach_status_t *stat)
7016 {
7017 drmach_common_t *cp;
7018 sbd_error_t *err;
7019
7020 rw_enter(&drmach_boards_rwlock, RW_READER);
7021
7022 if (!DRMACH_IS_ID(id)) {
7023 rw_exit(&drmach_boards_rwlock);
7024 return (drerr_new(0, ESTC_NOTID, NULL));
7025 }
7026
7027 cp = id;
7028
7029 err = cp->status(id, stat);
7030 rw_exit(&drmach_boards_rwlock);
7031 return (err);
7032 }
7033
7034 static sbd_error_t *
drmach_i_status(drmachid_t id,drmach_status_t * stat)7035 drmach_i_status(drmachid_t id, drmach_status_t *stat)
7036 {
7037 drmach_common_t *cp;
7038
7039 if (!DRMACH_IS_ID(id))
7040 return (drerr_new(0, ESTC_NOTID, NULL));
7041 cp = id;
7042
7043 return (cp->status(id, stat));
7044 }
7045
7046 /*ARGSUSED*/
7047 sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)7048 drmach_unconfigure(drmachid_t id, int flags)
7049 {
7050 drmach_device_t *dp;
7051 dev_info_t *rdip;
7052
7053 char name[OBP_MAXDRVNAME];
7054 int rv;
7055
7056 /*
7057 * Since CPU nodes are not configured, it is
7058 * necessary to skip the unconfigure step as
7059 * well.
7060 */
7061 if (DRMACH_IS_CPU_ID(id)) {
7062 return (NULL);
7063 }
7064
7065 for (; id; ) {
7066 dev_info_t *fdip = NULL;
7067
7068 if (!DRMACH_IS_DEVICE_ID(id))
7069 return (drerr_new(0, ESTC_INAPPROP, NULL));
7070 dp = id;
7071
7072 rdip = dp->node->n_getdip(dp->node);
7073
7074 /*
7075 * drmach_unconfigure() is always called on a configured branch.
7076 * So the root of the branch was held earlier and must exist.
7077 */
7078 ASSERT(rdip);
7079
7080 DRMACH_PR("drmach_unconfigure: unconfiguring DDI branch");
7081
7082 rv = dp->node->n_getprop(dp->node,
7083 "name", name, OBP_MAXDRVNAME);
7084
7085 /* The node must have a name */
7086 if (rv)
7087 return (0);
7088
7089 if (drmach_name2type_idx(name) < 0) {
7090 if (DRMACH_IS_MEM_ID(id)) {
7091 drmach_mem_t *mp = id;
7092 id = mp->next;
7093 } else {
7094 id = NULL;
7095 }
7096 continue;
7097 }
7098
7099 /*
7100 * NOTE: FORCE flag is no longer needed under devfs
7101 */
7102 ASSERT(e_ddi_branch_held(rdip));
7103 if (e_ddi_branch_unconfigure(rdip, &fdip, 0) != 0) {
7104 sbd_error_t *err = NULL;
7105 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
7106
7107 /*
7108 * If non-NULL, fdip is returned held and must be
7109 * released.
7110 */
7111 if (fdip != NULL) {
7112 (void) ddi_pathname(fdip, path);
7113 ddi_release_devi(fdip);
7114 } else {
7115 (void) ddi_pathname(rdip, path);
7116 }
7117
7118 err = drerr_new(1, ESTC_DRVFAIL, path);
7119
7120 kmem_free(path, MAXPATHLEN);
7121
7122 /*
7123 * If we were unconfiguring an IO board, a call was
7124 * made to man_dr_detach. We now need to call
7125 * man_dr_attach to regain man use of the eri.
7126 */
7127 if (DRMACH_IS_IO_ID(id)) {
7128 int (*func)(dev_info_t *dip);
7129
7130 func = (int (*)(dev_info_t *))kobj_getsymvalue\
7131 ("man_dr_attach", 0);
7132
7133 if (func) {
7134 drmach_io_inst_t ios;
7135 dev_info_t *pdip;
7136 int circ;
7137
7138 /*
7139 * Walk device tree to find rio dip for
7140 * the board
7141 * Since we are not interested in iosram
7142 * instance here, initialize it to 0, so
7143 * that the walk terminates as soon as
7144 * eri dip is found.
7145 */
7146 ios.iosram_inst = 0;
7147 ios.eri_dip = NULL;
7148 ios.bnum = dp->bp->bnum;
7149
7150 if (pdip = ddi_get_parent(rdip)) {
7151 ndi_hold_devi(pdip);
7152 ndi_devi_enter(pdip, &circ);
7153 }
7154 /*
7155 * Root node doesn't have to be held in
7156 * any way.
7157 */
7158 ASSERT(e_ddi_branch_held(rdip));
7159 ddi_walk_devs(rdip,
7160 drmach_board_find_io_insts,
7161 (void *)&ios);
7162
7163 DRMACH_PR("drmach_unconfigure: bnum=%d"
7164 " eri=0x%p\n",
7165 ios.bnum, (void *)ios.eri_dip);
7166
7167 if (pdip) {
7168 ndi_devi_exit(pdip, circ);
7169 ndi_rele_devi(pdip);
7170 }
7171
7172 if (ios.eri_dip) {
7173 DRMACH_PR("calling"
7174 " man_dr_attach\n");
7175 (void) (*func)(ios.eri_dip);
7176 /*
7177 * Release hold acquired in
7178 * drmach_board_find_io_insts()
7179 */
7180 ndi_rele_devi(ios.eri_dip);
7181 }
7182 }
7183 }
7184 return (err);
7185 }
7186
7187 if (DRMACH_IS_MEM_ID(id)) {
7188 drmach_mem_t *mp = id;
7189 id = mp->next;
7190 } else {
7191 id = NULL;
7192 }
7193 }
7194
7195 return (NULL);
7196 }
7197
7198 /*
7199 * drmach interfaces to legacy Starfire platmod logic
7200 * linkage via runtime symbol look up, called from plat_cpu_power*
7201 */
7202
7203 /*
7204 * Start up a cpu. It is possible that we're attempting to restart
7205 * the cpu after an UNCONFIGURE in which case the cpu will be
7206 * spinning in its cache. So, all we have to do is wakeup him up.
7207 * Under normal circumstances the cpu will be coming from a previous
7208 * CONNECT and thus will be spinning in OBP. In both cases, the
7209 * startup sequence is the same.
7210 */
7211 int
drmach_cpu_poweron(struct cpu * cp)7212 drmach_cpu_poweron(struct cpu *cp)
7213 {
7214 DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
7215
7216 ASSERT(MUTEX_HELD(&cpu_lock));
7217
7218 if (drmach_cpu_start(cp) != 0)
7219 return (EBUSY);
7220 else
7221 return (0);
7222 }
7223
7224 int
drmach_cpu_poweroff(struct cpu * cp)7225 drmach_cpu_poweroff(struct cpu *cp)
7226 {
7227 int ntries;
7228 processorid_t cpuid;
7229 void drmach_cpu_shutdown_self(void);
7230
7231 DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
7232
7233 ASSERT(MUTEX_HELD(&cpu_lock));
7234
7235 /*
7236 * XXX CHEETAH SUPPORT
7237 * for cheetah, we need to grab the iocage lock since iocage
7238 * memory is used for e$ flush.
7239 */
7240 if (drmach_is_cheetah) {
7241 mutex_enter(&drmach_iocage_lock);
7242 while (drmach_iocage_is_busy)
7243 cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
7244 drmach_iocage_is_busy = 1;
7245 drmach_iocage_mem_scrub(ecache_size * 2);
7246 mutex_exit(&drmach_iocage_lock);
7247 }
7248
7249 cpuid = cp->cpu_id;
7250
7251 /*
7252 * Set affinity to ensure consistent reading and writing of
7253 * drmach_xt_mb[cpuid] by one "master" CPU directing
7254 * the shutdown of the target CPU.
7255 */
7256 affinity_set(CPU->cpu_id);
7257
7258 /*
7259 * Capture all CPUs (except for detaching proc) to prevent
7260 * crosscalls to the detaching proc until it has cleared its
7261 * bit in cpu_ready_set.
7262 *
7263 * The CPUs remain paused and the prom_mutex is known to be free.
7264 * This prevents blocking when doing prom IEEE-1275 calls at a
7265 * high PIL level.
7266 */
7267 promsafe_pause_cpus();
7268
7269 /*
7270 * Quiesce interrupts on the target CPU. We do this by setting
7271 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
7272 * prevent it from receiving cross calls and cross traps.
7273 * This prevents the processor from receiving any new soft interrupts.
7274 */
7275 mp_cpu_quiesce(cp);
7276
7277 (void) prom_hotremovecpu(cpuid);
7278
7279 start_cpus();
7280
7281 /* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */
7282 drmach_xt_mb[cpuid] = 0x80;
7283
7284 xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
7285 (uint64_t)drmach_cpu_shutdown_self, NULL);
7286
7287 ntries = drmach_cpu_ntries;
7288 while (drmach_xt_mb[cpuid] && ntries) {
7289 DELAY(drmach_cpu_delay);
7290 ntries--;
7291 }
7292
7293 drmach_xt_mb[cpuid] = 0; /* steal the cache line back */
7294
7295 membar_sync(); /* make sure copy-back retires */
7296
7297 affinity_clear();
7298
7299 /*
7300 * XXX CHEETAH SUPPORT
7301 */
7302 if (drmach_is_cheetah) {
7303 mutex_enter(&drmach_iocage_lock);
7304 drmach_iocage_mem_scrub(ecache_size * 2);
7305 drmach_iocage_is_busy = 0;
7306 cv_signal(&drmach_iocage_cv);
7307 mutex_exit(&drmach_iocage_lock);
7308 }
7309
7310 DRMACH_PR("waited %d out of %d tries for "
7311 "drmach_cpu_shutdown_self on cpu%d",
7312 drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id);
7313
7314 /*
7315 * Do this here instead of drmach_cpu_shutdown_self() to
7316 * avoid an assertion failure panic in turnstile.c.
7317 */
7318 CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
7319
7320 return (0);
7321 }
7322
7323 void
drmach_iocage_mem_scrub(uint64_t nbytes)7324 drmach_iocage_mem_scrub(uint64_t nbytes)
7325 {
7326 extern uint32_t drmach_bc_bzero(void*, size_t);
7327 uint32_t rv;
7328
7329 ASSERT(MUTEX_HELD(&cpu_lock));
7330
7331 affinity_set(CPU->cpu_id);
7332
7333 rv = drmach_bc_bzero(drmach_iocage_vaddr, nbytes);
7334 if (rv != 0) {
7335 DRMACH_PR(
7336 "iocage scrub failed, drmach_bc_bzero returned %d\n", rv);
7337 rv = drmach_bc_bzero(drmach_iocage_vaddr, drmach_iocage_size);
7338 if (rv != 0)
7339 cmn_err(CE_PANIC,
7340 "iocage scrub failed, drmach_bc_bzero rv=%d\n",
7341 rv);
7342 }
7343
7344 cpu_flush_ecache();
7345
7346 affinity_clear();
7347 }
7348
7349 #define ALIGN(x, a) ((a) == 0 ? (uintptr_t)(x) : \
7350 (((uintptr_t)(x) + (uintptr_t)(a) - 1l) & ~((uintptr_t)(a) - 1l)))
7351
7352 static sbd_error_t *
drmach_iocage_mem_get(dr_testboard_req_t * tbrq)7353 drmach_iocage_mem_get(dr_testboard_req_t *tbrq)
7354 {
7355 pfn_t basepfn;
7356 pgcnt_t npages;
7357 extern int memscrub_delete_span(pfn_t, pgcnt_t);
7358 uint64_t drmach_iocage_paddr_mbytes;
7359
7360 ASSERT(drmach_iocage_paddr != -1);
7361
7362 basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
7363 npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
7364
7365 (void) memscrub_delete_span(basepfn, npages);
7366
7367 mutex_enter(&cpu_lock);
7368 drmach_iocage_mem_scrub(drmach_iocage_size);
7369 mutex_exit(&cpu_lock);
7370
7371 /*
7372 * HPOST wants the address of the cage to be 64 megabyte-aligned
7373 * and in megabyte units.
7374 * The size of the cage is also in megabyte units.
7375 */
7376 ASSERT(drmach_iocage_paddr == ALIGN(drmach_iocage_paddr, 0x4000000));
7377
7378 drmach_iocage_paddr_mbytes = drmach_iocage_paddr / 0x100000;
7379
7380 tbrq->memaddrhi = (uint32_t)(drmach_iocage_paddr_mbytes >> 32);
7381 tbrq->memaddrlo = (uint32_t)drmach_iocage_paddr_mbytes;
7382 tbrq->memlen = drmach_iocage_size / 0x100000;
7383
7384 DRMACH_PR("drmach_iocage_mem_get: hi: 0x%x", tbrq->memaddrhi);
7385 DRMACH_PR("drmach_iocage_mem_get: lo: 0x%x", tbrq->memaddrlo);
7386 DRMACH_PR("drmach_iocage_mem_get: size: 0x%x", tbrq->memlen);
7387
7388 return (NULL);
7389 }
7390
7391 static sbd_error_t *
drmach_iocage_mem_return(dr_testboard_reply_t * tbr)7392 drmach_iocage_mem_return(dr_testboard_reply_t *tbr)
7393 {
7394 _NOTE(ARGUNUSED(tbr))
7395
7396 pfn_t basepfn;
7397 pgcnt_t npages;
7398 extern int memscrub_add_span(pfn_t, pgcnt_t);
7399
7400 ASSERT(drmach_iocage_paddr != -1);
7401
7402 basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
7403 npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
7404
7405 (void) memscrub_add_span(basepfn, npages);
7406
7407 mutex_enter(&cpu_lock);
7408 mutex_enter(&drmach_iocage_lock);
7409 drmach_iocage_mem_scrub(drmach_iocage_size);
7410 drmach_iocage_is_busy = 0;
7411 cv_signal(&drmach_iocage_cv);
7412 mutex_exit(&drmach_iocage_lock);
7413 mutex_exit(&cpu_lock);
7414
7415 return (NULL);
7416 }
7417
7418 static int
drmach_cpu_intr_disable(cpu_t * cp)7419 drmach_cpu_intr_disable(cpu_t *cp)
7420 {
7421 if (cpu_intr_disable(cp) != 0)
7422 return (-1);
7423 return (0);
7424 }
7425
7426 static int
drmach_iocage_cpu_acquire(drmach_device_t * dp,cpu_flag_t * oflags)7427 drmach_iocage_cpu_acquire(drmach_device_t *dp, cpu_flag_t *oflags)
7428 {
7429 struct cpu *cp;
7430 processorid_t cpuid;
7431 static char *fn = "drmach_iocage_cpu_acquire";
7432 sbd_error_t *err;
7433 int impl;
7434
7435 ASSERT(DRMACH_IS_CPU_ID(dp));
7436 ASSERT(MUTEX_HELD(&cpu_lock));
7437
7438 cpuid = ((drmach_cpu_t *)dp)->cpuid;
7439
7440 DRMACH_PR("%s: attempting to acquire CPU id %d", fn, cpuid);
7441
7442 if (dp->busy)
7443 return (-1);
7444
7445 if ((cp = cpu_get(cpuid)) == NULL) {
7446 DRMACH_PR("%s: cpu_get(%d) returned NULL", fn, cpuid);
7447 return (-1);
7448 }
7449
7450 if (!CPU_ACTIVE(cp)) {
7451 DRMACH_PR("%s: skipping offlined CPU id %d", fn, cpuid);
7452 return (-1);
7453 }
7454
7455 /*
7456 * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
7457 * can fail to receive an XIR. To workaround this issue until a hardware
7458 * fix is implemented, we will exclude the selection of these CPUs.
7459 *
7460 * Once a fix is implemented in hardware, this code should be updated
7461 * to allow Jaguar CPUs that have the fix to be used. However, support
7462 * must be retained to skip revisions that do not have this fix.
7463 */
7464
7465 err = drmach_cpu_get_impl(dp, &impl);
7466 if (err) {
7467 DRMACH_PR("%s: error getting impl. of CPU id %d", fn, cpuid);
7468 sbd_err_clear(&err);
7469 return (-1);
7470 }
7471
7472 if (IS_JAGUAR(impl) && (STARCAT_CPUID_TO_LPORT(cpuid) == 0) &&
7473 drmach_iocage_exclude_jaguar_port_zero) {
7474 DRMACH_PR("%s: excluding CPU id %d: port 0 on jaguar",
7475 fn, cpuid);
7476 return (-1);
7477 }
7478
7479 ASSERT(oflags);
7480 *oflags = cp->cpu_flags;
7481
7482 if (cpu_offline(cp, 0)) {
7483 DRMACH_PR("%s: cpu_offline failed for CPU id %d", fn, cpuid);
7484 return (-1);
7485 }
7486
7487 if (cpu_poweroff(cp)) {
7488 DRMACH_PR("%s: cpu_poweroff failed for CPU id %d", fn, cpuid);
7489 if (cpu_online(cp)) {
7490 cmn_err(CE_WARN, "failed to online CPU id %d "
7491 "during I/O cage test selection", cpuid);
7492 }
7493 if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
7494 drmach_cpu_intr_disable(cp) != 0) {
7495 cmn_err(CE_WARN, "failed to restore CPU id %d "
7496 "no-intr during I/O cage test selection", cpuid);
7497 }
7498 return (-1);
7499 }
7500
7501 if (cpu_unconfigure(cpuid)) {
7502 DRMACH_PR("%s: cpu_unconfigure failed for CPU id %d", fn,
7503 cpuid);
7504 (void) cpu_configure(cpuid);
7505 if ((cp = cpu_get(cpuid)) == NULL) {
7506 cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
7507 "during I/O cage test selection", cpuid);
7508 dp->busy = 1;
7509 return (-1);
7510 }
7511 if (cpu_poweron(cp) || cpu_online(cp)) {
7512 cmn_err(CE_WARN, "failed to %s CPU id %d "
7513 "during I/O cage test selection",
7514 cpu_is_poweredoff(cp) ?
7515 "poweron" : "online", cpuid);
7516 }
7517 if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
7518 drmach_cpu_intr_disable(cp) != 0) {
7519 cmn_err(CE_WARN, "failed to restore CPU id %d "
7520 "no-intr during I/O cage test selection", cpuid);
7521 }
7522 return (-1);
7523 }
7524
7525 dp->busy = 1;
7526
7527 DRMACH_PR("%s: acquired CPU id %d", fn, cpuid);
7528
7529 return (0);
7530 }
7531
7532 /*
7533 * Attempt to acquire all the CPU devices passed in. It is
7534 * assumed that all the devices in the list are the cores of
7535 * a single CMP device. Non CMP devices can be handled as a
7536 * single core CMP by passing in a one element list.
7537 *
7538 * Success is only returned if *all* the devices in the list
7539 * can be acquired. In the failure case, none of the devices
7540 * in the list will be held as acquired.
7541 */
7542 static int
drmach_iocage_cmp_acquire(drmach_device_t ** dpp,cpu_flag_t * oflags)7543 drmach_iocage_cmp_acquire(drmach_device_t **dpp, cpu_flag_t *oflags)
7544 {
7545 int curr;
7546 int i;
7547 int rv = 0;
7548
7549 ASSERT((dpp != NULL) && (*dpp != NULL));
7550
7551 /*
7552 * Walk the list of CPU devices (cores of a CMP)
7553 * and attempt to acquire them. Bail out if an
7554 * error is encountered.
7555 */
7556 for (curr = 0; curr < MAX_CORES_PER_CMP; curr++) {
7557
7558 /* check for the end of the list */
7559 if (dpp[curr] == NULL) {
7560 break;
7561 }
7562
7563 ASSERT(DRMACH_IS_CPU_ID(dpp[curr]));
7564 ASSERT(dpp[curr]->portid == (*dpp)->portid);
7565
7566 rv = drmach_iocage_cpu_acquire(dpp[curr], &oflags[curr]);
7567 if (rv != 0) {
7568 break;
7569 }
7570 }
7571
7572 /*
7573 * Check for an error.
7574 */
7575 if (rv != 0) {
7576 /*
7577 * Make a best effort attempt to return any cores
7578 * that were already acquired before the error was
7579 * encountered.
7580 */
7581 for (i = 0; i < curr; i++) {
7582 (void) drmach_iocage_cpu_return(dpp[i], oflags[i]);
7583 }
7584 }
7585
7586 return (rv);
7587 }
7588
7589 static int
drmach_iocage_cpu_return(drmach_device_t * dp,cpu_flag_t oflags)7590 drmach_iocage_cpu_return(drmach_device_t *dp, cpu_flag_t oflags)
7591 {
7592 processorid_t cpuid;
7593 struct cpu *cp;
7594 int rv = 0;
7595 static char *fn = "drmach_iocage_cpu_return";
7596
7597 ASSERT(DRMACH_IS_CPU_ID(dp));
7598 ASSERT(MUTEX_HELD(&cpu_lock));
7599
7600 cpuid = ((drmach_cpu_t *)dp)->cpuid;
7601
7602 DRMACH_PR("%s: attempting to return CPU id: %d", fn, cpuid);
7603
7604 if (cpu_configure(cpuid)) {
7605 cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
7606 "after I/O cage test", cpuid);
7607 /*
7608 * The component was never set to unconfigured during the IO
7609 * cage test, so we need to leave marked as busy to prevent
7610 * further DR operations involving this component.
7611 */
7612 return (-1);
7613 }
7614
7615 if ((cp = cpu_get(cpuid)) == NULL) {
7616 cmn_err(CE_WARN, "cpu_get failed on CPU id %d after "
7617 "I/O cage test", cpuid);
7618 dp->busy = 0;
7619 return (-1);
7620 }
7621
7622 if (cpu_poweron(cp) || cpu_online(cp)) {
7623 cmn_err(CE_WARN, "failed to %s CPU id %d after I/O "
7624 "cage test", cpu_is_poweredoff(cp) ?
7625 "poweron" : "online", cpuid);
7626 rv = -1;
7627 }
7628
7629 /*
7630 * drmach_iocage_cpu_acquire will accept cpus in state P_ONLINE or
7631 * P_NOINTR. Need to return to previous user-visible state.
7632 */
7633 if (CPU_ACTIVE(cp) && cpu_flagged_nointr(oflags) &&
7634 drmach_cpu_intr_disable(cp) != 0) {
7635 cmn_err(CE_WARN, "failed to restore CPU id %d "
7636 "no-intr after I/O cage test", cpuid);
7637 rv = -1;
7638 }
7639
7640 dp->busy = 0;
7641
7642 DRMACH_PR("%s: returned CPU id: %d", fn, cpuid);
7643
7644 return (rv);
7645 }
7646
7647 static sbd_error_t *
drmach_iocage_cpu_get(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)7648 drmach_iocage_cpu_get(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
7649 cpu_flag_t *oflags)
7650 {
7651 drmach_board_t *bp;
7652 int b_rv;
7653 int b_idx;
7654 drmachid_t b_id;
7655 int found;
7656
7657 mutex_enter(&cpu_lock);
7658
7659 ASSERT(drmach_boards != NULL);
7660
7661 found = 0;
7662
7663 /*
7664 * Walk the board list.
7665 */
7666 b_rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
7667
7668 while (b_rv == 0) {
7669
7670 int d_rv;
7671 int d_idx;
7672 drmachid_t d_id;
7673
7674 bp = b_id;
7675
7676 if (bp->connected == 0 || bp->devices == NULL) {
7677 b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
7678 continue;
7679 }
7680
7681 /* An AXQ restriction disqualifies MCPU's as candidates. */
7682 if (DRMACH_BNUM2SLOT(bp->bnum) == 1) {
7683 b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
7684 continue;
7685 }
7686
7687 /*
7688 * Walk the device list of this board.
7689 */
7690 d_rv = drmach_array_first(bp->devices, &d_idx, &d_id);
7691
7692 while (d_rv == 0) {
7693
7694 drmach_device_t *ndp;
7695
7696 /* only interested in CPU devices */
7697 if (!DRMACH_IS_CPU_ID(d_id)) {
7698 d_rv = drmach_array_next(bp->devices, &d_idx,
7699 &d_id);
7700 continue;
7701 }
7702
7703 /*
7704 * The following code assumes two properties
7705 * of a CMP device:
7706 *
7707 * 1. All cores of a CMP are grouped together
7708 * in the device list.
7709 *
7710 * 2. There will only be a maximum of two cores
7711 * present in the CMP.
7712 *
7713 * If either of these two properties change,
7714 * this code will have to be revisited.
7715 */
7716
7717 dpp[0] = d_id;
7718 dpp[1] = NULL;
7719
7720 /*
7721 * Get the next device. It may or may not be used.
7722 */
7723 d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
7724 ndp = d_id;
7725
7726 if ((d_rv == 0) && DRMACH_IS_CPU_ID(d_id)) {
7727 /*
7728 * The second device is only interesting for
7729 * this pass if it has the same portid as the
7730 * first device. This implies that both are
7731 * cores of the same CMP.
7732 */
7733 if (dpp[0]->portid == ndp->portid) {
7734 dpp[1] = d_id;
7735 }
7736 }
7737
7738 /*
7739 * Attempt to acquire all cores of the CMP.
7740 */
7741 if (drmach_iocage_cmp_acquire(dpp, oflags) == 0) {
7742 found = 1;
7743 break;
7744 }
7745
7746 /*
7747 * Check if the search for the second core was
7748 * successful. If not, the next iteration should
7749 * use that device.
7750 */
7751 if (dpp[1] == NULL) {
7752 continue;
7753 }
7754
7755 d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
7756 }
7757
7758 if (found)
7759 break;
7760
7761 b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
7762 }
7763
7764 mutex_exit(&cpu_lock);
7765
7766 if (!found) {
7767 return (drerr_new(1, ESTC_IOCAGE_NO_CPU_AVAIL, NULL));
7768 }
7769
7770 tbrq->cpu_portid = (*dpp)->portid;
7771
7772 return (NULL);
7773 }
7774
7775 /*
7776 * Setup an iocage by acquiring a cpu and memory.
7777 */
7778 static sbd_error_t *
drmach_iocage_setup(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)7779 drmach_iocage_setup(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
7780 cpu_flag_t *oflags)
7781 {
7782 sbd_error_t *err;
7783
7784 err = drmach_iocage_cpu_get(tbrq, dpp, oflags);
7785 if (!err) {
7786 mutex_enter(&drmach_iocage_lock);
7787 while (drmach_iocage_is_busy)
7788 cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
7789 drmach_iocage_is_busy = 1;
7790 mutex_exit(&drmach_iocage_lock);
7791 err = drmach_iocage_mem_get(tbrq);
7792 if (err) {
7793 mutex_enter(&drmach_iocage_lock);
7794 drmach_iocage_is_busy = 0;
7795 cv_signal(&drmach_iocage_cv);
7796 mutex_exit(&drmach_iocage_lock);
7797 }
7798 }
7799 return (err);
7800 }
7801
7802 #define DRMACH_SCHIZO_PCI_LEAF_MAX 2
7803 #define DRMACH_SCHIZO_PCI_SLOT_MAX 8
7804 #define DRMACH_S1P_SAMPLE_MAX 2
7805
7806 typedef enum {
7807 DRMACH_POST_SUSPEND = 0,
7808 DRMACH_PRE_RESUME
7809 } drmach_sr_iter_t;
7810
7811 typedef struct {
7812 dev_info_t *dip;
7813 uint32_t portid;
7814 uint32_t pcr_sel_save;
7815 uint32_t pic_l2_io_q[DRMACH_S1P_SAMPLE_MAX];
7816 uint64_t reg_basepa;
7817 } drmach_s1p_axq_t;
7818
7819 typedef struct {
7820 dev_info_t *dip;
7821 uint32_t portid;
7822 uint64_t csr_basepa;
7823 struct {
7824 uint64_t slot_intr_state_diag;
7825 uint64_t obio_intr_state_diag;
7826 uint_t nmap_regs;
7827 uint64_t *intr_map_regs;
7828 } regs[DRMACH_S1P_SAMPLE_MAX];
7829 } drmach_s1p_pci_t;
7830
7831 typedef struct {
7832 uint64_t csr_basepa;
7833 struct {
7834 uint64_t csr;
7835 uint64_t errctrl;
7836 uint64_t errlog;
7837 } regs[DRMACH_S1P_SAMPLE_MAX];
7838 drmach_s1p_pci_t pci[DRMACH_SCHIZO_PCI_LEAF_MAX];
7839 } drmach_s1p_schizo_t;
7840
7841 typedef struct {
7842 drmach_s1p_axq_t axq;
7843 drmach_s1p_schizo_t schizo[STARCAT_SLOT1_IO_MAX];
7844 } drmach_slot1_pause_t;
7845
7846 /*
7847 * Table of saved state for paused slot1 devices.
7848 */
7849 static drmach_slot1_pause_t *drmach_slot1_paused[STARCAT_BDSET_MAX];
7850 static int drmach_slot1_pause_init = 1;
7851
7852 #ifdef DEBUG
7853 int drmach_slot1_pause_debug = 1;
7854 #else
7855 int drmach_slot1_pause_debug = 0;
7856 #endif /* DEBUG */
7857
7858 static int
drmach_is_slot1_pause_axq(dev_info_t * dip,char * name,int * id,uint64_t * reg)7859 drmach_is_slot1_pause_axq(dev_info_t *dip, char *name, int *id, uint64_t *reg)
7860 {
7861 int portid, exp, slot, i;
7862 drmach_reg_t regs[2];
7863 int reglen = sizeof (regs);
7864
7865 if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
7866 DDI_PROP_DONTPASS, "portid", -1)) == -1) {
7867 return (0);
7868 }
7869
7870 exp = (portid >> 5) & 0x1f;
7871 slot = portid & 0x1;
7872
7873 if (slot == 0 || strncmp(name, DRMACH_AXQ_NAMEPROP,
7874 strlen(DRMACH_AXQ_NAMEPROP))) {
7875 return (0);
7876 }
7877
7878 mutex_enter(&cpu_lock);
7879 for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) {
7880 if (cpu[MAKE_CPUID(exp, slot, i)]) {
7881 /* maxcat cpu present */
7882 mutex_exit(&cpu_lock);
7883 return (0);
7884 }
7885 }
7886 mutex_exit(&cpu_lock);
7887
7888 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
7889 "reg", (caddr_t)regs, ®len) != DDI_PROP_SUCCESS) {
7890 DRMACH_PR("drmach_is_slot1_pause_axq: no reg prop for "
7891 "axq dip=%p\n", (void *)dip);
7892 return (0);
7893 }
7894
7895 ASSERT(id && reg);
7896 *reg = (uint64_t)regs[0].reg_addr_hi << 32;
7897 *reg |= (uint64_t)regs[0].reg_addr_lo;
7898 *id = portid;
7899
7900 return (1);
7901 }
7902
7903 /*
7904 * Allocate an entry in the slot1_paused state table.
7905 */
7906 static void
drmach_slot1_pause_add_axq(dev_info_t * axq_dip,char * axq_name,int axq_portid,uint64_t reg,drmach_slot1_pause_t ** slot1_paused)7907 drmach_slot1_pause_add_axq(dev_info_t *axq_dip, char *axq_name, int axq_portid,
7908 uint64_t reg, drmach_slot1_pause_t **slot1_paused)
7909 {
7910 int axq_exp;
7911 drmach_slot1_pause_t *slot1;
7912
7913 axq_exp = (axq_portid >> 5) & 0x1f;
7914
7915 ASSERT(axq_portid & 0x1);
7916 ASSERT(slot1_paused[axq_exp] == NULL);
7917 ASSERT(strncmp(axq_name, DRMACH_AXQ_NAMEPROP,
7918 strlen(DRMACH_AXQ_NAMEPROP)) == 0);
7919
7920 slot1 = kmem_zalloc(sizeof (*slot1), KM_SLEEP);
7921
7922 /*
7923 * XXX This dip should really be held (via ndi_hold_devi())
7924 * before saving it in the axq pause structure. However that
7925 * would prevent DR as the pause data structures persist until
7926 * the next suspend. drmach code should be modified to free the
7927 * the slot 1 pause data structures for a boardset when its
7928 * slot 1 board is DRed out. The dip can then be released via
7929 * ndi_rele_devi() when the pause data structure is freed
7930 * allowing DR to proceed. Until this change is made, drmach
7931 * code should be careful about dereferencing the saved dip
7932 * as it may no longer exist.
7933 */
7934 slot1->axq.dip = axq_dip;
7935 slot1->axq.portid = axq_portid;
7936 slot1->axq.reg_basepa = reg;
7937 slot1_paused[axq_exp] = slot1;
7938 }
7939
7940 static void
drmach_s1p_pci_free(drmach_s1p_pci_t * pci)7941 drmach_s1p_pci_free(drmach_s1p_pci_t *pci)
7942 {
7943 int i;
7944
7945 for (i = 0; i < DRMACH_S1P_SAMPLE_MAX; i++) {
7946 if (pci->regs[i].intr_map_regs != NULL) {
7947 ASSERT(pci->regs[i].nmap_regs > 0);
7948 kmem_free(pci->regs[i].intr_map_regs,
7949 pci->regs[i].nmap_regs * sizeof (uint64_t));
7950 }
7951 }
7952 }
7953
7954 static void
drmach_slot1_pause_free(drmach_slot1_pause_t ** slot1_paused)7955 drmach_slot1_pause_free(drmach_slot1_pause_t **slot1_paused)
7956 {
7957 int i, j, k;
7958 drmach_slot1_pause_t *slot1;
7959
7960 for (i = 0; i < STARCAT_BDSET_MAX; i++) {
7961 if ((slot1 = slot1_paused[i]) == NULL)
7962 continue;
7963
7964 for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
7965 for (k = 0; k < DRMACH_SCHIZO_PCI_LEAF_MAX; k++)
7966 drmach_s1p_pci_free(&slot1->schizo[j].pci[k]);
7967
7968 kmem_free(slot1, sizeof (*slot1));
7969 slot1_paused[i] = NULL;
7970 }
7971 }
7972
7973 /*
7974 * Tree walk callback routine. If dip represents a Schizo PCI leaf,
7975 * fill in the appropriate info in the slot1_paused state table.
7976 */
7977 static int
drmach_find_slot1_io(dev_info_t * dip,void * arg)7978 drmach_find_slot1_io(dev_info_t *dip, void *arg)
7979 {
7980 int portid, exp, ioc_unum, leaf_unum;
7981 char buf[OBP_MAXDRVNAME];
7982 int buflen = sizeof (buf);
7983 drmach_reg_t regs[3];
7984 int reglen = sizeof (regs);
7985 uint32_t leaf_offset;
7986 uint64_t schizo_csr_pa, pci_csr_pa;
7987 drmach_s1p_pci_t *pci;
7988 drmach_slot1_pause_t **slot1_paused = (drmach_slot1_pause_t **)arg;
7989
7990 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
7991 "name", (caddr_t)buf, &buflen) != DDI_PROP_SUCCESS ||
7992 strncmp(buf, DRMACH_PCI_NAMEPROP, strlen(DRMACH_PCI_NAMEPROP))) {
7993 return (DDI_WALK_CONTINUE);
7994 }
7995
7996 if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
7997 DDI_PROP_DONTPASS, "portid", -1)) == -1) {
7998 return (DDI_WALK_CONTINUE);
7999 }
8000
8001 if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8002 "reg", (caddr_t)regs, ®len) != DDI_PROP_SUCCESS) {
8003 DRMACH_PR("drmach_find_slot1_io: no reg prop for pci "
8004 "dip=%p\n", (void *)dip);
8005 return (DDI_WALK_CONTINUE);
8006 }
8007
8008 exp = portid >> 5;
8009 ioc_unum = portid & 0x1;
8010 leaf_offset = regs[0].reg_addr_lo & 0x7fffff;
8011 pci_csr_pa = (uint64_t)regs[0].reg_addr_hi << 32;
8012 pci_csr_pa |= (uint64_t)regs[0].reg_addr_lo;
8013 schizo_csr_pa = (uint64_t)regs[1].reg_addr_hi << 32;
8014 schizo_csr_pa |= (uint64_t)regs[1].reg_addr_lo;
8015
8016 ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
8017 ASSERT(slot1_paused[exp] != NULL);
8018 ASSERT(leaf_offset == 0x600000 || leaf_offset == 0x700000);
8019 ASSERT(slot1_paused[exp]->schizo[ioc_unum].csr_basepa == 0x0UL ||
8020 slot1_paused[exp]->schizo[ioc_unum].csr_basepa == schizo_csr_pa);
8021
8022 leaf_unum = (leaf_offset == 0x600000) ? 0 : 1;
8023 slot1_paused[exp]->schizo[ioc_unum].csr_basepa = schizo_csr_pa;
8024 pci = &slot1_paused[exp]->schizo[ioc_unum].pci[leaf_unum];
8025
8026 /*
8027 * XXX This dip should really be held (via ndi_hold_devi())
8028 * before saving it in the pci pause structure. However that
8029 * would prevent DR as the pause data structures persist until
8030 * the next suspend. drmach code should be modified to free the
8031 * the slot 1 pause data structures for a boardset when its
8032 * slot 1 board is DRed out. The dip can then be released via
8033 * ndi_rele_devi() when the pause data structure is freed
8034 * allowing DR to proceed. Until this change is made, drmach
8035 * code should be careful about dereferencing the saved dip as
8036 * it may no longer exist.
8037 */
8038 pci->dip = dip;
8039 pci->portid = portid;
8040 pci->csr_basepa = pci_csr_pa;
8041
8042 DRMACH_PR("drmach_find_slot1_io: name=%s, portid=0x%x, dip=%p\n",
8043 buf, portid, (void *)dip);
8044
8045 return (DDI_WALK_PRUNECHILD);
8046 }
8047
8048 static void
drmach_slot1_pause_add_io(drmach_slot1_pause_t ** slot1_paused)8049 drmach_slot1_pause_add_io(drmach_slot1_pause_t **slot1_paused)
8050 {
8051 /*
8052 * Root node doesn't have to be held
8053 */
8054 ddi_walk_devs(ddi_root_node(), drmach_find_slot1_io,
8055 (void *)slot1_paused);
8056 }
8057
8058 /*
8059 * Save the interrupt mapping registers for each non-idle interrupt
8060 * represented by the bit pairs in the saved interrupt state
8061 * diagnostic registers for this PCI leaf.
8062 */
8063 static void
drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t * pci,drmach_sr_iter_t iter)8064 drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t *pci, drmach_sr_iter_t iter)
8065 {
8066 int i, cnt, ino;
8067 uint64_t reg;
8068 char *dname;
8069 uchar_t Xmits;
8070
8071 dname = ddi_binding_name(pci->dip);
8072 Xmits = (strcmp(dname, XMITS_BINDING_NAME) == 0) ? 1 : 0;
8073
8074 /*
8075 * 1st pass allocates, 2nd pass populates.
8076 */
8077 for (i = 0; i < 2; i++) {
8078 cnt = ino = 0;
8079
8080 /*
8081 * PCI slot interrupts
8082 */
8083 reg = pci->regs[iter].slot_intr_state_diag;
8084 while (reg) {
8085 /*
8086 * Xmits Interrupt Number Offset(ino) Assignments
8087 * 00-17 PCI Slot Interrupts
8088 * 18-1f Not Used
8089 */
8090 if ((Xmits) && (ino > 0x17))
8091 break;
8092 if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
8093 COMMON_CLEAR_INTR_REG_IDLE) {
8094 if (i) {
8095 pci->regs[iter].intr_map_regs[cnt] =
8096 lddphysio(pci->csr_basepa +
8097 SCHIZO_IB_INTR_MAP_REG_OFFSET +
8098 ino * sizeof (reg));
8099 }
8100 ++cnt;
8101 }
8102 ++ino;
8103 reg >>= 2;
8104 }
8105
8106 /*
8107 * Xmits Interrupt Number Offset(ino) Assignments
8108 * 20-2f Not Used
8109 * 30-37 Internal interrupts
8110 * 38-3e Not Used
8111 */
8112 ino = (Xmits) ? 0x30 : 0x20;
8113
8114 /*
8115 * OBIO and internal schizo interrupts
8116 * Each PCI leaf has a set of mapping registers for all
8117 * possible interrupt sources except the NewLink interrupts.
8118 */
8119 reg = pci->regs[iter].obio_intr_state_diag;
8120 while (reg && ino <= 0x38) {
8121 if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
8122 COMMON_CLEAR_INTR_REG_IDLE) {
8123 if (i) {
8124 pci->regs[iter].intr_map_regs[cnt] =
8125 lddphysio(pci->csr_basepa +
8126 SCHIZO_IB_INTR_MAP_REG_OFFSET +
8127 ino * sizeof (reg));
8128 }
8129 ++cnt;
8130 }
8131 ++ino;
8132 reg >>= 2;
8133 }
8134
8135 if (!i) {
8136 pci->regs[iter].nmap_regs = cnt;
8137 pci->regs[iter].intr_map_regs =
8138 kmem_zalloc(cnt * sizeof (reg), KM_SLEEP);
8139 }
8140 }
8141 }
8142
8143 static void
drmach_s1p_axq_update(drmach_s1p_axq_t * axq,drmach_sr_iter_t iter)8144 drmach_s1p_axq_update(drmach_s1p_axq_t *axq, drmach_sr_iter_t iter)
8145 {
8146 uint32_t reg;
8147
8148 if (axq->reg_basepa == 0x0UL)
8149 return;
8150
8151 if (iter == DRMACH_POST_SUSPEND) {
8152 axq->pcr_sel_save = ldphysio(axq->reg_basepa +
8153 AXQ_SLOT1_PERFCNT_SEL);
8154 /*
8155 * Select l2_io_queue counter by writing L2_IO_Q mux
8156 * input to bits 0-6 of perf cntr select reg.
8157 */
8158 reg = axq->pcr_sel_save;
8159 reg &= ~AXQ_PIC_CLEAR_MASK;
8160 reg |= L2_IO_Q;
8161
8162 stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL, reg);
8163 }
8164
8165 axq->pic_l2_io_q[iter] = ldphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT0);
8166
8167 if (iter == DRMACH_PRE_RESUME) {
8168 stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL,
8169 axq->pcr_sel_save);
8170 }
8171
8172 DRMACH_PR("drmach_s1p_axq_update: axq #%d pic_l2_io_q[%d]=%d\n",
8173 ddi_get_instance(axq->dip), iter, axq->pic_l2_io_q[iter]);
8174 }
8175
8176 static void
drmach_s1p_schizo_update(drmach_s1p_schizo_t * schizo,drmach_sr_iter_t iter)8177 drmach_s1p_schizo_update(drmach_s1p_schizo_t *schizo, drmach_sr_iter_t iter)
8178 {
8179 int i;
8180 drmach_s1p_pci_t *pci;
8181
8182 if (schizo->csr_basepa == 0x0UL)
8183 return;
8184
8185 schizo->regs[iter].csr =
8186 lddphysio(schizo->csr_basepa + SCHIZO_CB_CSR_OFFSET);
8187 schizo->regs[iter].errctrl =
8188 lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRCTRL_OFFSET);
8189 schizo->regs[iter].errlog =
8190 lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRLOG_OFFSET);
8191
8192 for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
8193 pci = &schizo->pci[i];
8194 if (pci->dip != NULL && pci->csr_basepa != 0x0UL) {
8195 pci->regs[iter].slot_intr_state_diag =
8196 lddphysio(pci->csr_basepa +
8197 COMMON_IB_SLOT_INTR_STATE_DIAG_REG);
8198
8199 pci->regs[iter].obio_intr_state_diag =
8200 lddphysio(pci->csr_basepa +
8201 COMMON_IB_OBIO_INTR_STATE_DIAG_REG);
8202
8203 drmach_s1p_intr_map_reg_save(pci, iter);
8204 }
8205 }
8206 }
8207
8208 /*
8209 * Called post-suspend and pre-resume to snapshot the suspend state
8210 * of slot1 AXQs and Schizos.
8211 */
8212 static void
drmach_slot1_pause_update(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)8213 drmach_slot1_pause_update(drmach_slot1_pause_t **slot1_paused,
8214 drmach_sr_iter_t iter)
8215 {
8216 int i, j;
8217 drmach_slot1_pause_t *slot1;
8218
8219 for (i = 0; i < STARCAT_BDSET_MAX; i++) {
8220 if ((slot1 = slot1_paused[i]) == NULL)
8221 continue;
8222
8223 drmach_s1p_axq_update(&slot1->axq, iter);
8224 for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
8225 drmach_s1p_schizo_update(&slot1->schizo[j], iter);
8226 }
8227 }
8228
8229 /*
8230 * Starcat hPCI Schizo devices.
8231 *
8232 * The name field is overloaded. NULL means the slot (interrupt concentrator
8233 * bus) is not used. intr_mask is a bit mask representing the 4 possible
8234 * interrupts per slot, on if valid (rio does not use interrupt lines 0, 1).
8235 */
8236 static struct {
8237 char *name;
8238 uint8_t intr_mask;
8239 } drmach_schz_slot_intr[][DRMACH_SCHIZO_PCI_LEAF_MAX] = {
8240 /* Schizo 0 */ /* Schizo 1 */
8241 {{"C3V0", 0xf}, {"C3V1", 0xf}}, /* slot 0 */
8242 {{"C5V0", 0xf}, {"C5V1", 0xf}}, /* slot 1 */
8243 {{"rio", 0xc}, {NULL, 0x0}}, /* slot 2 */
8244 {{NULL, 0x0}, {NULL, 0x0}}, /* slot 3 */
8245 {{"sbbc", 0xf}, {NULL, 0x0}}, /* slot 4 */
8246 {{NULL, 0x0}, {NULL, 0x0}}, /* slot 5 */
8247 {{NULL, 0x0}, {NULL, 0x0}}, /* slot 6 */
8248 {{NULL, 0x0}, {NULL, 0x0}} /* slot 7 */
8249 };
8250
8251 /*
8252 * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.4.4
8253 * "Interrupt Registers", Table 22-69, page 306.
8254 */
8255 static char *
drmach_schz_internal_ino2str(int ino)8256 drmach_schz_internal_ino2str(int ino)
8257 {
8258 int intr;
8259
8260 ASSERT(ino >= 0x30 && ino <= 0x37);
8261
8262 intr = ino & 0x7;
8263 switch (intr) {
8264 case (0x0): return ("Uncorrectable ECC error");
8265 case (0x1): return ("Correctable ECC error");
8266 case (0x2): return ("PCI Bus A Error");
8267 case (0x3): return ("PCI Bus B Error");
8268 case (0x4): return ("Safari Bus Error");
8269 default: return ("Reserved");
8270 }
8271 }
8272
8273 #define DRMACH_INTR_MASK_SHIFT(ino) ((ino) << 1)
8274
8275 static void
drmach_s1p_decode_slot_intr(int exp,int unum,drmach_s1p_pci_t * pci,int ino,drmach_sr_iter_t iter)8276 drmach_s1p_decode_slot_intr(int exp, int unum, drmach_s1p_pci_t *pci,
8277 int ino, drmach_sr_iter_t iter)
8278 {
8279 uint8_t intr_mask;
8280 char *slot_devname;
8281 char namebuf[OBP_MAXDRVNAME];
8282 int slot, intr_line, slot_valid, intr_valid;
8283
8284 ASSERT(ino >= 0 && ino <= 0x1f);
8285 ASSERT((pci->regs[iter].slot_intr_state_diag &
8286 (COMMON_CLEAR_INTR_REG_MASK << DRMACH_INTR_MASK_SHIFT(ino))) !=
8287 COMMON_CLEAR_INTR_REG_IDLE);
8288
8289 slot = (ino >> 2) & 0x7;
8290 intr_line = ino & 0x3;
8291
8292 slot_devname = drmach_schz_slot_intr[slot][unum].name;
8293 slot_valid = (slot_devname == NULL) ? 0 : 1;
8294 if (!slot_valid) {
8295 (void) snprintf(namebuf, sizeof (namebuf), "slot %d (INVALID)",
8296 slot);
8297 slot_devname = namebuf;
8298 }
8299
8300 intr_mask = drmach_schz_slot_intr[slot][unum].intr_mask;
8301 intr_valid = (1 << intr_line) & intr_mask;
8302
8303 prom_printf("IO%d/P%d PCI slot interrupt: ino=0x%x, source device=%s, "
8304 "interrupt line=%d%s\n", exp, unum, ino, slot_devname, intr_line,
8305 (slot_valid && !intr_valid) ? " (INVALID)" : "");
8306 }
8307
8308 /*
8309 * Log interrupt source device info for all valid, pending interrupts
8310 * on each Schizo PCI leaf. Called if Schizo has logged a Safari bus
8311 * error in the error ctrl reg.
8312 */
8313 static void
drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t * schizo,int exp,int unum,drmach_sr_iter_t iter)8314 drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t *schizo, int exp,
8315 int unum, drmach_sr_iter_t iter)
8316 {
8317 uint64_t reg;
8318 int i, n, ino;
8319 drmach_s1p_pci_t *pci;
8320
8321 ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
8322 ASSERT(unum < STARCAT_SLOT1_IO_MAX);
8323
8324 /*
8325 * Check the saved interrupt mapping registers. If interrupt is valid,
8326 * map the ino to the Schizo source device and check that the pci
8327 * slot and interrupt line are valid.
8328 */
8329 for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
8330 pci = &schizo->pci[i];
8331 for (n = 0; n < pci->regs[iter].nmap_regs; n++) {
8332 reg = pci->regs[iter].intr_map_regs[n];
8333 if (reg & COMMON_INTR_MAP_REG_VALID) {
8334 ino = reg & COMMON_INTR_MAP_REG_INO;
8335
8336 if (ino <= 0x1f) {
8337 /*
8338 * PCI slot interrupt
8339 */
8340 drmach_s1p_decode_slot_intr(exp, unum,
8341 pci, ino, iter);
8342 } else if (ino <= 0x2f) {
8343 /*
8344 * OBIO interrupt
8345 */
8346 prom_printf("IO%d/P%d OBIO interrupt: "
8347 "ino=0x%x\n", exp, unum, ino);
8348 } else if (ino <= 0x37) {
8349 /*
8350 * Internal interrupt
8351 */
8352 prom_printf("IO%d/P%d Internal "
8353 "interrupt: ino=0x%x (%s)\n",
8354 exp, unum, ino,
8355 drmach_schz_internal_ino2str(ino));
8356 } else {
8357 /*
8358 * NewLink interrupt
8359 */
8360 prom_printf("IO%d/P%d NewLink "
8361 "interrupt: ino=0x%x\n", exp,
8362 unum, ino);
8363 }
8364
8365 DRMACH_PR("drmach_s1p_schizo_log_intr: "
8366 "exp=%d, schizo=%d, pci_leaf=%c, "
8367 "ino=0x%x, intr_map_reg=0x%lx\n",
8368 exp, unum, (i == 0) ? 'A' : 'B', ino, reg);
8369 }
8370 }
8371 }
8372 }
8373
8374 /*
8375 * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.2.4
8376 * "Safari Error Control/Log Registers", Table 22-11, page 248.
8377 */
8378 #define DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR (0x1ull << 4)
8379
8380 /*
8381 * Check for possible error indicators prior to resuming the
8382 * AXQ driver, which will de-assert slot1 AXQ_DOMCTRL_PAUSE.
8383 */
8384 static void
drmach_slot1_pause_verify(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)8385 drmach_slot1_pause_verify(drmach_slot1_pause_t **slot1_paused,
8386 drmach_sr_iter_t iter)
8387 {
8388 int i, j;
8389 int errflag = 0;
8390 drmach_slot1_pause_t *slot1;
8391
8392 /*
8393 * Check for logged schizo bus error and pending interrupts.
8394 */
8395 for (i = 0; i < STARCAT_BDSET_MAX; i++) {
8396 if ((slot1 = slot1_paused[i]) == NULL)
8397 continue;
8398
8399 for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) {
8400 if (slot1->schizo[j].csr_basepa == 0x0UL)
8401 continue;
8402
8403 if (slot1->schizo[j].regs[iter].errlog &
8404 DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR) {
8405 if (!errflag) {
8406 prom_printf("DR WARNING: interrupt "
8407 "attempt detected during "
8408 "copy-rename (%s):\n",
8409 (iter == DRMACH_POST_SUSPEND) ?
8410 "post suspend" : "pre resume");
8411 ++errflag;
8412 }
8413 drmach_s1p_schizo_log_intr(&slot1->schizo[j],
8414 i, j, iter);
8415 }
8416 }
8417 }
8418
8419 /*
8420 * Check for changes in axq l2_io_q performance counters (2nd pass only)
8421 */
8422 if (iter == DRMACH_PRE_RESUME) {
8423 for (i = 0; i < STARCAT_BDSET_MAX; i++) {
8424 if ((slot1 = slot1_paused[i]) == NULL)
8425 continue;
8426
8427 if (slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND] !=
8428 slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]) {
8429 prom_printf("DR WARNING: IO transactions "
8430 "detected on IO%d during copy-rename: "
8431 "AXQ l2_io_q performance counter "
8432 "start=%d, end=%d\n", i,
8433 slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND],
8434 slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]);
8435 }
8436 }
8437 }
8438 }
8439
8440 struct drmach_sr_list {
8441 dev_info_t *dip;
8442 struct drmach_sr_list *next;
8443 struct drmach_sr_list *prev;
8444 };
8445
8446 static struct drmach_sr_ordered {
8447 char *name;
8448 struct drmach_sr_list *ring;
8449 } drmach_sr_ordered[] = {
8450 { "iosram", NULL },
8451 { "address-extender-queue", NULL },
8452 { NULL, NULL }, /* terminator -- required */
8453 };
8454
8455 static void
drmach_sr_insert(struct drmach_sr_list ** lp,dev_info_t * dip)8456 drmach_sr_insert(struct drmach_sr_list **lp, dev_info_t *dip)
8457 {
8458 struct drmach_sr_list *np;
8459
8460 DRMACH_PR("drmach_sr_insert: adding dip %p\n", (void *)dip);
8461
8462 np = (struct drmach_sr_list *)kmem_alloc(
8463 sizeof (struct drmach_sr_list), KM_SLEEP);
8464
8465 ndi_hold_devi(dip);
8466 np->dip = dip;
8467
8468 if (*lp == NULL) {
8469 /* establish list */
8470 *lp = np->next = np->prev = np;
8471 } else {
8472 /* place new node behind head node on ring list */
8473 np->prev = (*lp)->prev;
8474 np->next = *lp;
8475 np->prev->next = np;
8476 np->next->prev = np;
8477 }
8478 }
8479
8480 static void
drmach_sr_delete(struct drmach_sr_list ** lp,dev_info_t * dip)8481 drmach_sr_delete(struct drmach_sr_list **lp, dev_info_t *dip)
8482 {
8483 DRMACH_PR("drmach_sr_delete: searching for dip %p\n", (void *)dip);
8484
8485 if (*lp) {
8486 struct drmach_sr_list *xp;
8487
8488 /* start search with mostly likely node */
8489 xp = (*lp)->prev;
8490 do {
8491 if (xp->dip == dip) {
8492 xp->prev->next = xp->next;
8493 xp->next->prev = xp->prev;
8494
8495 if (xp == *lp)
8496 *lp = xp->next;
8497 if (xp == *lp)
8498 *lp = NULL;
8499 xp->dip = NULL;
8500 ndi_rele_devi(dip);
8501 kmem_free(xp, sizeof (*xp));
8502
8503 DRMACH_PR("drmach_sr_delete:"
8504 " disposed sr node for dip %p",
8505 (void *)dip);
8506 return;
8507 }
8508
8509 DRMACH_PR("drmach_sr_delete: still searching\n");
8510
8511 xp = xp->prev;
8512 } while (xp != (*lp)->prev);
8513 }
8514
8515 /* every dip should be found during resume */
8516 DRMACH_PR("ERROR: drmach_sr_delete: can't find dip %p", (void *)dip);
8517 }
8518
8519 int
drmach_verify_sr(dev_info_t * dip,int sflag)8520 drmach_verify_sr(dev_info_t *dip, int sflag)
8521 {
8522 int rv;
8523 int len;
8524 char name[OBP_MAXDRVNAME];
8525
8526 if (drmach_slot1_pause_debug) {
8527 if (sflag && drmach_slot1_pause_init) {
8528 drmach_slot1_pause_free(drmach_slot1_paused);
8529 drmach_slot1_pause_init = 0;
8530 } else if (!sflag && !drmach_slot1_pause_init) {
8531 /* schedule init for next suspend */
8532 drmach_slot1_pause_init = 1;
8533 }
8534 }
8535
8536 rv = ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
8537 "name", &len);
8538 if (rv == DDI_PROP_SUCCESS) {
8539 int portid;
8540 uint64_t reg;
8541 struct drmach_sr_ordered *op;
8542
8543 rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
8544 DDI_PROP_DONTPASS, "name", (caddr_t)name, &len);
8545
8546 if (rv != DDI_PROP_SUCCESS)
8547 return (0);
8548
8549 if (drmach_slot1_pause_debug && sflag &&
8550 drmach_is_slot1_pause_axq(dip, name, &portid, ®)) {
8551 drmach_slot1_pause_add_axq(dip, name, portid, reg,
8552 drmach_slot1_paused);
8553 }
8554
8555 for (op = drmach_sr_ordered; op->name; op++) {
8556 if (strncmp(op->name, name, strlen(op->name)) == 0) {
8557 if (sflag)
8558 drmach_sr_insert(&op->ring, dip);
8559 else
8560 drmach_sr_delete(&op->ring, dip);
8561 return (1);
8562 }
8563 }
8564 }
8565
8566 return (0);
8567 }
8568
8569 static void
drmach_sr_dip(dev_info_t * dip,int suspend)8570 drmach_sr_dip(dev_info_t *dip, int suspend)
8571 {
8572 int rv;
8573 major_t maj;
8574 char *name, *name_addr, *aka;
8575
8576 if ((name = ddi_get_name(dip)) == NULL)
8577 name = "<null name>";
8578 else if ((maj = ddi_name_to_major(name)) != -1)
8579 aka = ddi_major_to_name(maj);
8580 else
8581 aka = "<unknown>";
8582
8583 if ((name_addr = ddi_get_name_addr(dip)) == NULL)
8584 name_addr = "<null>";
8585
8586 prom_printf("\t%s %s@%s (aka %s)\n",
8587 suspend ? "suspending" : "resuming",
8588 name, name_addr, aka);
8589
8590 if (suspend) {
8591 rv = devi_detach(dip, DDI_SUSPEND);
8592 } else {
8593 rv = devi_attach(dip, DDI_RESUME);
8594 }
8595
8596 if (rv != DDI_SUCCESS) {
8597 prom_printf("\tFAILED to %s %s@%s\n",
8598 suspend ? "suspend" : "resume",
8599 name, name_addr);
8600 }
8601 }
8602
8603 void
drmach_suspend_last()8604 drmach_suspend_last()
8605 {
8606 struct drmach_sr_ordered *op;
8607
8608 if (drmach_slot1_pause_debug)
8609 drmach_slot1_pause_add_io(drmach_slot1_paused);
8610
8611 /*
8612 * The ordering array declares the strict sequence in which
8613 * the named drivers are to suspended. Each element in
8614 * the array may have a double-linked ring list of driver
8615 * instances (dip) in the order in which they were presented
8616 * to drmach_verify_sr. If present, walk the list in the
8617 * forward direction to suspend each instance.
8618 */
8619 for (op = drmach_sr_ordered; op->name; op++) {
8620 if (op->ring) {
8621 struct drmach_sr_list *rp;
8622
8623 rp = op->ring;
8624 do {
8625 drmach_sr_dip(rp->dip, 1);
8626 rp = rp->next;
8627 } while (rp != op->ring);
8628 }
8629 }
8630
8631 if (drmach_slot1_pause_debug) {
8632 drmach_slot1_pause_update(drmach_slot1_paused,
8633 DRMACH_POST_SUSPEND);
8634 drmach_slot1_pause_verify(drmach_slot1_paused,
8635 DRMACH_POST_SUSPEND);
8636 }
8637 }
8638
8639 void
drmach_resume_first()8640 drmach_resume_first()
8641 {
8642 struct drmach_sr_ordered *op = drmach_sr_ordered +
8643 (sizeof (drmach_sr_ordered) / sizeof (drmach_sr_ordered[0]));
8644
8645 if (drmach_slot1_pause_debug) {
8646 drmach_slot1_pause_update(drmach_slot1_paused,
8647 DRMACH_PRE_RESUME);
8648 drmach_slot1_pause_verify(drmach_slot1_paused,
8649 DRMACH_PRE_RESUME);
8650 }
8651
8652 op -= 1; /* point at terminating element */
8653
8654 /*
8655 * walk ordering array and rings backwards to resume dips
8656 * in reverse order in which they were suspended
8657 */
8658 while (--op >= drmach_sr_ordered) {
8659 if (op->ring) {
8660 struct drmach_sr_list *rp;
8661
8662 rp = op->ring->prev;
8663 do {
8664 drmach_sr_dip(rp->dip, 0);
8665 rp = rp->prev;
8666 } while (rp != op->ring->prev);
8667 }
8668 }
8669 }
8670
8671 /*
8672 * Log a DR sysevent.
8673 * Return value: 0 success, non-zero failure.
8674 */
8675 int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)8676 drmach_log_sysevent(int board, char *hint, int flag, int verbose)
8677 {
8678 sysevent_t *ev;
8679 sysevent_id_t eid;
8680 int rv, km_flag;
8681 sysevent_value_t evnt_val;
8682 sysevent_attr_list_t *evnt_attr_list = NULL;
8683 char attach_pnt[MAXNAMELEN];
8684
8685 km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
8686 attach_pnt[0] = '\0';
8687 if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
8688 rv = -1;
8689 goto logexit;
8690 }
8691 if (verbose)
8692 DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
8693 attach_pnt, hint, flag, verbose);
8694
8695 if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
8696 SUNW_KERN_PUB"dr", km_flag)) == NULL) {
8697 rv = -2;
8698 goto logexit;
8699 }
8700 evnt_val.value_type = SE_DATA_TYPE_STRING;
8701 evnt_val.value.sv_string = attach_pnt;
8702 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
8703 &evnt_val, km_flag)) != 0)
8704 goto logexit;
8705
8706 evnt_val.value_type = SE_DATA_TYPE_STRING;
8707 evnt_val.value.sv_string = hint;
8708 if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
8709 &evnt_val, km_flag)) != 0) {
8710 sysevent_free_attr(evnt_attr_list);
8711 goto logexit;
8712 }
8713
8714 (void) sysevent_attach_attributes(ev, evnt_attr_list);
8715
8716 /*
8717 * Log the event but do not sleep waiting for its
8718 * delivery. This provides insulation from syseventd.
8719 */
8720 rv = log_sysevent(ev, SE_NOSLEEP, &eid);
8721
8722 logexit:
8723 if (ev)
8724 sysevent_free(ev);
8725 if ((rv != 0) && verbose)
8726 cmn_err(CE_WARN,
8727 "drmach_log_sysevent failed (rv %d) for %s %s\n",
8728 rv, attach_pnt, hint);
8729
8730 return (rv);
8731 }
8732
8733 /*
8734 * Initialize the mem_slice portion of a claim/unconfig/unclaim mailbox message.
8735 * Only the valid entries are modified, so the array should be zeroed out
8736 * initially.
8737 */
8738 static void
drmach_msg_memslice_init(dr_memslice_t slice_arr[])8739 drmach_msg_memslice_init(dr_memslice_t slice_arr[]) {
8740 int i;
8741 char c;
8742
8743 ASSERT(mutex_owned(&drmach_slice_table_lock));
8744
8745 for (i = 0; i < AXQ_MAX_EXP; i++) {
8746 c = drmach_slice_table[i];
8747
8748 if (c & 0x20) {
8749 slice_arr[i].valid = 1;
8750 slice_arr[i].slice = c & 0x1f;
8751 }
8752 }
8753 }
8754
8755 /*
8756 * Initialize the mem_regs portion of a claim/unconfig/unclaim mailbox message.
8757 * Only the valid entries are modified, so the array should be zeroed out
8758 * initially.
8759 */
8760 static void
drmach_msg_memregs_init(dr_memregs_t regs_arr[])8761 drmach_msg_memregs_init(dr_memregs_t regs_arr[]) {
8762 int rv, exp, mcnum, bank;
8763 uint64_t madr;
8764 drmachid_t id;
8765 drmach_board_t *bp;
8766 drmach_mem_t *mp;
8767 dr_memregs_t *memregs;
8768
8769 /* CONSTCOND */
8770 ASSERT(DRMACH_MC_NBANKS == (PMBANKS_PER_PORT * LMBANKS_PER_PMBANK));
8771
8772 for (exp = 0; exp < 18; exp++) {
8773 rv = drmach_array_get(drmach_boards,
8774 DRMACH_EXPSLOT2BNUM(exp, 0), &id);
8775 ASSERT(rv == 0); /* should never be out of bounds */
8776 if (id == NULL) {
8777 continue;
8778 }
8779
8780 memregs = ®s_arr[exp];
8781 bp = (drmach_board_t *)id;
8782 for (mp = bp->mem; mp != NULL; mp = mp->next) {
8783 mcnum = mp->dev.portid & 0x3;
8784 for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
8785 drmach_mem_read_madr(mp, bank, &madr);
8786 if (madr & DRMACH_MC_VALID_MASK) {
8787 DRMACH_PR("%d.%d.%d.madr = 0x%lx\n",
8788 exp, mcnum, bank, madr);
8789 memregs->madr[mcnum][bank].hi =
8790 DRMACH_U64_TO_MCREGHI(madr);
8791 memregs->madr[mcnum][bank].lo =
8792 DRMACH_U64_TO_MCREGLO(madr);
8793 }
8794 }
8795 }
8796 }
8797 }
8798
8799 /*
8800 * Do not allow physical address range modification if either board on this
8801 * expander has processors in NULL LPA mode (CBASE=CBND=NULL).
8802 *
8803 * A side effect of NULL proc LPA mode in Starcat SSM is that local reads will
8804 * install the cache line as owned/dirty as a result of the RTSR transaction.
8805 * See section 5.2.3 of the Safari spec. All processors will read the bus sync
8806 * list before the rename after flushing local caches. When copy-rename
8807 * requires changing the physical address ranges (i.e. smaller memory target),
8808 * the bus sync list contains physical addresses that will not exist after the
8809 * rename. If these cache lines are owned due to a RTSR, a system error can
8810 * occur following the rename when these cache lines are evicted and a writeback
8811 * is attempted.
8812 *
8813 * Incoming parameter represents either the copy-rename source or a candidate
8814 * target memory board. On Starcat, only slot0 boards may have memory.
8815 */
8816 int
drmach_allow_memrange_modify(drmachid_t s0id)8817 drmach_allow_memrange_modify(drmachid_t s0id)
8818 {
8819 drmach_board_t *s0bp, *s1bp;
8820 drmachid_t s1id;
8821 int rv;
8822
8823 s0bp = s0id;
8824
8825 ASSERT(DRMACH_IS_BOARD_ID(s0id));
8826 ASSERT(DRMACH_BNUM2SLOT(s0bp->bnum) == 0);
8827
8828 if (s0bp->flags & DRMACH_NULL_PROC_LPA) {
8829 /*
8830 * This is reason enough to fail the request, no need
8831 * to check the device list for cpus.
8832 */
8833 return (0);
8834 }
8835
8836 /*
8837 * Check for MCPU board on the same expander.
8838 *
8839 * The board flag DRMACH_NULL_PROC_LPA can be set for all board
8840 * types, as it is derived at from the POST gdcd board flag
8841 * L1SSFLG_THIS_L1_NULL_PROC_LPA, which can be set (and should be
8842 * ignored) for boards with no processors. Since NULL proc LPA
8843 * applies only to processors, we walk the devices array to detect
8844 * MCPUs.
8845 */
8846 rv = drmach_array_get(drmach_boards, s0bp->bnum + 1, &s1id);
8847 s1bp = s1id;
8848 if (rv == 0 && s1bp != NULL) {
8849
8850 ASSERT(DRMACH_IS_BOARD_ID(s1id));
8851 ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
8852 ASSERT(DRMACH_BNUM2EXP(s0bp->bnum) ==
8853 DRMACH_BNUM2EXP(s1bp->bnum));
8854
8855 if ((s1bp->flags & DRMACH_NULL_PROC_LPA) &&
8856 s1bp->devices != NULL) {
8857 int d_idx;
8858 drmachid_t d_id;
8859
8860 rv = drmach_array_first(s1bp->devices, &d_idx, &d_id);
8861 while (rv == 0) {
8862 if (DRMACH_IS_CPU_ID(d_id)) {
8863 /*
8864 * Fail MCPU in NULL LPA mode.
8865 */
8866 return (0);
8867 }
8868
8869 rv = drmach_array_next(s1bp->devices, &d_idx,
8870 &d_id);
8871 }
8872 }
8873 }
8874
8875 return (1);
8876 }
8877