174a31ce6Stimh /* 274a31ce6Stimh * CDDL HEADER START 374a31ce6Stimh * 474a31ce6Stimh * The contents of this file are subject to the terms of the 574a31ce6Stimh * Common Development and Distribution License (the "License"). 674a31ce6Stimh * You may not use this file except in compliance with the License. 774a31ce6Stimh * 874a31ce6Stimh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 974a31ce6Stimh * or http://www.opensolaris.org/os/licensing. 1074a31ce6Stimh * See the License for the specific language governing permissions 1174a31ce6Stimh * and limitations under the License. 1274a31ce6Stimh * 1374a31ce6Stimh * When distributing Covered Code, include this CDDL HEADER in each 1474a31ce6Stimh * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1574a31ce6Stimh * If applicable, add the following below this CDDL HEADER, with the 1674a31ce6Stimh * fields enclosed by brackets "[]" replaced with your own identifying 1774a31ce6Stimh * information: Portions Copyright [yyyy] [name of copyright owner] 1874a31ce6Stimh * 1974a31ce6Stimh * CDDL HEADER END 2074a31ce6Stimh */ 2174a31ce6Stimh 2274a31ce6Stimh /* 23392e836bSGavin Maltby * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 2474a31ce6Stimh */ 2574a31ce6Stimh 2674a31ce6Stimh #include <limits.h> 2774a31ce6Stimh #include <strings.h> 2874a31ce6Stimh #include <string.h> 2974a31ce6Stimh #include <unistd.h> 3074a31ce6Stimh #include <stdio.h> 3174a31ce6Stimh #include <alloca.h> 3224db4641Seschrock #include <devid.h> 33faf8f993Sstephh #include <sys/stat.h> 3474a31ce6Stimh #include <libnvpair.h> 3574a31ce6Stimh #include <fm/topo_mod.h> 3625c6ff4bSstephh #include <fm/fmd_fmri.h> 3774a31ce6Stimh #include <sys/fm/protocol.h> 3874a31ce6Stimh 390eb822a1Scindi #include <topo_method.h> 4074a31ce6Stimh #include <topo_subr.h> 410eb822a1Scindi #include <dev.h> 4274a31ce6Stimh 4374a31ce6Stimh static int dev_enum(topo_mod_t *, tnode_t *, const char *, topo_instance_t, 440eb822a1Scindi topo_instance_t, void *, void *); 4574a31ce6Stimh static void dev_release(topo_mod_t *, tnode_t *); 4674a31ce6Stimh static int dev_fmri_nvl2str(topo_mod_t *, tnode_t *, topo_version_t, 4774a31ce6Stimh nvlist_t *, nvlist_t **); 4874a31ce6Stimh static int dev_fmri_str2nvl(topo_mod_t *, tnode_t *, topo_version_t, 4974a31ce6Stimh nvlist_t *, nvlist_t **); 5074a31ce6Stimh static int dev_fmri_create_meth(topo_mod_t *, tnode_t *, topo_version_t, 5174a31ce6Stimh nvlist_t *, nvlist_t **); 5224db4641Seschrock static int dev_fmri_present(topo_mod_t *, tnode_t *, topo_version_t, 5324db4641Seschrock nvlist_t *, nvlist_t **); 5425c6ff4bSstephh static int dev_fmri_replaced(topo_mod_t *, tnode_t *, topo_version_t, 5525c6ff4bSstephh nvlist_t *, nvlist_t **); 5624db4641Seschrock static int dev_fmri_unusable(topo_mod_t *, tnode_t *, topo_version_t, 5724db4641Seschrock nvlist_t *, nvlist_t **); 5825c6ff4bSstephh static int dev_fmri_service_state(topo_mod_t *, tnode_t *, topo_version_t, 5925c6ff4bSstephh nvlist_t *, nvlist_t **); 6074a31ce6Stimh 6174a31ce6Stimh static const topo_method_t dev_methods[] = { 6274a31ce6Stimh { TOPO_METH_NVL2STR, TOPO_METH_NVL2STR_DESC, TOPO_METH_NVL2STR_VERSION, 6374a31ce6Stimh TOPO_STABILITY_INTERNAL, dev_fmri_nvl2str }, 6474a31ce6Stimh { TOPO_METH_STR2NVL, TOPO_METH_STR2NVL_DESC, TOPO_METH_STR2NVL_VERSION, 6574a31ce6Stimh TOPO_STABILITY_INTERNAL, dev_fmri_str2nvl }, 6674a31ce6Stimh { TOPO_METH_FMRI, TOPO_METH_FMRI_DESC, TOPO_METH_FMRI_VERSION, 6774a31ce6Stimh TOPO_STABILITY_INTERNAL, dev_fmri_create_meth }, 6824db4641Seschrock { TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC, TOPO_METH_PRESENT_VERSION, 6924db4641Seschrock TOPO_STABILITY_INTERNAL, dev_fmri_present }, 7025c6ff4bSstephh { TOPO_METH_REPLACED, TOPO_METH_REPLACED_DESC, 7125c6ff4bSstephh TOPO_METH_REPLACED_VERSION, TOPO_STABILITY_INTERNAL, 7225c6ff4bSstephh dev_fmri_replaced }, 7324db4641Seschrock { TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC, 7424db4641Seschrock TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL, 7524db4641Seschrock dev_fmri_unusable }, 7625c6ff4bSstephh { TOPO_METH_SERVICE_STATE, TOPO_METH_SERVICE_STATE_DESC, 7725c6ff4bSstephh TOPO_METH_SERVICE_STATE_VERSION, TOPO_STABILITY_INTERNAL, 7825c6ff4bSstephh dev_fmri_service_state }, 7974a31ce6Stimh { NULL } 8074a31ce6Stimh }; 8174a31ce6Stimh 820eb822a1Scindi static const topo_modops_t dev_ops = 830eb822a1Scindi { dev_enum, dev_release }; 8474a31ce6Stimh static const topo_modinfo_t dev_info = 850eb822a1Scindi { "dev", FM_FMRI_SCHEME_DEV, DEV_VERSION, &dev_ops }; 8674a31ce6Stimh 870eb822a1Scindi int 880eb822a1Scindi dev_init(topo_mod_t *mod, topo_version_t version) 8974a31ce6Stimh { 900eb822a1Scindi if (getenv("TOPOHCDEBUG")) 910eb822a1Scindi topo_mod_setdebug(mod); 9274a31ce6Stimh topo_mod_dprintf(mod, "initializing dev builtin\n"); 9374a31ce6Stimh 940eb822a1Scindi if (version != DEV_VERSION) 950eb822a1Scindi return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 960eb822a1Scindi 970eb822a1Scindi if (topo_mod_register(mod, &dev_info, TOPO_VERSION) != 0) { 9874a31ce6Stimh topo_mod_dprintf(mod, "failed to register dev_info: " 9974a31ce6Stimh "%s\n", topo_mod_errmsg(mod)); 1000eb822a1Scindi return (-1); 10174a31ce6Stimh } 1020eb822a1Scindi 1030eb822a1Scindi return (0); 10474a31ce6Stimh } 10574a31ce6Stimh 10674a31ce6Stimh void 10774a31ce6Stimh dev_fini(topo_mod_t *mod) 10874a31ce6Stimh { 10974a31ce6Stimh topo_mod_unregister(mod); 11074a31ce6Stimh } 11174a31ce6Stimh 11274a31ce6Stimh /*ARGSUSED*/ 11374a31ce6Stimh static int 11474a31ce6Stimh dev_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 1150eb822a1Scindi topo_instance_t min, topo_instance_t max, void *notused1, void *notused2) 11674a31ce6Stimh { 117*f6e214c7SGavin Maltby /* 118*f6e214c7SGavin Maltby * Methods are registered, but there is no enumeration. Should 119*f6e214c7SGavin Maltby * enumeration be added be sure to cater for global vs non-global 120*f6e214c7SGavin Maltby * zones. 121*f6e214c7SGavin Maltby */ 12274a31ce6Stimh (void) topo_method_register(mod, pnode, dev_methods); 12374a31ce6Stimh return (0); 12474a31ce6Stimh } 12574a31ce6Stimh 12674a31ce6Stimh static void 12774a31ce6Stimh dev_release(topo_mod_t *mod, tnode_t *node) 12874a31ce6Stimh { 12974a31ce6Stimh topo_method_unregister_all(mod, node); 13074a31ce6Stimh } 13174a31ce6Stimh 13274a31ce6Stimh static ssize_t 13374a31ce6Stimh fmri_nvl2str(nvlist_t *nvl, char *buf, size_t buflen) 13474a31ce6Stimh { 135392e836bSGavin Maltby char *devid = NULL, *tpl0id = NULL; 13674a31ce6Stimh char *devpath = NULL; 137392e836bSGavin Maltby ssize_t size = 0; 138392e836bSGavin Maltby uint8_t version; 13974a31ce6Stimh int err; 14074a31ce6Stimh 14174a31ce6Stimh if (nvlist_lookup_uint8(nvl, FM_VERSION, &version) != 0 || 14274a31ce6Stimh version > FM_DEV_SCHEME_VERSION) 14374a31ce6Stimh return (-1); 14474a31ce6Stimh 145392e836bSGavin Maltby /* Get devid, if present */ 146392e836bSGavin Maltby err = nvlist_lookup_string(nvl, FM_FMRI_DEV_ID, &devid); 14774a31ce6Stimh if (err != 0 && err != ENOENT) 14874a31ce6Stimh return (-1); 14974a31ce6Stimh 150392e836bSGavin Maltby /* Get target-port-l0id, if present */ 151392e836bSGavin Maltby err = nvlist_lookup_string(nvl, FM_FMRI_DEV_TGTPTLUN0, &tpl0id); 15274a31ce6Stimh if (err != 0 && err != ENOENT) 15374a31ce6Stimh return (-1); 15474a31ce6Stimh 15574a31ce6Stimh /* There must be a device path present */ 15674a31ce6Stimh err = nvlist_lookup_string(nvl, FM_FMRI_DEV_PATH, &devpath); 15774a31ce6Stimh if (err != 0 || devpath == NULL) 15874a31ce6Stimh return (-1); 15974a31ce6Stimh 160392e836bSGavin Maltby /* 161392e836bSGavin Maltby * dev:/// 162392e836bSGavin Maltby * 163392e836bSGavin Maltby * The dev scheme does not render fmri authority information 164392e836bSGavin Maltby * in the string form of an fmri. It is meaningless to 165392e836bSGavin Maltby * transmit a dev scheme fmri outside of the immediate fault 166392e836bSGavin Maltby * manager. 167392e836bSGavin Maltby */ 16874a31ce6Stimh topo_fmristr_build(&size, 169392e836bSGavin Maltby buf, buflen, FM_FMRI_SCHEME_DEV, NULL, ":///"); 17074a31ce6Stimh 17174a31ce6Stimh /* device-id part, topo_fmristr_build does nothing if devid is NULL */ 17274a31ce6Stimh topo_fmristr_build(&size, 173392e836bSGavin Maltby buf, buflen, devid, ":" FM_FMRI_DEV_ID "=", NULL); 17474a31ce6Stimh 175392e836bSGavin Maltby /* target-port-l0id part */ 176392e836bSGavin Maltby topo_fmristr_build(&size, 177392e836bSGavin Maltby buf, buflen, tpl0id, ":" FM_FMRI_DEV_TGTPTLUN0 "=", NULL); 178392e836bSGavin Maltby 179392e836bSGavin Maltby /* 180392e836bSGavin Maltby * device-path part; the devpath should always start with a / 181392e836bSGavin Maltby * so you'd think we don't need to add a further / prefix here; 182392e836bSGavin Maltby * however past implementation has always added the / if 183392e836bSGavin Maltby * there is a devid component so we continue to do that 184392e836bSGavin Maltby * so strings match exactly as before. So we can have: 185392e836bSGavin Maltby * 186392e836bSGavin Maltby * dev:////pci@0,0/... 187392e836bSGavin Maltby * dev:///<devid-and-tpl0>//pci@0,0/... 188392e836bSGavin Maltby * 189392e836bSGavin Maltby * where <devid-and-tpl0> = 190392e836bSGavin Maltby * [:devid=<devid>][:target-port-l0id=<tpl0>] 191392e836bSGavin Maltby */ 192392e836bSGavin Maltby topo_fmristr_build(&size, buf, buflen, devpath, 193392e836bSGavin Maltby devid || tpl0id ? "/" : NULL, NULL); 19474a31ce6Stimh 19574a31ce6Stimh return (size); 19674a31ce6Stimh } 19774a31ce6Stimh 19874a31ce6Stimh /*ARGSUSED*/ 19974a31ce6Stimh static int 20074a31ce6Stimh dev_fmri_nvl2str(topo_mod_t *mod, tnode_t *node, topo_version_t version, 20174a31ce6Stimh nvlist_t *nvl, nvlist_t **out) 20274a31ce6Stimh { 20374a31ce6Stimh ssize_t len; 20474a31ce6Stimh char *name = NULL; 20574a31ce6Stimh nvlist_t *fmristr; 20674a31ce6Stimh 20774a31ce6Stimh if (version > TOPO_METH_NVL2STR_VERSION) 20874a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 20974a31ce6Stimh 21074a31ce6Stimh if ((len = fmri_nvl2str(nvl, NULL, 0)) == 0 || 21174a31ce6Stimh (name = topo_mod_alloc(mod, len + 1)) == NULL || 21274a31ce6Stimh fmri_nvl2str(nvl, name, len + 1) == 0) { 21374a31ce6Stimh if (name != NULL) 21474a31ce6Stimh topo_mod_free(mod, name, len + 1); 21574a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 21674a31ce6Stimh } 21774a31ce6Stimh 21874a31ce6Stimh if (topo_mod_nvalloc(mod, &fmristr, NV_UNIQUE_NAME) != 0) 21974a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 22074a31ce6Stimh if (nvlist_add_string(fmristr, "fmri-string", name) != 0) { 22174a31ce6Stimh topo_mod_free(mod, name, len + 1); 22274a31ce6Stimh nvlist_free(fmristr); 22374a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 22474a31ce6Stimh } 22574a31ce6Stimh topo_mod_free(mod, name, len + 1); 22674a31ce6Stimh *out = fmristr; 22774a31ce6Stimh 22874a31ce6Stimh return (0); 22974a31ce6Stimh } 23074a31ce6Stimh 23174a31ce6Stimh /*ARGSUSED*/ 23274a31ce6Stimh static int 23374a31ce6Stimh dev_fmri_str2nvl(topo_mod_t *mod, tnode_t *node, topo_version_t version, 23474a31ce6Stimh nvlist_t *in, nvlist_t **out) 23574a31ce6Stimh { 236392e836bSGavin Maltby char *cur, *devid = NULL, *tpl0id = NULL; 237392e836bSGavin Maltby char *str, *strcp; 23874a31ce6Stimh nvlist_t *fmri; 23974a31ce6Stimh char *devpath; 240392e836bSGavin Maltby size_t len; 24174a31ce6Stimh int err; 24274a31ce6Stimh 24374a31ce6Stimh if (version > TOPO_METH_STR2NVL_VERSION) 24474a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 24574a31ce6Stimh 24674a31ce6Stimh if (nvlist_lookup_string(in, "fmri-string", &str) != 0) 24774a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 24874a31ce6Stimh 249392e836bSGavin Maltby len = strlen(str); 250392e836bSGavin Maltby 251392e836bSGavin Maltby /* 252392e836bSGavin Maltby * We're expecting a string version of a dev scheme FMRI, and 253392e836bSGavin Maltby * no fmri authority information. 254392e836bSGavin Maltby * 255392e836bSGavin Maltby * The shortest legal string would be "dev:////" (len 8) for a string 256392e836bSGavin Maltby * with no FMRI auth info, no devid or target-port-l0id and 257392e836bSGavin Maltby * an empty devpath string. 258392e836bSGavin Maltby */ 259392e836bSGavin Maltby if (len < 8 || strncmp(str, "dev:///", 7) != 0) 26074a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 26174a31ce6Stimh 262392e836bSGavin Maltby strcp = alloca(len + 1); 263392e836bSGavin Maltby (void) memcpy(strcp, str, len); 264392e836bSGavin Maltby strcp[len] = '\0'; 265392e836bSGavin Maltby cur = strcp + 7; /* already parsed "dev:///" */ 26674a31ce6Stimh 267392e836bSGavin Maltby /* 268392e836bSGavin Maltby * If the first character after the "/" that terminates the (empty) 269392e836bSGavin Maltby * fmri authority is a colon then we have devid and/or target-port-l0id 270392e836bSGavin Maltby * info. They could be in either order. 271392e836bSGavin Maltby * 272392e836bSGavin Maltby * If not a colon then it must be the / that begins the devpath. 273392e836bSGavin Maltby */ 274392e836bSGavin Maltby if (*cur == ':') { 275392e836bSGavin Maltby char *eos, *part[2]; 276392e836bSGavin Maltby int i; 277392e836bSGavin Maltby /* 278392e836bSGavin Maltby * Look ahead to the "/" that starts the devpath. If not 279392e836bSGavin Maltby * found or if straight after the : then we're busted. 280392e836bSGavin Maltby */ 281392e836bSGavin Maltby eos = devpath = strchr(cur, '/'); 282392e836bSGavin Maltby if (devpath == NULL || devpath == cur + 1) 28374a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 284392e836bSGavin Maltby 285392e836bSGavin Maltby part[0] = ++cur; 286392e836bSGavin Maltby 287392e836bSGavin Maltby /* 288392e836bSGavin Maltby * Replace the initial "/" of the devpath with a NUL 289392e836bSGavin Maltby * to terminate the string before it. We'll undo this 290392e836bSGavin Maltby * before rendering devpath. 291392e836bSGavin Maltby */ 292392e836bSGavin Maltby *eos = '\0'; 293392e836bSGavin Maltby 294392e836bSGavin Maltby /* 295392e836bSGavin Maltby * We should now have a NUL-terminated string matching 296392e836bSGavin Maltby * foo=<pat1>[:bar=<pat2>] (we stepped over the initial :) 297392e836bSGavin Maltby * Look for a second colon; if found there must be space 298392e836bSGavin Maltby * after it for the additional component, but no more colons. 299392e836bSGavin Maltby */ 300392e836bSGavin Maltby if ((part[1] = strchr(cur, ':')) != NULL) { 301392e836bSGavin Maltby if (part[1] + 1 == eos || 302392e836bSGavin Maltby strchr(part[1] + 1, ':') != NULL) 303392e836bSGavin Maltby return (topo_mod_seterrno(mod, 304392e836bSGavin Maltby EMOD_FMRI_MALFORM)); 305392e836bSGavin Maltby *part[1] = '\0'; /* terminate part[0] */ 306392e836bSGavin Maltby part[1]++; 30774a31ce6Stimh } 30874a31ce6Stimh 309392e836bSGavin Maltby for (i = 0; i < 2; i++) { 310392e836bSGavin Maltby char *eq; 311392e836bSGavin Maltby 312392e836bSGavin Maltby if (!part[i]) 313392e836bSGavin Maltby continue; 314392e836bSGavin Maltby 315392e836bSGavin Maltby if ((eq = strchr(part[i], '=')) == NULL || 316392e836bSGavin Maltby *(eq + 1) == '\0') 317392e836bSGavin Maltby return (topo_mod_seterrno(mod, 318392e836bSGavin Maltby EMOD_FMRI_MALFORM)); 319392e836bSGavin Maltby 320392e836bSGavin Maltby *eq = '\0'; 321392e836bSGavin Maltby if (strcmp(part[i], FM_FMRI_DEV_ID) == 0) 322392e836bSGavin Maltby devid = eq + 1; 323392e836bSGavin Maltby else if (strcmp(part[i], FM_FMRI_DEV_TGTPTLUN0) == 0) 324392e836bSGavin Maltby tpl0id = eq + 1; 325392e836bSGavin Maltby else 326392e836bSGavin Maltby return (topo_mod_seterrno(mod, 327392e836bSGavin Maltby EMOD_FMRI_MALFORM)); 328392e836bSGavin Maltby } 329392e836bSGavin Maltby 330392e836bSGavin Maltby if (devid == NULL && tpl0id == NULL) 33174a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 33274a31ce6Stimh 333392e836bSGavin Maltby cur = devpath; /* initial slash is NULled */ 334392e836bSGavin Maltby } else if (*cur != '/') { 335392e836bSGavin Maltby /* the device-path should start with a slash */ 336392e836bSGavin Maltby return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 337392e836bSGavin Maltby } else { 338392e836bSGavin Maltby devpath = cur; 339392e836bSGavin Maltby } 340392e836bSGavin Maltby 34174a31ce6Stimh if (topo_mod_nvalloc(mod, &fmri, NV_UNIQUE_NAME) != 0) 34274a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 34374a31ce6Stimh 34474a31ce6Stimh err = nvlist_add_uint8(fmri, FM_VERSION, FM_DEV_SCHEME_VERSION); 34574a31ce6Stimh err |= nvlist_add_string(fmri, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 346392e836bSGavin Maltby 34774a31ce6Stimh if (devid != NULL) 34874a31ce6Stimh err |= nvlist_add_string(fmri, FM_FMRI_DEV_ID, devid); 34974a31ce6Stimh 350392e836bSGavin Maltby if (tpl0id != NULL) 351392e836bSGavin Maltby err |= nvlist_add_string(fmri, FM_FMRI_DEV_TGTPTLUN0, tpl0id); 352392e836bSGavin Maltby 353392e836bSGavin Maltby if (devid != NULL || tpl0id != NULL) 354392e836bSGavin Maltby *devpath = '/'; /* we NULed this earlier; put it back */ 355392e836bSGavin Maltby 356392e836bSGavin Maltby /* step over repeated initial / in the devpath */ 357392e836bSGavin Maltby while (*(devpath + 1) == '/') 358392e836bSGavin Maltby devpath++; 359392e836bSGavin Maltby 360392e836bSGavin Maltby err |= nvlist_add_string(fmri, FM_FMRI_DEV_PATH, devpath); 361392e836bSGavin Maltby 36274a31ce6Stimh if (err != 0) { 36374a31ce6Stimh nvlist_free(fmri); 36474a31ce6Stimh return (topo_mod_seterrno(mod, EMOD_FMRI_NVL)); 36574a31ce6Stimh } 366392e836bSGavin Maltby 36774a31ce6Stimh *out = fmri; 36874a31ce6Stimh 36974a31ce6Stimh return (0); 37074a31ce6Stimh } 37174a31ce6Stimh 37224db4641Seschrock /*ARGSUSED*/ 37324db4641Seschrock static int 37424db4641Seschrock dev_fmri_present(topo_mod_t *mod, tnode_t *node, topo_version_t version, 37524db4641Seschrock nvlist_t *in, nvlist_t **out) 37624db4641Seschrock { 37724db4641Seschrock uint8_t fmversion; 37824db4641Seschrock char *devpath = NULL; 37924db4641Seschrock uint32_t present; 380faf8f993Sstephh char *devid = NULL, *path; 381faf8f993Sstephh ddi_devid_t id; 382faf8f993Sstephh ddi_devid_t matchid; 383faf8f993Sstephh di_node_t dnode; 384faf8f993Sstephh struct stat sb; 385faf8f993Sstephh int len; 38624db4641Seschrock 38724db4641Seschrock if (version > TOPO_METH_PRESENT_VERSION) 38824db4641Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 38924db4641Seschrock 39024db4641Seschrock if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 39124db4641Seschrock fmversion > FM_DEV_SCHEME_VERSION || 39224db4641Seschrock nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 39324db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 39424db4641Seschrock 39524db4641Seschrock (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 39624db4641Seschrock 397faf8f993Sstephh if (devpath == NULL || strlen(devpath) == 0) 39824db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 39924db4641Seschrock 40024db4641Seschrock /* 401faf8f993Sstephh * stat() the device node in devfs. This will tell us if the device is 402faf8f993Sstephh * present or not. Don't stat the minor, just the whole device. 403faf8f993Sstephh * If the device is present and there is a devid, it must also match. 404faf8f993Sstephh * so di_init that one node. No need for DINFOFORCE. 40524db4641Seschrock */ 406faf8f993Sstephh len = strlen(devpath) + strlen("/devices") + 1; 407faf8f993Sstephh path = topo_mod_alloc(mod, len); 408faf8f993Sstephh (void) snprintf(path, len, "/devices%s", devpath); 409faf8f993Sstephh if (devid == NULL) { 410faf8f993Sstephh if (stat(path, &sb) != -1) 411faf8f993Sstephh present = 1; 412faf8f993Sstephh else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 41324db4641Seschrock present = 0; 414faf8f993Sstephh else { 415faf8f993Sstephh if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 416faf8f993Sstephh present = 0; 417faf8f993Sstephh else 418faf8f993Sstephh present = 1; 419faf8f993Sstephh di_fini(dnode); 42024db4641Seschrock } 421faf8f993Sstephh } else { 422faf8f993Sstephh if (stat(path, &sb) == -1) 423faf8f993Sstephh present = 0; 424faf8f993Sstephh else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 425faf8f993Sstephh present = 0; 426faf8f993Sstephh else { 427faf8f993Sstephh if ((id = di_devid(dnode)) == NULL || 428faf8f993Sstephh devid_str_decode(devid, &matchid, NULL) != 0) 429faf8f993Sstephh present = 0; 430faf8f993Sstephh else { 431faf8f993Sstephh if (devid_compare(id, matchid) != 0) 432faf8f993Sstephh present = 0; 433faf8f993Sstephh else 434faf8f993Sstephh present = 1; 435faf8f993Sstephh devid_free(matchid); 436faf8f993Sstephh } 437faf8f993Sstephh di_fini(dnode); 438faf8f993Sstephh } 439faf8f993Sstephh } 440faf8f993Sstephh topo_mod_free(mod, path, len); 44124db4641Seschrock 44224db4641Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 44324db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 44424db4641Seschrock if (nvlist_add_uint32(*out, TOPO_METH_PRESENT_RET, present) != 0) { 44524db4641Seschrock nvlist_free(*out); 44624db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 44724db4641Seschrock } 44824db4641Seschrock 44924db4641Seschrock return (0); 45024db4641Seschrock } 45124db4641Seschrock 45224db4641Seschrock /*ARGSUSED*/ 45324db4641Seschrock static int 45425c6ff4bSstephh dev_fmri_replaced(topo_mod_t *mod, tnode_t *node, topo_version_t version, 45525c6ff4bSstephh nvlist_t *in, nvlist_t **out) 45625c6ff4bSstephh { 45725c6ff4bSstephh uint8_t fmversion; 45825c6ff4bSstephh char *devpath = NULL; 45925c6ff4bSstephh uint32_t rval; 46025c6ff4bSstephh char *devid = NULL, *path; 46125c6ff4bSstephh ddi_devid_t id; 46225c6ff4bSstephh ddi_devid_t matchid; 46325c6ff4bSstephh di_node_t dnode; 46425c6ff4bSstephh struct stat sb; 46525c6ff4bSstephh int len; 46625c6ff4bSstephh 46725c6ff4bSstephh if (version > TOPO_METH_REPLACED_VERSION) 46825c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 46925c6ff4bSstephh 47025c6ff4bSstephh if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 47125c6ff4bSstephh fmversion > FM_DEV_SCHEME_VERSION || 47225c6ff4bSstephh nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 47325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 47425c6ff4bSstephh 47525c6ff4bSstephh (void) nvlist_lookup_string(in, FM_FMRI_DEV_ID, &devid); 47625c6ff4bSstephh 47725c6ff4bSstephh if (devpath == NULL || strlen(devpath) == 0) 47825c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 47925c6ff4bSstephh 48025c6ff4bSstephh /* 48125c6ff4bSstephh * stat() the device node in devfs. This will tell us if the device is 48225c6ff4bSstephh * present or not. Don't stat the minor, just the whole device. 48325c6ff4bSstephh * If the device is present and there is a devid, it must also match. 48425c6ff4bSstephh * so di_init that one node. No need for DINFOFORCE. 48525c6ff4bSstephh */ 48625c6ff4bSstephh len = strlen(devpath) + strlen("/devices") + 1; 48725c6ff4bSstephh path = topo_mod_alloc(mod, len); 48825c6ff4bSstephh (void) snprintf(path, len, "/devices%s", devpath); 48925c6ff4bSstephh if (devid == NULL) { 49025c6ff4bSstephh if (stat(path, &sb) != -1) 49125c6ff4bSstephh rval = FMD_OBJ_STATE_UNKNOWN; 49225c6ff4bSstephh else if ((dnode = di_init("/", DINFOCACHE)) == DI_NODE_NIL) 493e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN; 49425c6ff4bSstephh else { 49525c6ff4bSstephh if (di_lookup_node(dnode, devpath) == DI_NODE_NIL) 496e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN; 49725c6ff4bSstephh else 49825c6ff4bSstephh rval = FMD_OBJ_STATE_UNKNOWN; 49925c6ff4bSstephh di_fini(dnode); 50025c6ff4bSstephh } 50125c6ff4bSstephh } else { 50225c6ff4bSstephh if (stat(path, &sb) == -1) 503e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN; 50425c6ff4bSstephh else if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) 505e58a33b6SStephen Hanson rval = FMD_OBJ_STATE_UNKNOWN; 50625c6ff4bSstephh else { 50725c6ff4bSstephh if ((id = di_devid(dnode)) == NULL || 50825c6ff4bSstephh devid_str_decode(devid, &matchid, NULL) != 0) 50925c6ff4bSstephh rval = FMD_OBJ_STATE_UNKNOWN; 51025c6ff4bSstephh else { 51125c6ff4bSstephh if (devid_compare(id, matchid) != 0) 51225c6ff4bSstephh rval = FMD_OBJ_STATE_REPLACED; 51325c6ff4bSstephh else 51425c6ff4bSstephh rval = FMD_OBJ_STATE_STILL_PRESENT; 51525c6ff4bSstephh devid_free(matchid); 51625c6ff4bSstephh } 51725c6ff4bSstephh di_fini(dnode); 51825c6ff4bSstephh } 51925c6ff4bSstephh } 52025c6ff4bSstephh topo_mod_free(mod, path, len); 52125c6ff4bSstephh 52225c6ff4bSstephh if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 52325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 52425c6ff4bSstephh if (nvlist_add_uint32(*out, TOPO_METH_REPLACED_RET, rval) != 0) { 52525c6ff4bSstephh nvlist_free(*out); 52625c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 52725c6ff4bSstephh } 52825c6ff4bSstephh 52925c6ff4bSstephh return (0); 53025c6ff4bSstephh } 53125c6ff4bSstephh 53225c6ff4bSstephh /*ARGSUSED*/ 53325c6ff4bSstephh static int 53424db4641Seschrock dev_fmri_unusable(topo_mod_t *mod, tnode_t *node, topo_version_t version, 53524db4641Seschrock nvlist_t *in, nvlist_t **out) 53624db4641Seschrock { 53724db4641Seschrock di_node_t dnode; 53824db4641Seschrock uint8_t fmversion; 53924db4641Seschrock char *devpath = NULL; 54024db4641Seschrock uint32_t unusable; 54124db4641Seschrock uint_t state; 54224db4641Seschrock 54325c6ff4bSstephh if (version > TOPO_METH_UNUSABLE_VERSION) 54424db4641Seschrock return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 54524db4641Seschrock 54624db4641Seschrock if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 54724db4641Seschrock fmversion > FM_DEV_SCHEME_VERSION || 54824db4641Seschrock nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 54924db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 55024db4641Seschrock 55124db4641Seschrock if (devpath == NULL) 55224db4641Seschrock return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 55324db4641Seschrock 55424db4641Seschrock if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 55524db4641Seschrock if (errno != ENXIO) 55624db4641Seschrock return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 55724db4641Seschrock unusable = 1; 55824db4641Seschrock } else { 55925e8c5aaSvikram uint_t retired = di_retired(dnode); 56024db4641Seschrock state = di_state(dnode); 56125e8c5aaSvikram if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 56225e8c5aaSvikram DI_BUS_QUIESCED | DI_BUS_DOWN))) 56324db4641Seschrock unusable = 1; 56424db4641Seschrock else 56524db4641Seschrock unusable = 0; 56624db4641Seschrock di_fini(dnode); 56724db4641Seschrock } 56824db4641Seschrock 56924db4641Seschrock if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 57024db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 5713e2676e0Svikram if (nvlist_add_uint32(*out, TOPO_METH_UNUSABLE_RET, unusable) != 0) { 57224db4641Seschrock nvlist_free(*out); 57324db4641Seschrock return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 57424db4641Seschrock } 57524db4641Seschrock 57624db4641Seschrock return (0); 57724db4641Seschrock } 57824db4641Seschrock 57925c6ff4bSstephh /*ARGSUSED*/ 58025c6ff4bSstephh static int 58125c6ff4bSstephh dev_fmri_service_state(topo_mod_t *mod, tnode_t *node, topo_version_t version, 58225c6ff4bSstephh nvlist_t *in, nvlist_t **out) 58325c6ff4bSstephh { 58425c6ff4bSstephh di_node_t dnode; 58525c6ff4bSstephh uint8_t fmversion; 58625c6ff4bSstephh char *devpath = NULL; 58725c6ff4bSstephh uint32_t service_state; 58825c6ff4bSstephh uint_t state; 58925c6ff4bSstephh 59025c6ff4bSstephh if (version > TOPO_METH_SERVICE_STATE_VERSION) 59125c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_VER_NEW)); 59225c6ff4bSstephh 59325c6ff4bSstephh if (nvlist_lookup_uint8(in, FM_VERSION, &fmversion) != 0 || 59425c6ff4bSstephh fmversion > FM_DEV_SCHEME_VERSION || 59525c6ff4bSstephh nvlist_lookup_string(in, FM_FMRI_DEV_PATH, &devpath) != 0) 59625c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 59725c6ff4bSstephh 59825c6ff4bSstephh if (devpath == NULL) 59925c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_FMRI_MALFORM)); 60025c6ff4bSstephh 60125c6ff4bSstephh if ((dnode = di_init(devpath, DINFOCPYONE)) == DI_NODE_NIL) { 60225c6ff4bSstephh if (errno != ENXIO) 60325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM)); 60425c6ff4bSstephh service_state = FMD_SERVICE_STATE_UNUSABLE; 60525c6ff4bSstephh } else { 60625c6ff4bSstephh uint_t retired = di_retired(dnode); 60725c6ff4bSstephh state = di_state(dnode); 60825c6ff4bSstephh if (retired || (state & (DI_DEVICE_OFFLINE | DI_DEVICE_DOWN | 60925c6ff4bSstephh DI_BUS_QUIESCED | DI_BUS_DOWN))) 61025c6ff4bSstephh service_state = FMD_SERVICE_STATE_UNUSABLE; 61125c6ff4bSstephh else if (state & DI_DEVICE_DEGRADED) 61225c6ff4bSstephh service_state = FMD_SERVICE_STATE_DEGRADED; 61325c6ff4bSstephh else 61425c6ff4bSstephh service_state = FMD_SERVICE_STATE_OK; 61525c6ff4bSstephh di_fini(dnode); 61625c6ff4bSstephh } 61725c6ff4bSstephh 61825c6ff4bSstephh if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) != 0) 61925c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 62025c6ff4bSstephh if (nvlist_add_uint32(*out, TOPO_METH_SERVICE_STATE_RET, 62125c6ff4bSstephh service_state) != 0) { 62225c6ff4bSstephh nvlist_free(*out); 62325c6ff4bSstephh return (topo_mod_seterrno(mod, EMOD_NVL_INVAL)); 62425c6ff4bSstephh } 62525c6ff4bSstephh 62625c6ff4bSstephh return (0); 62725c6ff4bSstephh } 62825c6ff4bSstephh 62974a31ce6Stimh static nvlist_t * 63074a31ce6Stimh dev_fmri_create(topo_mod_t *mp, const char *id, const char *path) 63174a31ce6Stimh { 63274a31ce6Stimh nvlist_t *out = NULL; 63374a31ce6Stimh int e; 63474a31ce6Stimh 63574a31ce6Stimh if (topo_mod_nvalloc(mp, &out, NV_UNIQUE_NAME) != 0) { 63674a31ce6Stimh (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 63774a31ce6Stimh return (NULL); 63874a31ce6Stimh } 63974a31ce6Stimh e = nvlist_add_string(out, FM_FMRI_SCHEME, FM_FMRI_SCHEME_DEV); 64074a31ce6Stimh e |= nvlist_add_uint8(out, FM_VERSION, FM_DEV_SCHEME_VERSION); 64174a31ce6Stimh e |= nvlist_add_string(out, FM_FMRI_DEV_PATH, path); 64274a31ce6Stimh 64374a31ce6Stimh if (id != NULL) 64474a31ce6Stimh e |= nvlist_add_string(out, FM_FMRI_DEV_ID, id); 64574a31ce6Stimh 64674a31ce6Stimh if (e == 0) 64774a31ce6Stimh return (out); 64874a31ce6Stimh 64974a31ce6Stimh topo_mod_dprintf(mp, "construction of dev nvl failed"); 65074a31ce6Stimh (void) topo_mod_seterrno(mp, EMOD_FMRI_NVL); 65174a31ce6Stimh nvlist_free(out); 65274a31ce6Stimh return (NULL); 65374a31ce6Stimh } 65474a31ce6Stimh 65574a31ce6Stimh /*ARGSUSED*/ 65674a31ce6Stimh static int 65774a31ce6Stimh dev_fmri_create_meth(topo_mod_t *mp, tnode_t *node, topo_version_t version, 65874a31ce6Stimh nvlist_t *in, nvlist_t **out) 65974a31ce6Stimh { 66074a31ce6Stimh nvlist_t *args = NULL; 66174a31ce6Stimh char *path, *id = NULL; 66274a31ce6Stimh 66374a31ce6Stimh if (version > TOPO_METH_FMRI_VERSION) 66474a31ce6Stimh return (topo_mod_seterrno(mp, EMOD_VER_NEW)); 66574a31ce6Stimh 66674a31ce6Stimh if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_NVL, &args) != 0 || 66774a31ce6Stimh nvlist_lookup_string(args, FM_FMRI_DEV_PATH, &path) != 0) { 66874a31ce6Stimh topo_mod_dprintf(mp, "no path string in method argument\n"); 66974a31ce6Stimh return (topo_mod_seterrno(mp, EMOD_METHOD_INVAL)); 67074a31ce6Stimh } 67174a31ce6Stimh 67274a31ce6Stimh (void) nvlist_lookup_string(args, FM_FMRI_DEV_ID, &id); 67374a31ce6Stimh 67474a31ce6Stimh if ((*out = dev_fmri_create(mp, id, path)) == NULL) 67574a31ce6Stimh return (-1); 67674a31ce6Stimh return (0); 67774a31ce6Stimh } 678