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 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <errno.h> 30 #include <limits.h> 31 #include <strings.h> 32 #include <unistd.h> 33 #include <fm/topo_mod.h> 34 #include <sys/fm/protocol.h> 35 36 #include <topo_method.h> 37 #include <cpu.h> 38 39 static int cpu_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 40 topo_instance_t, void *, void *); 41 static void cpu_release(topo_mod_t *, tnode_t *); 42 static int cpu_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 43 nvlist_t **); 44 static int cpu_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 45 nvlist_t **); 46 static int cpu_fmri_asru(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *, 47 nvlist_t **); 48 static nvlist_t *fmri_create(topo_mod_t *, uint32_t, uint8_t, char *); 49 50 static const topo_method_t cpu_methods[] = { 51 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 52 TOPO_STABILITY_INTERNAL, cpu_nvl2str }, 53 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 54 TOPO_STABILITY_INTERNAL, cpu_str2nvl }, 55 { TOPO_METH_ASRU_COMPUTE, TOPO_METH_ASRU_COMPUTE_DESC, 56 TOPO_METH_ASRU_COMPUTE_VERSION, TOPO_STABILITY_INTERNAL, 57 cpu_fmri_asru }, 58 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 59 TOPO_STABILITY_INTERNAL, cpu_fmri_asru }, 60 { NULL } 61 }; 62 63 static const topo_modops_t cpu_ops = 64 { cpu_enum, cpu_release }; 65 66 static const topo_modinfo_t cpu_info = 67 { "cpu", FM_FMRI_SCHEME_CPU, CPU_VERSION, &cpu_ops }; 68 69 int 70 cpu_init(topo_mod_t *mod, topo_version_t version) 71 { 72 cpu_node_t *cpuip; 73 74 if (getenv("TOPOCPUDEBUG")) 75 topo_mod_setdebug(mod); 76 topo_mod_dprintf(mod, "initializing cpu builtin\n"); 77 78 if (version != CPU_VERSION) 79 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 80 81 if ((cpuip = topo_mod_zalloc(mod, sizeof (cpu_node_t))) == NULL) 82 return (topo_mod_seterrno(mod, EMOD_NOMEM)); 83 84 if ((cpuip->cn_kc = kstat_open()) == NULL) { 85 topo_mod_dprintf(mod, "kstat_open failed: %s\n", 86 strerror(errno)); 87 topo_mod_free(mod, cpuip, sizeof (cpu_node_t)); 88 return (-1); 89 } 90 91 cpuip->cn_ncpustats = sysconf(_SC_CPUID_MAX); 92 if ((cpuip->cn_cpustats = topo_mod_zalloc(mod, ( 93 cpuip->cn_ncpustats + 1) * sizeof (kstat_t *))) == NULL) { 94 (void) kstat_close(cpuip->cn_kc); 95 topo_mod_free(mod, cpuip, sizeof (cpu_node_t)); 96 return (-1); 97 } 98 99 if (topo_mod_register(mod, &cpu_info, TOPO_VERSION) != 0) { 100 topo_mod_dprintf(mod, "failed to register cpu_info: " 101 "%s\n", topo_mod_errmsg(mod)); 102 topo_mod_free(mod, cpuip->cn_cpustats, 103 (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *)); 104 (void) kstat_close(cpuip->cn_kc); 105 topo_mod_free(mod, cpuip, sizeof (cpu_node_t)); 106 return (-1); 107 } 108 109 topo_mod_setspecific(mod, (void *)cpuip); 110 111 return (0); 112 } 113 114 void 115 cpu_fini(topo_mod_t *mod) 116 { 117 cpu_node_t *cpuip; 118 119 cpuip = topo_mod_getspecific(mod); 120 121 if (cpuip->cn_cpustats != NULL) 122 topo_mod_free(mod, cpuip->cn_cpustats, 123 (cpuip->cn_ncpustats + 1) * sizeof (kstat_t *)); 124 125 (void) kstat_close(cpuip->cn_kc); 126 topo_mod_free(mod, cpuip, sizeof (cpu_node_t)); 127 128 topo_mod_unregister(mod); 129 } 130 131 static int 132 cpu_kstat_init(cpu_node_t *cpuip, int i) 133 { 134 kstat_t *ksp; 135 136 if (cpuip->cn_cpustats[i] == NULL) { 137 if ((ksp = kstat_lookup(cpuip->cn_kc, "cpu_info", i, NULL)) == 138 NULL || kstat_read(cpuip->cn_kc, ksp, NULL) < 0) 139 return (-1); 140 141 cpuip->cn_cpustats[i] = ksp; 142 } else { 143 ksp = cpuip->cn_cpustats[i]; 144 } 145 146 return (ksp->ks_instance); 147 } 148 149 /*ARGSUSED*/ 150 static int 151 cpu_create(topo_mod_t *mod, tnode_t *rnode, const char *name, 152 topo_instance_t min, topo_instance_t max, cpu_node_t *cpuip) 153 { 154 int i; 155 processorid_t cpu_id; 156 char *s, sbuf[21]; 157 kstat_named_t *ks; 158 nvlist_t *fmri; 159 160 for (i = 0; i <= cpuip->cn_ncpustats; i++) { 161 162 if ((cpu_id = cpu_kstat_init(cpuip, i)) < 0) 163 continue; 164 165 if ((ks = kstat_data_lookup(cpuip->cn_cpustats[i], 166 "device_ID")) != NULL) { 167 (void) snprintf(sbuf, 21, "%llX", ks->value.ui64); 168 s = sbuf; 169 } else { 170 s = NULL; 171 } 172 173 if ((fmri = fmri_create(mod, cpu_id, 0, s)) == NULL) 174 continue; 175 (void) topo_node_bind(mod, rnode, name, cpu_id, fmri); 176 nvlist_free(fmri); 177 } 178 179 return (0); 180 } 181 182 183 /*ARGSUSED*/ 184 static int 185 cpu_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 186 topo_instance_t min, topo_instance_t max, void *arg, void *notused2) 187 { 188 cpu_node_t *cpuip = (cpu_node_t *)arg; 189 190 if (topo_node_range_create(mod, pnode, "cpu", 0, 191 cpuip->cn_ncpustats + 1) < 0) { 192 topo_mod_dprintf(mod, "cpu enumeration failed to create cpu " 193 "range [0-%d]: %s\n", cpuip->cn_ncpustats + 1, 194 topo_mod_errmsg(mod)); 195 return (-1); /* mod_errno set */ 196 } 197 198 (void) topo_method_register(mod, pnode, cpu_methods); 199 200 return (cpu_create(mod, pnode, name, min, max, cpuip)); 201 } 202 203 static void 204 cpu_release(topo_mod_t *mod, tnode_t *node) 205 { 206 topo_method_unregister_all(mod, node); 207 } 208 209 ssize_t 210 fmri_nvl2str(nvlist_t *nvl, uint8_t version, char *buf, size_t buflen) 211 { 212 int rc; 213 uint32_t cpuid; 214 uint64_t serint; 215 char *serstr; 216 217 if (version == CPU_SCHEME_VERSION0) { 218 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0 || 219 nvlist_lookup_uint64(nvl, FM_FMRI_CPU_SERIAL_ID, &serint) 220 != 0) 221 return (0); 222 223 return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%llX", 224 FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID, 225 (u_longlong_t)serint)); 226 } else if (version == CPU_SCHEME_VERSION1) { 227 if (nvlist_lookup_uint32(nvl, FM_FMRI_CPU_ID, &cpuid) != 0) 228 return (0); 229 230 /* 231 * Serial number is an optional element 232 */ 233 if ((rc = nvlist_lookup_string(nvl, FM_FMRI_CPU_SERIAL_ID, 234 &serstr)) != 0) 235 if (rc == ENOENT) 236 return (snprintf(buf, buflen, "cpu:///%s=%u", 237 FM_FMRI_CPU_ID, cpuid)); 238 else 239 return (0); 240 else 241 return (snprintf(buf, buflen, "cpu:///%s=%u/%s=%s", 242 FM_FMRI_CPU_ID, cpuid, FM_FMRI_CPU_SERIAL_ID, 243 serstr)); 244 245 } else { 246 return (0); 247 } 248 } 249 250 /*ARGSUSED*/ 251 static int 252 cpu_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 253 nvlist_t *in, nvlist_t **out) 254 { 255 uint8_t fver; 256 ssize_t len; 257 char *name; 258 259 if (version > TOPO_METH_NVL2STR_VERSION) 260 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 261 262 if (nvlist_lookup_uint8(in, FM_VERSION, &fver) != 0) 263 return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION)); 264 265 if ((len = fmri_nvl2str(in, fver, NULL, 0)) == 0 || 266 (name = topo_mod_alloc(mod, len + 1)) == NULL || 267 fmri_nvl2str(in, fver, name, len + 1) == 0) 268 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 269 270 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) < 0) { 271 topo_mod_free(mod, name, len + 1); 272 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 273 } 274 275 if (nvlist_add_string(*out, "fmri-string", name) != 0) { 276 topo_mod_free(mod, name, len + 1); 277 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 278 } 279 topo_mod_free(mod, name, len + 1); 280 281 return (0); 282 } 283 284 /*ARGSUSED*/ 285 static int 286 cpu_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 287 nvlist_t *in, nvlist_t **out) 288 { 289 int err; 290 ulong_t cpuid; 291 char *str, *s, *end; 292 char *serial = NULL; 293 nvlist_t *fmri; 294 295 if (version > TOPO_METH_STR2NVL_VERSION) 296 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 297 298 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 299 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 300 301 /* We're expecting a string version of a cpu scheme FMRI */ 302 if (strncmp(str, "cpu:///", 7) != 0) 303 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 304 305 s = strchr(str + 7, '='); 306 if (s == NULL) 307 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 308 309 ++s; 310 cpuid = strtoul(s, &end, 0); 311 312 if (cpuid == ULONG_MAX && errno == ERANGE) 313 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 314 315 if (*(s = end) == '/') { 316 s = strchr(s, '='); 317 ++s; 318 serial = s; 319 } 320 321 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 322 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 323 324 err = nvlist_add_uint8(fmri, FM_VERSION, CPU_SCHEME_VERSION1); 325 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); 326 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, (uint32_t)cpuid); 327 err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, 0); 328 if (serial != NULL) 329 err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, 330 serial); 331 332 if (err != 0) { 333 nvlist_free(fmri); 334 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 335 } 336 *out = fmri; 337 338 return (0); 339 } 340 341 static nvlist_t * 342 fmri_create(topo_mod_t *mod, uint32_t cpu_id, uint8_t cpumask, char *s) 343 { 344 int err; 345 nvlist_t *fmri; 346 347 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) { 348 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 349 return (NULL); 350 } 351 352 err = nvlist_add_uint8(fmri, FM_VERSION, FM_CPU_SCHEME_VERSION); 353 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_CPU); 354 err |= nvlist_add_uint32(fmri, FM_FMRI_CPU_ID, cpu_id); 355 err |= nvlist_add_uint8(fmri, FM_FMRI_CPU_MASK, cpumask); 356 if (s != NULL) 357 err |= nvlist_add_string(fmri, FM_FMRI_CPU_SERIAL_ID, s); 358 if (err != 0) { 359 nvlist_free(fmri); 360 (void) topo_mod_seterrno(mod, EMOD_FMRI_NVL); 361 return (NULL); 362 } 363 364 return (fmri); 365 } 366 367 /*ARGSUSED*/ 368 static int 369 cpu_fmri_asru(topo_mod_t *mod, tnode_t *node, topo_version_t version, 370 nvlist_t *in, nvlist_t **out) 371 { 372 int rc; 373 uint32_t cpu_id; 374 uint8_t cpumask = 0; 375 char *serial = NULL; 376 377 if ((rc = nvlist_lookup_uint32(in, FM_FMRI_CPU_ID, &cpu_id)) != 0) { 378 if (rc == ENOENT) 379 return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL)); 380 else 381 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 382 } 383 384 (void) nvlist_lookup_string(in, FM_FMRI_CPU_SERIAL_ID, &serial); 385 (void) nvlist_lookup_uint8(in, FM_FMRI_CPU_MASK, &cpumask); 386 387 *out = fmri_create(mod, cpu_id, cpumask, serial); 388 389 return (0); 390 } 391