Home | History | Annotate | Download | only in platform-cpu
      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 *
     80 cpu_alloc(size_t size)
     81 {
     82 	return (umem_alloc(size, UMEM_DEFAULT));
     83 }
     84 
     85 static void
     86 cpu_free(void *data, size_t size)
     87 {
     88 	umem_free(data, size);
     89 }
     90 
     91 static int
     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
    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
    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
    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
    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
    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
    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 *
    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 *
    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
    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
    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
    520 cpu_release(topo_mod_t *mod, tnode_t *node)
    521 {
    522 	topo_method_unregister_all(mod, node);
    523 }
    524