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