xref: /freebsd/sys/dev/bnxt/bnxt_en/bnxt_mgmt.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
1 /*
2  * Broadcom NetXtreme-C/E network driver.
3  *
4  * Copyright (c) 2022 Broadcom, All Rights Reserved.
5  * The term Broadcom refers to Broadcom Limited and/or its subsidiaries
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS'
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  * THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include "bnxt_mgmt.h"
30 #include "bnxt.h"
31 #include "bnxt_hwrm.h"
32 #include <dev/pci/pcireg.h>
33 #include <dev/pci/pcivar.h>
34 #include <sys/endian.h>
35 #include <sys/lock.h>
36 
37 /* Function prototypes */
38 static d_open_t      bnxt_mgmt_open;
39 static d_close_t     bnxt_mgmt_close;
40 static d_ioctl_t     bnxt_mgmt_ioctl;
41 
42 /* Character device entry points */
43 static struct cdevsw bnxt_mgmt_cdevsw = {
44 	.d_version = D_VERSION,
45 	.d_open = bnxt_mgmt_open,
46 	.d_close = bnxt_mgmt_close,
47 	.d_ioctl = bnxt_mgmt_ioctl,
48 	.d_name = "bnxt_mgmt",
49 };
50 
51 /* Global vars */
52 static struct cdev *bnxt_mgmt_dev;
53 struct mtx		mgmt_lock;
54 
55 MALLOC_DEFINE(M_BNXT, "bnxt_mgmt_buffer", "buffer for bnxt_mgmt module");
56 
57 /*
58  * This function is called by the kld[un]load(2) system calls to
59  * determine what actions to take when a module is loaded or unloaded.
60  */
61 static int
62 bnxt_mgmt_loader(struct module *m, int what, void *arg)
63 {
64 	int error = 0;
65 
66 	switch (what) {
67 	case MOD_LOAD:
68 		error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK,
69 		    &bnxt_mgmt_dev,
70 		    &bnxt_mgmt_cdevsw,
71 		    0,
72 		    UID_ROOT,
73 		    GID_WHEEL,
74 		    0600,
75 		    "bnxt_mgmt");
76 		if (error != 0) {
77 			printf("%s: %s:%s:%d Failed to create the"
78 			       "bnxt_mgmt device node\n", DRIVER_NAME,
79 			       __FILE__, __FUNCTION__, __LINE__);
80 			return (error);
81 		}
82 
83 		mtx_init(&mgmt_lock, "BNXT MGMT Lock", NULL, MTX_DEF);
84 
85 		break;
86 	case MOD_UNLOAD:
87 		mtx_destroy(&mgmt_lock);
88 		destroy_dev(bnxt_mgmt_dev);
89 		break;
90 	default:
91 		error = EOPNOTSUPP;
92 		break;
93 	}
94 
95 	return (error);
96 }
97 
98 static int
99 bnxt_mgmt_process_dcb(struct cdev *dev, u_long cmd, caddr_t data,
100 		       int flag, struct thread *td)
101 {
102 	struct bnxt_softc *softc = NULL;
103 	struct bnxt_mgmt_dcb mgmt_dcb = {};
104 	void *user_ptr;
105 	int ret = 0;
106 
107 	memcpy(&user_ptr, data, sizeof(user_ptr));
108 	if (copyin(user_ptr, &mgmt_dcb, sizeof(mgmt_dcb))) {
109 		printf("%s: %s:%d Failed to copy data from user\n",
110 			DRIVER_NAME, __FUNCTION__, __LINE__);
111 		return -EFAULT;
112 	}
113 	softc = bnxt_find_dev(mgmt_dcb.hdr.domain, mgmt_dcb.hdr.bus,
114 			      mgmt_dcb.hdr.devfn, NULL);
115 	if (!softc) {
116 		printf("%s: %s:%d unable to find softc reference\n",
117 			DRIVER_NAME, __FUNCTION__, __LINE__);
118 		return -ENODEV;
119 	}
120 
121 	switch (mgmt_dcb.op) {
122 	case BNXT_MGMT_DCB_GET_ETS:
123 		bnxt_dcb_ieee_getets(softc, &mgmt_dcb.req.ets);
124 		break;
125 	case BNXT_MGMT_DCB_SET_ETS:
126 		bnxt_dcb_ieee_setets(softc, &mgmt_dcb.req.ets);
127 		break;
128 	case BNXT_MGMT_DCB_GET_PFC:
129 		bnxt_dcb_ieee_getpfc(softc, &mgmt_dcb.req.pfc);
130 		break;
131 	case BNXT_MGMT_DCB_SET_PFC:
132 		bnxt_dcb_ieee_setpfc(softc, &mgmt_dcb.req.pfc);
133 		break;
134 	case BNXT_MGMT_DCB_SET_APP:
135 		bnxt_dcb_ieee_setapp(softc, &mgmt_dcb.req.app_tlv.app[0]);
136 		break;
137 	case BNXT_MGMT_DCB_DEL_APP:
138 		bnxt_dcb_ieee_delapp(softc, &mgmt_dcb.req.app_tlv.app[0]);
139 		break;
140 	case BNXT_MGMT_DCB_LIST_APP:
141 		bnxt_dcb_ieee_listapp(softc, &mgmt_dcb.req.app_tlv.app[0],
142 				      &mgmt_dcb.req.app_tlv.num_app);
143 		break;
144 	default:
145 		device_printf(softc->dev, "%s:%d Invalid op 0x%x\n",
146 			      __FUNCTION__, __LINE__, mgmt_dcb.op);
147 		ret = -EFAULT;
148 		goto end;
149 	}
150 
151 	if (copyout(&mgmt_dcb, user_ptr, sizeof(mgmt_dcb))) {
152 		device_printf(softc->dev, "%s:%d Failed to copy response to user\n",
153 			      __FUNCTION__, __LINE__);
154 		ret = -EFAULT;
155 		goto end;
156 	}
157 
158 end:
159 	return ret;
160 }
161 
162 static int
163 bnxt_mgmt_process_hwrm(struct cdev *dev, u_long cmd, caddr_t data,
164 		       int flag, struct thread *td)
165 {
166 	struct bnxt_softc *softc = NULL;
167 	struct bnxt_mgmt_req mgmt_req = {};
168 	struct bnxt_mgmt_fw_msg msg_temp, *msg, *msg2 = NULL;
169 	struct iflib_dma_info dma_data = {};
170 	void *user_ptr, *req, *resp;
171 	int ret = 0;
172 	uint16_t num_ind = 0;
173 
174 	memcpy(&user_ptr, data, sizeof(user_ptr));
175 	if (copyin(user_ptr, &mgmt_req, sizeof(struct bnxt_mgmt_req))) {
176 		printf("%s: %s:%d Failed to copy data from user\n",
177 			DRIVER_NAME, __FUNCTION__, __LINE__);
178 		return -EFAULT;
179 	}
180 	softc = bnxt_find_dev(mgmt_req.hdr.domain, mgmt_req.hdr.bus,
181 			      mgmt_req.hdr.devfn, NULL);
182 	if (!softc) {
183 		printf("%s: %s:%d unable to find softc reference\n",
184 			DRIVER_NAME, __FUNCTION__, __LINE__);
185 		return -ENODEV;
186 	}
187 
188 	if (copyin((void*)mgmt_req.req.hreq, &msg_temp, sizeof(msg_temp))) {
189 		device_printf(softc->dev, "%s:%d Failed to copy data from user\n",
190 			      __FUNCTION__, __LINE__);
191 		return -EFAULT;
192 	}
193 
194 	if (msg_temp.len_req > BNXT_MGMT_MAX_HWRM_REQ_LENGTH ||
195 			msg_temp.len_resp > BNXT_MGMT_MAX_HWRM_RESP_LENGTH) {
196 		device_printf(softc->dev, "%s:%d Invalid length\n",
197 			      __FUNCTION__, __LINE__);
198 		return -EINVAL;
199 	}
200 
201 	if (msg_temp.num_dma_indications > 1) {
202 		device_printf(softc->dev, "%s:%d Max num_dma_indications "
203 			      "supported is 1 \n", __FUNCTION__, __LINE__);
204 		return -EINVAL;
205 	}
206 
207 	req = malloc(msg_temp.len_req, M_BNXT, M_WAITOK | M_ZERO);
208 	if(!req) {
209 		device_printf(softc->dev, "%s:%d Memory allocation failed",
210 			      __FUNCTION__, __LINE__);
211 		return -ENOMEM;
212 	}
213 
214 	resp = malloc(msg_temp.len_resp, M_BNXT, M_WAITOK | M_ZERO);
215 	if(!resp) {
216 		device_printf(softc->dev, "%s:%d Memory allocation failed",
217 			      __FUNCTION__, __LINE__);
218 		ret = -ENOMEM;
219 		goto end;
220 	}
221 
222 	if (copyin((void *)msg_temp.usr_req, req, msg_temp.len_req)) {
223 		device_printf(softc->dev, "%s:%d Failed to copy data from user\n",
224 			      __FUNCTION__, __LINE__);
225 		ret = -EFAULT;
226 		goto end;
227 	}
228 
229 	msg = &msg_temp;
230 	num_ind = msg_temp.num_dma_indications;
231 	if (num_ind) {
232 		int size;
233 		void *dma_ptr;
234 		uint64_t *dmap;
235 
236 		size = sizeof(struct bnxt_mgmt_fw_msg) +
237 			     (num_ind * sizeof(struct dma_info));
238 
239 		msg2 = malloc(size, M_BNXT, M_WAITOK | M_ZERO);
240 		if(!msg2) {
241 			device_printf(softc->dev, "%s:%d Memory allocation failed",
242 				      __FUNCTION__, __LINE__);
243 			ret = -ENOMEM;
244 			goto end;
245 		}
246 
247 		if (copyin((void *)mgmt_req.req.hreq, msg2, size)) {
248 			device_printf(softc->dev, "%s:%d Failed to copy"
249 				      "data from user\n", __FUNCTION__, __LINE__);
250 			ret = -EFAULT;
251 			goto end;
252 		}
253 		msg = msg2;
254 
255 		ret = iflib_dma_alloc(softc->ctx, msg->dma[0].length, &dma_data,
256 				    BUS_DMA_NOWAIT);
257 		if (ret) {
258 			device_printf(softc->dev, "%s:%d iflib_dma_alloc"
259 				      "failed with ret = 0x%x\n", __FUNCTION__,
260 				      __LINE__, ret);
261 			ret = -ENOMEM;
262 			goto end;
263 		}
264 
265 		if (!(msg->dma[0].read_or_write)) {
266 			if (copyin((void *)msg->dma[0].data,
267 				   dma_data.idi_vaddr,
268 				   msg->dma[0].length)) {
269 				device_printf(softc->dev, "%s:%d Failed to copy"
270 					      "data from user\n", __FUNCTION__,
271 					      __LINE__);
272 				ret = -EFAULT;
273 				goto end;
274 			}
275 		}
276 		dma_ptr = (void *) ((uint64_t) req + msg->dma[0].offset);
277 		dmap = dma_ptr;
278 		*dmap = htole64(dma_data.idi_paddr);
279 	}
280 
281 	ret = bnxt_hwrm_passthrough(softc, req, msg->len_req, resp, msg->len_resp, msg->timeout);
282 	if(ret)
283 		goto end;
284 
285 	if (num_ind) {
286 		if ((msg->dma[0].read_or_write)) {
287 			if (copyout(dma_data.idi_vaddr,
288 				    (void *)msg->dma[0].data,
289 				    msg->dma[0].length)) {
290 				device_printf(softc->dev, "%s:%d Failed to copy data"
291 					      "to user\n", __FUNCTION__, __LINE__);
292 				ret = -EFAULT;
293 				goto end;
294 			}
295 		}
296 	}
297 
298 	if (copyout(resp, (void *) msg->usr_resp, msg->len_resp)) {
299 		device_printf(softc->dev, "%s:%d Failed to copy response to user\n",
300 			      __FUNCTION__, __LINE__);
301 		ret = -EFAULT;
302 		goto end;
303 	}
304 
305 end:
306 	if (req)
307 		free(req, M_BNXT);
308 	if (resp)
309 		free(resp, M_BNXT);
310 	if (msg2)
311 		free(msg2, M_BNXT);
312 	if (dma_data.idi_paddr)
313 		iflib_dma_free(&dma_data);
314 	return ret;
315 }
316 
317 static int
318 bnxt_mgmt_get_dev_info(struct cdev *dev, u_long cmd, caddr_t data,
319 		       int flag, struct thread *td)
320 {
321 	struct bnxt_softc *softc = NULL;
322 	struct bnxt_dev_info dev_info;
323 	void *user_ptr;
324 	uint32_t dev_sn_lo, dev_sn_hi;
325 	int dev_sn_offset = 0;
326 	char dsn[16];
327 	uint16_t lnk;
328 	int capreg;
329 
330 	memcpy(&user_ptr, data, sizeof(user_ptr));
331 	if (copyin(user_ptr, &dev_info, sizeof(dev_info))) {
332 		printf("%s: %s:%d Failed to copy data from user\n",
333 			DRIVER_NAME, __FUNCTION__, __LINE__);
334 		return -EFAULT;
335 	}
336 
337 	softc = bnxt_find_dev(0, 0, 0, dev_info.nic_info.dev_name);
338 	if (!softc) {
339 		printf("%s: %s:%d unable to find softc reference\n",
340 			DRIVER_NAME, __FUNCTION__, __LINE__);
341 		return -ENODEV;
342 	}
343 
344 	strncpy(dev_info.nic_info.driver_version, bnxt_driver_version, 64);
345 	strncpy(dev_info.nic_info.driver_name, device_get_name(softc->dev), 64);
346 	dev_info.pci_info.domain_no = softc->domain;
347 	dev_info.pci_info.bus_no = softc->bus;
348 	dev_info.pci_info.device_no = softc->slot;
349 	dev_info.pci_info.function_no = softc->function;
350 	dev_info.pci_info.vendor_id = pci_get_vendor(softc->dev);
351 	dev_info.pci_info.device_id = pci_get_device(softc->dev);
352 	dev_info.pci_info.sub_system_vendor_id = pci_get_subvendor(softc->dev);
353 	dev_info.pci_info.sub_system_device_id = pci_get_subdevice(softc->dev);
354 	dev_info.pci_info.revision = pci_read_config(softc->dev, PCIR_REVID, 1);
355 	dev_info.pci_info.chip_rev_id = (dev_info.pci_info.device_id << 16);
356 	dev_info.pci_info.chip_rev_id |= dev_info.pci_info.revision;
357 	if (pci_find_extcap(softc->dev, PCIZ_SERNUM, &dev_sn_offset)) {
358 		device_printf(softc->dev, "%s:%d device serial number is not found"
359 			      "or not supported\n", __FUNCTION__, __LINE__);
360 	} else {
361 		dev_sn_lo = pci_read_config(softc->dev, dev_sn_offset + 4, 4);
362 		dev_sn_hi = pci_read_config(softc->dev, dev_sn_offset + 8, 4);
363 		snprintf(dsn, sizeof(dsn), "%02x%02x%02x%02x%02x%02x%02x%02x",
364 			 (dev_sn_lo & 0x000000FF),
365 			 (dev_sn_lo >> 8) & 0x0000FF,
366 			 (dev_sn_lo >> 16) & 0x00FF,
367 			 (dev_sn_lo >> 24 ) & 0xFF,
368 			 (dev_sn_hi & 0x000000FF),
369 			 (dev_sn_hi >> 8) & 0x0000FF,
370 			 (dev_sn_hi >> 16) & 0x00FF,
371 			 (dev_sn_hi >> 24 ) & 0xFF);
372 		strncpy(dev_info.nic_info.device_serial_number, dsn, sizeof(dsn));
373 	}
374 
375 	if_t ifp = iflib_get_ifp(softc->ctx);
376 	dev_info.nic_info.mtu = if_getmtu(ifp);
377 	memcpy(dev_info.nic_info.mac, softc->func.mac_addr, ETHER_ADDR_LEN);
378 
379 	if (pci_find_cap(softc->dev, PCIY_EXPRESS, &capreg)) {
380 		device_printf(softc->dev, "%s:%d pci link capability is not found"
381 			      "or not supported\n", __FUNCTION__, __LINE__);
382 	} else {
383 		lnk = pci_read_config(softc->dev, capreg + PCIER_LINK_STA, 2);
384 		dev_info.nic_info.pci_link_speed = (lnk & PCIEM_LINK_STA_SPEED);
385 		dev_info.nic_info.pci_link_width = (lnk & PCIEM_LINK_STA_WIDTH) >> 4;
386 	}
387 
388 	if (copyout(&dev_info, user_ptr, sizeof(dev_info))) {
389 		device_printf(softc->dev, "%s:%d Failed to copy data to user\n",
390 			      __FUNCTION__, __LINE__);
391 		return -EFAULT;
392 	}
393 
394 	return 0;
395 }
396 
397 /*
398  * IOCTL entry point.
399  */
400 static int
401 bnxt_mgmt_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag,
402 		struct thread *td)
403 {
404 	int ret = 0;
405 
406 	switch(cmd) {
407 	case BNXT_MGMT_OPCODE_GET_DEV_INFO:
408 		ret = bnxt_mgmt_get_dev_info(dev, cmd, data, flag, td);
409 		break;
410 	case BNXT_MGMT_OPCODE_PASSTHROUGH_HWRM:
411 		mtx_lock(&mgmt_lock);
412 		ret = bnxt_mgmt_process_hwrm(dev, cmd, data, flag, td);
413 		mtx_unlock(&mgmt_lock);
414 		break;
415 	case BNXT_MGMT_OPCODE_DCB_OPS:
416 		ret = bnxt_mgmt_process_dcb(dev, cmd, data, flag, td);
417 		break;
418 	default:
419 		printf("%s: Unknown command 0x%lx\n", DRIVER_NAME, cmd);
420 		ret = -EINVAL;
421 		break;
422 	}
423 
424 	return ret;
425 }
426 
427 static int
428 bnxt_mgmt_close(struct cdev *dev, int flags, int devtype, struct thread *td)
429 {
430 	return (0);
431 }
432 
433 static int
434 bnxt_mgmt_open(struct cdev *dev, int flags, int devtype, struct thread *td)
435 {
436 	return (0);
437 }
438 
439 DEV_MODULE(bnxt_mgmt, bnxt_mgmt_loader, NULL);
440 
441