xref: /illumos-gate/usr/src/uts/intel/io/mc-amd/mcamd_subr.c (revision f304523c1c8b168f5db72cb0e24ee8318a974f8d)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Stub routines used to link in files from $SRC/common/mc
29  */
30 
31 #include <sys/types.h>
32 #include <sys/cmn_err.h>
33 #include <sys/ddi.h>
34 #include <sys/sunddi.h>
35 #include <sys/varargs.h>
36 #include <sys/fm/util.h>
37 #include <sys/fm/cpu/AMD.h>
38 #include <sys/fm/protocol.h>
39 #include <sys/mc.h>
40 
41 #include <mcamd.h>
42 #include <mcamd_off.h>
43 
44 int mcamd_debug = 0; /* see mcamd_api.h for MCAMD_DBG_* values */
45 
46 struct mc_offmap {
47 	int mcom_code;
48 	uint_t mcom_offset;
49 };
50 
51 static uint_t
52 nodetype(mcamd_node_t *node)
53 {
54 	mc_hdr_t *mch = (mc_hdr_t *)node;
55 	return (mch->mch_type);
56 }
57 
58 static void *
59 node2type(mcamd_node_t *node, int type)
60 {
61 	mc_hdr_t *mch = (mc_hdr_t *)node;
62 	ASSERT(mch->mch_type == type);
63 	return (mch);
64 }
65 
66 /*
67  * Iterate over all memory controllers.
68  */
69 /*ARGSUSED*/
70 mcamd_node_t *
71 mcamd_mc_next(mcamd_hdl_t *hdl, mcamd_node_t *root, mcamd_node_t *last)
72 {
73 	mc_t *mc;
74 
75 	ASSERT(RW_LOCK_HELD(&mc_lock));
76 
77 	if (last == NULL)
78 		return ((mcamd_node_t *)mc_list);
79 
80 	mc = node2type(last, MC_NT_MC);
81 
82 	return ((mcamd_node_t *)mc->mc_next);
83 }
84 
85 /*
86  * Iterate over all chip-selects of a MC or all chip-selects of a DIMM
87  * depending on the node type of 'node'.  In the DIMM case we do not
88  * have a linked list of associated chip-selects but an array of pointer
89  * to them.
90  */
91 /*ARGSUSED*/
92 mcamd_node_t *
93 mcamd_cs_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
94 {
95 	uint_t nt = nodetype(node);
96 	mc_t *mc;
97 	mc_cs_t *mccs;
98 	mc_dimm_t *mcd;
99 	int i;
100 	void *retval;
101 
102 	ASSERT(nt == MC_NT_MC || nt == MC_NT_DIMM);
103 
104 	if (last == NULL) {
105 		switch (nt) {
106 		case MC_NT_MC:
107 			mc = node2type(node, MC_NT_MC);
108 			retval = mc->mc_cslist;
109 			break;
110 		case MC_NT_DIMM:
111 			mcd = node2type(node, MC_NT_DIMM);
112 			retval = mcd->mcd_cs[0];
113 			break;
114 		}
115 	} else {
116 		mccs = node2type(last, MC_NT_CS);
117 
118 		switch (nt) {
119 		case MC_NT_MC:
120 			retval = mccs->mccs_next;
121 			break;
122 		case MC_NT_DIMM:
123 			mcd = node2type(node, MC_NT_DIMM);
124 			for (i = 0; i < MC_CHIP_DIMMRANKMAX; i++) {
125 				if (mcd->mcd_cs[i] == mccs)
126 					break;
127 			}
128 			if (i == MC_CHIP_DIMMRANKMAX)
129 				cmn_err(CE_PANIC, "Bad last value for "
130 				    "mcamd_cs_next");
131 
132 			if (i == MC_CHIP_DIMMRANKMAX - 1)
133 				retval = NULL;
134 			else
135 				retval = mcd->mcd_cs[i + 1];
136 			break;
137 		}
138 	}
139 
140 	return ((mcamd_node_t *)retval);
141 }
142 
143 /*
144  * Iterate over all DIMMs of an MC or all DIMMs of a chip-select depending
145  * on the node type of 'node'.  In the chip-select case we do not have
146  * a linked list of associated DIMMs but an array of pointers to them.
147  */
148 /*ARGSUSED*/
149 mcamd_node_t *
150 mcamd_dimm_next(mcamd_hdl_t *hdl, mcamd_node_t *node, mcamd_node_t *last)
151 {
152 	uint_t nt = nodetype(node);
153 	mc_t *mc;
154 	mc_cs_t *mccs;
155 	mc_dimm_t *mcd;
156 	int i;
157 	void *retval;
158 
159 	ASSERT(nt == MC_NT_MC || nt == MC_NT_CS);
160 
161 	if (last == NULL) {
162 		switch (nt) {
163 		case MC_NT_MC:
164 			mc = node2type(node, MC_NT_MC);
165 			retval =  mc->mc_dimmlist;
166 			break;
167 		case MC_NT_CS:
168 			mccs = node2type(node, MC_NT_CS);
169 			retval = mccs->mccs_dimm[0];
170 			break;
171 		}
172 	} else {
173 		mcd = node2type(last, MC_NT_DIMM);
174 
175 		switch (nt) {
176 		case MC_NT_MC:
177 			retval = mcd->mcd_next;
178 			break;
179 		case MC_NT_CS:
180 			mccs = node2type(node, MC_NT_CS);
181 			for (i = 0; i < MC_CHIP_DIMMPERCS; i++) {
182 				if (mccs->mccs_dimm[i] == mcd)
183 					break;
184 			}
185 			if (i == MC_CHIP_DIMMPERCS)
186 				cmn_err(CE_PANIC, "Bad last value for "
187 				    "mcamd_dimm_next");
188 
189 			if (i == MC_CHIP_DIMMPERCS - 1)
190 				retval = NULL;
191 			else
192 				retval = mccs->mccs_dimm[i + 1];
193 			break;
194 		}
195 	}
196 
197 	return ((mcamd_node_t *)retval);
198 }
199 
200 /*ARGSUSED*/
201 mcamd_node_t *
202 mcamd_cs_mc(mcamd_hdl_t *hdl, mcamd_node_t *csnode)
203 {
204 	mc_cs_t *mccs = node2type(csnode, MC_NT_CS);
205 	return ((mcamd_node_t *)mccs->mccs_mc);
206 }
207 
208 /*ARGSUSED*/
209 mcamd_node_t *
210 mcamd_dimm_mc(mcamd_hdl_t *hdl, mcamd_node_t *dnode)
211 {
212 	mc_dimm_t *mcd = node2type(dnode, MC_NT_DIMM);
213 	return ((mcamd_node_t *)mcd->mcd_mc);
214 }
215 
216 /*
217  * Node properties.  A property is accessed through a property number code;
218  * we search these tables for a match (choosing table from node type) and
219  * return the uint64_t property at the indicated offset into the node
220  * structure.  All properties must be of type uint64_t.  It is assumed that
221  * property lookup does not have to be super-fast - we search linearly
222  * down the (small) lists.
223  */
224 static const struct mc_offmap mcamd_mc_offmap[] = {
225 	{ MCAMD_PROP_NUM, MCAMD_MC_OFF_NUM },
226 	{ MCAMD_PROP_REV, MCAMD_MC_OFF_REV },
227 	{ MCAMD_PROP_BASE_ADDR, MCAMD_MC_OFF_BASE_ADDR },
228 	{ MCAMD_PROP_LIM_ADDR, MCAMD_MC_OFF_LIM_ADDR },
229 	{ MCAMD_PROP_ILEN, MCAMD_MC_OFF_ILEN },
230 	{ MCAMD_PROP_ILSEL, MCAMD_MC_OFF_ILSEL },
231 	{ MCAMD_PROP_CSINTLVFCTR, MCAMD_MC_OFF_CSINTLVFCTR },
232 	{ MCAMD_PROP_DRAMHOLE_SIZE, MCAMD_MC_OFF_DRAMHOLE_SIZE },
233 	{ MCAMD_PROP_ACCESS_WIDTH, MCAMD_MC_OFF_ACCWIDTH },
234 	{ MCAMD_PROP_CSBANKMAPREG, MCAMD_MC_OFF_CSBANKMAPREG },
235 	{ MCAMD_PROP_BANKSWZL, MCAMD_MC_OFF_BNKSWZL },
236 	{ MCAMD_PROP_MOD64MUX, MCAMD_MC_OFF_MOD64MUX },
237 	{ MCAMD_PROP_SPARECS, MCAMD_MC_OFF_SPARECS },
238 	{ MCAMD_PROP_BADCS, MCAMD_MC_OFF_BADCS },
239 };
240 
241 static const struct mc_offmap mcamd_cs_offmap[] = {
242 	{ MCAMD_PROP_NUM, MCAMD_CS_OFF_NUM },
243 	{ MCAMD_PROP_BASE_ADDR, MCAMD_CS_OFF_BASE_ADDR },
244 	{ MCAMD_PROP_MASK, MCAMD_CS_OFF_MASK },
245 	{ MCAMD_PROP_SIZE, MCAMD_CS_OFF_SIZE },
246 	{ MCAMD_PROP_CSBE, MCAMD_CS_OFF_CSBE },
247 	{ MCAMD_PROP_SPARE, MCAMD_CS_OFF_SPARE },
248 	{ MCAMD_PROP_TESTFAIL, MCAMD_CS_OFF_TESTFAIL },
249 	{ MCAMD_PROP_CSDIMM1, MCAMD_CS_OFF_DIMMNUMS },
250 	{ MCAMD_PROP_CSDIMM2, MCAMD_CS_OFF_DIMMNUMS +
251 	    MCAMD_CS_OFF_DIMMNUMS_INCR },
252 	{ MCAMD_PROP_DIMMRANK, MCAMD_CS_OFF_DIMMRANK },
253 };
254 
255 static const struct mc_offmap mcamd_dimm_offmap[] = {
256 	{ MCAMD_PROP_NUM, MCAMD_DIMM_OFF_NUM },
257 	{ MCAMD_PROP_SIZE, MCAMD_DIMM_OFF_SIZE },
258 };
259 
260 struct nt_offmap {
261 	const struct mc_offmap *omp;
262 	int mapents;
263 };
264 
265 /*ARGSUSED*/
266 static int
267 findoffset(mcamd_hdl_t *hdl, mcamd_node_t *node, struct nt_offmap *arr,
268     int code, uint_t *offset)
269 {
270 	int i;
271 	mc_hdr_t *mch = (mc_hdr_t *)node;
272 	int nt = mch->mch_type;
273 	const struct mc_offmap *omp;
274 
275 	if (nt > MC_NT_NTYPES || (omp = arr[nt].omp) == NULL)
276 		return (0);
277 
278 	for (i = 0; i < arr[nt].mapents; i++, omp++) {
279 		if (omp->mcom_code == code) {
280 			*offset = omp->mcom_offset;
281 			return (1);
282 		}
283 	}
284 
285 	return (0);
286 }
287 
288 /*ARGSUSED*/
289 int
290 mcamd_get_numprop(mcamd_hdl_t *hdl, mcamd_node_t *node,
291     mcamd_propcode_t code, mcamd_prop_t *valp)
292 {
293 	int found;
294 	uint_t offset;
295 
296 	struct nt_offmap props[] = {
297 		{ mcamd_mc_offmap,	/* MC_NT_MC */
298 		    sizeof (mcamd_mc_offmap) / sizeof (struct mc_offmap) },
299 		{ mcamd_cs_offmap,	/* MC_NT_CS */
300 		    sizeof (mcamd_cs_offmap) / sizeof (struct mc_offmap) },
301 		{ mcamd_dimm_offmap,	/* MC_NT_DIMM */
302 		    sizeof (mcamd_dimm_offmap) / sizeof (struct mc_offmap) }
303 	};
304 
305 	found = findoffset(hdl, node, &props[0], code, &offset);
306 	ASSERT(found);
307 
308 	if (found)
309 		*valp = *(uint64_t *)((uintptr_t)node + offset);
310 
311 	return (found == 1);
312 }
313 
314 int
315 mcamd_get_numprops(mcamd_hdl_t *hdl, ...)
316 {
317 	va_list ap;
318 	mcamd_node_t *node;
319 	mcamd_propcode_t code;
320 	mcamd_prop_t *valp;
321 
322 	va_start(ap, hdl);
323 	while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
324 		code = va_arg(ap, mcamd_propcode_t);
325 		valp = va_arg(ap, mcamd_prop_t *);
326 		if (!mcamd_get_numprop(hdl, node, code, valp))
327 			return (0);
328 	}
329 	va_end(ap);
330 	return (1);
331 }
332 
333 static const struct mc_offmap mcreg_offmap[] = {
334 	{ MCAMD_REG_DRAMBASE, MCAMD_MC_OFF_DRAMBASE_REG },
335 	{ MCAMD_REG_DRAMLIMIT, MCAMD_MC_OFF_DRAMLIMIT_REG },
336 	{ MCAMD_REG_DRAMHOLE, MCAMD_MC_OFF_DRAMHOLE_REG },
337 	{ MCAMD_REG_DRAMCFGLO, MCAMD_MC_OFF_DRAMCFGLO_REG },
338 	{ MCAMD_REG_DRAMCFGHI, MCAMD_MC_OFF_DRAMCFGHI_REG },
339 };
340 
341 static const struct mc_offmap csreg_offmap[] = {
342 	{ MCAMD_REG_CSBASE, MCAMD_CS_OFF_CSBASE_REG },
343 	{ MCAMD_REG_CSMASK, MCAMD_CS_OFF_CSMASK_REG },
344 };
345 
346 /*ARGSUSED*/
347 int
348 mcamd_get_cfgreg(struct mcamd_hdl *hdl, mcamd_node_t *node,
349     mcamd_regcode_t code, uint32_t *valp)
350 {
351 	int found;
352 	uint_t offset;
353 
354 	struct nt_offmap regs[] = {
355 		{ mcreg_offmap,	/* MC_NT_MC */
356 		    sizeof (mcreg_offmap) / sizeof (struct mc_offmap) },
357 		{ csreg_offmap,	/* MC_NT_CS */
358 		    sizeof (csreg_offmap) / sizeof (struct mc_offmap) },
359 		{ NULL, 0 }		/* MC_NT_DIMM */
360 	};
361 
362 	found = findoffset(hdl, node, &regs[0], code, &offset);
363 	ASSERT(found);
364 
365 	ASSERT(found);
366 	if (found)
367 		*valp = *(uint32_t *)((uintptr_t)node + offset);
368 
369 	return (found == 1);
370 }
371 
372 int
373 mcamd_get_cfgregs(mcamd_hdl_t *hdl, ...)
374 {
375 	va_list ap;
376 	mcamd_node_t *node;
377 	mcamd_regcode_t code;
378 	uint32_t *valp;
379 
380 	va_start(ap, hdl);
381 	while ((node = va_arg(ap, mcamd_node_t *)) != NULL) {
382 		code = va_arg(ap, mcamd_regcode_t);
383 		valp = va_arg(ap, uint32_t *);
384 		if (!mcamd_get_cfgreg(hdl, node, code, valp))
385 			return (0);
386 	}
387 	va_end(ap);
388 	return (1);
389 }
390 
391 
392 int
393 mcamd_errno(mcamd_hdl_t *mcamd)
394 {
395 	return (mcamd->mcamd_errno);
396 }
397 
398 int
399 mcamd_set_errno(mcamd_hdl_t *mcamd, int err)
400 {
401 	mcamd->mcamd_errno = err;
402 	return (-1);
403 }
404 
405 void
406 mcamd_dprintf(mcamd_hdl_t *mcamd, int mask, const char *fmt, ...)
407 {
408 	va_list ap;
409 
410 	if (!(mcamd->mcamd_debug & mask))
411 		return;
412 
413 	va_start(ap, fmt);
414 	vcmn_err(mask & MCAMD_DBG_ERR ? CE_WARN : CE_NOTE, fmt, ap);
415 	va_end(ap);
416 }
417 
418 void
419 mcamd_mkhdl(mcamd_hdl_t *hdl)
420 {
421 	hdl->mcamd_errno = 0;
422 	hdl->mcamd_debug = mcamd_debug;
423 }
424 
425 cmi_errno_t
426 mcamd_cmierr(int err, mcamd_hdl_t *hdl)
427 {
428 	if (err == 0)
429 		return (CMI_SUCCESS);
430 
431 	switch (mcamd_errno(hdl)) {
432 	case EMCAMD_SYNDINVALID:
433 		return (CMIERR_MC_SYNDROME);
434 
435 	case EMCAMD_TREEINVALID:
436 		return (CMIERR_MC_BADSTATE);
437 
438 	case EMCAMD_NOADDR:
439 		return (CMIERR_MC_NOADDR);
440 
441 	case EMCAMD_INSUFF_RES:
442 		return (CMIERR_MC_ADDRBITS);
443 
444 	default:
445 		return (CMIERR_UNKNOWN);
446 	}
447 
448 }
449 
450 /*ARGSUSED*/
451 cmi_errno_t
452 mcamd_patounum_wrap(void *arg, uint64_t pa, uint8_t valid_hi, uint8_t valid_lo,
453     uint32_t synd, int syndtype, mc_unum_t *unump)
454 {
455 	mcamd_hdl_t mcamd;
456 	int rc;
457 
458 	mcamd_mkhdl(&mcamd);
459 
460 	rw_enter(&mc_lock, RW_READER);
461 
462 	rc = mcamd_patounum(&mcamd, (mcamd_node_t *)mc_list, pa,
463 	    valid_hi, valid_lo, synd, syndtype, unump);
464 
465 #ifdef DEBUG
466 	/*
467 	 * Apply the reverse operation to verify the result.  If there is
468 	 * a problem complain but continue.
469 	 */
470 	if (rc == 0 && MCAMD_RC_OFFSET_VALID(unump->unum_offset)) {
471 		uint64_t rpa;
472 		if (mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump,
473 		    &rpa) != 0 || rpa != pa) {
474 			mcamd_dprintf(&mcamd, MCAMD_DBG_ERR,
475 			    "mcamd_patounum_wrap: offset calculation "
476 			    "verification for PA 0x%llx failed\n", pa);
477 		}
478 	}
479 #endif
480 	rw_exit(&mc_lock);
481 
482 	return (mcamd_cmierr(rc, &mcamd));
483 }
484 
485 static int
486 fmri2unum(nvlist_t *nvl, mc_unum_t *unump)
487 {
488 	int i;
489 	uint64_t offset;
490 	nvlist_t *hcsp, **hcl;
491 	uint_t npr;
492 
493 	if (nvlist_lookup_nvlist(nvl, FM_FMRI_HC_SPECIFIC, &hcsp) != 0 ||
494 	    (nvlist_lookup_uint64(hcsp, "asru-" FM_FMRI_HC_SPECIFIC_OFFSET,
495 	    &offset) != 0 && nvlist_lookup_uint64(hcsp,
496 	    FM_FMRI_HC_SPECIFIC_OFFSET, &offset) != 0) ||
497 	    nvlist_lookup_nvlist_array(nvl, FM_FMRI_HC_LIST, &hcl, &npr) != 0)
498 		return (0);
499 
500 
501 	bzero(unump, sizeof (mc_unum_t));
502 	unump->unum_chan = MC_INVALNUM;
503 	for (i = 0; i < MC_UNUM_NDIMM; i++)
504 		unump->unum_dimms[i] = MC_INVALNUM;
505 
506 	for (i = 0; i < npr; i++) {
507 		char *hcnm, *hcid;
508 		long v;
509 
510 		if (nvlist_lookup_string(hcl[i], FM_FMRI_HC_NAME, &hcnm) != 0 ||
511 		    nvlist_lookup_string(hcl[i], FM_FMRI_HC_ID, &hcid) != 0 ||
512 		    ddi_strtol(hcid, NULL, 0, &v) != 0)
513 			return (0);
514 
515 		if (strcmp(hcnm, "motherboard") == 0)
516 			unump->unum_board = (int)v;
517 		else if (strcmp(hcnm, "chip") == 0)
518 			unump->unum_chip = (int)v;
519 		else if (strcmp(hcnm, "memory-controller") == 0)
520 			unump->unum_mc = (int)v;
521 		else if (strcmp(hcnm, "chip-select") == 0)
522 			unump->unum_cs = (int)v;
523 		else if (strcmp(hcnm, "dimm") == 0)
524 			unump->unum_dimms[0] = (int)v;
525 		else if (strcmp(hcnm, "rank") == 0)
526 			unump->unum_rank = (int)v;
527 	}
528 
529 	unump->unum_offset = offset;
530 
531 	return (1);
532 }
533 
534 /*ARGSUSED*/
535 cmi_errno_t
536 mcamd_unumtopa_wrap(void *arg, mc_unum_t *unump, nvlist_t *nvl, uint64_t *pap)
537 {
538 	mcamd_hdl_t mcamd;
539 	int rc;
540 	mc_unum_t unum;
541 
542 	ASSERT(unump == NULL || nvl == NULL);	/* enforced at cmi level */
543 
544 	if (unump == NULL) {
545 		if (!fmri2unum(nvl, &unum))
546 			return (CMIERR_MC_INVALUNUM);
547 		unump = &unum;
548 	}
549 
550 	mcamd_mkhdl(&mcamd);
551 
552 	rw_enter(&mc_lock, RW_READER);
553 	rc = mcamd_unumtopa(&mcamd, (mcamd_node_t *)mc_list, unump, pap);
554 	rw_exit(&mc_lock);
555 
556 	return (mcamd_cmierr(rc, &mcamd));
557 }
558 
559 static void
560 mc_ereport_dimm_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump)
561 {
562 	int i;
563 
564 	for (i = 0; i < MC_UNUM_NDIMM; i++) {
565 		if (unump->unum_dimms[i] == MC_INVALNUM)
566 			break;
567 
568 		elems[(*nump)++] = fm_nvlist_create(NULL);
569 		fm_fmri_hc_set(elems[i], FM_HC_SCHEME_VERSION, NULL, NULL, 5,
570 		    "motherboard",  unump->unum_board,
571 		    "chip", unump->unum_chip,
572 		    "memory-controller", unump->unum_mc,
573 		    "dimm", unump->unum_dimms[i],
574 		    "rank", unump->unum_rank);
575 	}
576 }
577 
578 static void
579 mc_ereport_cs_resource(mc_unum_t *unump, nvlist_t *elems[], int *nump)
580 {
581 	elems[0] = fm_nvlist_create(NULL);
582 	fm_fmri_hc_set(elems[0], FM_HC_SCHEME_VERSION, NULL, NULL, 4,
583 	    "motherboard",  unump->unum_board,
584 	    "chip", unump->unum_chip,
585 	    "memory-controller", unump->unum_mc,
586 	    "chip-select", unump->unum_cs);
587 	*nump = 1;
588 }
589 
590 /*
591  * Create the 'resource' payload member from the unum info.  If valid
592  * dimm numbers are present in the unum info then create members
593  * identifying the dimm and rank;  otherwise if a valid chip-select
594  * number is indicated then create a member identifying the chip-select
595  * topology node.
596  */
597 static void
598 mc_ereport_add_resource(nvlist_t *payload, mc_unum_t *unump)
599 {
600 	nvlist_t *elems[MC_UNUM_NDIMM];
601 	int nelems = 0;
602 	int i;
603 
604 	if (unump->unum_dimms[0] != MC_INVALNUM)
605 		mc_ereport_dimm_resource(unump, elems, &nelems);
606 	else if (unump->unum_cs != MC_INVALNUM)
607 		mc_ereport_cs_resource(unump, elems, &nelems);
608 
609 	if (nelems > 0) {
610 		fm_payload_set(payload, FM_EREPORT_PAYLOAD_NAME_RESOURCE,
611 		    DATA_TYPE_NVLIST_ARRAY, nelems, elems, NULL);
612 
613 		for (i = 0; i < nelems; i++)
614 			fm_nvlist_destroy(elems[i], FM_NVA_FREE);
615 	}
616 }
617 
618 static void
619 mc_ereport_add_payload(nvlist_t *ereport, uint64_t members, mc_unum_t *unump)
620 {
621 	if (members & FM_EREPORT_PAYLOAD_FLAG_RESOURCE &&
622 	    unump != NULL)
623 		mc_ereport_add_resource(ereport, unump);
624 }
625 
626 static nvlist_t *
627 mc_fmri_create(mc_t *mc)
628 {
629 	nvlist_t *nvl = fm_nvlist_create(NULL);
630 
631 	fm_fmri_hc_set(nvl, FM_HC_SCHEME_VERSION, NULL, NULL, 3,
632 	    "motherboard", 0,
633 	    "chip", mc->mc_props.mcp_num,
634 	    "memory-controller", 0);
635 
636 	return (nvl);
637 }
638 
639 /*
640  * Simple ereport generator for errors detected by the memory controller.
641  * Posts an ereport of class ereport.cpu.amd.<class_sfx> with a resource nvlist
642  * derived from the given mc_unum_t.  There are no other payload members.
643  * The mc argument is used to formulate a detector and this mc should
644  * correspond with that identified in the mc_unum_t.
645  *
646  * There is no control of which members to include the the resulting ereport -
647  * it will be an ereport formed using the given class suffix, detector
648  * indicated as the memory-controller and with a resource generated by
649  * expanding the given mc_unum_t.
650  *
651  * We do not use any special nv allocator here and so this is not suitable
652  * for use during panic.  It is intended for use during MC topology
653  * discovery and other controlled circumstances.
654  */
655 void
656 mcamd_ereport_post(mc_t *mc, const char *class_sfx, mc_unum_t *unump,
657     uint64_t payload)
658 {
659 	nvlist_t *ereport, *detector;
660 	char buf[FM_MAX_CLASS];
661 
662 	ereport = fm_nvlist_create(NULL);
663 	detector = mc_fmri_create(mc);
664 
665 	(void) snprintf(buf, FM_MAX_CLASS, "%s.%s.%s", FM_ERROR_CPU,
666 	    "amd", class_sfx);
667 	fm_ereport_set(ereport, FM_EREPORT_VERSION, buf,
668 	    fm_ena_generate(gethrtime(), FM_ENA_FMT1), detector, NULL);
669 	fm_nvlist_destroy(detector, FM_NVA_FREE);
670 
671 	mc_ereport_add_payload(ereport, payload, unump);
672 
673 	(void) fm_ereport_post(ereport, EVCH_TRYHARD);
674 	fm_nvlist_destroy(ereport, FM_NVA_FREE);
675 }
676 
677 static const cmi_mc_ops_t mcamd_mc_ops = {
678 	mcamd_patounum_wrap,	/* cmi_mc_patounum */
679 	mcamd_unumtopa_wrap,	/* cmi_mc_unumtopa */
680 	NULL			/* cmi_mc_logout */
681 };
682 
683 void
684 mcamd_mc_register(cmi_hdl_t hdl, mc_t *mc)
685 {
686 	cmi_mc_register(hdl, &mcamd_mc_ops, mc);
687 }
688