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
nic_create_transceiver(topo_mod_t * mod,tnode_t * pnode,dladm_handle_t handle,datalink_id_t linkid,uint_t tranid)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
nic_enum(topo_mod_t * mod,tnode_t * pnode,const char * name,topo_instance_t min,topo_instance_t max,void * modarg,void * data)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
_topo_init(topo_mod_t * mod,topo_version_t version)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
_topo_fini(topo_mod_t * mod)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