/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include /* * This enumerator creates cpu-schemed nodes for each strand found in the * sun4v Physical Rource Inventory (PRI). * Each node export four methods present(), expand() replaced() and unusable(). * */ #define PLATFORM_CPU_NAME "platform-cpu" #define PLATFORM_CPU_VERSION TOPO_VERSION #define CPU_NODE_NAME "cpu" /* Forward declaration */ static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, topo_instance_t, void *, void *); static void cpu_release(topo_mod_t *, tnode_t *); static int cpu_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static int cpu_replaced(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static int cpu_expand(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static int cpu_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, nvlist_t **); static const topo_modops_t cpu_ops = { cpu_enum, cpu_release }; static const topo_modinfo_t cpu_info = { PLATFORM_CPU_NAME, FM_FMRI_SCHEME_CPU, PLATFORM_CPU_VERSION, &cpu_ops }; static const topo_method_t cpu_methods[] = { { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, TOPO_STABILITY_INTERNAL, cpu_present }, { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, cpu_replaced }, { TOPO_METH_EXPAND, TOPO_METH_EXPAND_DESC, TOPO_METH_EXPAND_VERSION, TOPO_STABILITY_INTERNAL, cpu_expand }, { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, cpu_unusable }, { NULL } }; static void * cpu_alloc(size_t size) { return (umem_alloc(size, UMEM_DEFAULT)); } static void cpu_free(void *data, size_t size) { umem_free(data, size); } static int cpu_read_serial(nvlist_t *in, uint64_t *serial) { uint8_t version; uint64_t int_serial; char *str_serial, *end; int rc = 0; if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0) return (1); if (version == CPU_SCHEME_VERSION0) { if ((rc = nvlist_lookup_uint64(in, FM_FMRI_CPU_SERIAL_ID, &int_serial)) == 0) { *serial = int_serial; } } else { if ((rc = nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, &str_serial)) == 0) { *serial = (uint64_t)strtoull(str_serial, &end, 16); if (str_serial == end) rc = 1; } } return (rc); } int _topo_init(topo_mod_t *mod) { md_info_t *chip; if (getenv("TOPOPLATFORMCPUDBG")) topo_mod_setdebug(mod); topo_mod_dprintf(mod, "initializing %s enumerator\n", PLATFORM_CPU_NAME); if ((chip = topo_mod_zalloc(mod, sizeof (md_info_t))) == NULL) return (-1); if (cpu_mdesc_init(mod, chip) != 0) { topo_mod_dprintf(mod, "failed to get cpus from the PRI/MD\n"); topo_mod_free(mod, chip, sizeof (md_info_t)); return (-1); } topo_mod_setspecific(mod, (void *)chip); if (topo_mod_register(mod, &cpu_info, TOPO_VERSION) != 0) { topo_mod_dprintf(mod, "failed to register %s: %s\n", PLATFORM_CPU_NAME, topo_mod_errmsg(mod)); cpu_mdesc_fini(mod, chip); topo_mod_free(mod, chip, sizeof (md_info_t)); return (-1); } topo_mod_dprintf(mod, "%s enumerator inited\n", PLATFORM_CPU_NAME); return (0); } void _topo_fini(topo_mod_t *mod) { md_info_t *chip; chip = (md_info_t *)topo_mod_getspecific(mod); cpu_mdesc_fini(mod, chip); topo_mod_free(mod, chip, sizeof (md_info_t)); topo_mod_unregister(mod); } /*ARGSUSED*/ static int cpu_present(topo_mod_t *mod, tnode_t *node, topo_version_t vers, nvlist_t *in, nvlist_t **out) { uint8_t version; uint32_t cpuid; uint64_t nvlserid; uint32_t present = 0; md_cpumap_t *mcmp; md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); /* * Get the physical cpuid */ if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || version > FM_CPU_SCHEME_VERSION || nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } /* * Find the cpuid entry * If the input nvl contains a serial number, the cpu is identified * by a tuple * Otherwise, the cpu is identified by the . */ if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) { if (cpu_read_serial(in, &nvlserid) == 0) present = nvlserid == mcmp->cpumap_serialno; else present = 1; } /* return the present status */ if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { nvlist_free(*out); return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } return (0); } /*ARGSUSED*/ static int cpu_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t vers, nvlist_t *in, nvlist_t **out) { uint8_t version; uint32_t cpuid; uint64_t nvlserid; uint32_t rval = FMD_OBJ_STATE_NOT_PRESENT; md_cpumap_t *mcmp; md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); /* * Get the physical cpuid */ if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || version > FM_CPU_SCHEME_VERSION || nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } /* * Find the cpuid entry * If the input nvl contains a serial number, the cpu is identified * by a tuple * Otherwise, the cpu is identified by the . */ if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) { if (cpu_read_serial(in, &nvlserid) == 0) rval = (nvlserid == mcmp->cpumap_serialno) ? FMD_OBJ_STATE_STILL_PRESENT : FMD_OBJ_STATE_REPLACED; else rval = FMD_OBJ_STATE_UNKNOWN; } /* return the replaced status */ if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { nvlist_free(*out); return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } return (0); } /*ARGSUSED*/ static int cpu_expand(topo_mod_t *mod, tnode_t *node, topo_version_t vers, nvlist_t *in, nvlist_t **out) { int rc; uint8_t version; uint32_t cpuid; uint64_t nvlserid; md_cpumap_t *mcmp = NULL; md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || version > FM_CPU_SCHEME_VERSION || nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } /* Find the cpuid entry */ if ((mcmp = cpu_find_cpumap(chip, cpuid)) == NULL) return (-1); if ((rc = cpu_read_serial(in, &nvlserid)) == 0) { if (nvlserid != mcmp->cpumap_serialno) return (-1); } else if (rc != ENOENT) return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); else { if ((rc = nvlist_add_uint64(in, FM_FMRI_CPU_SERIAL_ID, mcmp->cpumap_serialno)) != 0) { return (topo_mod_seterrno(mod, rc)); } } topo_mod_dprintf(mod, "nvlserid=%llX\n", nvlserid); if (mcmp != NULL && mcmp->cpumap_chipidx >= 0 && mcmp->cpumap_chipidx < chip->nprocs && chip->procs && chip->procs[mcmp->cpumap_chipidx].fru) { int len; char *str; md_fru_t *frup = chip->procs[mcmp->cpumap_chipidx].fru; /* part number + dash number */ len = (frup->part ? strlen(frup->part) : 0) + (frup->dash ? strlen(frup->dash) : 0) + 1; str = cpu_alloc(len); (void) snprintf(str, len, "%s%s", frup->part ? frup->part : MD_STR_BLANK, frup->dash ? frup->dash : MD_STR_BLANK); (void) nvlist_add_string(in, FM_FMRI_HC_PART, str); cpu_free(str, len); /* fru name */ (void) nvlist_add_string(in, FM_FMRI_CPU_CPUFRU, frup->nac ? frup->nac : MD_STR_BLANK); /* fru serial */ in->nvl_nvflag = NV_UNIQUE_NAME_TYPE; (void) nvlist_add_string(in, FM_FMRI_HC_SERIAL_ID, frup->serial ? frup->serial : MD_STR_BLANK); } return (0); } /*ARGSUSED*/ static int cpu_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t vers, nvlist_t *in, nvlist_t **out) { int rc = -1; uint8_t version; int status; uint32_t cpuid; ldom_hdl_t *lhp; uint64_t nvlserid; uint32_t present = 0; md_cpumap_t *mcmp; md_info_t *chip = (md_info_t *)topo_mod_getspecific(mod); uint32_t type = 0; if (nvlist_lookup_uint8(in, FM_VERSION, &version) != 0 || version > FM_CPU_SCHEME_VERSION || nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpuid) != 0) { return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } /* * Check the cpu presence */ if ((mcmp = cpu_find_cpumap(chip, cpuid)) != NULL) { if (cpu_read_serial(in, &nvlserid) == 0) present = nvlserid == mcmp->cpumap_serialno; else present = 1; } if (present == 0) { return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } lhp = ldom_init(cpu_alloc, cpu_free); if (lhp == NULL) { return (topo_mod_seterrno(mod, EMOD_NOMEM)); } (void) ldom_get_type(lhp, &type); status = ldom_fmri_status(lhp, in); rc = (status == P_FAULTED || (status == P_OFFLINE && ((type & LDOM_TYPE_CONTROL) != 0))); ldom_fini(lhp); /* return the unusable status */ if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, rc) != 0) { nvlist_free(*out); return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); } return (0); } static nvlist_t * cpu_fmri_create(topo_mod_t *mod, uint32_t cpuid, char *serial, uint8_t cpumask) { int err; nvlist_t *fmri; if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) return (NULL); err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION); err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpuid); err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask); if (serial != NULL) err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, serial); if (err != 0) { nvlist_free(fmri); (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); return (NULL); } return (fmri); } static tnode_t * cpu_tnode_create(topo_mod_t *mod, tnode_t *parent, const char *name, topo_instance_t i, char *serial, void *priv) { int cpu_mask = 0; nvlist_t *fmri; tnode_t *ntn; fmri = cpu_fmri_create(mod, i, serial, cpu_mask); if (fmri == NULL) { topo_mod_dprintf(mod, "Unable to make nvlist for %s bind: %s.\n", name, topo_mod_errmsg(mod)); return (NULL); } ntn = topo_node_bind(mod, parent, name, i, fmri); if (ntn == NULL) { topo_mod_dprintf(mod, "topo_node_bind (%s%d/%s%d) failed: %s\n", topo_node_name(parent), topo_node_instance(parent), name, i, topo_strerror(topo_mod_errno(mod))); nvlist_free(fmri); return (NULL); } nvlist_free(fmri); topo_node_setspecific(ntn, priv); return (ntn); } /*ARGSUSED*/ static int cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name, md_info_t *chip) { int i; int min = -1; int max = -1; int nerr = 0; int pid; char sbuf[32]; tnode_t *cnode; topo_mod_dprintf(mod, "enumerating cpus\n"); /* * find the min/max id of cpus per this cmp and create a cpu range */ for (i = 0; i < chip->ncpus; i++) { if ((min < 0) || (chip->cpus[i].cpumap_pid < min)) min = chip->cpus[i].cpumap_pid; if ((max < 0) || (chip->cpus[i].cpumap_pid > max)) max = chip->cpus[i].cpumap_pid; } if (min < 0 || max < 0) return (-1); topo_node_range_destroy(rnode, name); if (topo_node_range_create(mod, rnode, name, 0, max+1) < 0) { topo_mod_dprintf(mod, "failed to create cpu range[0,%d]: %s\n", max, topo_mod_errmsg(mod)); return (-1); } /* * Create the cpu nodes */ for (i = 0; i < chip->ncpus; i++) { (void) snprintf(sbuf, sizeof (sbuf), "%llx", chip->cpus[i].cpumap_serialno); /* physical cpuid */ pid = chip->cpus[i].cpumap_pid; cnode = cpu_tnode_create(mod, rnode, name, (topo_instance_t)pid, sbuf, NULL); if (cnode == NULL) { topo_mod_dprintf(mod, "failed to create a cpu=%d node: %s\n", pid, topo_mod_errmsg(mod)); nerr++; continue; } } if (nerr != 0) (void) topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM); return (0); } /*ARGSUSED*/ 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) { topo_mod_dprintf(mod, "%s enumerating %s\n", PLATFORM_CPU_NAME, name); if (topo_method_register(mod, rnode, cpu_methods) < 0) { topo_mod_dprintf(mod, "topo_method_register failed: %s\n", topo_strerror(topo_mod_errno(mod))); return (-1); } if (strcmp(name, CPU_NODE_NAME) == 0) return (cpu_create(mod, rnode, name, (md_info_t *)arg)); return (0); } /*ARGSUSED*/ static void cpu_release(topo_mod_t *mod, tnode_t *node) { topo_method_unregister_all(mod, node); }