1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2017, Joyent, Inc. 14 */ 15 16 /* 17 * This module covers enumerating properties of physical NICs. At this time, as 18 * various devices are discovered that may relate to various networking gear, we 19 * will attempt to enumerate ports and transceivers under them, if requested. 20 */ 21 22 #include <strings.h> 23 #include <libdevinfo.h> 24 #include <libdladm.h> 25 #include <libdllink.h> 26 #include <libsff.h> 27 #include <unistd.h> 28 #include <sys/dld_ioc.h> 29 #include <sys/dld.h> 30 31 #include <sys/fm/protocol.h> 32 #include <fm/topo_mod.h> 33 #include <fm/topo_list.h> 34 #include <fm/topo_method.h> 35 36 #include <topo_port.h> 37 #include <topo_transceiver.h> 38 39 #include "topo_nic.h" 40 41 /* 42 * Create an instance of a transceiver with the specified id. We must create 43 * both its port and the transceiver node. 44 */ 45 static int 46 nic_create_transceiver(topo_mod_t *mod, tnode_t *pnode, dladm_handle_t handle, 47 datalink_id_t linkid, uint_t tranid) 48 { 49 int ret; 50 tnode_t *port; 51 dld_ioc_gettran_t dgt; 52 dld_ioc_tranio_t dti; 53 uint8_t buf[256]; 54 char ouibuf[16]; 55 char *vendor = NULL, *part = NULL, *rev = NULL, *serial = NULL; 56 nvlist_t *nvl = NULL; 57 58 if ((ret = port_create_sff(mod, pnode, tranid, &port)) != 0) 59 return (ret); 60 61 bzero(&dgt, sizeof (dgt)); 62 dgt.dgt_linkid = linkid; 63 dgt.dgt_tran_id = tranid; 64 65 if (ioctl(dladm_dld_fd(handle), DLDIOC_GETTRAN, &dgt) != 0) { 66 if (errno == ENOTSUP) 67 return (0); 68 return (-1); 69 } 70 71 if (dgt.dgt_present == 0) 72 return (0); 73 74 bzero(&dti, sizeof (dti)); 75 dti.dti_linkid = linkid; 76 dti.dti_tran_id = tranid; 77 dti.dti_page = 0xa0; 78 dti.dti_nbytes = sizeof (buf); 79 dti.dti_buf = (uintptr_t)buf; 80 81 if (ioctl(dladm_dld_fd(handle), DLDIOC_READTRAN, &dti) == 0) { 82 uchar_t *oui; 83 uint_t nbyte; 84 85 if (libsff_parse(buf, dti.dti_nbytes, dti.dti_page, 86 &nvl) == 0) { 87 if ((ret = nvlist_lookup_string(nvl, LIBSFF_KEY_VENDOR, 88 &vendor)) != 0 && nvlist_lookup_byte_array(nvl, 89 LIBSFF_KEY_OUI, &oui, &nbyte) == 0 && nbyte == 3) { 90 if (snprintf(ouibuf, sizeof (ouibuf), 91 "%02x:%02x:%02x", oui[0], oui[1], oui[2]) < 92 sizeof (ouibuf)) { 93 vendor = ouibuf; 94 } 95 } else if (ret != 0) { 96 vendor = NULL; 97 } 98 99 if (nvlist_lookup_string(nvl, LIBSFF_KEY_PART, 100 &part) != 0) { 101 part = NULL; 102 } 103 104 if (nvlist_lookup_string(nvl, LIBSFF_KEY_REVISION, 105 &rev) != 0) { 106 rev = NULL; 107 } 108 109 if (nvlist_lookup_string(nvl, LIBSFF_KEY_SERIAL, 110 &serial) != 0) { 111 serial = NULL; 112 } 113 } 114 } 115 116 if (transceiver_range_create(mod, port, 0, 0) != 0) { 117 nvlist_free(nvl); 118 return (-1); 119 } 120 121 if (transceiver_create_sff(mod, port, 0, dgt.dgt_usable, vendor, part, 122 rev, serial, NULL) != 0) { 123 nvlist_free(nvl); 124 return (-1); 125 } 126 127 nvlist_free(nvl); 128 return (0); 129 } 130 131 /* ARGSUSED */ 132 static int 133 nic_enum(topo_mod_t *mod, tnode_t *pnode, const char *name, 134 topo_instance_t min, topo_instance_t max, void *modarg, void *data) 135 { 136 di_node_t din = data; 137 datalink_id_t linkid; 138 dladm_handle_t handle; 139 dld_ioc_gettran_t dgt; 140 uint_t ntrans, i; 141 char dname[MAXNAMELEN]; 142 143 if (strcmp(name, NIC) != 0) { 144 topo_mod_dprintf(mod, "nic_enum: asked to enumerate unknown " 145 "component: %s\n", name); 146 return (-1); 147 } 148 149 if (din == NULL) { 150 topo_mod_dprintf(mod, "nic_enum: missing data argument\n"); 151 return (-1); 152 } 153 154 if ((handle = topo_mod_getspecific(mod)) == NULL) { 155 topo_mod_dprintf(mod, "nic_enum: failed to get nic module " 156 "specific data\n"); 157 return (-1); 158 } 159 160 if (snprintf(dname, sizeof (dname), "%s%d", di_driver_name(din), 161 di_instance(din)) >= sizeof (dname)) { 162 topo_mod_dprintf(mod, "nic_enum: device name overflowed " 163 "internal buffer\n"); 164 return (-1); 165 } 166 167 if (dladm_dev2linkid(handle, dname, &linkid) != DLADM_STATUS_OK) 168 return (-1); 169 170 bzero(&dgt, sizeof (dgt)); 171 dgt.dgt_linkid = linkid; 172 dgt.dgt_tran_id = DLDIOC_GETTRAN_GETNTRAN; 173 174 if (ioctl(dladm_dld_fd(handle), DLDIOC_GETTRAN, &dgt) != 0) { 175 if (errno == ENOTSUP) 176 return (0); 177 return (-1); 178 } 179 180 ntrans = dgt.dgt_tran_id; 181 if (ntrans == 0) 182 return (0); 183 184 if (port_range_create(mod, pnode, 0, ntrans - 1) != 0) 185 return (-1); 186 187 for (i = 0; i < ntrans; i++) { 188 if (nic_create_transceiver(mod, pnode, handle, linkid, i) != 0) 189 return (-1); 190 } 191 192 return (0); 193 } 194 195 static const topo_modops_t nic_ops = { 196 nic_enum, NULL 197 }; 198 199 static topo_modinfo_t nic_mod = { 200 NIC, FM_FMRI_SCHEME_HC, NIC_VERSION, &nic_ops 201 }; 202 203 int 204 _topo_init(topo_mod_t *mod, topo_version_t version) 205 { 206 dladm_handle_t handle; 207 208 if (getenv("TOPONICDEBUG") != NULL) 209 topo_mod_setdebug(mod); 210 211 topo_mod_dprintf(mod, "_mod_init: " 212 "initializing %s enumerator\n", NIC); 213 214 if (version != NIC_VERSION) { 215 return (-1); 216 } 217 218 if (dladm_open(&handle) != 0) 219 return (-1); 220 221 if (topo_mod_register(mod, &nic_mod, TOPO_VERSION) != 0) { 222 dladm_close(handle); 223 return (-1); 224 } 225 226 topo_mod_setspecific(mod, handle); 227 228 return (0); 229 } 230 231 void 232 _topo_fini(topo_mod_t *mod) 233 { 234 dladm_handle_t handle; 235 236 if ((handle = topo_mod_getspecific(mod)) == NULL) 237 return; 238 239 dladm_close(handle); 240 topo_mod_setspecific(mod, NULL); 241 } 242