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.
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.
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]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Support routines for DIMMs.
40 #include <fm/fmd_api.h>
41 #include <sys/fm/protocol.h>
43 #include <sys/nvpair.h>
45 #include <cmd_hc_sun4v.h>
46 #include <cmd_branch.h>
50 * Some errors (RxE/FRx pairs) don't have accurate DIMM (resource) FMRIs,
51 * because sufficient information was unavailable prior to correlation.
52 * When the DE completes the pair, it uses this routine to retrieve the
56 cmd_dimm_fmri_derive(fmd_hdl_t
*hdl
, uint64_t afar
, uint16_t synd
,
61 if ((fmri
= cmd_mem_fmri_derive(hdl
, afar
, afsr
, synd
)) == NULL
)
64 if (fmd_nvl_fmri_expand(hdl
, fmri
) < 0) {
73 cmd_dimm_fru(cmd_dimm_t
*dimm
)
75 return (dimm
->dimm_asru_nvl
);
79 cmd_dimm_create_fault(fmd_hdl_t
*hdl
, cmd_dimm_t
*dimm
, const char *fltnm
,
83 nvlist_t
*flt
, *nvlfru
;
85 * Do NOT issue hc scheme FRU FMRIs for ultraSPARC-T1 platforms.
86 * The SP will misinterpret the FRU. Instead, reuse the ASRU FMRI
88 * Use the BR string as a distinguisher. BR (branch) is only
89 * present in ultraSPARC-T2/T2plus DIMM unums
91 if (strstr(dimm
->dimm_unum
, "BR") == NULL
) {
92 flt
= cmd_nvl_create_fault(hdl
, fltnm
, cert
,
93 dimm
->dimm_asru_nvl
, dimm
->dimm_asru_nvl
, NULL
);
95 nvlfru
= cmd_mem2hc(hdl
, dimm
->dimm_asru_nvl
);
96 flt
= cmd_nvl_create_fault(hdl
, fltnm
, cert
,
97 dimm
->dimm_asru_nvl
, nvlfru
, NULL
);
100 return (cmd_fault_add_location(hdl
, flt
, dimm
->dimm_unum
));
102 return (cmd_nvl_create_fault(hdl
, fltnm
, cert
, dimm
->dimm_asru_nvl
,
103 dimm
->dimm_asru_nvl
, NULL
));
108 cmd_dimm_free(fmd_hdl_t
*hdl
, cmd_dimm_t
*dimm
, int destroy
)
110 cmd_case_t
*cc
= &dimm
->dimm_case
;
113 tstamp_t
*tsp
, *next
;
116 cmd_branch_t
*branch
;
118 if (cc
->cc_cp
!= NULL
) {
119 cmd_case_fini(hdl
, cc
->cc_cp
, destroy
);
120 if (cc
->cc_serdnm
!= NULL
) {
121 if (fmd_serd_exists(hdl
, cc
->cc_serdnm
) &&
123 fmd_serd_destroy(hdl
, cc
->cc_serdnm
);
124 fmd_hdl_strfree(hdl
, cc
->cc_serdnm
);
128 for (i
= 0; i
< CMD_MAX_CKWDS
; i
++) {
129 while ((q
= cmd_list_next(&dimm
->mq_root
[i
])) != NULL
) {
130 if (q
->mq_serdnm
!= NULL
) {
131 if (fmd_serd_exists(hdl
, q
->mq_serdnm
)) {
132 fmd_serd_destroy(hdl
, q
->mq_serdnm
);
134 fmd_hdl_strfree(hdl
, q
->mq_serdnm
);
138 for (tsp
= cmd_list_next(&q
->mq_dupce_tstamp
);
139 tsp
!= NULL
; tsp
= next
) {
140 next
= cmd_list_next(tsp
);
141 cmd_list_delete(&q
->mq_dupce_tstamp
,
143 fmd_hdl_free(hdl
, tsp
, sizeof (tstamp_t
));
146 cmd_list_delete(&dimm
->mq_root
[i
], q
);
147 fmd_hdl_free(hdl
, q
, sizeof (cmd_mq_t
));
151 if (dimm
->dimm_bank
!= NULL
)
152 cmd_bank_remove_dimm(hdl
, dimm
->dimm_bank
, dimm
);
155 branch
= cmd_branch_lookup_by_unum(hdl
, dimm
->dimm_unum
);
157 cmd_branch_remove_dimm(hdl
, branch
, dimm
);
160 cmd_fmri_fini(hdl
, &dimm
->dimm_asru
, destroy
);
163 fmd_buf_destroy(hdl
, NULL
, dimm
->dimm_bufname
);
165 cmd_list_delete(&cmd
.cmd_dimms
, dimm
);
166 fmd_hdl_free(hdl
, dimm
, sizeof (cmd_dimm_t
));
170 cmd_dimm_destroy(fmd_hdl_t
*hdl
, cmd_dimm_t
*dimm
)
173 fmd_stat_destroy(hdl
, 1, &(dimm
->dimm_retstat
));
174 cmd_dimm_free(hdl
, dimm
, FMD_B_TRUE
);
178 dimm_lookup_by_unum(const char *unum
)
182 for (dimm
= cmd_list_next(&cmd
.cmd_dimms
); dimm
!= NULL
;
183 dimm
= cmd_list_next(dimm
)) {
184 if (strcmp(dimm
->dimm_unum
, unum
) == 0)
192 dimm_attach_to_bank(fmd_hdl_t
*hdl
, cmd_dimm_t
*dimm
)
196 for (bank
= cmd_list_next(&cmd
.cmd_banks
); bank
!= NULL
;
197 bank
= cmd_list_next(bank
)) {
198 if (fmd_nvl_fmri_contains(hdl
, bank
->bank_asru_nvl
,
199 dimm
->dimm_asru_nvl
)) {
200 cmd_bank_add_dimm(hdl
, bank
, dimm
);
207 cmd_dimm_create(fmd_hdl_t
*hdl
, nvlist_t
*asru
)
213 char **serids
= NULL
;
215 if (!fmd_nvl_fmri_present(hdl
, asru
)) {
216 fmd_hdl_debug(hdl
, "dimm_lookup: discarding old ereport\n");
220 if ((unum
= cmd_fmri_get_unum(asru
)) == NULL
) {
221 CMD_STAT_BUMP(bad_mem_asru
);
226 if (nvlist_lookup_string_array(asru
, FM_FMRI_HC_SERIAL_ID
, &serids
,
228 fmd_hdl_debug(hdl
, "sun4v mem: FMRI does not"
229 " have serial_ids\n");
230 CMD_STAT_BUMP(bad_mem_asru
);
234 fmri
= cmd_mem_fmri_create(unum
, serids
, nserids
);
235 if (fmd_nvl_fmri_expand(hdl
, fmri
) < 0) {
236 CMD_STAT_BUMP(bad_mem_asru
);
241 fmd_hdl_debug(hdl
, "dimm_create: creating new DIMM %s\n", unum
);
242 CMD_STAT_BUMP(dimm_creat
);
244 dimm
= fmd_hdl_zalloc(hdl
, sizeof (cmd_dimm_t
), FMD_SLEEP
);
245 dimm
->dimm_nodetype
= CMD_NT_DIMM
;
246 dimm
->dimm_version
= CMD_DIMM_VERSION
;
247 dimm
->dimm_phys_addr_low
= ULLONG_MAX
;
248 dimm
->dimm_phys_addr_hi
= 0;
249 dimm
->dimm_syl_error
= USHRT_MAX
;
251 cmd_bufname(dimm
->dimm_bufname
, sizeof (dimm
->dimm_bufname
), "dimm_%s",
253 cmd_fmri_init(hdl
, &dimm
->dimm_asru
, fmri
, "dimm_asru_%s", unum
);
257 (void) nvlist_lookup_string(dimm
->dimm_asru_nvl
, FM_FMRI_MEM_UNUM
,
258 (char **)&dimm
->dimm_unum
);
260 dimm_attach_to_bank(hdl
, dimm
);
262 cmd_mem_retirestat_create(hdl
, &dimm
->dimm_retstat
, dimm
->dimm_unum
, 0,
263 CMD_DIMM_STAT_PREFIX
);
265 cmd_list_append(&cmd
.cmd_dimms
, dimm
);
266 cmd_dimm_dirty(hdl
, dimm
);
272 cmd_dimm_lookup(fmd_hdl_t
*hdl
, nvlist_t
*asru
)
277 if ((unum
= cmd_fmri_get_unum(asru
)) == NULL
) {
278 CMD_STAT_BUMP(bad_mem_asru
);
282 dimm
= dimm_lookup_by_unum(unum
);
284 if (dimm
!= NULL
&& !fmd_nvl_fmri_present(hdl
, dimm
->dimm_asru_nvl
)) {
286 * The DIMM doesn't exist anymore, so we need to delete the
287 * state structure, which is now out of date. The containing
288 * bank (if any) is also out of date, so blow it away too.
290 fmd_hdl_debug(hdl
, "dimm_lookup: discarding old dimm\n");
292 if (dimm
->dimm_bank
!= NULL
)
293 cmd_bank_destroy(hdl
, dimm
->dimm_bank
);
294 cmd_dimm_destroy(hdl
, dimm
);
303 dimm_v0tov2(fmd_hdl_t
*hdl
, cmd_dimm_0_t
*old
, size_t oldsz
)
307 if (oldsz
!= sizeof (cmd_dimm_0_t
)) {
308 fmd_hdl_abort(hdl
, "size of state doesn't match size of "
309 "version 0 state (%u bytes).\n", sizeof (cmd_dimm_0_t
));
312 new = fmd_hdl_zalloc(hdl
, sizeof (cmd_dimm_t
), FMD_SLEEP
);
313 new->dimm_header
= old
->dimm0_header
;
314 new->dimm_version
= CMD_DIMM_VERSION
;
315 new->dimm_asru
= old
->dimm0_asru
;
316 new->dimm_nretired
= old
->dimm0_nretired
;
317 new->dimm_phys_addr_hi
= 0;
318 new->dimm_phys_addr_low
= ULLONG_MAX
;
320 fmd_hdl_free(hdl
, old
, oldsz
);
325 dimm_v1tov2(fmd_hdl_t
*hdl
, cmd_dimm_1_t
*old
, size_t oldsz
)
330 if (oldsz
!= sizeof (cmd_dimm_1_t
)) {
331 fmd_hdl_abort(hdl
, "size of state doesn't match size of "
332 "version 1 state (%u bytes).\n", sizeof (cmd_dimm_1_t
));
335 new = fmd_hdl_zalloc(hdl
, sizeof (cmd_dimm_t
), FMD_SLEEP
);
337 new->dimm_header
= old
->dimm1_header
;
338 new->dimm_version
= CMD_DIMM_VERSION
;
339 new->dimm_asru
= old
->dimm1_asru
;
340 new->dimm_nretired
= old
->dimm1_nretired
;
341 new->dimm_flags
= old
->dimm1_flags
;
342 new->dimm_phys_addr_hi
= 0;
343 new->dimm_phys_addr_low
= ULLONG_MAX
;
345 fmd_hdl_free(hdl
, old
, oldsz
);
350 dimm_wrapv2(fmd_hdl_t
*hdl
, cmd_dimm_pers_t
*pers
, size_t psz
)
354 if (psz
!= sizeof (cmd_dimm_pers_t
)) {
355 fmd_hdl_abort(hdl
, "size of state doesn't match size of "
356 "version 1 state (%u bytes).\n", sizeof (cmd_dimm_pers_t
));
359 dimm
= fmd_hdl_zalloc(hdl
, sizeof (cmd_dimm_t
), FMD_SLEEP
);
360 bcopy(pers
, dimm
, sizeof (cmd_dimm_pers_t
));
361 fmd_hdl_free(hdl
, pers
, psz
);
366 cmd_dimm_restore(fmd_hdl_t
*hdl
, fmd_case_t
*cp
, cmd_case_ptr_t
*ptr
)
370 for (dimm
= cmd_list_next(&cmd
.cmd_dimms
); dimm
!= NULL
;
371 dimm
= cmd_list_next(dimm
)) {
372 if (strcmp(dimm
->dimm_bufname
, ptr
->ptr_name
) == 0)
380 fmd_hdl_debug(hdl
, "restoring dimm from %s\n", ptr
->ptr_name
);
382 if ((dimmsz
= fmd_buf_size(hdl
, NULL
, ptr
->ptr_name
)) == 0) {
383 fmd_hdl_abort(hdl
, "dimm referenced by case %s does "
384 "not exist in saved state\n",
385 fmd_case_uuid(hdl
, cp
));
386 } else if (dimmsz
> CMD_DIMM_MAXSIZE
||
387 dimmsz
< CMD_DIMM_MINSIZE
) {
389 "dimm buffer referenced by case %s "
390 "is out of bounds (is %u bytes, max %u, min %u)\n",
391 fmd_case_uuid(hdl
, cp
), dimmsz
,
392 CMD_DIMM_MAXSIZE
, CMD_DIMM_MINSIZE
);
395 if ((dimm
= cmd_buf_read(hdl
, NULL
, ptr
->ptr_name
,
397 fmd_hdl_abort(hdl
, "failed to read dimm buf %s",
401 fmd_hdl_debug(hdl
, "found %d in version field\n",
404 if (CMD_DIMM_VERSIONED(dimm
)) {
405 switch (dimm
->dimm_version
) {
406 case CMD_DIMM_VERSION_1
:
407 dimm
= dimm_v1tov2(hdl
, (cmd_dimm_1_t
*)dimm
,
410 case CMD_DIMM_VERSION_2
:
411 dimm
= dimm_wrapv2(hdl
, (cmd_dimm_pers_t
*)dimm
,
415 fmd_hdl_abort(hdl
, "unknown version (found %d) "
416 "for dimm state referenced by case %s.\n",
417 dimm
->dimm_version
, fmd_case_uuid(hdl
, cp
));
421 dimm
= dimm_v0tov2(hdl
, (cmd_dimm_0_t
*)dimm
, dimmsz
);
426 CMD_STAT_BUMP(dimm_migrat
);
427 cmd_dimm_dirty(hdl
, dimm
);
430 cmd_fmri_restore(hdl
, &dimm
->dimm_asru
);
432 if ((errno
= nvlist_lookup_string(dimm
->dimm_asru_nvl
,
433 FM_FMRI_MEM_UNUM
, (char **)&dimm
->dimm_unum
)) != 0)
434 fmd_hdl_abort(hdl
, "failed to retrieve unum from asru");
436 dimm_attach_to_bank(hdl
, dimm
);
438 cmd_mem_retirestat_create(hdl
, &dimm
->dimm_retstat
,
439 dimm
->dimm_unum
, dimm
->dimm_nretired
, CMD_DIMM_STAT_PREFIX
);
441 cmd_list_append(&cmd
.cmd_dimms
, dimm
);
444 switch (ptr
->ptr_subtype
) {
445 case BUG_PTR_DIMM_CASE
:
446 fmd_hdl_debug(hdl
, "recovering from out of order dimm ptr\n");
447 cmd_case_redirect(hdl
, cp
, CMD_PTR_DIMM_CASE
);
449 case CMD_PTR_DIMM_CASE
:
450 cmd_mem_case_restore(hdl
, &dimm
->dimm_case
, cp
, "dimm",
454 fmd_hdl_abort(hdl
, "invalid %s subtype %d\n",
455 ptr
->ptr_name
, ptr
->ptr_subtype
);
462 cmd_dimm_validate(fmd_hdl_t
*hdl
)
464 cmd_dimm_t
*dimm
, *next
;
466 for (dimm
= cmd_list_next(&cmd
.cmd_dimms
); dimm
!= NULL
; dimm
= next
) {
467 next
= cmd_list_next(dimm
);
469 if (!fmd_nvl_fmri_present(hdl
, dimm
->dimm_asru_nvl
))
470 cmd_dimm_destroy(hdl
, dimm
);
475 cmd_dimm_dirty(fmd_hdl_t
*hdl
, cmd_dimm_t
*dimm
)
477 if (fmd_buf_size(hdl
, NULL
, dimm
->dimm_bufname
) !=
478 sizeof (cmd_dimm_pers_t
))
479 fmd_buf_destroy(hdl
, NULL
, dimm
->dimm_bufname
);
481 /* No need to rewrite the FMRIs in the dimm - they don't change */
482 fmd_buf_write(hdl
, NULL
, dimm
->dimm_bufname
, &dimm
->dimm_pers
,
483 sizeof (cmd_dimm_pers_t
));
487 cmd_dimm_gc(fmd_hdl_t
*hdl
)
489 cmd_dimm_validate(hdl
);
493 cmd_dimm_fini(fmd_hdl_t
*hdl
)
497 while ((dimm
= cmd_list_next(&cmd
.cmd_dimms
)) != NULL
)
498 cmd_dimm_free(hdl
, dimm
, FMD_B_FALSE
);
503 cmd_dimm_save_symbol_error(cmd_dimm_t
*dimm
, uint16_t upos
)
505 cmd_dimm_t
*d
= NULL
, *next
= NULL
;
507 for (d
= cmd_list_next(&cmd
.cmd_dimms
); d
!= NULL
; d
= next
) {
508 next
= cmd_list_next(d
);
509 if (cmd_same_datapath_dimms(dimm
, d
))
510 d
->dimm_syl_error
= upos
;
515 cmd_dimm_check_symbol_error(cmd_dimm_t
*dimm
, uint16_t synd
)
518 cmd_dimm_t
*d
, *next
;
520 if ((upos
= cmd_synd2upos(synd
)) < 0)
523 for (d
= cmd_list_next(&cmd
.cmd_dimms
); d
!= NULL
; d
= next
) {
524 next
= cmd_list_next(d
);
525 if (cmd_same_datapath_dimms(dimm
, d
) &&
526 (d
->dimm_syl_error
== upos
))