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