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