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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * Support routines for DIMMs.
29  */
30 
31 #include <gmem_mem.h>
32 #include <gmem_dimm.h>
33 #include <gmem.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <fcntl.h>
38 #include <unistd.h>
39 #include <fm/fmd_api.h>
40 #include <fm/libtopo.h>
41 #include <sys/fm/protocol.h>
42 #include <sys/mem.h>
43 #include <sys/nvpair.h>
44 
45 nvlist_t *dimm_nvl;
46 
47 typedef struct dimmid {
48 	char serial[100];
49 	int type;
50 } dimmid_t;
51 
52 nvlist_t *
53 gmem_dimm_fru(gmem_dimm_t *dimm)
54 {
55 	return (dimm->dimm_asru_nvl);
56 }
57 
58 static void
59 gmem_dimm_free(fmd_hdl_t *hdl, gmem_dimm_t *dimm, int destroy)
60 {
61 	gmem_case_t *cc = &dimm->dimm_case;
62 	int i;
63 	gmem_mq_t *q;
64 
65 	if (cc->cc_cp != NULL) {
66 		gmem_case_fini(hdl, cc->cc_cp, destroy);
67 		if (cc->cc_serdnm != NULL) {
68 			if (fmd_serd_exists(hdl, cc->cc_serdnm) &&
69 			    destroy)
70 				fmd_serd_destroy(hdl, cc->cc_serdnm);
71 			fmd_hdl_strfree(hdl, cc->cc_serdnm);
72 		}
73 	}
74 
75 	gmem_fmri_fini(hdl, &dimm->dimm_asru, destroy);
76 
77 	for (i = 0; i < GMEM_MAX_CKWDS; i++) {
78 		while ((q = gmem_list_next(&dimm->mq_root[i])) != NULL) {
79 			if (q->mq_serdnm != NULL) {
80 				if (fmd_serd_exists(hdl, q->mq_serdnm))
81 					fmd_serd_destroy(hdl, q->mq_serdnm);
82 				fmd_hdl_strfree(hdl, q->mq_serdnm);
83 				q->mq_serdnm = NULL;
84 			}
85 			gmem_list_delete(&dimm->mq_root[i], q);
86 			fmd_hdl_free(hdl, q, sizeof (gmem_mq_t));
87 		}
88 	}
89 
90 	if (destroy)
91 		fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);
92 
93 	gmem_list_delete(&gmem.gm_dimms, dimm);
94 	fmd_hdl_free(hdl, dimm, sizeof (gmem_dimm_t));
95 }
96 
97 void
98 gmem_dimm_destroy(fmd_hdl_t *hdl, gmem_dimm_t *dimm)
99 {
100 	fmd_stat_destroy(hdl, 1, &(dimm->dimm_retstat));
101 	gmem_dimm_free(hdl, dimm, FMD_B_TRUE);
102 }
103 
104 static gmem_dimm_t *
105 dimm_lookup_by_serial(const char *serial)
106 {
107 	gmem_dimm_t *dimm;
108 
109 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
110 	    dimm = gmem_list_next(dimm)) {
111 		if (strcmp(dimm->dimm_serial, serial) == 0)
112 			return (dimm);
113 	}
114 
115 	return (NULL);
116 }
117 
118 gmem_dimm_t *
119 gmem_dimm_create(fmd_hdl_t *hdl, nvlist_t *asru)
120 {
121 	gmem_dimm_t *dimm;
122 	nvlist_t *fmri;
123 	char *serial;
124 
125 	if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial) != 0) {
126 		fmd_hdl_debug(hdl, "Unable to get dimm serial\n");
127 		return (NULL);
128 	}
129 
130 	if (nvlist_dup(asru, &fmri, 0) != 0) {
131 		fmd_hdl_debug(hdl, "dimm create nvlist dup failed");
132 		return (NULL);
133 	}
134 
135 	fmd_hdl_debug(hdl, "dimm_create: creating new DIMM serial=%s\n",
136 	    serial);
137 	GMEM_STAT_BUMP(dimm_creat);
138 
139 	dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
140 	dimm->dimm_nodetype = GMEM_NT_DIMM;
141 	dimm->dimm_version = GMEM_DIMM_VERSION;
142 
143 	gmem_bufname(dimm->dimm_bufname, sizeof (dimm->dimm_bufname), "dimm_%s",
144 	    serial);
145 	gmem_fmri_init(hdl, &dimm->dimm_asru, fmri, "dimm_asru_%s", serial);
146 
147 	nvlist_free(fmri);
148 
149 	(void) nvlist_lookup_string(dimm->dimm_asru_nvl, FM_FMRI_HC_SERIAL_ID,
150 	    (char **)&dimm->dimm_serial);
151 
152 	gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat, dimm->dimm_serial,
153 	    0, GMEM_DIMM_STAT_PREFIX);
154 
155 	gmem_list_append(&gmem.gm_dimms, dimm);
156 	gmem_dimm_dirty(hdl, dimm);
157 
158 	return (dimm);
159 }
160 
161 gmem_dimm_t *
162 gmem_dimm_lookup(fmd_hdl_t *hdl, nvlist_t *asru)
163 {
164 	gmem_dimm_t *dimm;
165 	char *serial;
166 	int err;
167 
168 	err = nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &serial);
169 
170 	if (err != 0) {
171 		fmd_hdl_debug(hdl, "Can't get dimm serial number\n");
172 		GMEM_STAT_BUMP(bad_mem_resource);
173 		return (NULL);
174 	}
175 
176 	dimm = dimm_lookup_by_serial(serial);
177 	return (dimm);
178 }
179 
180 static gmem_dimm_t *
181 gmem_dimm_wrapv0(fmd_hdl_t *hdl, gmem_dimm_pers_t *pers, size_t psz)
182 {
183 	gmem_dimm_t *dimm;
184 
185 	if (psz != sizeof (gmem_dimm_pers_t)) {
186 		fmd_hdl_abort(hdl, "size of state doesn't match size of "
187 		    "version 0 state (%u bytes).\n", sizeof (gmem_dimm_pers_t));
188 	}
189 
190 	dimm = fmd_hdl_zalloc(hdl, sizeof (gmem_dimm_t), FMD_SLEEP);
191 	bcopy(pers, dimm, sizeof (gmem_dimm_pers_t));
192 	fmd_hdl_free(hdl, pers, psz);
193 	return (dimm);
194 }
195 
196 void *
197 gmem_dimm_restore(fmd_hdl_t *hdl, fmd_case_t *cp, gmem_case_ptr_t *ptr)
198 {
199 	gmem_dimm_t *dimm;
200 
201 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL;
202 	    dimm = gmem_list_next(dimm)) {
203 		if (strcmp(dimm->dimm_bufname, ptr->ptr_name) == 0)
204 			break;
205 	}
206 
207 	if (dimm == NULL) {
208 		size_t dimmsz;
209 
210 		fmd_hdl_debug(hdl, "restoring dimm from %s\n", ptr->ptr_name);
211 
212 		if ((dimmsz = fmd_buf_size(hdl, NULL, ptr->ptr_name)) == 0) {
213 			fmd_hdl_abort(hdl, "dimm referenced by case %s does "
214 			    "not exist in saved state\n",
215 			    fmd_case_uuid(hdl, cp));
216 		} else if (dimmsz > GMEM_DIMM_MAXSIZE ||
217 		    dimmsz < GMEM_DIMM_MINSIZE) {
218 			fmd_hdl_abort(hdl, "dimm buffer referenced by case %s "
219 			    "is out of bounds (is %u bytes, max %u, min %u)\n",
220 			    fmd_case_uuid(hdl, cp), dimmsz,
221 			    GMEM_DIMM_MAXSIZE, GMEM_DIMM_MINSIZE);
222 		}
223 
224 		if ((dimm = gmem_buf_read(hdl, NULL, ptr->ptr_name,
225 		    dimmsz)) == NULL) {
226 			fmd_hdl_abort(hdl, "failed to read dimm buf %s",
227 			    ptr->ptr_name);
228 		}
229 
230 		fmd_hdl_debug(hdl, "found %d in version field\n",
231 		    dimm->dimm_version);
232 
233 		switch (dimm->dimm_version) {
234 		case GMEM_DIMM_VERSION_0:
235 			dimm = gmem_dimm_wrapv0(hdl, (gmem_dimm_pers_t *)dimm,
236 			    dimmsz);
237 			break;
238 		default:
239 			fmd_hdl_abort(hdl, "unknown version (found %d) "
240 			    "for dimm state referenced by case %s.\n",
241 			    dimm->dimm_version, fmd_case_uuid(hdl, cp));
242 			break;
243 		}
244 
245 		gmem_fmri_restore(hdl, &dimm->dimm_asru);
246 
247 		if ((errno = nvlist_lookup_string(dimm->dimm_asru_nvl,
248 		    FM_FMRI_HC_SERIAL_ID, (char **)&dimm->dimm_serial)) != 0)
249 			fmd_hdl_abort(hdl,
250 			    "failed to retrieve serial from asru");
251 
252 
253 		gmem_mem_retirestat_create(hdl, &dimm->dimm_retstat,
254 		    dimm->dimm_serial, dimm->dimm_nretired,
255 		    GMEM_DIMM_STAT_PREFIX);
256 
257 		gmem_list_append(&gmem.gm_dimms, dimm);
258 	}
259 
260 	switch (ptr->ptr_subtype) {
261 	case GMEM_PTR_DIMM_CASE:
262 		gmem_mem_case_restore(hdl, &dimm->dimm_case, cp, "dimm",
263 		    dimm->dimm_serial);
264 		break;
265 	default:
266 		fmd_hdl_abort(hdl, "invalid %s subtype %d\n",
267 		    ptr->ptr_name, ptr->ptr_subtype);
268 	}
269 
270 	return (dimm);
271 }
272 
273 void
274 gmem_dimm_validate(fmd_hdl_t *hdl)
275 {
276 	gmem_dimm_t *dimm, *next;
277 
278 	for (dimm = gmem_list_next(&gmem.gm_dimms); dimm != NULL; dimm = next) {
279 		next = gmem_list_next(dimm);
280 
281 		if (!gmem_dimm_present(hdl, dimm->dimm_asru_nvl))
282 			gmem_dimm_destroy(hdl, dimm);
283 	}
284 }
285 
286 void
287 gmem_dimm_dirty(fmd_hdl_t *hdl, gmem_dimm_t *dimm)
288 {
289 	if (fmd_buf_size(hdl, NULL, dimm->dimm_bufname) !=
290 	    sizeof (gmem_dimm_pers_t))
291 		fmd_buf_destroy(hdl, NULL, dimm->dimm_bufname);
292 
293 	/* No need to rewrite the FMRIs in the dimm - they don't change */
294 	fmd_buf_write(hdl, NULL, dimm->dimm_bufname, &dimm->dimm_pers,
295 	    sizeof (gmem_dimm_pers_t));
296 }
297 
298 void
299 gmem_dimm_gc(fmd_hdl_t *hdl)
300 {
301 	gmem_dimm_validate(hdl);
302 }
303 
304 void
305 gmem_dimm_fini(fmd_hdl_t *hdl)
306 {
307 	gmem_dimm_t *dimm;
308 
309 	while ((dimm = gmem_list_next(&gmem.gm_dimms)) != NULL)
310 		gmem_dimm_free(hdl, dimm, FMD_B_FALSE);
311 }
312 
313 
314 /*ARGSUSED*/
315 static int
316 find_dimm_hc_fmri(topo_hdl_t *thp, tnode_t *node, void *arg)
317 {
318 
319 	char *topo_sn;
320 	dimmid_t *dimmid = (dimmid_t *)arg;
321 	nvlist_t *fru = NULL;
322 	nvlist_t *rsc = NULL;
323 	nvlist_t *asru = NULL;
324 	int err;
325 
326 	if (topo_node_fru(node, &fru, NULL, &err) < 0)
327 		return (TOPO_WALK_NEXT);
328 
329 	err = nvlist_lookup_string(fru, FM_FMRI_HC_SERIAL_ID, &topo_sn);
330 	if (err != 0) {
331 		nvlist_free(fru);
332 		return (TOPO_WALK_NEXT);
333 	}
334 
335 	if (strcmp(dimmid->serial, topo_sn) != 0) {
336 		nvlist_free(fru);
337 		return (TOPO_WALK_NEXT);
338 	}
339 
340 	switch (dimmid->type) {
341 		case FINDFRU:
342 			(void) nvlist_dup(fru, &dimm_nvl, NV_UNIQUE_NAME);
343 			break;
344 		case FINDRSC:
345 			(void) topo_node_resource(node, &rsc, &err);
346 			if (rsc != NULL) {
347 				(void) nvlist_dup(rsc, &dimm_nvl,
348 				    NV_UNIQUE_NAME);
349 				nvlist_free(rsc);
350 			}
351 			break;
352 		case FINDASRU:
353 			(void) topo_node_asru(node, &asru, NULL, &err);
354 			if (asru != NULL) {
355 				(void) nvlist_dup(asru, &dimm_nvl,
356 				    NV_UNIQUE_NAME);
357 				nvlist_free(asru);
358 			}
359 			break;
360 		default:
361 			break;
362 	}
363 	nvlist_free(fru);
364 	return (TOPO_WALK_TERMINATE);
365 }
366 
367 nvlist_t *
368 gmem_find_dimm_by_sn(fmd_hdl_t *hdl, dimmid_t *dimmid) {
369 	topo_hdl_t *thp;
370 	topo_walk_t *twp;
371 	int err;
372 	dimm_nvl = NULL;
373 
374 	if ((thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) == NULL)
375 		return (NULL);
376 
377 	if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC,
378 	    find_dimm_hc_fmri, dimmid, &err)) == NULL) {
379 		fmd_hdl_topo_rele(hdl, thp);
380 		return (NULL);
381 	}
382 
383 	(void) topo_walk_step(twp, TOPO_WALK_CHILD);
384 	topo_walk_fini(twp);
385 	fmd_hdl_topo_rele(hdl, thp);
386 	return (dimm_nvl);
387 }
388 
389 nvlist_t *
390 gmem_find_dimm_fru(fmd_hdl_t *hdl, char *sn)
391 {
392 	dimmid_t fru;
393 	(void) strcpy(fru.serial, sn);
394 	fru.type = FINDFRU;
395 	return (gmem_find_dimm_by_sn(hdl, &fru));
396 }
397 
398 nvlist_t *
399 gmem_find_dimm_rsc(fmd_hdl_t *hdl, char *sn)
400 {
401 	dimmid_t rsc;
402 	(void) strcpy(rsc.serial, sn);
403 	rsc.type = FINDRSC;
404 	return (gmem_find_dimm_by_sn(hdl, &rsc));
405 }
406 
407 nvlist_t *
408 gmem_find_dimm_asru(fmd_hdl_t *hdl, char *sn)
409 {
410 	dimmid_t asru;
411 	(void) strcpy(asru.serial, sn);
412 	asru.type = FINDASRU;
413 	return (gmem_find_dimm_by_sn(hdl, &asru));
414 }
415 
416 int
417 gmem_dimm_present(fmd_hdl_t *hdl, nvlist_t *asru)
418 {
419 	char *sn;
420 	nvlist_t *dimm = NULL;
421 
422 	if (nvlist_lookup_string(asru, FM_FMRI_HC_SERIAL_ID, &sn) != 0) {
423 		fmd_hdl_debug(hdl, "Unable to get dimm serial\n");
424 		return (0);
425 	}
426 	dimm = gmem_find_dimm_fru(hdl, sn);
427 	if (dimm == NULL) {
428 		fmd_hdl_debug(hdl, "Dimm sn=%s is not present\n", sn);
429 		return (0);
430 	}
431 	if (dimm != NULL)
432 		nvlist_free(dimm);
433 	return (1);
434 }
435