Back to home page

OSCL-LXR

 
 

    


0001 /* Broadcom NetXtreme-C/E network driver.
0002  *
0003  * Copyright (c) 2021 Broadcom Limited
0004  *
0005  * This program is free software; you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation.
0008  */
0009 
0010 #include <linux/types.h>
0011 #include <linux/errno.h>
0012 #include <linux/pci.h>
0013 #include "bnxt_hsi.h"
0014 #include "bnxt.h"
0015 #include "bnxt_hwrm.h"
0016 #include "bnxt_coredump.h"
0017 
0018 static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg,
0019                   struct bnxt_hwrm_dbg_dma_info *info)
0020 {
0021     struct hwrm_dbg_cmn_input *cmn_req = msg;
0022     __le16 *seq_ptr = msg + info->seq_off;
0023     struct hwrm_dbg_cmn_output *cmn_resp;
0024     u16 seq = 0, len, segs_off;
0025     dma_addr_t dma_handle;
0026     void *dma_buf, *resp;
0027     int rc, off = 0;
0028 
0029     dma_buf = hwrm_req_dma_slice(bp, msg, info->dma_len, &dma_handle);
0030     if (!dma_buf) {
0031         hwrm_req_drop(bp, msg);
0032         return -ENOMEM;
0033     }
0034 
0035     hwrm_req_timeout(bp, msg, bp->hwrm_cmd_max_timeout);
0036     cmn_resp = hwrm_req_hold(bp, msg);
0037     resp = cmn_resp;
0038 
0039     segs_off = offsetof(struct hwrm_dbg_coredump_list_output,
0040                 total_segments);
0041     cmn_req->host_dest_addr = cpu_to_le64(dma_handle);
0042     cmn_req->host_buf_len = cpu_to_le32(info->dma_len);
0043     while (1) {
0044         *seq_ptr = cpu_to_le16(seq);
0045         rc = hwrm_req_send(bp, msg);
0046         if (rc)
0047             break;
0048 
0049         len = le16_to_cpu(*((__le16 *)(resp + info->data_len_off)));
0050         if (!seq &&
0051             cmn_req->req_type == cpu_to_le16(HWRM_DBG_COREDUMP_LIST)) {
0052             info->segs = le16_to_cpu(*((__le16 *)(resp +
0053                                   segs_off)));
0054             if (!info->segs) {
0055                 rc = -EIO;
0056                 break;
0057             }
0058 
0059             info->dest_buf_size = info->segs *
0060                     sizeof(struct coredump_segment_record);
0061             info->dest_buf = kmalloc(info->dest_buf_size,
0062                          GFP_KERNEL);
0063             if (!info->dest_buf) {
0064                 rc = -ENOMEM;
0065                 break;
0066             }
0067         }
0068 
0069         if (info->dest_buf) {
0070             if ((info->seg_start + off + len) <=
0071                 BNXT_COREDUMP_BUF_LEN(info->buf_len)) {
0072                 memcpy(info->dest_buf + off, dma_buf, len);
0073             } else {
0074                 rc = -ENOBUFS;
0075                 break;
0076             }
0077         }
0078 
0079         if (cmn_req->req_type ==
0080                 cpu_to_le16(HWRM_DBG_COREDUMP_RETRIEVE))
0081             info->dest_buf_size += len;
0082 
0083         if (!(cmn_resp->flags & HWRM_DBG_CMN_FLAGS_MORE))
0084             break;
0085 
0086         seq++;
0087         off += len;
0088     }
0089     hwrm_req_drop(bp, msg);
0090     return rc;
0091 }
0092 
0093 static int bnxt_hwrm_dbg_coredump_list(struct bnxt *bp,
0094                        struct bnxt_coredump *coredump)
0095 {
0096     struct bnxt_hwrm_dbg_dma_info info = {NULL};
0097     struct hwrm_dbg_coredump_list_input *req;
0098     int rc;
0099 
0100     rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_LIST);
0101     if (rc)
0102         return rc;
0103 
0104     info.dma_len = COREDUMP_LIST_BUF_LEN;
0105     info.seq_off = offsetof(struct hwrm_dbg_coredump_list_input, seq_no);
0106     info.data_len_off = offsetof(struct hwrm_dbg_coredump_list_output,
0107                      data_len);
0108 
0109     rc = bnxt_hwrm_dbg_dma_data(bp, req, &info);
0110     if (!rc) {
0111         coredump->data = info.dest_buf;
0112         coredump->data_size = info.dest_buf_size;
0113         coredump->total_segs = info.segs;
0114     }
0115     return rc;
0116 }
0117 
0118 static int bnxt_hwrm_dbg_coredump_initiate(struct bnxt *bp, u16 component_id,
0119                        u16 segment_id)
0120 {
0121     struct hwrm_dbg_coredump_initiate_input *req;
0122     int rc;
0123 
0124     rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_INITIATE);
0125     if (rc)
0126         return rc;
0127 
0128     hwrm_req_timeout(bp, req, bp->hwrm_cmd_max_timeout);
0129     req->component_id = cpu_to_le16(component_id);
0130     req->segment_id = cpu_to_le16(segment_id);
0131 
0132     return hwrm_req_send(bp, req);
0133 }
0134 
0135 static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id,
0136                        u16 segment_id, u32 *seg_len,
0137                        void *buf, u32 buf_len, u32 offset)
0138 {
0139     struct hwrm_dbg_coredump_retrieve_input *req;
0140     struct bnxt_hwrm_dbg_dma_info info = {NULL};
0141     int rc;
0142 
0143     rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_RETRIEVE);
0144     if (rc)
0145         return rc;
0146 
0147     req->component_id = cpu_to_le16(component_id);
0148     req->segment_id = cpu_to_le16(segment_id);
0149 
0150     info.dma_len = COREDUMP_RETRIEVE_BUF_LEN;
0151     info.seq_off = offsetof(struct hwrm_dbg_coredump_retrieve_input,
0152                 seq_no);
0153     info.data_len_off = offsetof(struct hwrm_dbg_coredump_retrieve_output,
0154                      data_len);
0155     if (buf) {
0156         info.dest_buf = buf + offset;
0157         info.buf_len = buf_len;
0158         info.seg_start = offset;
0159     }
0160 
0161     rc = bnxt_hwrm_dbg_dma_data(bp, req, &info);
0162     if (!rc)
0163         *seg_len = info.dest_buf_size;
0164 
0165     return rc;
0166 }
0167 
0168 static void
0169 bnxt_fill_coredump_seg_hdr(struct bnxt *bp,
0170                struct bnxt_coredump_segment_hdr *seg_hdr,
0171                struct coredump_segment_record *seg_rec, u32 seg_len,
0172                int status, u32 duration, u32 instance)
0173 {
0174     memset(seg_hdr, 0, sizeof(*seg_hdr));
0175     memcpy(seg_hdr->signature, "sEgM", 4);
0176     if (seg_rec) {
0177         seg_hdr->component_id = (__force __le32)seg_rec->component_id;
0178         seg_hdr->segment_id = (__force __le32)seg_rec->segment_id;
0179         seg_hdr->low_version = seg_rec->version_low;
0180         seg_hdr->high_version = seg_rec->version_hi;
0181         seg_hdr->flags = cpu_to_le32(seg_rec->compress_flags);
0182     } else {
0183         /* For hwrm_ver_get response Component id = 2
0184          * and Segment id = 0
0185          */
0186         seg_hdr->component_id = cpu_to_le32(2);
0187         seg_hdr->segment_id = 0;
0188     }
0189     seg_hdr->function_id = cpu_to_le16(bp->pdev->devfn);
0190     seg_hdr->length = cpu_to_le32(seg_len);
0191     seg_hdr->status = cpu_to_le32(status);
0192     seg_hdr->duration = cpu_to_le32(duration);
0193     seg_hdr->data_offset = cpu_to_le32(sizeof(*seg_hdr));
0194     seg_hdr->instance = cpu_to_le32(instance);
0195 }
0196 
0197 static void bnxt_fill_cmdline(struct bnxt_coredump_record *record)
0198 {
0199     struct mm_struct *mm = current->mm;
0200     int i, len, last = 0;
0201 
0202     if (mm) {
0203         len = min_t(int, mm->arg_end - mm->arg_start,
0204                 sizeof(record->commandline) - 1);
0205         if (len && !copy_from_user(record->commandline,
0206                        (char __user *)mm->arg_start, len)) {
0207             for (i = 0; i < len; i++) {
0208                 if (record->commandline[i])
0209                     last = i;
0210                 else
0211                     record->commandline[i] = ' ';
0212             }
0213             record->commandline[last + 1] = 0;
0214             return;
0215         }
0216     }
0217 
0218     strscpy(record->commandline, current->comm, TASK_COMM_LEN);
0219 }
0220 
0221 static void
0222 bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record,
0223               time64_t start, s16 start_utc, u16 total_segs,
0224               int status)
0225 {
0226     time64_t end = ktime_get_real_seconds();
0227     u32 os_ver_major = 0, os_ver_minor = 0;
0228     struct tm tm;
0229 
0230     time64_to_tm(start, 0, &tm);
0231     memset(record, 0, sizeof(*record));
0232     memcpy(record->signature, "cOrE", 4);
0233     record->flags = 0;
0234     record->low_version = 0;
0235     record->high_version = 1;
0236     record->asic_state = 0;
0237     strscpy(record->system_name, utsname()->nodename,
0238         sizeof(record->system_name));
0239     record->year = cpu_to_le16(tm.tm_year + 1900);
0240     record->month = cpu_to_le16(tm.tm_mon + 1);
0241     record->day = cpu_to_le16(tm.tm_mday);
0242     record->hour = cpu_to_le16(tm.tm_hour);
0243     record->minute = cpu_to_le16(tm.tm_min);
0244     record->second = cpu_to_le16(tm.tm_sec);
0245     record->utc_bias = cpu_to_le16(start_utc);
0246     bnxt_fill_cmdline(record);
0247     record->total_segments = cpu_to_le32(total_segs);
0248 
0249     if (sscanf(utsname()->release, "%u.%u", &os_ver_major, &os_ver_minor) != 2)
0250         netdev_warn(bp->dev, "Unknown OS release in coredump\n");
0251     record->os_ver_major = cpu_to_le32(os_ver_major);
0252     record->os_ver_minor = cpu_to_le32(os_ver_minor);
0253 
0254     strscpy(record->os_name, utsname()->sysname, sizeof(record->os_name));
0255     time64_to_tm(end, 0, &tm);
0256     record->end_year = cpu_to_le16(tm.tm_year + 1900);
0257     record->end_month = cpu_to_le16(tm.tm_mon + 1);
0258     record->end_day = cpu_to_le16(tm.tm_mday);
0259     record->end_hour = cpu_to_le16(tm.tm_hour);
0260     record->end_minute = cpu_to_le16(tm.tm_min);
0261     record->end_second = cpu_to_le16(tm.tm_sec);
0262     record->end_utc_bias = cpu_to_le16(sys_tz.tz_minuteswest * 60);
0263     record->asic_id1 = cpu_to_le32(bp->chip_num << 16 |
0264                        bp->ver_resp.chip_rev << 8 |
0265                        bp->ver_resp.chip_metal);
0266     record->asic_id2 = 0;
0267     record->coredump_status = cpu_to_le32(status);
0268     record->ioctl_low_version = 0;
0269     record->ioctl_high_version = 0;
0270 }
0271 
0272 static int __bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
0273 {
0274     u32 ver_get_resp_len = sizeof(struct hwrm_ver_get_output);
0275     u32 offset = 0, seg_hdr_len, seg_record_len, buf_len = 0;
0276     struct coredump_segment_record *seg_record = NULL;
0277     struct bnxt_coredump_segment_hdr seg_hdr;
0278     struct bnxt_coredump coredump = {NULL};
0279     time64_t start_time;
0280     u16 start_utc;
0281     int rc = 0, i;
0282 
0283     if (buf)
0284         buf_len = *dump_len;
0285 
0286     start_time = ktime_get_real_seconds();
0287     start_utc = sys_tz.tz_minuteswest * 60;
0288     seg_hdr_len = sizeof(seg_hdr);
0289 
0290     /* First segment should be hwrm_ver_get response */
0291     *dump_len = seg_hdr_len + ver_get_resp_len;
0292     if (buf) {
0293         bnxt_fill_coredump_seg_hdr(bp, &seg_hdr, NULL, ver_get_resp_len,
0294                        0, 0, 0);
0295         memcpy(buf + offset, &seg_hdr, seg_hdr_len);
0296         offset += seg_hdr_len;
0297         memcpy(buf + offset, &bp->ver_resp, ver_get_resp_len);
0298         offset += ver_get_resp_len;
0299     }
0300 
0301     rc = bnxt_hwrm_dbg_coredump_list(bp, &coredump);
0302     if (rc) {
0303         netdev_err(bp->dev, "Failed to get coredump segment list\n");
0304         goto err;
0305     }
0306 
0307     *dump_len += seg_hdr_len * coredump.total_segs;
0308 
0309     seg_record = (struct coredump_segment_record *)coredump.data;
0310     seg_record_len = sizeof(*seg_record);
0311 
0312     for (i = 0; i < coredump.total_segs; i++) {
0313         u16 comp_id = le16_to_cpu(seg_record->component_id);
0314         u16 seg_id = le16_to_cpu(seg_record->segment_id);
0315         u32 duration = 0, seg_len = 0;
0316         unsigned long start, end;
0317 
0318         if (buf && ((offset + seg_hdr_len) >
0319                 BNXT_COREDUMP_BUF_LEN(buf_len))) {
0320             rc = -ENOBUFS;
0321             goto err;
0322         }
0323 
0324         start = jiffies;
0325 
0326         rc = bnxt_hwrm_dbg_coredump_initiate(bp, comp_id, seg_id);
0327         if (rc) {
0328             netdev_err(bp->dev,
0329                    "Failed to initiate coredump for seg = %d\n",
0330                    seg_record->segment_id);
0331             goto next_seg;
0332         }
0333 
0334         /* Write segment data into the buffer */
0335         rc = bnxt_hwrm_dbg_coredump_retrieve(bp, comp_id, seg_id,
0336                              &seg_len, buf, buf_len,
0337                              offset + seg_hdr_len);
0338         if (rc && rc == -ENOBUFS)
0339             goto err;
0340         else if (rc)
0341             netdev_err(bp->dev,
0342                    "Failed to retrieve coredump for seg = %d\n",
0343                    seg_record->segment_id);
0344 
0345 next_seg:
0346         end = jiffies;
0347         duration = jiffies_to_msecs(end - start);
0348         bnxt_fill_coredump_seg_hdr(bp, &seg_hdr, seg_record, seg_len,
0349                        rc, duration, 0);
0350 
0351         if (buf) {
0352             /* Write segment header into the buffer */
0353             memcpy(buf + offset, &seg_hdr, seg_hdr_len);
0354             offset += seg_hdr_len + seg_len;
0355         }
0356 
0357         *dump_len += seg_len;
0358         seg_record =
0359             (struct coredump_segment_record *)((u8 *)seg_record +
0360                                seg_record_len);
0361     }
0362 
0363 err:
0364     if (buf)
0365         bnxt_fill_coredump_record(bp, buf + offset, start_time,
0366                       start_utc, coredump.total_segs + 1,
0367                       rc);
0368     kfree(coredump.data);
0369     *dump_len += sizeof(struct bnxt_coredump_record);
0370     if (rc == -ENOBUFS)
0371         netdev_err(bp->dev, "Firmware returned large coredump buffer\n");
0372     return rc;
0373 }
0374 
0375 int bnxt_get_coredump(struct bnxt *bp, u16 dump_type, void *buf, u32 *dump_len)
0376 {
0377     if (dump_type == BNXT_DUMP_CRASH) {
0378 #ifdef CONFIG_TEE_BNXT_FW
0379         return tee_bnxt_copy_coredump(buf, 0, *dump_len);
0380 #else
0381         return -EOPNOTSUPP;
0382 #endif
0383     } else {
0384         return __bnxt_get_coredump(bp, buf, dump_len);
0385     }
0386 }
0387 
0388 static int bnxt_hwrm_get_dump_len(struct bnxt *bp, u16 dump_type, u32 *dump_len)
0389 {
0390     struct hwrm_dbg_qcfg_output *resp;
0391     struct hwrm_dbg_qcfg_input *req;
0392     int rc, hdr_len = 0;
0393 
0394     if (!(bp->fw_cap & BNXT_FW_CAP_DBG_QCAPS))
0395         return -EOPNOTSUPP;
0396 
0397     if (dump_type == BNXT_DUMP_CRASH &&
0398         !(bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_SOC_DDR))
0399         return -EOPNOTSUPP;
0400 
0401     rc = hwrm_req_init(bp, req, HWRM_DBG_QCFG);
0402     if (rc)
0403         return rc;
0404 
0405     req->fid = cpu_to_le16(0xffff);
0406     if (dump_type == BNXT_DUMP_CRASH)
0407         req->flags = cpu_to_le16(DBG_QCFG_REQ_FLAGS_CRASHDUMP_SIZE_FOR_DEST_DEST_SOC_DDR);
0408 
0409     resp = hwrm_req_hold(bp, req);
0410     rc = hwrm_req_send(bp, req);
0411     if (rc)
0412         goto get_dump_len_exit;
0413 
0414     if (dump_type == BNXT_DUMP_CRASH) {
0415         *dump_len = le32_to_cpu(resp->crashdump_size);
0416     } else {
0417         /* Driver adds coredump header and "HWRM_VER_GET response"
0418          * segment additionally to coredump.
0419          */
0420         hdr_len = sizeof(struct bnxt_coredump_segment_hdr) +
0421         sizeof(struct hwrm_ver_get_output) +
0422         sizeof(struct bnxt_coredump_record);
0423         *dump_len = le32_to_cpu(resp->coredump_size) + hdr_len;
0424     }
0425     if (*dump_len <= hdr_len)
0426         rc = -EINVAL;
0427 
0428 get_dump_len_exit:
0429     hwrm_req_drop(bp, req);
0430     return rc;
0431 }
0432 
0433 u32 bnxt_get_coredump_length(struct bnxt *bp, u16 dump_type)
0434 {
0435     u32 len = 0;
0436 
0437     if (bnxt_hwrm_get_dump_len(bp, dump_type, &len)) {
0438         if (dump_type == BNXT_DUMP_CRASH)
0439             len = BNXT_CRASH_DUMP_LEN;
0440         else
0441             __bnxt_get_coredump(bp, NULL, &len);
0442     }
0443     return len;
0444 }