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