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 resp = malloc(msg_temp.len_resp, M_BNXT, M_WAITOK | M_ZERO); 209 210 if (copyin((void *)msg_temp.usr_req, req, msg_temp.len_req)) { 211 device_printf(softc->dev, "%s:%d Failed to copy data from user\n", 212 __FUNCTION__, __LINE__); 213 ret = -EFAULT; 214 goto end; 215 } 216 217 msg = &msg_temp; 218 num_ind = msg_temp.num_dma_indications; 219 if (num_ind) { 220 int size; 221 void *dma_ptr; 222 uint64_t *dmap; 223 224 size = sizeof(struct bnxt_mgmt_fw_msg) + 225 (num_ind * sizeof(struct dma_info)); 226 227 msg2 = malloc(size, M_BNXT, M_WAITOK | M_ZERO); 228 229 if (copyin((void *)mgmt_req.req.hreq, msg2, size)) { 230 device_printf(softc->dev, "%s:%d Failed to copy" 231 "data from user\n", __FUNCTION__, __LINE__); 232 ret = -EFAULT; 233 goto end; 234 } 235 msg = msg2; 236 237 ret = iflib_dma_alloc(softc->ctx, msg->dma[0].length, &dma_data, 238 BUS_DMA_NOWAIT); 239 if (ret) { 240 device_printf(softc->dev, "%s:%d iflib_dma_alloc" 241 "failed with ret = 0x%x\n", __FUNCTION__, 242 __LINE__, ret); 243 ret = -ENOMEM; 244 goto end; 245 } 246 247 if (!(msg->dma[0].read_or_write)) { 248 if (copyin((void *)msg->dma[0].data, 249 dma_data.idi_vaddr, 250 msg->dma[0].length)) { 251 device_printf(softc->dev, "%s:%d Failed to copy" 252 "data from user\n", __FUNCTION__, 253 __LINE__); 254 ret = -EFAULT; 255 goto end; 256 } 257 } 258 dma_ptr = (void *) ((uint64_t) req + msg->dma[0].offset); 259 dmap = dma_ptr; 260 *dmap = htole64(dma_data.idi_paddr); 261 } 262 263 ret = bnxt_hwrm_passthrough(softc, req, msg->len_req, resp, msg->len_resp, msg->timeout); 264 if(ret) 265 goto end; 266 267 if (num_ind) { 268 if ((msg->dma[0].read_or_write)) { 269 if (copyout(dma_data.idi_vaddr, 270 (void *)msg->dma[0].data, 271 msg->dma[0].length)) { 272 device_printf(softc->dev, "%s:%d Failed to copy data" 273 "to user\n", __FUNCTION__, __LINE__); 274 ret = -EFAULT; 275 goto end; 276 } 277 } 278 } 279 280 if (copyout(resp, (void *) msg->usr_resp, msg->len_resp)) { 281 device_printf(softc->dev, "%s:%d Failed to copy response to user\n", 282 __FUNCTION__, __LINE__); 283 ret = -EFAULT; 284 goto end; 285 } 286 287 end: 288 if (req) 289 free(req, M_BNXT); 290 if (resp) 291 free(resp, M_BNXT); 292 if (msg2) 293 free(msg2, M_BNXT); 294 if (dma_data.idi_paddr) 295 iflib_dma_free(&dma_data); 296 return ret; 297 } 298 299 static int 300 bnxt_mgmt_get_dev_info(struct cdev *dev, u_long cmd, caddr_t data, 301 int flag, struct thread *td) 302 { 303 struct bnxt_softc *softc = NULL; 304 struct bnxt_dev_info dev_info; 305 void *user_ptr; 306 uint32_t dev_sn_lo, dev_sn_hi; 307 int dev_sn_offset = 0; 308 char dsn[16]; 309 uint16_t lnk; 310 int capreg; 311 312 memcpy(&user_ptr, data, sizeof(user_ptr)); 313 if (copyin(user_ptr, &dev_info, sizeof(dev_info))) { 314 printf("%s: %s:%d Failed to copy data from user\n", 315 DRIVER_NAME, __FUNCTION__, __LINE__); 316 return -EFAULT; 317 } 318 319 softc = bnxt_find_dev(0, 0, 0, dev_info.nic_info.dev_name); 320 if (!softc) { 321 printf("%s: %s:%d unable to find softc reference\n", 322 DRIVER_NAME, __FUNCTION__, __LINE__); 323 return -ENODEV; 324 } 325 326 strncpy(dev_info.nic_info.driver_version, bnxt_driver_version, 64); 327 strncpy(dev_info.nic_info.driver_name, device_get_name(softc->dev), 64); 328 dev_info.pci_info.domain_no = softc->domain; 329 dev_info.pci_info.bus_no = softc->bus; 330 dev_info.pci_info.device_no = softc->slot; 331 dev_info.pci_info.function_no = softc->function; 332 dev_info.pci_info.vendor_id = pci_get_vendor(softc->dev); 333 dev_info.pci_info.device_id = pci_get_device(softc->dev); 334 dev_info.pci_info.sub_system_vendor_id = pci_get_subvendor(softc->dev); 335 dev_info.pci_info.sub_system_device_id = pci_get_subdevice(softc->dev); 336 dev_info.pci_info.revision = pci_read_config(softc->dev, PCIR_REVID, 1); 337 dev_info.pci_info.chip_rev_id = (dev_info.pci_info.device_id << 16); 338 dev_info.pci_info.chip_rev_id |= dev_info.pci_info.revision; 339 if (pci_find_extcap(softc->dev, PCIZ_SERNUM, &dev_sn_offset)) { 340 device_printf(softc->dev, "%s:%d device serial number is not found" 341 "or not supported\n", __FUNCTION__, __LINE__); 342 } else { 343 dev_sn_lo = pci_read_config(softc->dev, dev_sn_offset + 4, 4); 344 dev_sn_hi = pci_read_config(softc->dev, dev_sn_offset + 8, 4); 345 snprintf(dsn, sizeof(dsn), "%02x%02x%02x%02x%02x%02x%02x%02x", 346 (dev_sn_lo & 0x000000FF), 347 (dev_sn_lo >> 8) & 0x0000FF, 348 (dev_sn_lo >> 16) & 0x00FF, 349 (dev_sn_lo >> 24 ) & 0xFF, 350 (dev_sn_hi & 0x000000FF), 351 (dev_sn_hi >> 8) & 0x0000FF, 352 (dev_sn_hi >> 16) & 0x00FF, 353 (dev_sn_hi >> 24 ) & 0xFF); 354 strncpy(dev_info.nic_info.device_serial_number, dsn, sizeof(dsn)); 355 } 356 357 if_t ifp = iflib_get_ifp(softc->ctx); 358 dev_info.nic_info.mtu = if_getmtu(ifp); 359 memcpy(dev_info.nic_info.mac, softc->func.mac_addr, ETHER_ADDR_LEN); 360 361 if (pci_find_cap(softc->dev, PCIY_EXPRESS, &capreg)) { 362 device_printf(softc->dev, "%s:%d pci link capability is not found" 363 "or not supported\n", __FUNCTION__, __LINE__); 364 } else { 365 lnk = pci_read_config(softc->dev, capreg + PCIER_LINK_STA, 2); 366 dev_info.nic_info.pci_link_speed = (lnk & PCIEM_LINK_STA_SPEED); 367 dev_info.nic_info.pci_link_width = (lnk & PCIEM_LINK_STA_WIDTH) >> 4; 368 } 369 370 if (copyout(&dev_info, user_ptr, sizeof(dev_info))) { 371 device_printf(softc->dev, "%s:%d Failed to copy data to user\n", 372 __FUNCTION__, __LINE__); 373 return -EFAULT; 374 } 375 376 return 0; 377 } 378 379 /* 380 * IOCTL entry point. 381 */ 382 static int 383 bnxt_mgmt_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 384 struct thread *td) 385 { 386 int ret = 0; 387 388 switch(cmd) { 389 case BNXT_MGMT_OPCODE_GET_DEV_INFO: 390 ret = bnxt_mgmt_get_dev_info(dev, cmd, data, flag, td); 391 break; 392 case BNXT_MGMT_OPCODE_PASSTHROUGH_HWRM: 393 mtx_lock(&mgmt_lock); 394 ret = bnxt_mgmt_process_hwrm(dev, cmd, data, flag, td); 395 mtx_unlock(&mgmt_lock); 396 break; 397 case BNXT_MGMT_OPCODE_DCB_OPS: 398 ret = bnxt_mgmt_process_dcb(dev, cmd, data, flag, td); 399 break; 400 default: 401 printf("%s: Unknown command 0x%lx\n", DRIVER_NAME, cmd); 402 ret = -EINVAL; 403 break; 404 } 405 406 return ret; 407 } 408 409 static int 410 bnxt_mgmt_close(struct cdev *dev, int flags, int devtype, struct thread *td) 411 { 412 return (0); 413 } 414 415 static int 416 bnxt_mgmt_open(struct cdev *dev, int flags, int devtype, struct thread *td) 417 { 418 return (0); 419 } 420 421 DEV_MODULE(bnxt_mgmt, bnxt_mgmt_loader, NULL); 422 423