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 <strings.h>
28 #include <umem.h>
29 #include <fm/topo_mod.h>
30 #include <fm/fmd_fmri.h>
31 #include <sys/fm/ldom.h>
32 #include <sys/fm/protocol.h>
33
34 #include <cpu_mdesc.h>
35
36 /*
37 * This enumerator creates cpu-schemed nodes for each strand found in the
38 * sun4v Physical Rource Inventory (PRI).
39 * Each node export four methods present(), expand() replaced() and unusable().
40 *
41 */
42
43 #define PLATFORM_CPU_NAME "platform-cpu"
44 #define PLATFORM_CPU_VERSION TOPO_VERSION
45 #define CPU_NODE_NAME "cpu"
46
47
48 /* Forward declaration */
49 static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t,
50 topo_instance_t, void *, void *);
51 static void cpu_release(topo_mod_t *, tnode_t *);
52 static int cpu_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
53 nvlist_t **);
54 static int cpu_replaced(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
55 nvlist_t **);
56 static int cpu_expand(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
57 nvlist_t **);
58 static int cpu_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
59 nvlist_t **);
60
61 static const topo_modops_t cpu_ops =
62 { cpu_enum, cpu_release };
63 static const topo_modinfo_t cpu_info =
64 { PLATFORM_CPU_NAME, FM_FMRI_SCHEME_CPU, PLATFORM_CPU_VERSION,
65 &cpu_ops };
66
67 static const topo_method_t cpu_methods[] = {
68 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
69 TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL, cpu_present },
70 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC,
71 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, cpu_replaced },
72 { TOPO_METH_EXPAND, TOPO_METH_EXPAND_DESC,
73 TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, cpu_expand },
74 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
75 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, cpu_unusable },
76 { NULL }
77 };
78
79 static void *
cpu_alloc(size_t size)80 cpu_alloc(size_t size)
81 {
82 return (umem_alloc(size, UMEM_DEFAULT));
83 }
84
85 static void
cpu_free(void * data,size_t size)86 cpu_free(void *data, size_t size)
87 {
88 umem_free(data, size);
89 }
90
91 static int
cpu_read_serial(nvlist_t * in,uint64_t * serial)92 cpu_read_serial(nvlist_t *in, uint64_t *serial)
93 {
94 uint8_t version;
95 uint64_t int_serial;
96 char *str_serial, *end;
97 int rc = 0;
98
99 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0)
100 return (1);
101
102 if (version == CPU_SCHEME_VERSION0) {
103 if ((rc = nvlist_lookup_uint64(in, FM_FMRI_CPU_SERIAL_ID,
104 &int_serial)) == 0) {
105 *serial = int_serial;
106 }
107 } else {
108 if ((rc = nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID,
109 &str_serial)) == 0) {
110 *serial = (uint64_t)strtoull(str_serial, &end, 16);
111 if (str_serial == end)
112 rc = 1;
113 }
114 }
115 return (rc);
116 }
117
118 int
_topo_init(topo_mod_t * mod)119 _topo_init(topo_mod_t *mod)
120 {
121 md_info_t *chip;
122
123 if (getenv("TOPOPLATFORMCPUDBG"))
124 topo_mod_setdebug(mod);
125 topo_mod_dprintf(mod, "initializing %s enumerator\n",
126 PLATFORM_CPU_NAME);
127
128 if ((chip = topo_mod_zalloc(mod, sizeof (md_info_t))) == NULL)
129 return (-1);
130
131 if (cpu_mdesc_init(mod, chip) != 0) {
132 topo_mod_dprintf(mod, "failed to get cpus from the PRI/MD\n");
133 topo_mod_free(mod, chip, sizeof (md_info_t));
134 return (-1);
135 }
136
137 topo_mod_setspecific(mod, (void *)chip);
138
139 if (topo_mod_register(mod, &cpu_info, TOPO_VERSION) != 0) {
140 topo_mod_dprintf(mod, "failed to register %s: %s\n",
141 PLATFORM_CPU_NAME, topo_mod_errmsg(mod));
142 cpu_mdesc_fini(mod, chip);
143 topo_mod_free(mod, chip, sizeof (md_info_t));
144 return (-1);
145 }
146
147 topo_mod_dprintf(mod, "%s enumerator inited\n", PLATFORM_CPU_NAME);
148
149 return (0);
150 }
151
152 void
_topo_fini(topo_mod_t * mod)153 _topo_fini(topo_mod_t *mod)
154 {
155 md_info_t *chip;
156
157 chip = (md_info_t *)topo_mod_getspecific(mod);
158
159 cpu_mdesc_fini(mod, chip);
160
161 topo_mod_free(mod, chip, sizeof (md_info_t));
162
163 topo_mod_unregister(mod);
164
165 }
166
167 /*ARGSUSED*/
168 static int
cpu_present(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)169 cpu_present(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
170 nvlist_t *in, nvlist_t **out)
171 {
172 uint8_t version;
173 uint32_t cpuid;
174 uint64_t nvlserid;
175 uint32_t present = 0;
176 md_cpumap_t *mcmp;
177 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod);
178
179 /*
180 * Get the physical cpuid
181 */
182 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
183 version > FM_CPU_SCHEME_VERSION ||
184 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) {
185 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
186 }
187
188 /*
189 * Find the cpuid entry
190 * If the input nvl contains a serial number, the cpu is identified
191 * by a tuple <cpuid, cpuserial>
192 * Otherwise, the cpu is identified by the <cpuid>.
193 */
194 if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) {
195 if (cpu_read_serial(in, &nvlserid) == 0)
196 present = nvlserid == mcmp->cpumap_serialno;
197 else
198 present = 1;
199 }
200
201 /* return the present status */
202 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
203 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
204 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) {
205 nvlist_free(*out);
206 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
207 }
208
209 return (0);
210 }
211
212 /*ARGSUSED*/
213 static int
cpu_replaced(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)214 cpu_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
215 nvlist_t *in, nvlist_t **out)
216 {
217 uint8_t version;
218 uint32_t cpuid;
219 uint64_t nvlserid;
220 uint32_t rval = FMD_OBJ_STATE_NOT_PRESENT;
221 md_cpumap_t *mcmp;
222 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod);
223
224 /*
225 * Get the physical cpuid
226 */
227 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
228 version > FM_CPU_SCHEME_VERSION ||
229 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) {
230 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
231 }
232
233 /*
234 * Find the cpuid entry
235 * If the input nvl contains a serial number, the cpu is identified
236 * by a tuple <cpuid, cpuserial>
237 * Otherwise, the cpu is identified by the <cpuid>.
238 */
239 if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) {
240 if (cpu_read_serial(in, &nvlserid) == 0)
241 rval = (nvlserid == mcmp->cpumap_serialno) ?
242 FMD_OBJ_STATE_STILL_PRESENT :
243 FMD_OBJ_STATE_REPLACED;
244 else
245 rval = FMD_OBJ_STATE_UNKNOWN;
246 }
247
248 /* return the replaced status */
249 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
250 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
251 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) {
252 nvlist_free(*out);
253 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
254 }
255
256 return (0);
257 }
258
259 /*ARGSUSED*/
260 static int
cpu_expand(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)261 cpu_expand(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
262 nvlist_t *in, nvlist_t **out)
263 {
264 int rc;
265 uint8_t version;
266 uint32_t cpuid;
267 uint64_t nvlserid;
268 md_cpumap_t *mcmp = NULL;
269 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod);
270
271 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
272 version > FM_CPU_SCHEME_VERSION ||
273 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) {
274 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
275 }
276
277 /* Find the cpuid entry */
278 if ((mcmp = cpu_find_cpumap(chip, cpuid)) == NULL)
279 return (-1);
280
281 if ((rc = cpu_read_serial(in, &nvlserid)) == 0) {
282 if (nvlserid != mcmp->cpumap_serialno)
283 return (-1);
284 } else if (rc != ENOENT)
285 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
286 else {
287 if ((rc = nvlist_add_uint64(in, FM_FMRI_CPU_SERIAL_ID,
288 mcmp->cpumap_serialno)) != 0) {
289 return (topo_mod_seterrno(mod, rc));
290 }
291 }
292
293 topo_mod_dprintf(mod, "nvlserid=%llX\n", nvlserid);
294
295 if (mcmp != NULL &&
296 mcmp->cpumap_chipidx >= 0 &&
297 mcmp->cpumap_chipidx < chip->nprocs &&
298 chip->procs &&
299 chip->procs[mcmp->cpumap_chipidx].fru) {
300 int len;
301 char *str;
302 md_fru_t *frup = chip->procs[mcmp->cpumap_chipidx].fru;
303
304 /* part number + dash number */
305 len = (frup->part ? strlen(frup->part) : 0) +
306 (frup->dash ? strlen(frup->dash) : 0) + 1;
307 str = cpu_alloc(len);
308 (void) snprintf(str, len, "%s%s",
309 frup->part ? frup->part : MD_STR_BLANK,
310 frup->dash ? frup->dash : MD_STR_BLANK);
311 (void) nvlist_add_string(in, FM_FMRI_HC_PART, str);
312 cpu_free(str, len);
313
314 /* fru name */
315 (void) nvlist_add_string(in, FM_FMRI_CPU_CPUFRU,
316 frup->nac ? frup->nac : MD_STR_BLANK);
317
318 /* fru serial */
319 in->nvl_nvflag = NV_UNIQUE_NAME_TYPE;
320 (void) nvlist_add_string(in, FM_FMRI_HC_SERIAL_ID,
321 frup->serial ? frup->serial : MD_STR_BLANK);
322 }
323
324 return (0);
325 }
326
327 /*ARGSUSED*/
328 static int
cpu_unusable(topo_mod_t * mod,tnode_t * node,topo_version_t vers,nvlist_t * in,nvlist_t ** out)329 cpu_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
330 nvlist_t *in, nvlist_t **out)
331 {
332 int rc = -1;
333 uint8_t version;
334 int status;
335 uint32_t cpuid;
336 ldom_hdl_t *lhp;
337 uint64_t nvlserid;
338 uint32_t present = 0;
339 md_cpumap_t *mcmp;
340 md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod);
341 uint32_t type = 0;
342
343 if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 ||
344 version > FM_CPU_SCHEME_VERSION ||
345 nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) {
346 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
347 }
348
349 /*
350 * Check the cpu presence
351 */
352 if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) {
353 if (cpu_read_serial(in, &nvlserid) == 0)
354 present = nvlserid == mcmp->cpumap_serialno;
355 else
356 present = 1;
357 }
358 if (present == 0) {
359 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
360 }
361
362 lhp = ldom_init(cpu_alloc, cpu_free);
363 if (lhp == NULL) {
364 return (topo_mod_seterrno(mod, EMOD_NOMEM));
365 }
366 (void) ldom_get_type(lhp, &type);
367 status = ldom_fmri_status(lhp, in);
368 rc = (status == P_FAULTED ||
369 (status == P_OFFLINE && ((type & LDOM_TYPE_CONTROL) != 0)));
370 ldom_fini(lhp);
371
372 /* return the unusable status */
373 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0)
374 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
375 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, rc) != 0) {
376 nvlist_free(*out);
377 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
378 }
379
380 return (0);
381 }
382
383
384 static nvlist_t *
cpu_fmri_create(topo_mod_t * mod,uint32_t cpuid,char * serial,uint8_t cpumask)385 cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *serial, uint8_t cpumask)
386 {
387 int err;
388 nvlist_t *fmri;
389
390 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0)
391 return (NULL);
392 err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION);
393 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU);
394 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpuid);
395 err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask);
396 if (serial != NULL)
397 err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, serial);
398 if (err != 0) {
399 nvlist_free(fmri);
400 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL);
401 return (NULL);
402 }
403
404 return (fmri);
405 }
406
407 static tnode_t *
cpu_tnode_create(topo_mod_t * mod,tnode_t * parent,const char * name,topo_instance_t i,char * serial,void * priv)408 cpu_tnode_create(topo_mod_t *mod, tnode_t *parent,
409 const char *name, topo_instance_t i, char *serial, void *priv)
410 {
411 int cpu_mask = 0;
412 nvlist_t *fmri;
413 tnode_t *ntn;
414
415 fmri = cpu_fmri_create(mod, i, serial, cpu_mask);
416 if (fmri == NULL) {
417 topo_mod_dprintf(mod,
418 "Unable to make nvlist for %s bind: %s.\n",
419 name, topo_mod_errmsg(mod));
420 return (NULL);
421 }
422
423 ntn = topo_node_bind(mod, parent, name, i, fmri);
424 if (ntn == NULL) {
425 topo_mod_dprintf(mod,
426 "topo_node_bind (%s%d/%s%d) failed: %s\n",
427 topo_node_name(parent), topo_node_instance(parent),
428 name, i,
429 topo_strerror(topo_mod_errno(mod)));
430 nvlist_free(fmri);
431 return (NULL);
432 }
433 nvlist_free(fmri);
434 topo_node_setspecific(ntn, priv);
435
436 return (ntn);
437 }
438
439 /*ARGSUSED*/
440 static int
cpu_create(topo_mod_t * mod,tnode_t * rnode,const char * name,md_info_t * chip)441 cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name, md_info_t *chip)
442 {
443 int i;
444 int min = -1;
445 int max = -1;
446 int nerr = 0;
447 int pid;
448 char sbuf[32];
449 tnode_t *cnode;
450
451 topo_mod_dprintf(mod, "enumerating cpus\n");
452
453 /*
454 * find the min/max id of cpus per this cmp and create a cpu range
455 */
456 for (i = 0; i < chip->ncpus; i++) {
457 if ((min < 0) || (chip->cpus[i].cpumap_pid < min))
458 min = chip->cpus[i].cpumap_pid;
459 if ((max < 0) || (chip->cpus[i].cpumap_pid > max))
460 max = chip->cpus[i].cpumap_pid;
461 }
462 if (min < 0 || max < 0)
463 return (-1);
464 topo_node_range_destroy(rnode, name);
465 if (topo_node_range_create(mod, rnode, name, 0, max+1) < 0) {
466 topo_mod_dprintf(mod, "failed to create cpu range[0,%d]: %s\n",
467 max, topo_mod_errmsg(mod));
468 return (-1);
469 }
470
471 /*
472 * Create the cpu nodes
473 */
474 for (i = 0; i < chip->ncpus; i++) {
475
476 (void) snprintf(sbuf, sizeof (sbuf), "%llx",
477 chip->cpus[i].cpumap_serialno);
478
479 /* physical cpuid */
480 pid = chip->cpus[i].cpumap_pid;
481 cnode = cpu_tnode_create(mod, rnode, name,
482 (topo_instance_t)pid, sbuf, NULL);
483 if (cnode == NULL) {
484 topo_mod_dprintf(mod,
485 "failed to create a cpu=%d node: %s\n",
486 pid, topo_mod_errmsg(mod));
487 nerr++;
488 continue;
489 }
490
491 }
492
493 if (nerr != 0)
494 (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM);
495
496 return (0);
497 }
498
499 /*ARGSUSED*/
500 static int
cpu_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * notused)501 cpu_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
502 topo_instance_t min, topo_instance_t max, void *arg, void *notused)
503 {
504 topo_mod_dprintf(mod, "%s enumerating %s\n", PLATFORM_CPU_NAME, name);
505
506 if (topo_method_register(mod, rnode, cpu_methods) < 0) {
507 topo_mod_dprintf(mod, "topo_method_register failed: %s\n",
508 topo_strerror(topo_mod_errno(mod)));
509 return (-1);
510 }
511
512 if (strcmp(name, CPU_NODE_NAME) == 0)
513 return (cpu_create(mod, rnode, name, (md_info_t *)arg));
514
515 return (0);
516 }
517
518 /*ARGSUSED*/
519 static void
cpu_release(topo_mod_t * mod,tnode_t * node)520 cpu_release(topo_mod_t *mod, tnode_t *node)
521 {
522 topo_method_unregister_all(mod, node);
523 }
524