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