cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

bnxt_coredump.c (12217B)


      1/* Broadcom NetXtreme-C/E network driver.
      2 *
      3 * Copyright (c) 2021 Broadcom Limited
      4 *
      5 * This program is free software; you can redistribute it and/or modify
      6 * it under the terms of the GNU General Public License as published by
      7 * the Free Software Foundation.
      8 */
      9
     10#include <linux/types.h>
     11#include <linux/errno.h>
     12#include <linux/pci.h>
     13#include "bnxt_hsi.h"
     14#include "bnxt.h"
     15#include "bnxt_hwrm.h"
     16#include "bnxt_coredump.h"
     17
     18static int bnxt_hwrm_dbg_dma_data(struct bnxt *bp, void *msg,
     19				  struct bnxt_hwrm_dbg_dma_info *info)
     20{
     21	struct hwrm_dbg_cmn_input *cmn_req = msg;
     22	__le16 *seq_ptr = msg + info->seq_off;
     23	struct hwrm_dbg_cmn_output *cmn_resp;
     24	u16 seq = 0, len, segs_off;
     25	dma_addr_t dma_handle;
     26	void *dma_buf, *resp;
     27	int rc, off = 0;
     28
     29	dma_buf = hwrm_req_dma_slice(bp, msg, info->dma_len, &dma_handle);
     30	if (!dma_buf) {
     31		hwrm_req_drop(bp, msg);
     32		return -ENOMEM;
     33	}
     34
     35	hwrm_req_timeout(bp, msg, bp->hwrm_cmd_max_timeout);
     36	cmn_resp = hwrm_req_hold(bp, msg);
     37	resp = cmn_resp;
     38
     39	segs_off = offsetof(struct hwrm_dbg_coredump_list_output,
     40			    total_segments);
     41	cmn_req->host_dest_addr = cpu_to_le64(dma_handle);
     42	cmn_req->host_buf_len = cpu_to_le32(info->dma_len);
     43	while (1) {
     44		*seq_ptr = cpu_to_le16(seq);
     45		rc = hwrm_req_send(bp, msg);
     46		if (rc)
     47			break;
     48
     49		len = le16_to_cpu(*((__le16 *)(resp + info->data_len_off)));
     50		if (!seq &&
     51		    cmn_req->req_type == cpu_to_le16(HWRM_DBG_COREDUMP_LIST)) {
     52			info->segs = le16_to_cpu(*((__le16 *)(resp +
     53							      segs_off)));
     54			if (!info->segs) {
     55				rc = -EIO;
     56				break;
     57			}
     58
     59			info->dest_buf_size = info->segs *
     60					sizeof(struct coredump_segment_record);
     61			info->dest_buf = kmalloc(info->dest_buf_size,
     62						 GFP_KERNEL);
     63			if (!info->dest_buf) {
     64				rc = -ENOMEM;
     65				break;
     66			}
     67		}
     68
     69		if (info->dest_buf) {
     70			if ((info->seg_start + off + len) <=
     71			    BNXT_COREDUMP_BUF_LEN(info->buf_len)) {
     72				memcpy(info->dest_buf + off, dma_buf, len);
     73			} else {
     74				rc = -ENOBUFS;
     75				break;
     76			}
     77		}
     78
     79		if (cmn_req->req_type ==
     80				cpu_to_le16(HWRM_DBG_COREDUMP_RETRIEVE))
     81			info->dest_buf_size += len;
     82
     83		if (!(cmn_resp->flags & HWRM_DBG_CMN_FLAGS_MORE))
     84			break;
     85
     86		seq++;
     87		off += len;
     88	}
     89	hwrm_req_drop(bp, msg);
     90	return rc;
     91}
     92
     93static int bnxt_hwrm_dbg_coredump_list(struct bnxt *bp,
     94				       struct bnxt_coredump *coredump)
     95{
     96	struct bnxt_hwrm_dbg_dma_info info = {NULL};
     97	struct hwrm_dbg_coredump_list_input *req;
     98	int rc;
     99
    100	rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_LIST);
    101	if (rc)
    102		return rc;
    103
    104	info.dma_len = COREDUMP_LIST_BUF_LEN;
    105	info.seq_off = offsetof(struct hwrm_dbg_coredump_list_input, seq_no);
    106	info.data_len_off = offsetof(struct hwrm_dbg_coredump_list_output,
    107				     data_len);
    108
    109	rc = bnxt_hwrm_dbg_dma_data(bp, req, &info);
    110	if (!rc) {
    111		coredump->data = info.dest_buf;
    112		coredump->data_size = info.dest_buf_size;
    113		coredump->total_segs = info.segs;
    114	}
    115	return rc;
    116}
    117
    118static int bnxt_hwrm_dbg_coredump_initiate(struct bnxt *bp, u16 component_id,
    119					   u16 segment_id)
    120{
    121	struct hwrm_dbg_coredump_initiate_input *req;
    122	int rc;
    123
    124	rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_INITIATE);
    125	if (rc)
    126		return rc;
    127
    128	hwrm_req_timeout(bp, req, bp->hwrm_cmd_max_timeout);
    129	req->component_id = cpu_to_le16(component_id);
    130	req->segment_id = cpu_to_le16(segment_id);
    131
    132	return hwrm_req_send(bp, req);
    133}
    134
    135static int bnxt_hwrm_dbg_coredump_retrieve(struct bnxt *bp, u16 component_id,
    136					   u16 segment_id, u32 *seg_len,
    137					   void *buf, u32 buf_len, u32 offset)
    138{
    139	struct hwrm_dbg_coredump_retrieve_input *req;
    140	struct bnxt_hwrm_dbg_dma_info info = {NULL};
    141	int rc;
    142
    143	rc = hwrm_req_init(bp, req, HWRM_DBG_COREDUMP_RETRIEVE);
    144	if (rc)
    145		return rc;
    146
    147	req->component_id = cpu_to_le16(component_id);
    148	req->segment_id = cpu_to_le16(segment_id);
    149
    150	info.dma_len = COREDUMP_RETRIEVE_BUF_LEN;
    151	info.seq_off = offsetof(struct hwrm_dbg_coredump_retrieve_input,
    152				seq_no);
    153	info.data_len_off = offsetof(struct hwrm_dbg_coredump_retrieve_output,
    154				     data_len);
    155	if (buf) {
    156		info.dest_buf = buf + offset;
    157		info.buf_len = buf_len;
    158		info.seg_start = offset;
    159	}
    160
    161	rc = bnxt_hwrm_dbg_dma_data(bp, req, &info);
    162	if (!rc)
    163		*seg_len = info.dest_buf_size;
    164
    165	return rc;
    166}
    167
    168static void
    169bnxt_fill_coredump_seg_hdr(struct bnxt *bp,
    170			   struct bnxt_coredump_segment_hdr *seg_hdr,
    171			   struct coredump_segment_record *seg_rec, u32 seg_len,
    172			   int status, u32 duration, u32 instance)
    173{
    174	memset(seg_hdr, 0, sizeof(*seg_hdr));
    175	memcpy(seg_hdr->signature, "sEgM", 4);
    176	if (seg_rec) {
    177		seg_hdr->component_id = (__force __le32)seg_rec->component_id;
    178		seg_hdr->segment_id = (__force __le32)seg_rec->segment_id;
    179		seg_hdr->low_version = seg_rec->version_low;
    180		seg_hdr->high_version = seg_rec->version_hi;
    181		seg_hdr->flags = cpu_to_le32(seg_rec->compress_flags);
    182	} else {
    183		/* For hwrm_ver_get response Component id = 2
    184		 * and Segment id = 0
    185		 */
    186		seg_hdr->component_id = cpu_to_le32(2);
    187		seg_hdr->segment_id = 0;
    188	}
    189	seg_hdr->function_id = cpu_to_le16(bp->pdev->devfn);
    190	seg_hdr->length = cpu_to_le32(seg_len);
    191	seg_hdr->status = cpu_to_le32(status);
    192	seg_hdr->duration = cpu_to_le32(duration);
    193	seg_hdr->data_offset = cpu_to_le32(sizeof(*seg_hdr));
    194	seg_hdr->instance = cpu_to_le32(instance);
    195}
    196
    197static void bnxt_fill_cmdline(struct bnxt_coredump_record *record)
    198{
    199	struct mm_struct *mm = current->mm;
    200	int i, len, last = 0;
    201
    202	if (mm) {
    203		len = min_t(int, mm->arg_end - mm->arg_start,
    204			    sizeof(record->commandline) - 1);
    205		if (len && !copy_from_user(record->commandline,
    206					   (char __user *)mm->arg_start, len)) {
    207			for (i = 0; i < len; i++) {
    208				if (record->commandline[i])
    209					last = i;
    210				else
    211					record->commandline[i] = ' ';
    212			}
    213			record->commandline[last + 1] = 0;
    214			return;
    215		}
    216	}
    217
    218	strscpy(record->commandline, current->comm, TASK_COMM_LEN);
    219}
    220
    221static void
    222bnxt_fill_coredump_record(struct bnxt *bp, struct bnxt_coredump_record *record,
    223			  time64_t start, s16 start_utc, u16 total_segs,
    224			  int status)
    225{
    226	time64_t end = ktime_get_real_seconds();
    227	u32 os_ver_major = 0, os_ver_minor = 0;
    228	struct tm tm;
    229
    230	time64_to_tm(start, 0, &tm);
    231	memset(record, 0, sizeof(*record));
    232	memcpy(record->signature, "cOrE", 4);
    233	record->flags = 0;
    234	record->low_version = 0;
    235	record->high_version = 1;
    236	record->asic_state = 0;
    237	strscpy(record->system_name, utsname()->nodename,
    238		sizeof(record->system_name));
    239	record->year = cpu_to_le16(tm.tm_year + 1900);
    240	record->month = cpu_to_le16(tm.tm_mon + 1);
    241	record->day = cpu_to_le16(tm.tm_mday);
    242	record->hour = cpu_to_le16(tm.tm_hour);
    243	record->minute = cpu_to_le16(tm.tm_min);
    244	record->second = cpu_to_le16(tm.tm_sec);
    245	record->utc_bias = cpu_to_le16(start_utc);
    246	bnxt_fill_cmdline(record);
    247	record->total_segments = cpu_to_le32(total_segs);
    248
    249	if (sscanf(utsname()->release, "%u.%u", &os_ver_major, &os_ver_minor) != 2)
    250		netdev_warn(bp->dev, "Unknown OS release in coredump\n");
    251	record->os_ver_major = cpu_to_le32(os_ver_major);
    252	record->os_ver_minor = cpu_to_le32(os_ver_minor);
    253
    254	strscpy(record->os_name, utsname()->sysname, sizeof(record->os_name));
    255	time64_to_tm(end, 0, &tm);
    256	record->end_year = cpu_to_le16(tm.tm_year + 1900);
    257	record->end_month = cpu_to_le16(tm.tm_mon + 1);
    258	record->end_day = cpu_to_le16(tm.tm_mday);
    259	record->end_hour = cpu_to_le16(tm.tm_hour);
    260	record->end_minute = cpu_to_le16(tm.tm_min);
    261	record->end_second = cpu_to_le16(tm.tm_sec);
    262	record->end_utc_bias = cpu_to_le16(sys_tz.tz_minuteswest * 60);
    263	record->asic_id1 = cpu_to_le32(bp->chip_num << 16 |
    264				       bp->ver_resp.chip_rev << 8 |
    265				       bp->ver_resp.chip_metal);
    266	record->asic_id2 = 0;
    267	record->coredump_status = cpu_to_le32(status);
    268	record->ioctl_low_version = 0;
    269	record->ioctl_high_version = 0;
    270}
    271
    272static int __bnxt_get_coredump(struct bnxt *bp, void *buf, u32 *dump_len)
    273{
    274	u32 ver_get_resp_len = sizeof(struct hwrm_ver_get_output);
    275	u32 offset = 0, seg_hdr_len, seg_record_len, buf_len = 0;
    276	struct coredump_segment_record *seg_record = NULL;
    277	struct bnxt_coredump_segment_hdr seg_hdr;
    278	struct bnxt_coredump coredump = {NULL};
    279	time64_t start_time;
    280	u16 start_utc;
    281	int rc = 0, i;
    282
    283	if (buf)
    284		buf_len = *dump_len;
    285
    286	start_time = ktime_get_real_seconds();
    287	start_utc = sys_tz.tz_minuteswest * 60;
    288	seg_hdr_len = sizeof(seg_hdr);
    289
    290	/* First segment should be hwrm_ver_get response */
    291	*dump_len = seg_hdr_len + ver_get_resp_len;
    292	if (buf) {
    293		bnxt_fill_coredump_seg_hdr(bp, &seg_hdr, NULL, ver_get_resp_len,
    294					   0, 0, 0);
    295		memcpy(buf + offset, &seg_hdr, seg_hdr_len);
    296		offset += seg_hdr_len;
    297		memcpy(buf + offset, &bp->ver_resp, ver_get_resp_len);
    298		offset += ver_get_resp_len;
    299	}
    300
    301	rc = bnxt_hwrm_dbg_coredump_list(bp, &coredump);
    302	if (rc) {
    303		netdev_err(bp->dev, "Failed to get coredump segment list\n");
    304		goto err;
    305	}
    306
    307	*dump_len += seg_hdr_len * coredump.total_segs;
    308
    309	seg_record = (struct coredump_segment_record *)coredump.data;
    310	seg_record_len = sizeof(*seg_record);
    311
    312	for (i = 0; i < coredump.total_segs; i++) {
    313		u16 comp_id = le16_to_cpu(seg_record->component_id);
    314		u16 seg_id = le16_to_cpu(seg_record->segment_id);
    315		u32 duration = 0, seg_len = 0;
    316		unsigned long start, end;
    317
    318		if (buf && ((offset + seg_hdr_len) >
    319			    BNXT_COREDUMP_BUF_LEN(buf_len))) {
    320			rc = -ENOBUFS;
    321			goto err;
    322		}
    323
    324		start = jiffies;
    325
    326		rc = bnxt_hwrm_dbg_coredump_initiate(bp, comp_id, seg_id);
    327		if (rc) {
    328			netdev_err(bp->dev,
    329				   "Failed to initiate coredump for seg = %d\n",
    330				   seg_record->segment_id);
    331			goto next_seg;
    332		}
    333
    334		/* Write segment data into the buffer */
    335		rc = bnxt_hwrm_dbg_coredump_retrieve(bp, comp_id, seg_id,
    336						     &seg_len, buf, buf_len,
    337						     offset + seg_hdr_len);
    338		if (rc && rc == -ENOBUFS)
    339			goto err;
    340		else if (rc)
    341			netdev_err(bp->dev,
    342				   "Failed to retrieve coredump for seg = %d\n",
    343				   seg_record->segment_id);
    344
    345next_seg:
    346		end = jiffies;
    347		duration = jiffies_to_msecs(end - start);
    348		bnxt_fill_coredump_seg_hdr(bp, &seg_hdr, seg_record, seg_len,
    349					   rc, duration, 0);
    350
    351		if (buf) {
    352			/* Write segment header into the buffer */
    353			memcpy(buf + offset, &seg_hdr, seg_hdr_len);
    354			offset += seg_hdr_len + seg_len;
    355		}
    356
    357		*dump_len += seg_len;
    358		seg_record =
    359			(struct coredump_segment_record *)((u8 *)seg_record +
    360							   seg_record_len);
    361	}
    362
    363err:
    364	if (buf)
    365		bnxt_fill_coredump_record(bp, buf + offset, start_time,
    366					  start_utc, coredump.total_segs + 1,
    367					  rc);
    368	kfree(coredump.data);
    369	*dump_len += sizeof(struct bnxt_coredump_record);
    370	if (rc == -ENOBUFS)
    371		netdev_err(bp->dev, "Firmware returned large coredump buffer\n");
    372	return rc;
    373}
    374
    375int bnxt_get_coredump(struct bnxt *bp, u16 dump_type, void *buf, u32 *dump_len)
    376{
    377	if (dump_type == BNXT_DUMP_CRASH) {
    378#ifdef CONFIG_TEE_BNXT_FW
    379		return tee_bnxt_copy_coredump(buf, 0, *dump_len);
    380#else
    381		return -EOPNOTSUPP;
    382#endif
    383	} else {
    384		return __bnxt_get_coredump(bp, buf, dump_len);
    385	}
    386}
    387
    388static int bnxt_hwrm_get_dump_len(struct bnxt *bp, u16 dump_type, u32 *dump_len)
    389{
    390	struct hwrm_dbg_qcfg_output *resp;
    391	struct hwrm_dbg_qcfg_input *req;
    392	int rc, hdr_len = 0;
    393
    394	if (!(bp->fw_cap & BNXT_FW_CAP_DBG_QCAPS))
    395		return -EOPNOTSUPP;
    396
    397	if (dump_type == BNXT_DUMP_CRASH &&
    398	    !(bp->fw_dbg_cap & DBG_QCAPS_RESP_FLAGS_CRASHDUMP_SOC_DDR))
    399		return -EOPNOTSUPP;
    400
    401	rc = hwrm_req_init(bp, req, HWRM_DBG_QCFG);
    402	if (rc)
    403		return rc;
    404
    405	req->fid = cpu_to_le16(0xffff);
    406	if (dump_type == BNXT_DUMP_CRASH)
    407		req->flags = cpu_to_le16(DBG_QCFG_REQ_FLAGS_CRASHDUMP_SIZE_FOR_DEST_DEST_SOC_DDR);
    408
    409	resp = hwrm_req_hold(bp, req);
    410	rc = hwrm_req_send(bp, req);
    411	if (rc)
    412		goto get_dump_len_exit;
    413
    414	if (dump_type == BNXT_DUMP_CRASH) {
    415		*dump_len = le32_to_cpu(resp->crashdump_size);
    416	} else {
    417		/* Driver adds coredump header and "HWRM_VER_GET response"
    418		 * segment additionally to coredump.
    419		 */
    420		hdr_len = sizeof(struct bnxt_coredump_segment_hdr) +
    421		sizeof(struct hwrm_ver_get_output) +
    422		sizeof(struct bnxt_coredump_record);
    423		*dump_len = le32_to_cpu(resp->coredump_size) + hdr_len;
    424	}
    425	if (*dump_len <= hdr_len)
    426		rc = -EINVAL;
    427
    428get_dump_len_exit:
    429	hwrm_req_drop(bp, req);
    430	return rc;
    431}
    432
    433u32 bnxt_get_coredump_length(struct bnxt *bp, u16 dump_type)
    434{
    435	u32 len = 0;
    436
    437	if (bnxt_hwrm_get_dump_len(bp, dump_type, &len)) {
    438		if (dump_type == BNXT_DUMP_CRASH)
    439			len = BNXT_CRASH_DUMP_LEN;
    440		else
    441			__bnxt_get_coredump(bp, NULL, &len);
    442	}
    443	return len;
    444}