0001
0002
0003
0004
0005
0006
0007
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
0184
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
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
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
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
0418
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 }