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 "bnxt_coredump.h" 33 #include "bnxt_log.h" 34 #include <dev/pci/pcireg.h> 35 #include <dev/pci/pcivar.h> 36 #include <sys/endian.h> 37 #include <sys/lock.h> 38 39 /* Function prototypes */ 40 static d_open_t bnxt_mgmt_open; 41 static d_close_t bnxt_mgmt_close; 42 static d_ioctl_t bnxt_mgmt_ioctl; 43 44 /* Character device entry points */ 45 static struct cdevsw bnxt_mgmt_cdevsw = { 46 .d_version = D_VERSION, 47 .d_open = bnxt_mgmt_open, 48 .d_close = bnxt_mgmt_close, 49 .d_ioctl = bnxt_mgmt_ioctl, 50 .d_name = "bnxt_mgmt", 51 }; 52 53 /* Global vars */ 54 static struct cdev *bnxt_mgmt_dev; 55 struct sx mgmt_lock; 56 57 MALLOC_DEFINE(M_BNXT, "bnxt_mgmt_buffer", "buffer for bnxt_mgmt module"); 58 59 60 static uint32_t 61 bnxt_get_driver_coredump_len(struct bnxt_softc *softc) 62 { 63 64 uint32_t type, i, j, n; 65 uint32_t buf_size = 0; 66 int ctx_page_count = 0; 67 int segment_len = 0; 68 int driver_segment_record_len = 0; 69 uint32_t dump_len = 0; 70 int record_len = sizeof(struct bnxt_driver_segment_record); 71 struct bnxt_ctx_mem_info *ctx = softc->ctx_mem; 72 73 if (!ctx) 74 return (dump_len); 75 76 for (type = BNXT_CTX_SRT_TRACE; type <= BNXT_CTX_ROCE_HWRM_TRACE; 77 type++) { 78 struct bnxt_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; 79 struct bnxt_ctx_pg_info *ctx_pg = ctxm->pg_info; 80 81 if (!ctx_pg) 82 continue; 83 84 if (ctxm->instance_bmap) 85 n = bitcount32(ctxm->instance_bmap); 86 else 87 n = 1; 88 89 for (i = 0; i < n; i++) { 90 struct bnxt_ring_mem_info *rmem = &ctx_pg->ring_mem; 91 92 if (ctx_pg->nr_pages > MAX_CTX_PAGES || 93 ctx_pg->ctx_pg_tbl) { 94 int k = 0, nr_tbls = rmem->nr_pages; 95 96 for (k = 0; k < nr_tbls; k++) { 97 struct bnxt_ctx_pg_info *pg_tbl; 98 struct bnxt_ring_mem_info *rmem2; 99 100 pg_tbl = ctx_pg->ctx_pg_tbl[k]; 101 if (!pg_tbl) 102 continue; 103 rmem2 = &pg_tbl->ring_mem; 104 for (j = 0; j < rmem2->nr_pages; j++) { 105 if (!rmem2->pg_arr[j].idi_vaddr) 106 continue; 107 ctx_page_count++; 108 } 109 } 110 } else { 111 struct bnxt_ring_mem_info *rmem2 = rmem; 112 113 for (j = 0; j < rmem2->nr_pages; j++) { 114 if (!rmem2->pg_arr[j].idi_vaddr) 115 continue; 116 ctx_page_count++; 117 } 118 } 119 } 120 segment_len += 64; 121 driver_segment_record_len += record_len; 122 } 123 124 buf_size = driver_segment_record_len + segment_len + 125 (ctx_page_count * 4096); 126 127 return (buf_size); 128 } 129 130 inline void 131 bnxt_bs_trace_check_wrapping(struct bnxt_bs_trace_info *bs_trace, 132 u32 offset) 133 { 134 if (!bs_trace->wrapped && 135 *bs_trace->magic_byte != BNXT_TRACE_BUF_MAGIC_BYTE) 136 bs_trace->wrapped = 1; 137 bs_trace->last_offset = offset; 138 } 139 140 141 142 static int 143 bnxt_hwrm_dbg_log_buffer_flush(struct bnxt_softc *bp, u16 type, u32 flags, 144 u32 *offset) 145 { 146 int status = 0; 147 148 hwrm_dbg_log_buffer_flush_input_t buff_flush_req; 149 hwrm_dbg_log_buffer_flush_output_t *buff_flush_resp = 150 (hwrm_dbg_log_buffer_flush_output_t *)(void *) 151 bp->hwrm_cmd_resp.idi_vaddr; 152 bnxt_hwrm_cmd_hdr_init(bp, &buff_flush_req, HWRM_DBG_LOG_BUFFER_FLUSH); 153 buff_flush_req.type = type; 154 buff_flush_req.flags = flags; 155 156 status = hwrm_send_message(bp, &buff_flush_req, sizeof(buff_flush_req)); 157 if (!status) 158 *offset = buff_flush_resp->current_buffer_offset; 159 return (status); 160 } 161 162 static void 163 bnxt_fill_driver_segment_record(struct bnxt_softc *bp, 164 struct bnxt_driver_segment_record *drv_seg_rec, 165 struct bnxt_ctx_mem_type *ctxm, uint16_t type) 166 { 167 struct bnxt_bs_trace_info *bs_trace = &bp->bs_trace[type]; 168 uint32_t offset; 169 170 if (bnxt_hwrm_dbg_log_buffer_flush(bp, type, 0, &offset) == 0) { 171 bnxt_bs_trace_check_wrapping(bs_trace, offset); 172 } 173 drv_seg_rec->max_entries = ctxm->max_entries; 174 drv_seg_rec->entry_size = ctxm->entry_size; 175 drv_seg_rec->offset = bs_trace->last_offset; 176 drv_seg_rec->wrapped = bs_trace->wrapped; 177 } 178 179 static void 180 bnxt_retrieve_driver_coredump(struct bnxt_softc *softc, void *buf, 181 uint16_t type, uint32_t *seg_len) 182 { 183 struct bnxt_driver_segment_record drv_seg_rec = {0}; 184 struct bnxt_ctx_mem_info *ctx = softc->ctx_mem; 185 struct bnxt_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; 186 struct bnxt_ctx_pg_info *ctx_pg = ctxm->pg_info; 187 uint32_t dump_len, data_offset, record_len, seg_hdr_len; 188 uint32_t i, j, k, n = 1, nr_tbls; 189 190 dump_len = 0; 191 192 record_len = sizeof(struct bnxt_driver_segment_record); 193 seg_hdr_len = sizeof(struct bnxt_coredump_segment_hdr); 194 data_offset = seg_hdr_len + record_len; 195 196 bnxt_fill_driver_segment_record(softc, &drv_seg_rec, ctxm, 197 (type - BNXT_CTX_SRT_TRACE)); 198 199 for (i = 0; i < n; i++) { 200 struct bnxt_ring_mem_info *rmem = &ctx_pg->ring_mem; 201 202 if (ctx_pg->nr_pages > MAX_CTX_PAGES || ctx_pg->ctx_pg_tbl) { 203 nr_tbls = rmem->nr_pages; 204 for (j = 0; j < nr_tbls; j++) { 205 struct bnxt_ctx_pg_info *pg_tbl; 206 struct bnxt_ring_mem_info *rmem2; 207 208 pg_tbl = ctx_pg->ctx_pg_tbl[j]; 209 if (!pg_tbl) 210 continue; 211 rmem2 = &pg_tbl->ring_mem; 212 for (k = 0; k < rmem2->nr_pages; k++) { 213 if (!rmem2->pg_arr[k].idi_vaddr) 214 continue; 215 memcpy((uint8_t *)buf + data_offset, 216 rmem2->pg_arr[k].idi_vaddr, 217 rmem2->page_size); 218 data_offset += rmem2->page_size; 219 dump_len += rmem2->page_size; 220 } 221 } 222 } else { 223 for (k = 0; k < rmem->nr_pages; k++) { 224 if (!rmem->pg_arr[k].idi_vaddr) 225 continue; 226 memcpy((uint8_t *)buf + data_offset, 227 rmem->pg_arr[k].idi_vaddr, 228 rmem->page_size); 229 data_offset += rmem->page_size; 230 dump_len += rmem->page_size; 231 } 232 } 233 } 234 memcpy((uint8_t *)buf + seg_hdr_len, &drv_seg_rec, record_len); 235 *seg_len = dump_len + record_len; 236 } 237 238 void 239 bnxt_get_ctx_coredump(struct bnxt_softc *softc, void *buf) 240 { 241 struct bnxt_ctx_mem_info *ctx = softc->ctx_mem; 242 struct bnxt_coredump_segment_hdr seg_hdr; 243 uint32_t type = 0, i = 0; 244 uint32_t seg_hdr_len = 0; 245 246 seg_hdr_len = sizeof(seg_hdr); 247 for (type = BNXT_CTX_SRT_TRACE, i = DRV_SRT_TRACE_SEG_ID; 248 type <= BNXT_CTX_ROCE_HWRM_TRACE; type++, i++) { 249 struct bnxt_ctx_mem_type *ctxm = &ctx->ctx_arr[type]; 250 uint16_t comp_id = DRV_COREDUMP_COMP_ID; 251 uint16_t seg_id = i; 252 uint32_t seg_len = 0; 253 254 ctxm = &ctx->ctx_arr[type]; 255 256 if (!(ctxm->flags & BNXT_CTX_MEM_TYPE_VALID) || 257 !ctxm->mem_valid) 258 continue; 259 260 bnxt_retrieve_driver_coredump(softc, buf, type, &seg_len); 261 262 bnxt_fill_coredump_seg_hdr(softc, &seg_hdr, NULL, seg_len, 263 0, 0, 0, comp_id, seg_id); 264 265 memcpy((uint8_t *)buf, &seg_hdr, seg_hdr_len); 266 buf = (uint8_t *)buf + seg_hdr_len + seg_len; 267 } 268 } 269 270 /* DDR Crash Dump IOCTL handler */ 271 static int 272 bnxt_mgmt_crash_dump(struct cdev *dev, u_long cmd, caddr_t data, 273 int flag, struct thread *td) 274 { 275 struct bnxt_softc *softc = NULL; 276 struct bnxt_mgmt_crash_dump mgmt_crash_dump = {0}; 277 void *user_ptr; 278 int ret = 0; 279 void *dump_buf = NULL; 280 uint32_t dump_len; 281 282 memcpy(&user_ptr, data, sizeof(user_ptr)); 283 if (copyin(user_ptr, &mgmt_crash_dump, sizeof(mgmt_crash_dump))) { 284 printf("%s: %s:%d Failed to copy data from user\n", 285 DRIVER_NAME, __func__, __LINE__); 286 return (-EFAULT); 287 } 288 softc = bnxt_find_dev(mgmt_crash_dump.hdr.domain, 289 mgmt_crash_dump.hdr.bus, 290 mgmt_crash_dump.hdr.devfn, NULL); 291 if (!softc) { 292 printf("%s: %s:%d unable to find softc reference\n", 293 DRIVER_NAME, __func__, __LINE__); 294 return (-ENODEV); 295 } 296 297 switch (mgmt_crash_dump.op) { 298 case BNXT_MGMT_SET_DUMP_FLAG: 299 if (mgmt_crash_dump.req.set_flag.dump_flag > 300 BNXT_DUMP_LIVE_WITH_CTX_L1_CACHE) { 301 device_printf(softc->dev, 302 "Supports only Live(0), Crash(1), Driver(2), " 303 "Live with cached context(3) dumps.\n"); 304 ret = -EINVAL; 305 break; 306 } 307 308 if (mgmt_crash_dump.req.set_flag.dump_flag == BNXT_DUMP_CRASH) { 309 if (softc->fw_dbg_cap & BNXT_FW_DBG_CAP_CRASHDUMP_SOC) { 310 device_printf(softc->dev, 311 "Cannot collect crash dump as TEE is not supported.\n"); 312 ret = -ENOTSUP; 313 break; 314 } else if (!(softc->fw_dbg_cap & 315 BNXT_FW_DBG_CAP_CRASHDUMP_HOST)) { 316 device_printf(softc->dev, 317 "FW does not support crash dump collection.\n"); 318 ret = -ENOTSUP; 319 break; 320 } 321 } 322 323 softc->dump_flag = mgmt_crash_dump.req.set_flag.dump_flag; 324 break; 325 326 case BNXT_MGMT_GET_DUMP_FLAG: 327 if (softc->hwrm_spec_code < 0x10801) { 328 ret = -ENOTSUP; 329 break; 330 } 331 332 /* Build FW version - same as Linux bnxt_get_dump_flag() */ 333 mgmt_crash_dump.req.get_flag.version = 334 (softc->ver_resp.hwrm_fw_maj_8b << 24) | 335 (softc->ver_resp.hwrm_fw_min_8b << 16) | 336 (softc->ver_resp.hwrm_fw_bld_8b << 8) | 337 (softc->ver_resp.hwrm_fw_rsvd_8b); 338 339 mgmt_crash_dump.req.get_flag.dump_flag = softc->dump_flag; 340 mgmt_crash_dump.req.get_flag.dump_len = 341 bnxt_get_coredump_length(softc, softc->dump_flag); 342 break; 343 344 case BNXT_MGMT_GET_DUMP_DATA: 345 if (softc->hwrm_spec_code < 0x10801) { 346 ret = -ENOTSUP; 347 break; 348 } 349 350 dump_len = bnxt_get_coredump_length(softc, 351 mgmt_crash_dump.req.get_data.dump_flag); 352 if (dump_len == 0) { 353 device_printf(softc->dev, "No dump data available\n"); 354 ret = -ENOENT; 355 break; 356 } 357 358 if (mgmt_crash_dump.req.get_data.buffer_size < dump_len) { 359 device_printf(softc->dev, 360 "Buffer too small: need %u bytes, got %zu bytes\n", 361 dump_len, mgmt_crash_dump.req.get_data.buffer_size); 362 mgmt_crash_dump.req.get_data.dump_len = dump_len; 363 ret = -ENOSPC; 364 break; 365 } 366 367 dump_buf = malloc(dump_len, M_BNXT, M_WAITOK); 368 if (!dump_buf) { 369 ret = -ENOMEM; 370 break; 371 } 372 373 ret = bnxt_get_coredump(softc, 374 mgmt_crash_dump.req.get_data.dump_flag, 375 dump_buf, &dump_len); 376 if (ret) { 377 device_printf(softc->dev, 378 "Failed to get coredump: %d\n", ret); 379 free(dump_buf, M_BNXT); 380 break; 381 } 382 383 if (copyout(dump_buf, 384 mgmt_crash_dump.req.get_data.dump_buffer, dump_len)) { 385 device_printf(softc->dev, 386 "%s:%d Failed to copy dump data to user\n", 387 __func__, __LINE__); 388 ret = -EFAULT; 389 free(dump_buf, M_BNXT); 390 break; 391 } 392 393 mgmt_crash_dump.req.get_data.dump_len = dump_len; 394 mgmt_crash_dump.req.get_data.dump_flag = softc->dump_flag; 395 free(dump_buf, M_BNXT); 396 break; 397 398 default: 399 device_printf(softc->dev, "%s:%d Invalid op 0x%x\n", 400 __func__, __LINE__, mgmt_crash_dump.op); 401 ret = -EFAULT; 402 break; 403 } 404 405 if (!ret && copyout(&mgmt_crash_dump, user_ptr, 406 sizeof(mgmt_crash_dump))) { 407 device_printf(softc->dev, 408 "%s:%d Failed to copy response to user\n", 409 __func__, __LINE__); 410 ret = -EFAULT; 411 } 412 413 return (ret); 414 } 415 416 /* 417 * This function is called by the kld[un]load(2) system calls to 418 * determine what actions to take when a module is loaded or unloaded. 419 */ 420 static int 421 bnxt_mgmt_loader(struct module *m, int what, void *arg) 422 { 423 int error = 0; 424 425 switch (what) { 426 case MOD_LOAD: 427 error = make_dev_p(MAKEDEV_CHECKNAME | MAKEDEV_WAITOK, 428 &bnxt_mgmt_dev, 429 &bnxt_mgmt_cdevsw, 430 0, 431 UID_ROOT, 432 GID_WHEEL, 433 0600, 434 "bnxt_mgmt"); 435 if (error != 0) { 436 printf("%s: %s:%s:%d Failed to create the" 437 "bnxt_mgmt device node\n", DRIVER_NAME, 438 __FILE__, __func__, __LINE__); 439 return (error); 440 } 441 442 sx_init(&mgmt_lock, "BNXT MGMT Lock"); 443 444 break; 445 case MOD_UNLOAD: 446 sx_destroy(&mgmt_lock); 447 destroy_dev(bnxt_mgmt_dev); 448 break; 449 default: 450 error = EOPNOTSUPP; 451 break; 452 } 453 454 return (error); 455 } 456 457 static int 458 bnxt_mgmt_process_dcb(struct cdev *dev, u_long cmd, caddr_t data, 459 int flag, struct thread *td) 460 { 461 struct bnxt_softc *softc = NULL; 462 struct bnxt_mgmt_dcb mgmt_dcb = {}; 463 void *user_ptr; 464 int ret = 0; 465 466 memcpy(&user_ptr, data, sizeof(user_ptr)); 467 if (copyin(user_ptr, &mgmt_dcb, sizeof(mgmt_dcb))) { 468 printf("%s: %s:%d Failed to copy data from user\n", 469 DRIVER_NAME, __func__, __LINE__); 470 return -EFAULT; 471 } 472 softc = bnxt_find_dev(mgmt_dcb.hdr.domain, mgmt_dcb.hdr.bus, 473 mgmt_dcb.hdr.devfn, NULL); 474 if (!softc) { 475 printf("%s: %s:%d unable to find softc reference\n", 476 DRIVER_NAME, __func__, __LINE__); 477 return -ENODEV; 478 } 479 480 switch (mgmt_dcb.op) { 481 case BNXT_MGMT_DCB_GET_ETS: 482 bnxt_dcb_ieee_getets(softc, &mgmt_dcb.req.ets); 483 break; 484 case BNXT_MGMT_DCB_SET_ETS: 485 bnxt_dcb_ieee_setets(softc, &mgmt_dcb.req.ets); 486 break; 487 case BNXT_MGMT_DCB_GET_PFC: 488 bnxt_dcb_ieee_getpfc(softc, &mgmt_dcb.req.pfc); 489 break; 490 case BNXT_MGMT_DCB_SET_PFC: 491 bnxt_dcb_ieee_setpfc(softc, &mgmt_dcb.req.pfc); 492 break; 493 case BNXT_MGMT_DCB_SET_APP: 494 bnxt_dcb_ieee_setapp(softc, &mgmt_dcb.req.app_tlv.app[0]); 495 break; 496 case BNXT_MGMT_DCB_DEL_APP: 497 bnxt_dcb_ieee_delapp(softc, &mgmt_dcb.req.app_tlv.app[0]); 498 break; 499 case BNXT_MGMT_DCB_LIST_APP: 500 bnxt_dcb_ieee_listapp(softc, &mgmt_dcb.req.app_tlv.app[0], 501 nitems(mgmt_dcb.req.app_tlv.app), 502 &mgmt_dcb.req.app_tlv.num_app); 503 break; 504 default: 505 device_printf(softc->dev, "%s:%d Invalid op 0x%x\n", 506 __func__, __LINE__, mgmt_dcb.op); 507 ret = -EFAULT; 508 goto end; 509 } 510 511 if (copyout(&mgmt_dcb, user_ptr, sizeof(mgmt_dcb))) { 512 device_printf(softc->dev, "%s:%d Failed to copy response to user\n", 513 __func__, __LINE__); 514 ret = -EFAULT; 515 goto end; 516 } 517 518 end: 519 return ret; 520 } 521 522 static int 523 bnxt_mgmt_process_hwrm(struct cdev *dev, u_long cmd, caddr_t data, 524 int flag, struct thread *td) 525 { 526 struct bnxt_softc *softc = NULL; 527 struct bnxt_mgmt_req mgmt_req = {}; 528 struct bnxt_mgmt_fw_msg msg_temp, *msg, *msg2 = NULL; 529 void *user_ptr, *req, *resp; 530 int ret = 0, num_allocated = 0, i; 531 uint16_t num_ind = 0; 532 533 memcpy(&user_ptr, data, sizeof(user_ptr)); 534 if (copyin(user_ptr, &mgmt_req, sizeof(struct bnxt_mgmt_req))) { 535 printf("%s: %s:%d Failed to copy data from user\n", 536 DRIVER_NAME, __func__, __LINE__); 537 return -EFAULT; 538 } 539 softc = bnxt_find_dev(mgmt_req.hdr.domain, mgmt_req.hdr.bus, 540 mgmt_req.hdr.devfn, NULL); 541 if (!softc) { 542 printf("%s: %s:%d unable to find softc reference\n", 543 DRIVER_NAME, __func__, __LINE__); 544 return -ENODEV; 545 } 546 547 if (copyin((void*)mgmt_req.req.hreq, &msg_temp, sizeof(msg_temp))) { 548 device_printf(softc->dev, "%s:%d Failed to copy data from user\n", 549 __func__, __LINE__); 550 return -EFAULT; 551 } 552 553 if (msg_temp.len_req > BNXT_MGMT_MAX_HWRM_REQ_LENGTH || 554 msg_temp.len_resp > BNXT_MGMT_MAX_HWRM_RESP_LENGTH) { 555 device_printf(softc->dev, "%s:%d Invalid length\n", 556 __func__, __LINE__); 557 return -EINVAL; 558 } 559 560 if (msg_temp.num_dma_indications > MAX_NUM_DMA_INDICATIONS) { 561 device_printf(softc->dev, "%s:%d Max num_dma_indications " 562 "supported is %d\n", __func__, __LINE__, 563 MAX_NUM_DMA_INDICATIONS); 564 return -EINVAL; 565 } 566 567 req = malloc(msg_temp.len_req, M_BNXT, M_WAITOK | M_ZERO); 568 resp = malloc(msg_temp.len_resp, M_BNXT, M_WAITOK | M_ZERO); 569 570 if (copyin((void *)msg_temp.usr_req, req, msg_temp.len_req)) { 571 device_printf(softc->dev, "%s:%d Failed to copy data from user\n", 572 __func__, __LINE__); 573 ret = -EFAULT; 574 goto end; 575 } 576 577 msg = &msg_temp; 578 num_ind = msg_temp.num_dma_indications; 579 if (num_ind) { 580 int size; 581 void *dma_ptr; 582 uint64_t *dmap; 583 584 size = sizeof(struct bnxt_mgmt_fw_msg) + 585 (num_ind * sizeof(struct dma_info)); 586 587 msg2 = malloc(size, M_BNXT, M_WAITOK | M_ZERO); 588 589 if (copyin((void *)mgmt_req.req.hreq, msg2, size)) { 590 device_printf(softc->dev, "%s:%d Failed to copy" 591 "data from user\n", __func__, __LINE__); 592 ret = -EFAULT; 593 goto end; 594 } 595 msg = msg2; 596 597 for (i = 0; i < num_ind; i++) { 598 599 if (msg->dma[i].length == 0) { 600 device_printf(softc->dev, 601 "%s:%d i:%d Invalid DMA memory length\n", 602 __func__, __LINE__, i); 603 ret = -ENOMEM; 604 goto end; 605 } 606 607 memset(&softc->mgmt_dma_data[i], 0, sizeof(struct iflib_dma_info)); 608 609 ret = iflib_dma_alloc(softc->ctx, msg->dma[i].length, &softc->mgmt_dma_data[i], 610 BUS_DMA_WAITOK); 611 if (ret) { 612 device_printf(softc->dev, 613 "%s:%d iflib_dma_alloc failed with ret = 0x%x\n", 614 __func__, __LINE__, ret); 615 ret = -ENOMEM; 616 goto end; 617 } 618 619 num_allocated++; 620 if (!(msg->dma[i].read_or_write)) { 621 if (copyin((void *)msg->dma[i].data, 622 softc->mgmt_dma_data[i].idi_vaddr, 623 msg->dma[i].length)) { 624 device_printf(softc->dev, 625 "%s:%d Failed to copy data from user\n", 626 __func__, __LINE__); 627 ret = -EFAULT; 628 goto end; 629 } 630 } 631 dma_ptr = (void *) ((uint64_t) req + msg->dma[i].offset); 632 dmap = dma_ptr; 633 *dmap = htole64(softc->mgmt_dma_data[i].idi_paddr); 634 } 635 } 636 637 ret = bnxt_hwrm_passthrough(softc, req, msg->len_req, resp, msg->len_resp, msg->timeout); 638 if (ret) 639 goto end; 640 641 for (i = 0; i < num_ind; i++) { 642 if ((msg->dma[i].read_or_write)) { 643 if (copyout(softc->mgmt_dma_data[i].idi_vaddr, 644 (void *)msg->dma[i].data, 645 msg->dma[i].length)) { 646 device_printf(softc->dev, 647 "%s:%d Failed to copy data to user\n", 648 __func__, __LINE__); 649 ret = -EFAULT; 650 goto end; 651 } 652 } 653 } 654 655 if (copyout(resp, (void *) msg->usr_resp, msg->len_resp)) { 656 device_printf(softc->dev, "%s:%d Failed to copy response to user\n", 657 __func__, __LINE__); 658 ret = -EFAULT; 659 goto end; 660 } 661 662 end: 663 if (req) 664 free(req, M_BNXT); 665 if (resp) 666 free(resp, M_BNXT); 667 if (msg2) 668 free(msg2, M_BNXT); 669 if (num_allocated) { 670 for (i = 0; i < num_allocated; i++) 671 if (softc->mgmt_dma_data[i].idi_paddr) 672 iflib_dma_free(&softc->mgmt_dma_data[i]); 673 } 674 return ret; 675 } 676 677 static int 678 bnxt_mgmt_get_dev_info(struct cdev *dev, u_long cmd, caddr_t data, 679 int flag, struct thread *td) 680 { 681 struct bnxt_softc *softc = NULL; 682 struct bnxt_dev_info dev_info; 683 void *user_ptr; 684 uint32_t dev_sn_lo, dev_sn_hi; 685 int dev_sn_offset = 0; 686 char dsn[16]; 687 uint16_t lnk; 688 int capreg; 689 690 memcpy(&user_ptr, data, sizeof(user_ptr)); 691 if (copyin(user_ptr, &dev_info, sizeof(dev_info))) { 692 printf("%s: %s:%d Failed to copy data from user\n", 693 DRIVER_NAME, __func__, __LINE__); 694 return -EFAULT; 695 } 696 697 softc = bnxt_find_dev(0, 0, 0, dev_info.nic_info.dev_name); 698 if (!softc) { 699 printf("%s: %s:%d unable to find softc reference\n", 700 DRIVER_NAME, __func__, __LINE__); 701 return -ENODEV; 702 } 703 704 strncpy(dev_info.nic_info.driver_version, bnxt_driver_version, 64); 705 strncpy(dev_info.nic_info.driver_name, device_get_name(softc->dev), 64); 706 dev_info.pci_info.domain_no = softc->domain; 707 dev_info.pci_info.bus_no = softc->bus; 708 dev_info.pci_info.device_no = softc->slot; 709 dev_info.pci_info.function_no = softc->function; 710 dev_info.pci_info.vendor_id = pci_get_vendor(softc->dev); 711 dev_info.pci_info.device_id = pci_get_device(softc->dev); 712 dev_info.pci_info.sub_system_vendor_id = pci_get_subvendor(softc->dev); 713 dev_info.pci_info.sub_system_device_id = pci_get_subdevice(softc->dev); 714 dev_info.pci_info.revision = pci_read_config(softc->dev, PCIR_REVID, 1); 715 dev_info.pci_info.chip_rev_id = (dev_info.pci_info.device_id << 16); 716 dev_info.pci_info.chip_rev_id |= dev_info.pci_info.revision; 717 if (pci_find_extcap(softc->dev, PCIZ_SERNUM, &dev_sn_offset)) { 718 device_printf(softc->dev, "%s:%d device serial number is not found" 719 "or not supported\n", __func__, __LINE__); 720 } else { 721 dev_sn_lo = pci_read_config(softc->dev, dev_sn_offset + 4, 4); 722 dev_sn_hi = pci_read_config(softc->dev, dev_sn_offset + 8, 4); 723 snprintf(dsn, sizeof(dsn), "%02x%02x%02x%02x%02x%02x%02x%02x", 724 (dev_sn_lo & 0x000000FF), 725 (dev_sn_lo >> 8) & 0x0000FF, 726 (dev_sn_lo >> 16) & 0x00FF, 727 (dev_sn_lo >> 24 ) & 0xFF, 728 (dev_sn_hi & 0x000000FF), 729 (dev_sn_hi >> 8) & 0x0000FF, 730 (dev_sn_hi >> 16) & 0x00FF, 731 (dev_sn_hi >> 24 ) & 0xFF); 732 strncpy(dev_info.nic_info.device_serial_number, dsn, sizeof(dsn)); 733 } 734 735 if_t ifp = iflib_get_ifp(softc->ctx); 736 dev_info.nic_info.mtu = if_getmtu(ifp); 737 memcpy(dev_info.nic_info.mac, softc->func.mac_addr, ETHER_ADDR_LEN); 738 739 if (pci_find_cap(softc->dev, PCIY_EXPRESS, &capreg)) { 740 device_printf(softc->dev, "%s:%d pci link capability is not found" 741 "or not supported\n", __func__, __LINE__); 742 } else { 743 lnk = pci_read_config(softc->dev, capreg + PCIER_LINK_STA, 2); 744 dev_info.nic_info.pci_link_speed = (lnk & PCIEM_LINK_STA_SPEED); 745 dev_info.nic_info.pci_link_width = (lnk & PCIEM_LINK_STA_WIDTH) >> 4; 746 } 747 748 if (copyout(&dev_info, user_ptr, sizeof(dev_info))) { 749 device_printf(softc->dev, "%s:%d Failed to copy data to user\n", 750 __func__, __LINE__); 751 return (-EFAULT); 752 } 753 754 return (0); 755 } 756 757 static int 758 bnxt_mgmt_drv_dump(struct cdev *dev, u_long cmd, caddr_t data, 759 int flag, struct thread *td) 760 { 761 struct bnxt_softc *softc = NULL; 762 void *buf = NULL; 763 struct bnxt_logger *logger = NULL, *lg_tmp; 764 int buf_sz = 0; 765 struct bnxt_mgmt_drv_dump mgmt_drv_dump = {}; 766 void *user_ptr; 767 int ret = 0, offset = 0; 768 769 memcpy(&user_ptr, data, sizeof(user_ptr)); 770 if (copyin(user_ptr, &mgmt_drv_dump, sizeof(mgmt_drv_dump))) { 771 printf("%s: %s:%d Failed to copy data from user\n", 772 DRIVER_NAME, __func__, __LINE__); 773 return (-EFAULT); 774 } 775 softc = bnxt_find_dev(mgmt_drv_dump.hdr.domain, mgmt_drv_dump.hdr.bus, 776 mgmt_drv_dump.hdr.devfn, NULL); 777 if (!softc) { 778 printf("%s: %s:%d unable to find softc reference\n", 779 DRIVER_NAME, __func__, __LINE__); 780 return (-ENODEV); 781 } 782 783 switch (mgmt_drv_dump.op) { 784 case BNXT_MGMT_GET_DRV_DUMP_SIZE: 785 mtx_lock(&softc->log_lock); 786 TAILQ_FOREACH_SAFE(logger, &softc->loggers_list, list, lg_tmp) 787 buf_sz += logger->buffer_size; 788 mtx_unlock(&softc->log_lock); 789 790 mgmt_drv_dump.buf_size = buf_sz + 791 bnxt_get_driver_coredump_len(softc); 792 if (copyout(&mgmt_drv_dump, user_ptr, sizeof(mgmt_drv_dump))) { 793 device_printf(softc->dev, 794 "%s:%d Failed to copy response to user\n", 795 __func__, __LINE__); 796 ret = -EFAULT; 797 } 798 break; 799 case BNXT_MGMT_GET_DRV_DUMP: 800 buf = malloc(mgmt_drv_dump.buf_size, M_BNXT, M_WAITOK); 801 /*Dump the driver logs */ 802 memset(buf, 0, mgmt_drv_dump.buf_size); 803 offset = bnxt_start_logging_driver_coredump(softc, buf); 804 805 if (!offset) { 806 device_printf(softc->dev, 807 "%s:%d Drivers logs are empty\n", 808 __func__, __LINE__); 809 } 810 811 /* Dump the ctx logs*/ 812 if (softc->ctx_mem) 813 bnxt_get_ctx_coredump(softc, (uint8_t *)buf + offset); 814 815 if (copyout(buf, mgmt_drv_dump.buf, mgmt_drv_dump.buf_size)) { 816 device_printf(softc->dev, 817 "%s:%d Failed to copy response to user\n", 818 __func__, __LINE__); 819 ret = -EFAULT; 820 } 821 822 free(buf, M_BNXT); 823 break; 824 default: 825 device_printf(softc->dev, "%s:%d Invalid op 0x%x\n", 826 __func__, __LINE__, mgmt_drv_dump.op); 827 ret = -EFAULT; 828 } 829 830 return (ret); 831 } 832 833 /* 834 * IOCTL entry point. 835 */ 836 static int 837 bnxt_mgmt_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, 838 struct thread *td) 839 { 840 int ret = 0; 841 842 switch(cmd) { 843 case IO_BNXT_MGMT_OPCODE_GET_DEV_INFO: 844 case IOW_BNXT_MGMT_OPCODE_GET_DEV_INFO: 845 ret = bnxt_mgmt_get_dev_info(dev, cmd, data, flag, td); 846 break; 847 case IO_BNXT_MGMT_OPCODE_PASSTHROUGH_HWRM: 848 case IOW_BNXT_MGMT_OPCODE_PASSTHROUGH_HWRM: 849 sx_xlock(&mgmt_lock); 850 ret = bnxt_mgmt_process_hwrm(dev, cmd, data, flag, td); 851 sx_xunlock(&mgmt_lock); 852 break; 853 case IO_BNXT_MGMT_OPCODE_DCB_OPS: 854 case IOW_BNXT_MGMT_OPCODE_DCB_OPS: 855 ret = bnxt_mgmt_process_dcb(dev, cmd, data, flag, td); 856 break; 857 case IO_BNXT_MGMT_OPCODE_DRV_DUMP: 858 case IOW_BNXT_MGMT_OPCODE_DRV_DUMP: 859 ret = bnxt_mgmt_drv_dump(dev, cmd, data, flag, td); 860 break; 861 case IO_BNXT_MGMT_OPCODE_CRASH_DUMP: 862 case IOW_BNXT_MGMT_OPCODE_CRASH_DUMP: 863 ret = bnxt_mgmt_crash_dump(dev, cmd, data, flag, td); 864 break; 865 default: 866 printf("%s: Unknown command 0x%lx\n", DRIVER_NAME, cmd); 867 ret = -EINVAL; 868 break; 869 } 870 871 return (ret); 872 } 873 874 static int 875 bnxt_mgmt_close(struct cdev *dev, int flags, int devtype, struct thread *td) 876 { 877 return (0); 878 } 879 880 static int 881 bnxt_mgmt_open(struct cdev *dev, int flags, int devtype, struct thread *td) 882 { 883 return (0); 884 } 885 886 DEV_MODULE(bnxt_mgmt, bnxt_mgmt_loader, NULL); 887 888