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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <strings.h>
30 #include <sys/types.h>
31 #include <fm/topo_mod.h>
32 #include <sys/fm/protocol.h>
33
34 #include <unistd.h>
35 #include <sys/param.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <umem.h>
39
40 #include <mem_mdesc.h>
41
42 /*
43 * Enumerates the DIMMS in a system. For each DIMM found, the necessary nodes
44 * are also constructed.
45 */
46
47 #define DIMM_VERSION TOPO_VERSION
48 #define DIMM_NODE_NAME "dimm"
49
50 extern topo_method_t pi_mem_methods[];
51
52 /* Forward declaration */
53 static int dimm_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
54 topo_instance_t, void *, void *);
55 static void dimm_release(topo_mod_t *, tnode_t *);
56
57 static const topo_modops_t dimm_ops =
58 { dimm_enum, dimm_release };
59 static const topo_modinfo_t dimm_info =
60 { "dimm", FM_FMRI_SCHEME_HC, DIMM_VERSION, &dimm_ops };
61
62 static const topo_pgroup_info_t mem_auth_pgroup = {
63 FM_FMRI_AUTHORITY,
64 TOPO_STABILITY_PRIVATE,
65 TOPO_STABILITY_PRIVATE,
66 1
67 };
68
69 int
_topo_init(topo_mod_t * mod)70 _topo_init(topo_mod_t *mod)
71 {
72 md_mem_info_t *mem;
73
74 if (getenv("TOPOMEMDBG"))
75 topo_mod_setdebug(mod);
76 topo_mod_dprintf(mod, "initializing mem enumerator\n");
77
78 if ((mem = topo_mod_zalloc(mod, sizeof (md_mem_info_t))) == NULL)
79 return (-1);
80
81 if (mem_mdesc_init(mod, mem) != 0) {
82 topo_mod_dprintf(mod, "failed to get dimms from the PRI/MD\n");
83 topo_mod_free(mod, mem, sizeof (md_mem_info_t));
84 return (-1);
85 }
86
87 topo_mod_setspecific(mod, (void *)mem);
88
89 if (topo_mod_register(mod, &dimm_info, TOPO_VERSION) != 0) {
90 topo_mod_dprintf(mod, "failed to register hc: "
91 "%s\n", topo_mod_errmsg(mod));
92 mem_mdesc_fini(mod, mem);
93 topo_mod_free(mod, mem, sizeof (md_mem_info_t));
94 return (-1);
95 }
96
97 topo_mod_dprintf(mod, "mem enumerator inited\n");
98
99 return (0);
100 }
101
102 void
_topo_fini(topo_mod_t * mod)103 _topo_fini(topo_mod_t *mod)
104 {
105 md_mem_info_t *mem;
106
107 mem = (md_mem_info_t *)topo_mod_getspecific(mod);
108
109 mem_mdesc_fini(mod, mem);
110
111 topo_mod_free(mod, mem, sizeof (md_mem_info_t));
112
113 topo_mod_unregister(mod);
114 }
115
116 static tnode_t *
mem_tnode_create(topo_mod_t * mod,tnode_t * parent,const char * name,topo_instance_t i,char * serial,nvlist_t * fru,char * label,void * priv)117 mem_tnode_create(topo_mod_t *mod, tnode_t *parent,
118 const char *name, topo_instance_t i, char *serial,
119 nvlist_t *fru, char *label, void *priv)
120 {
121 int err;
122 nvlist_t *fmri;
123 tnode_t *ntn;
124 nvlist_t *auth = topo_mod_auth(mod, parent);
125
126 fmri = topo_mod_hcfmri(mod, parent, FM_HC_SCHEME_VERSION, name, i,
127 NULL, auth, NULL, NULL, serial);
128 nvlist_free(auth);
129 if (fmri == NULL) {
130 topo_mod_dprintf(mod,
131 "Unable to make nvlist for %s bind: %s.\n",
132 name, topo_mod_errmsg(mod));
133 return (NULL);
134 }
135
136 ntn = topo_node_bind(mod, parent, name, i, fmri);
137 if (ntn == NULL) {
138 topo_mod_dprintf(mod,
139 "topo_node_bind (%s%d/%s%d) failed: %s\n",
140 topo_node_name(parent), topo_node_instance(parent),
141 name, i,
142 topo_strerror(topo_mod_errno(mod)));
143 nvlist_free(fmri);
144 return (NULL);
145 }
146 nvlist_free(fmri);
147 topo_node_setspecific(ntn, priv);
148
149 if (topo_pgroup_create(ntn, &mem_auth_pgroup, &err) == 0) {
150 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
151 FM_FMRI_AUTH_PRODUCT, &err);
152 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
153 FM_FMRI_AUTH_PRODUCT_SN, &err);
154 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
155 FM_FMRI_AUTH_CHASSIS, &err);
156 (void) topo_prop_inherit(ntn, FM_FMRI_AUTHORITY,
157 FM_FMRI_AUTH_SERVER, &err);
158 }
159
160 (void) topo_node_label_set(ntn, label, &err);
161 (void) topo_node_fru_set(ntn, fru, 0, &err);
162
163 return (ntn);
164 }
165
166 typedef struct {
167 const char *nh_name;
168 const char *nh_sscan;
169 } nac_hc_t;
170
171 static const nac_hc_t nac_mem_tbl[] = {
172 {"branch", "BR%d" },
173 {"dram-channel", "CH%d" },
174 {"rank", "R%d" },
175 {"dimm", "D%d" },
176 {"memboard", "MR%d" },
177 {"memboard", "MEM%d" },
178 {"chip", "CMP%d" }
179 };
180
181 static const char *
nac2hc(const char * s,int * inst)182 nac2hc(const char *s, int *inst)
183 {
184 int i;
185
186 if (s == NULL)
187 return (NULL);
188
189 for (i = 0; i < sizeof (nac_mem_tbl) / sizeof (nac_hc_t); i++) {
190 if (sscanf(s, nac_mem_tbl[i].nh_sscan, inst) == 1)
191 return (nac_mem_tbl[i].nh_name);
192 }
193 return (NULL);
194 }
195
196 static int
create_one_dimm(topo_mod_t * mod,tnode_t * pnode,int inst,mem_dimm_map_t * dp)197 create_one_dimm(topo_mod_t *mod, tnode_t *pnode, int inst, mem_dimm_map_t *dp)
198 {
199 tnode_t *cnode;
200 nvlist_t *rsrc, *fru;
201 int nerr = 0, err;
202 nvlist_t *auth = NULL;
203
204 /*
205 * Because mem_tnode_create will fill in a "FRU" value by default,
206 * but not an "ASRU" value, we have to compute the desired "FRU"
207 * value -before- calling mem_tnode_create, but it's ok to
208 * topo_mod_asru_set() the ASRU value after the topo_node is
209 * created.
210 */
211
212 auth = topo_mod_auth(mod, pnode);
213 if ((fru = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION, "dimm",
214 inst, NULL, auth, dp->dm_part, NULL, dp->dm_serid)) == NULL)
215 nerr++;
216 nvlist_free(auth);
217
218 cnode = mem_tnode_create(mod, pnode, "dimm", inst,
219 dp->dm_serid, fru, dp->dm_label, NULL);
220 nvlist_free(fru);
221 if (cnode == NULL)
222 return (++nerr);
223
224 rsrc = NULL;
225 /* ASRU will be computed by topo method */
226 if (topo_node_resource(cnode, &rsrc, &err) < 0 ||
227 topo_method_register(mod, cnode, pi_mem_methods) < 0 ||
228 topo_node_asru_set(cnode, rsrc, TOPO_ASRU_COMPUTE, &err) < 0)
229 nerr++;
230 nvlist_free(rsrc);
231
232 return (nerr);
233 }
234
235 int
slashorend(const char * s,int start)236 slashorend(const char *s, int start)
237 {
238 const char *t = s + start;
239
240 if ((t = strchr(t, '/')) == NULL)
241 return (strlen(s)); /* end */
242 else
243 return (t - s); /* next slash */
244 }
245
246 /*
247 * mem_range_create and mem_inst_create are mutually recursive routines which
248 * together create the node hierarchy for one dimm and its siblings.
249 * mem_range_create is called when creating the first instance of a given node
250 * type as child of a parent instance, because it is then, and only then,
251 * that a topo range must be created. It calls mem_inst_create for its first
252 * and subsequent instances. The recursion always starts with
253 * mem_range_create, so it performs the up-front sanity checks.
254 *
255 * Note: the list of mem_dimm_map_t's pointed at by dp must be sorted
256 * alphabetically by *dm_label.
257 */
258
259 static int mem_range_create(topo_mod_t *, tnode_t *, int, mem_dimm_map_t *);
260
261 static int
mem_inst_create(topo_mod_t * mod,tnode_t * pnode,int pflen,mem_dimm_map_t * dp)262 mem_inst_create(topo_mod_t *mod, tnode_t *pnode, int pflen, mem_dimm_map_t *dp)
263 {
264 int inst, pfnext;
265 const char *nodename;
266 tnode_t *cnode;
267 mem_dimm_map_t *d;
268 nvlist_t *fru;
269 int nerr = 0;
270
271 pfnext = slashorend(dp->dm_label, pflen);
272 nodename = nac2hc((dp->dm_label) + pflen, &inst);
273 d = dp;
274 if (strcmp(nodename, "dimm") == 0) {
275 return (create_one_dimm(mod, pnode, inst, dp));
276 } else if (*(d->dm_label + pfnext) == '\0') { /* this node has a fru */
277 fru = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
278 nodename, inst, NULL, NULL, dp->dm_part, NULL,
279 dp->dm_serid);
280 cnode = mem_tnode_create(mod, pnode, nodename, inst,
281 dp->dm_serid, fru, dp->dm_label, NULL);
282 nvlist_free(fru);
283 d = dp->dm_next; /* next mem_dimm_map_t could be child */
284 } else {
285 cnode = mem_tnode_create(mod, pnode, nodename, inst,
286 NULL, NULL, NULL, NULL);
287 }
288 if ((d != NULL) &&
289 strncmp(dp->dm_label, d->dm_label, pfnext) == 0)
290 nerr += mem_range_create(mod, cnode, pfnext+1, d);
291 return (nerr);
292 }
293
294 int
mem_range_create(topo_mod_t * mod,tnode_t * pnode,int pflen,mem_dimm_map_t * dp)295 mem_range_create(topo_mod_t *mod, tnode_t *pnode, int pflen,
296 mem_dimm_map_t *dp)
297 {
298 int inst, pfnext;
299 const char *nodename;
300 mem_dimm_map_t *d;
301 int nerr = 0;
302
303 if (pnode == NULL)
304 return (1); /* definitely an error */
305 if (*(dp->dm_label + pflen) == '\0')
306 return (1); /* recursed too far */
307
308 pfnext = slashorend(dp->dm_label, pflen);
309 nodename = nac2hc(dp->dm_label + pflen, &inst);
310
311 if (nodename != NULL) {
312 if (topo_node_range_create(mod, pnode, nodename, 0,
313 MEM_DIMM_MAX) < 0) {
314 topo_mod_dprintf(mod, "failed to create "
315 "DIMM range %s error %s\n", nodename,
316 topo_mod_errmsg(mod));
317 return (-1);
318 }
319 } else {
320 /*
321 * Skip over NAC elements other than those listed
322 * above. These elements will appear
323 * in the DIMM's unum, but not in hc: scheme hierarchy.
324 */
325
326 return (mem_range_create(mod, pnode, pfnext+1, dp));
327 }
328
329 nerr += mem_inst_create(mod, pnode, pflen, dp);
330
331 for (d = dp->dm_next; d != NULL; d = d->dm_next) {
332 if (strncmp(dp->dm_label, d->dm_label, pfnext) == 0)
333 continue; /* child of 1st instance -- already done */
334 else if (strncmp(dp->dm_label, d->dm_label,
335 pflen) == 0) { /* child of same parent */
336 if (nodename == nac2hc((d->dm_label)+pflen, &inst)) {
337 /*
338 * Same nodename as sibling. Don't create
339 * new range, or the enumeration will die.
340 */
341 nerr += mem_inst_create(mod, pnode, pflen, d);
342 dp = d;
343 } else {
344 nodename = nac2hc((d->dm_label)+pflen, &inst);
345 nerr += mem_range_create(mod, pnode, pflen, d);
346 dp = d;
347 }
348 }
349 else
350 return (nerr); /* finished all children of my parent */
351 }
352 return (nerr); /* reached end of mem_dimm_map_t list */
353 }
354 static int
mem_create(topo_mod_t * mod,tnode_t * rnode,md_mem_info_t * cm)355 mem_create(topo_mod_t *mod, tnode_t *rnode, md_mem_info_t *cm)
356 {
357 int l, nerrs;
358 char nodename[10]; /* allows up to 10^6 chips in system */
359 char *p;
360 mem_dimm_map_t *dp;
361
362 if (strcmp(topo_node_name(rnode), "chip") == 0) {
363
364 (void) snprintf(nodename, 10, "CMP%d",
365 topo_node_instance(rnode));
366
367 for (dp = cm->mem_dm; dp != NULL; dp = dp->dm_next) {
368 p = strstr(dp->dm_label, nodename);
369 if (p != NULL && (p = strchr(p, '/')) != NULL) {
370 l = p - (dp->dm_label) + 1;
371 break;
372 }
373 }
374 } else if (strcmp(topo_node_name(rnode), "motherboard") == 0) {
375 for (dp = cm->mem_dm; dp != NULL; dp = dp->dm_next) {
376 p = strstr(dp->dm_label, "MB/MEM");
377 if (p != NULL) {
378 l = 3; /* start with MEM */
379 break;
380 }
381 }
382 } else {
383 return (1);
384 }
385
386 if (dp != NULL)
387 nerrs = mem_range_create(mod, rnode, l, dp);
388 else
389 nerrs = 1;
390 return (nerrs);
391 }
392
393
394 /*
395 * The hc-scheme memory enumerator is invoked from within a platform
396 * toplogy file. Make sure that the invocation is either
397 * 1) a child of the chip enumerator, which will cause the argument "rnode"
398 * below to be a chip node, and the dimm structures specific for that chip can
399 * then be built from its specific node, or
400 * 2) a child of the motherboard enumerator -- for Batoka and similar machines
401 * with cpu-boards.
402 */
403
404 /*ARGSUSED*/
405 static int
dimm_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * notused)406 dimm_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
407 topo_instance_t min, topo_instance_t max, void *arg, void *notused)
408 {
409 md_mem_info_t *mem = (md_mem_info_t *)arg;
410
411 if (strcmp(name, DIMM_NODE_NAME) == 0)
412 return (mem_create(mod, rnode, mem));
413
414 return (-1);
415 }
416
417 /*ARGSUSED*/
418 static void
dimm_release(topo_mod_t * mp,tnode_t * node)419 dimm_release(topo_mod_t *mp, tnode_t *node)
420 {
421 }
422