xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/nic/topo_nic.c (revision bf5d9f18edeb77c14df996d367853599bdd43fd1)
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