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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2023 Oxide Computer Company 25 */ 26 27 #include <limits.h> 28 #include <strings.h> 29 #include <string.h> 30 #include <unistd.h> 31 #include <stdio.h> 32 #include <alloca.h> 33 #include <devid.h> 34 #include <sys/stat.h> 35 #include <libnvpair.h> 36 #include <fm/topo_mod.h> 37 #include <fm/fmd_fmri.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_replaced(topo_mod_t *, tnode_t *, topo_version_t, 56 nvlist_t *, nvlist_t **); 57 static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, 58 nvlist_t *, nvlist_t **); 59 static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t, 60 nvlist_t *, nvlist_t **); 61 62 static const topo_method_t dev_methods[] = { 63 { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 64 TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str }, 65 { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 66 TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, 67 { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 68 TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, 69 { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 70 TOPO_STABILITY_INTERNAL, dev_fmri_present }, 71 { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 72 TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 73 dev_fmri_replaced }, 74 { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 75 TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 76 dev_fmri_unusable }, 77 { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 78 TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 79 dev_fmri_service_state }, 80 { NULL } 81 }; 82 83 static const topo_modops_t dev_ops = 84 { dev_enum, dev_release }; 85 static const topo_modinfo_t dev_info = 86 { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops }; 87 88 int 89 dev_init(topo_mod_t *mod, topo_version_t version) 90 { 91 if (getenv("TOPOHCDEBUG")) 92 topo_mod_setdebug(mod); 93 topo_mod_dprintf(mod, "initializing dev builtin\n"); 94 95 if (version != DEV_VERSION) 96 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 97 98 if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) { 99 topo_mod_dprintf(mod, "failed to register dev_info: " 100 "%s\n", topo_mod_errmsg(mod)); 101 return (-1); 102 } 103 104 return (0); 105 } 106 107 void 108 dev_fini(topo_mod_t *mod) 109 { 110 topo_mod_unregister(mod); 111 } 112 113 static int 114 dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 115 topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 116 { 117 /* 118 * Methods are registered, but there is no enumeration. Should 119 * enumeration be added be sure to cater for global vs non-global 120 * zones. 121 */ 122 (void) topo_method_register(mod, pnode, dev_methods); 123 return (0); 124 } 125 126 static void 127 dev_release(topo_mod_t *mod, tnode_t *node) 128 { 129 topo_method_unregister_all(mod, node); 130 } 131 132 static size_t 133 fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 134 { 135 char *devid = NULL, *tpl0id = NULL; 136 char *devpath = NULL; 137 size_t size = 0; 138 uint8_t version; 139 int err; 140 141 if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 142 version > FM_DEV_SCHEME_VERSION) 143 return (0); 144 145 /* Get devid, if present */ 146 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 147 if (err != 0 && err != ENOENT) 148 return (0); 149 150 /* Get target-port-l0id, if present */ 151 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id); 152 if (err != 0 && err != ENOENT) 153 return (0); 154 155 /* There must be a device path present */ 156 err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 157 if (err != 0 || devpath == NULL) 158 return (0); 159 160 /* 161 * dev:/// 162 * 163 * The dev scheme does not render fmri authority information 164 * in the string form of an fmri. It is meaningless to 165 * transmit a dev scheme fmri outside of the immediate fault 166 * manager. 167 */ 168 if (!topo_fmristr_build(&size, buf, buflen, 169 FM_FMRI_SCHEME_DEV, NULL, ":///")) { 170 return (0); 171 } 172 173 /* device-id part, topo_fmristr_build does nothing if devid is NULL */ 174 if (!topo_fmristr_build(&size, buf, buflen, 175 devid, ":" FM_FMRI_DEV_ID "=", NULL)) { 176 return (0); 177 } 178 179 /* target-port-l0id part */ 180 if (!topo_fmristr_build(&size, buf, buflen, 181 tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL)) { 182 return (0); 183 } 184 185 /* 186 * device-path part; the devpath should always start with a / 187 * so you'd think we don't need to add a further / prefix here; 188 * however past implementation has always added the / if 189 * there is a devid component so we continue to do that 190 * so strings match exactly as before. So we can have: 191 * 192 * dev:////pci@0,0/... 193 * dev:///<devid-and-tpl0>//pci@0,0/... 194 * 195 * where <devid-and-tpl0> = 196 * [:devid=<devid>][:target-port-l0id=<tpl0>] 197 */ 198 if (!topo_fmristr_build(&size, buf, buflen, 199 devpath, devid || tpl0id ? "/" : NULL, NULL)) { 200 return (0); 201 } 202 203 return (size); 204 } 205 206 static int 207 dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 208 nvlist_t *nvl, nvlist_t **out) 209 { 210 size_t len; 211 char *name = NULL; 212 nvlist_t *fmristr; 213 214 if (version > TOPO_METH_NVL2STR_VERSION) 215 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 216 217 if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 218 (name = topo_mod_alloc(mod, len + 1)) == NULL || 219 fmri_nvl2str(nvl, name, len + 1) == 0) { 220 if (name != NULL) 221 topo_mod_free(mod, name, len + 1); 222 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 223 } 224 225 if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 226 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 227 if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 228 topo_mod_free(mod, name, len + 1); 229 nvlist_free(fmristr); 230 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 231 } 232 topo_mod_free(mod, name, len + 1); 233 *out = fmristr; 234 235 return (0); 236 } 237 238 static int 239 dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 240 nvlist_t *in, nvlist_t **out) 241 { 242 char *cur, *devid = NULL, *tpl0id = NULL; 243 char *str, *strcp; 244 nvlist_t *fmri; 245 char *devpath; 246 size_t len; 247 int err; 248 249 if (version > TOPO_METH_STR2NVL_VERSION) 250 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 251 252 if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 253 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 254 255 len = strlen(str); 256 257 /* 258 * We're expecting a string version of a dev scheme FMRI, and 259 * no fmri authority information. 260 * 261 * The shortest legal string would be "dev:////" (len 8) for a string 262 * with no FMRI auth info, no devid or target-port-l0id and 263 * an empty devpath string. 264 */ 265 if (len < 8 || strncmp(str, "dev:///", 7) != 0) 266 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 267 268 strcp = alloca(len + 1); 269 (void) memcpy(strcp, str, len); 270 strcp[len] = '\0'; 271 cur = strcp + 7; /* already parsed "dev:///" */ 272 273 /* 274 * If the first character after the "/" that terminates the (empty) 275 * fmri authority is a colon then we have devid and/or target-port-l0id 276 * info. They could be in either order. 277 * 278 * If not a colon then it must be the / that begins the devpath. 279 */ 280 if (*cur == ':') { 281 char *eos, *part[2]; 282 int i; 283 /* 284 * Look ahead to the "/" that starts the devpath. If not 285 * found or if straight after the : then we're busted. 286 */ 287 eos = devpath = strchr(cur, '/'); 288 if (devpath == NULL || devpath == cur + 1) 289 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 290 291 part[0] = ++cur; 292 293 /* 294 * Replace the initial "/" of the devpath with a NUL 295 * to terminate the string before it. We'll undo this 296 * before rendering devpath. 297 */ 298 *eos = '\0'; 299 300 /* 301 * We should now have a NUL-terminated string matching 302 * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :) 303 * Look for a second colon; if found there must be space 304 * after it for the additional component, but no more colons. 305 */ 306 if ((part[1] = strchr(cur, ':')) != NULL) { 307 if (part[1] + 1 == eos || 308 strchr(part[1] + 1, ':') != NULL) 309 return (topo_mod_seterrno(mod, 310 EMOD_FMRI_MALFORM)); 311 *part[1] = '\0'; /* terminate part[0] */ 312 part[1]++; 313 } 314 315 for (i = 0; i < 2; i++) { 316 char *eq; 317 318 if (!part[i]) 319 continue; 320 321 if ((eq = strchr(part[i], '=')) == NULL || 322 *(eq + 1) == '\0') 323 return (topo_mod_seterrno(mod, 324 EMOD_FMRI_MALFORM)); 325 326 *eq = '\0'; 327 if (strcmp(part[i], FM_FMRI_DEV_ID) == 0) 328 devid = eq + 1; 329 else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0) 330 tpl0id = eq + 1; 331 else 332 return (topo_mod_seterrno(mod, 333 EMOD_FMRI_MALFORM)); 334 } 335 336 if (devid == NULL && tpl0id == NULL) 337 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 338 339 cur = devpath; /* initial slash is NULled */ 340 } else if (*cur != '/') { 341 /* the device-path should start with a slash */ 342 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 343 } else { 344 devpath = cur; 345 } 346 347 if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 348 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 349 350 err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); 351 err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 352 353 if (devid != NULL) 354 err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); 355 356 if (tpl0id != NULL) 357 err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id); 358 359 if (devid != NULL || tpl0id != NULL) 360 *devpath = '/'; /* we NULed this earlier; put it back */ 361 362 /* step over repeated initial / in the devpath */ 363 while (*(devpath + 1) == '/') 364 devpath++; 365 366 err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); 367 368 if (err != 0) { 369 nvlist_free(fmri); 370 return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 371 } 372 373 *out = fmri; 374 375 return (0); 376 } 377 378 static int 379 dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 380 nvlist_t *in, nvlist_t **out) 381 { 382 uint8_t fmversion; 383 char *devpath = NULL; 384 uint32_t present; 385 char *devid = NULL, *path; 386 ddi_devid_t id; 387 ddi_devid_t matchid; 388 di_node_t dnode; 389 struct stat sb; 390 int len; 391 392 if (version > TOPO_METH_PRESENT_VERSION) 393 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 394 395 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 396 fmversion > FM_DEV_SCHEME_VERSION || 397 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 398 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 399 400 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 401 402 if (devpath == NULL || strlen(devpath) == 0) 403 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 404 405 /* 406 * stat() the device node in devfs. This will tell us if the device is 407 * present or not. Don't stat the minor, just the whole device. 408 * If the device is present and there is a devid, it must also match. 409 * so di_init that one node. No need for DINFOFORCE. 410 */ 411 len = strlen(devpath) + strlen("/devices") + 1; 412 path = topo_mod_alloc(mod, len); 413 (void) snprintf(path, len, "/devices%s", devpath); 414 if (devid == NULL) { 415 if (stat(path, &sb) != -1) 416 present = 1; 417 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 418 present = 0; 419 else { 420 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 421 present = 0; 422 else 423 present = 1; 424 di_fini(dnode); 425 } 426 } else { 427 if (stat(path, &sb) == -1) 428 present = 0; 429 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 430 present = 0; 431 else { 432 if ((id = di_devid(dnode)) == NULL || 433 devid_str_decode(devid, &matchid, NULL) != 0) 434 present = 0; 435 else { 436 if (devid_compare(id, matchid) != 0) 437 present = 0; 438 else 439 present = 1; 440 devid_free(matchid); 441 } 442 di_fini(dnode); 443 } 444 } 445 topo_mod_free(mod, path, len); 446 447 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 448 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 449 if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 450 nvlist_free(*out); 451 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 452 } 453 454 return (0); 455 } 456 457 static int 458 dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 459 nvlist_t *in, nvlist_t **out) 460 { 461 uint8_t fmversion; 462 char *devpath = NULL; 463 uint32_t rval; 464 char *devid = NULL, *path; 465 ddi_devid_t id; 466 ddi_devid_t matchid; 467 di_node_t dnode; 468 struct stat sb; 469 int len; 470 471 if (version > TOPO_METH_REPLACED_VERSION) 472 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 473 474 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 475 fmversion > FM_DEV_SCHEME_VERSION || 476 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 477 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 478 479 (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 480 481 if (devpath == NULL || strlen(devpath) == 0) 482 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 483 484 /* 485 * stat() the device node in devfs. This will tell us if the device is 486 * present or not. Don't stat the minor, just the whole device. 487 * If the device is present and there is a devid, it must also match. 488 * so di_init that one node. No need for DINFOFORCE. 489 */ 490 len = strlen(devpath) + strlen("/devices") + 1; 491 path = topo_mod_alloc(mod, len); 492 (void) snprintf(path, len, "/devices%s", devpath); 493 if (devid == NULL) { 494 if (stat(path, &sb) != -1) 495 rval = FMD_OBJ_STATE_UNKNOWN; 496 else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 497 rval = FMD_OBJ_STATE_UNKNOWN; 498 else { 499 if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 500 rval = FMD_OBJ_STATE_UNKNOWN; 501 else 502 rval = FMD_OBJ_STATE_UNKNOWN; 503 di_fini(dnode); 504 } 505 } else { 506 if (stat(path, &sb) == -1) 507 rval = FMD_OBJ_STATE_UNKNOWN; 508 else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 509 rval = FMD_OBJ_STATE_UNKNOWN; 510 else { 511 if ((id = di_devid(dnode)) == NULL || 512 devid_str_decode(devid, &matchid, NULL) != 0) 513 rval = FMD_OBJ_STATE_UNKNOWN; 514 else { 515 if (devid_compare(id, matchid) != 0) 516 rval = FMD_OBJ_STATE_REPLACED; 517 else 518 rval = FMD_OBJ_STATE_STILL_PRESENT; 519 devid_free(matchid); 520 } 521 di_fini(dnode); 522 } 523 } 524 topo_mod_free(mod, path, len); 525 526 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 527 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 528 if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 529 nvlist_free(*out); 530 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 531 } 532 533 return (0); 534 } 535 536 static int 537 dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 538 nvlist_t *in, nvlist_t **out) 539 { 540 di_node_t dnode; 541 uint8_t fmversion; 542 char *devpath = NULL; 543 uint32_t unusable; 544 uint_t state; 545 546 if (version > TOPO_METH_UNUSABLE_VERSION) 547 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 548 549 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 550 fmversion > FM_DEV_SCHEME_VERSION || 551 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 552 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 553 554 if (devpath == NULL) 555 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 556 557 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 558 if (errno != ENXIO) 559 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 560 unusable = 1; 561 } else { 562 uint_t retired = di_retired(dnode); 563 state = di_state(dnode); 564 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 565 DI_BUS_QUIESCED | DI_BUS_DOWN))) 566 unusable = 1; 567 else 568 unusable = 0; 569 di_fini(dnode); 570 } 571 572 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 573 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 574 if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 575 nvlist_free(*out); 576 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 577 } 578 579 return (0); 580 } 581 582 static int 583 dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 584 nvlist_t *in, nvlist_t **out) 585 { 586 di_node_t dnode; 587 uint8_t fmversion; 588 char *devpath = NULL; 589 uint32_t service_state; 590 uint_t state; 591 592 if (version > TOPO_METH_SERVICE_STATE_VERSION) 593 return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 594 595 if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 596 fmversion > FM_DEV_SCHEME_VERSION || 597 nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 598 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 599 600 if (devpath == NULL) 601 return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 602 603 if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 604 if (errno != ENXIO) 605 return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 606 service_state = FMD_SERVICE_STATE_UNUSABLE; 607 } else { 608 uint_t retired = di_retired(dnode); 609 state = di_state(dnode); 610 if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 611 DI_BUS_QUIESCED | DI_BUS_DOWN))) 612 service_state = FMD_SERVICE_STATE_UNUSABLE; 613 else if (state & DI_DEVICE_DEGRADED) 614 service_state = FMD_SERVICE_STATE_DEGRADED; 615 else 616 service_state = FMD_SERVICE_STATE_OK; 617 di_fini(dnode); 618 } 619 620 if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 621 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 622 if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET, 623 service_state) != 0) { 624 nvlist_free(*out); 625 return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 626 } 627 628 return (0); 629 } 630 631 static nvlist_t * 632 dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) 633 { 634 nvlist_t *out = NULL; 635 int e; 636 637 if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 638 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 639 return (NULL); 640 } 641 e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 642 e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION); 643 e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path); 644 645 if (id != NULL) 646 e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id); 647 648 if (e == 0) 649 return (out); 650 651 topo_mod_dprintf(mp, "construction of dev nvl failed"); 652 (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 653 nvlist_free(out); 654 return (NULL); 655 } 656 657 static int 658 dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 659 nvlist_t *in, nvlist_t **out) 660 { 661 nvlist_t *args = NULL; 662 char *path, *id = NULL; 663 664 if (version > TOPO_METH_FMRI_VERSION) 665 return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 666 667 if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 668 nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) { 669 topo_mod_dprintf(mp, "no path string in method argument\n"); 670 return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 671 } 672 673 (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id); 674 675 if ((*out = dev_fmri_create(mp, id, path)) == NULL) 676 return (-1); 677 return (0); 678 } 679