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 2007 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 <limits.h> 30 #include <strings.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <stdio.h> 34 #include <alloca.h> 35 #include <devid.h> 36 #include <libnvpair.h> 37 #include <fm/topo_mod.h> 38 #include <sys/fm/protocol.h> 39 40 #include <topo_method.h> 41 #include <topo_subr.h> 42 #include <dev.h> 43 44 static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 45 topo_instance_t, void *, void *); 46 static void dev_release(topo_mod_t *, tnode_t *); 47 static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 48 nvlist_t *, nvlist_t **); 49 static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 50 nvlist_t *, nvlist_t **); 51 static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 52 nvlist_t *, nvlist_t **); 53 static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, 54 nvlist_t *, nvlist_t **); 55 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, 56 nvlist_t *, nvlist_t **); 57 58 static const topo_method_t dev_methods[] = { 59 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 60 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str }, 61 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 62 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, 63 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 64 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, 65 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 66 TOPO_STABILITY_INTERNAL, dev_fmri_present }, 67 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 68 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 69 dev_fmri_unusable }, 70 { NULL } 71 }; 72 73 static const topo_modops_t dev_ops = 74 { dev_enum, dev_release }; 75 static const topo_modinfo_t dev_info = 76 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops }; 77 78 int 79 dev_init(topo_mod_t *mod, topo_version_t version) 80 { 81 if (getenv("TOPOHCDEBUG")) 82 topo_mod_setdebug(mod); 83 topo_mod_dprintf(mod, "initializing dev builtin\n"); 84 85 if (version != DEV_VERSION) 86 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 87 88 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) { 89 topo_mod_dprintf(mod, "failed to register dev_info: " 90 "%s\n", topo_mod_errmsg(mod)); 91 return (-1); 92 } 93 94 return (0); 95 } 96 97 void 98 dev_fini(topo_mod_t *mod) 99 { 100 topo_mod_unregister(mod); 101 } 102 103 /*ARGSUSED*/ 104 static int 105 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 106 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 107 { 108 (void) topo_method_register(mod, pnode, dev_methods); 109 return (0); 110 } 111 112 static void 113 dev_release(topo_mod_t *mod, tnode_t *node) 114 { 115 topo_method_unregister_all(mod, node); 116 } 117 118 static ssize_t 119 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 120 { 121 nvlist_t *anvl = NULL; 122 uint8_t version; 123 ssize_t size = 0; 124 char *devid = NULL; 125 char *devpath = NULL; 126 char *achas = NULL; 127 char *adom = NULL; 128 char *aprod = NULL; 129 char *asrvr = NULL; 130 char *ahost = NULL; 131 int more_auth = 0; 132 int err; 133 134 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 135 version > FM_DEV_SCHEME_VERSION) 136 return (-1); 137 138 /* Get authority, if present */ 139 err = nvlist_lookup_nvlist(nvl, FM_FMRI_AUTHORITY, &anvl); 140 if (err != 0 && err != ENOENT) 141 return (-1); 142 143 /* Get devid, if present */ 144 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 145 if (err != 0 && err != ENOENT) 146 return (-1); 147 148 /* There must be a device path present */ 149 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 150 if (err != 0 || devpath == NULL) 151 return (-1); 152 153 if (anvl != NULL) { 154 (void) nvlist_lookup_string(anvl, 155 FM_FMRI_AUTH_PRODUCT, &aprod); 156 (void) nvlist_lookup_string(anvl, 157 FM_FMRI_AUTH_CHASSIS, &achas); 158 (void) nvlist_lookup_string(anvl, 159 FM_FMRI_AUTH_DOMAIN, &adom); 160 (void) nvlist_lookup_string(anvl, 161 FM_FMRI_AUTH_SERVER, &asrvr); 162 (void) nvlist_lookup_string(anvl, 163 FM_FMRI_AUTH_HOST, &ahost); 164 if (aprod != NULL) 165 more_auth++; 166 if (achas != NULL) 167 more_auth++; 168 if (adom != NULL) 169 more_auth++; 170 if (asrvr != NULL) 171 more_auth++; 172 if (ahost != NULL) 173 more_auth++; 174 } 175 176 /* dev:// */ 177 topo_fmristr_build(&size, 178 buf, buflen, FM_FMRI_SCHEME_DEV, NULL, "://"); 179 180 /* authority, if any */ 181 if (aprod != NULL) 182 topo_fmristr_build(&size, 183 buf, buflen, aprod, FM_FMRI_AUTH_PRODUCT "=", 184 --more_auth > 0 ? "," : NULL); 185 if (achas != NULL) 186 topo_fmristr_build(&size, 187 buf, buflen, achas, FM_FMRI_AUTH_CHASSIS "=", 188 --more_auth > 0 ? "," : NULL); 189 if (adom != NULL) 190 topo_fmristr_build(&size, 191 buf, buflen, adom, FM_FMRI_AUTH_DOMAIN "=", 192 --more_auth > 0 ? "," : NULL); 193 if (asrvr != NULL) 194 topo_fmristr_build(&size, 195 buf, buflen, asrvr, FM_FMRI_AUTH_SERVER "=", 196 --more_auth > 0 ? "," : NULL); 197 if (ahost != NULL) 198 topo_fmristr_build(&size, 199 buf, buflen, ahost, FM_FMRI_AUTH_HOST "=", 200 NULL); 201 202 /* device-id part, topo_fmristr_build does nothing if devid is NULL */ 203 topo_fmristr_build(&size, 204 buf, buflen, devid, "/:" FM_FMRI_DEV_ID "=", NULL); 205 206 /* device-path part */ 207 topo_fmristr_build(&size, buf, buflen, devpath, "/", NULL); 208 209 return (size); 210 } 211 212 /*ARGSUSED*/ 213 static int 214 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 215 nvlist_t *nvl, nvlist_t **out) 216 { 217 ssize_t len; 218 char *name = NULL; 219 nvlist_t *fmristr; 220 221 if (version > TOPO_METH_NVL2STR_VERSION) 222 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 223 224 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 225 (name = topo_mod_alloc(mod, len + 1)) == NULL || 226 fmri_nvl2str(nvl, name, len + 1) == 0) { 227 if (name != NULL) 228 topo_mod_free(mod, name, len + 1); 229 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 230 } 231 232 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 233 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 234 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 235 topo_mod_free(mod, name, len + 1); 236 nvlist_free(fmristr); 237 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 238 } 239 topo_mod_free(mod, name, len + 1); 240 *out = fmristr; 241 242 return (0); 243 } 244 245 /*ARGSUSED*/ 246 static int 247 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 248 nvlist_t *in, nvlist_t **out) 249 { 250 nvlist_t *fmri; 251 char *devpath; 252 char *devid = NULL; 253 char *str; 254 int err; 255 256 if (version > TOPO_METH_STR2NVL_VERSION) 257 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 258 259 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 260 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 261 262 /* We're expecting a string version of a dev scheme FMRI */ 263 if (strncmp(str, "dev:///", 7) != 0) 264 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 265 266 devpath = str + 7; 267 if (strncmp(devpath, ":" FM_FMRI_DEV_ID "=", 7) == 0) { 268 char *n; 269 int len; 270 271 n = strchr(devpath + 7, '/'); 272 if (n == NULL) 273 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 274 len = n - (devpath + 7); 275 devid = alloca(len + 1); 276 (void) memcpy(devid, devpath + 7, len); 277 devid[len] = 0; 278 devpath = n + 1; 279 } 280 281 /* the device-path should start with a slash */ 282 if (*devpath != '/') 283 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 284 285 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 286 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 287 288 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); 289 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 290 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); 291 if (devid != NULL) 292 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); 293 294 if (err != 0) { 295 nvlist_free(fmri); 296 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 297 } 298 *out = fmri; 299 300 return (0); 301 } 302 303 /* 304 * callback routine for di_walk_minor() 305 */ 306 struct walkinfo { 307 int matched; 308 const char *path; 309 const char *devid; 310 int len; 311 }; 312 313 static int 314 dev_match(di_node_t node, void *arg) 315 { 316 struct walkinfo *wip = (struct walkinfo *)arg; 317 char *path = di_devfs_path(node); 318 ddi_devid_t devid; 319 char *devidstr = NULL; 320 321 if (path != NULL && strncmp(path, wip->path, wip->len) == 0) { 322 /* 323 * If we found the match we were looking for, check to see if a 324 * devid was specified. If so, then this must also match. 325 */ 326 if (wip->devid) { 327 if ((devid = di_devid(node)) == NULL || 328 (devidstr = devid_str_encode(devid, NULL)) == NULL) 329 goto out; 330 331 if (strcmp(devidstr, wip->devid) != 0) 332 goto out; 333 } 334 335 /* 336 * Either the devid wasn't specified, or it correctly matched. 337 * In this case, indicate a successful match and terminate the 338 * walk. 339 */ 340 wip->matched = 1; 341 di_devfs_path_free(path); 342 devid_str_free(devidstr); 343 return (DI_WALK_TERMINATE); 344 } 345 346 out: 347 if (path != NULL) 348 di_devfs_path_free(path); 349 devid_str_free(devidstr); 350 return (DI_WALK_CONTINUE); 351 } 352 353 /*ARGSUSED*/ 354 static int 355 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 356 nvlist_t *in, nvlist_t **out) 357 { 358 di_node_t parent; 359 uint8_t fmversion; 360 char *devpath = NULL; 361 char *parentpath; 362 char *cp; 363 struct walkinfo walkinfo; 364 uint32_t present; 365 char *devid = NULL; 366 367 if (version > TOPO_METH_PRESENT_VERSION) 368 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 369 370 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 371 fmversion > FM_DEV_SCHEME_VERSION || 372 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 373 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 374 375 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 376 377 if (devpath == NULL || (walkinfo.len = strlen(devpath)) == 0) 378 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 379 380 /* strip off last component of path */ 381 parentpath = alloca(walkinfo.len + 1); 382 (void) strcpy(parentpath, devpath); 383 if ((cp = strrchr(parentpath, '/')) == NULL) 384 parentpath = "/"; 385 else 386 *cp = '\0'; 387 388 /* if the result is an empty path, start walk at "/" */ 389 if (*parentpath == '\0') 390 parentpath = "/"; 391 392 /* 393 * DINFOFORCE is required for the devid to be present. 394 */ 395 if ((parent = di_init(parentpath, 396 DINFOSUBTREE | DINFOFORCE)) == DI_NODE_NIL) { 397 if (errno != ENXIO) 398 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 399 present = 0; 400 } else { 401 walkinfo.matched = 0; 402 walkinfo.path = devpath; 403 walkinfo.devid = devid; 404 (void) di_walk_node(parent, 405 DI_WALK_SIBFIRST, (void *)&walkinfo, dev_match); 406 di_fini(parent); 407 408 present = walkinfo.matched; 409 } 410 411 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 412 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 413 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 414 nvlist_free(*out); 415 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 416 } 417 418 return (0); 419 } 420 421 /*ARGSUSED*/ 422 static int 423 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 424 nvlist_t *in, nvlist_t **out) 425 { 426 di_node_t dnode; 427 uint8_t fmversion; 428 char *devpath = NULL; 429 uint32_t unusable; 430 uint_t state; 431 432 if (version > TOPO_METH_PRESENT_VERSION) 433 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 434 435 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 436 fmversion > FM_DEV_SCHEME_VERSION || 437 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 438 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 439 440 if (devpath == NULL) 441 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 442 443 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 444 if (errno != ENXIO) 445 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 446 unusable = 1; 447 } else { 448 uint_t retired = di_retired(dnode); 449 state = di_state(dnode); 450 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 451 DI_BUS_QUIESCED | DI_BUS_DOWN))) 452 unusable = 1; 453 else 454 unusable = 0; 455 di_fini(dnode); 456 } 457 458 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 459 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 460 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 461 nvlist_free(*out); 462 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 463 } 464 465 return (0); 466 } 467 468 static nvlist_t * 469 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) 470 { 471 nvlist_t *out = NULL; 472 int e; 473 474 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 475 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 476 return (NULL); 477 } 478 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 479 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION); 480 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path); 481 482 if (id != NULL) 483 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id); 484 485 if (e == 0) 486 return (out); 487 488 topo_mod_dprintf(mp, "construction of dev nvl failed"); 489 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 490 nvlist_free(out); 491 return (NULL); 492 } 493 494 /*ARGSUSED*/ 495 static int 496 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 497 nvlist_t *in, nvlist_t **out) 498 { 499 nvlist_t *args = NULL; 500 char *path, *id = NULL; 501 502 if (version > TOPO_METH_FMRI_VERSION) 503 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 504 505 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 506 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) { 507 topo_mod_dprintf(mp, "no path string in method argument\n"); 508 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 509 } 510 511 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id); 512 513 if ((*out = dev_fmri_create(mp, id, path)) == NULL) 514 return (-1); 515 return (0); 516 } 517