162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * UEFI Common Platform Error Record (CPER) support for CXL Section.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2022 Advanced Micro Devices, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Smita Koralahalli <Smita.KoralahalliChannabasappa@amd.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/cper.h>
1162306a36Sopenharmony_ci#include "cper_cxl.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define PROT_ERR_VALID_AGENT_TYPE		BIT_ULL(0)
1462306a36Sopenharmony_ci#define PROT_ERR_VALID_AGENT_ADDRESS		BIT_ULL(1)
1562306a36Sopenharmony_ci#define PROT_ERR_VALID_DEVICE_ID		BIT_ULL(2)
1662306a36Sopenharmony_ci#define PROT_ERR_VALID_SERIAL_NUMBER		BIT_ULL(3)
1762306a36Sopenharmony_ci#define PROT_ERR_VALID_CAPABILITY		BIT_ULL(4)
1862306a36Sopenharmony_ci#define PROT_ERR_VALID_DVSEC			BIT_ULL(5)
1962306a36Sopenharmony_ci#define PROT_ERR_VALID_ERROR_LOG		BIT_ULL(6)
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* CXL RAS Capability Structure, CXL v3.0 sec 8.2.4.16 */
2262306a36Sopenharmony_cistruct cxl_ras_capability_regs {
2362306a36Sopenharmony_ci	u32 uncor_status;
2462306a36Sopenharmony_ci	u32 uncor_mask;
2562306a36Sopenharmony_ci	u32 uncor_severity;
2662306a36Sopenharmony_ci	u32 cor_status;
2762306a36Sopenharmony_ci	u32 cor_mask;
2862306a36Sopenharmony_ci	u32 cap_control;
2962306a36Sopenharmony_ci	u32 header_log[16];
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic const char * const prot_err_agent_type_strs[] = {
3362306a36Sopenharmony_ci	"Restricted CXL Device",
3462306a36Sopenharmony_ci	"Restricted CXL Host Downstream Port",
3562306a36Sopenharmony_ci	"CXL Device",
3662306a36Sopenharmony_ci	"CXL Logical Device",
3762306a36Sopenharmony_ci	"CXL Fabric Manager managed Logical Device",
3862306a36Sopenharmony_ci	"CXL Root Port",
3962306a36Sopenharmony_ci	"CXL Downstream Switch Port",
4062306a36Sopenharmony_ci	"CXL Upstream Switch Port",
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci/*
4462306a36Sopenharmony_ci * The layout of the enumeration and the values matches CXL Agent Type
4562306a36Sopenharmony_ci * field in the UEFI 2.10 Section N.2.13,
4662306a36Sopenharmony_ci */
4762306a36Sopenharmony_cienum {
4862306a36Sopenharmony_ci	RCD,	/* Restricted CXL Device */
4962306a36Sopenharmony_ci	RCH_DP,	/* Restricted CXL Host Downstream Port */
5062306a36Sopenharmony_ci	DEVICE,	/* CXL Device */
5162306a36Sopenharmony_ci	LD,	/* CXL Logical Device */
5262306a36Sopenharmony_ci	FMLD,	/* CXL Fabric Manager managed Logical Device */
5362306a36Sopenharmony_ci	RP,	/* CXL Root Port */
5462306a36Sopenharmony_ci	DSP,	/* CXL Downstream Switch Port */
5562306a36Sopenharmony_ci	USP,	/* CXL Upstream Switch Port */
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_civoid cper_print_prot_err(const char *pfx, const struct cper_sec_prot_err *prot_err)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	if (prot_err->valid_bits & PROT_ERR_VALID_AGENT_TYPE)
6162306a36Sopenharmony_ci		pr_info("%s agent_type: %d, %s\n", pfx, prot_err->agent_type,
6262306a36Sopenharmony_ci			prot_err->agent_type < ARRAY_SIZE(prot_err_agent_type_strs)
6362306a36Sopenharmony_ci			? prot_err_agent_type_strs[prot_err->agent_type]
6462306a36Sopenharmony_ci			: "unknown");
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (prot_err->valid_bits & PROT_ERR_VALID_AGENT_ADDRESS) {
6762306a36Sopenharmony_ci		switch (prot_err->agent_type) {
6862306a36Sopenharmony_ci		/*
6962306a36Sopenharmony_ci		 * According to UEFI 2.10 Section N.2.13, the term CXL Device
7062306a36Sopenharmony_ci		 * is used to refer to Restricted CXL Device, CXL Device, CXL
7162306a36Sopenharmony_ci		 * Logical Device or a CXL Fabric Manager Managed Logical
7262306a36Sopenharmony_ci		 * Device.
7362306a36Sopenharmony_ci		 */
7462306a36Sopenharmony_ci		case RCD:
7562306a36Sopenharmony_ci		case DEVICE:
7662306a36Sopenharmony_ci		case LD:
7762306a36Sopenharmony_ci		case FMLD:
7862306a36Sopenharmony_ci		case RP:
7962306a36Sopenharmony_ci		case DSP:
8062306a36Sopenharmony_ci		case USP:
8162306a36Sopenharmony_ci			pr_info("%s agent_address: %04x:%02x:%02x.%x\n",
8262306a36Sopenharmony_ci				pfx, prot_err->agent_addr.segment,
8362306a36Sopenharmony_ci				prot_err->agent_addr.bus,
8462306a36Sopenharmony_ci				prot_err->agent_addr.device,
8562306a36Sopenharmony_ci				prot_err->agent_addr.function);
8662306a36Sopenharmony_ci			break;
8762306a36Sopenharmony_ci		case RCH_DP:
8862306a36Sopenharmony_ci			pr_info("%s rcrb_base_address: 0x%016llx\n", pfx,
8962306a36Sopenharmony_ci				prot_err->agent_addr.rcrb_base_addr);
9062306a36Sopenharmony_ci			break;
9162306a36Sopenharmony_ci		default:
9262306a36Sopenharmony_ci			break;
9362306a36Sopenharmony_ci		}
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (prot_err->valid_bits & PROT_ERR_VALID_DEVICE_ID) {
9762306a36Sopenharmony_ci		const __u8 *class_code;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		switch (prot_err->agent_type) {
10062306a36Sopenharmony_ci		case RCD:
10162306a36Sopenharmony_ci		case DEVICE:
10262306a36Sopenharmony_ci		case LD:
10362306a36Sopenharmony_ci		case FMLD:
10462306a36Sopenharmony_ci		case RP:
10562306a36Sopenharmony_ci		case DSP:
10662306a36Sopenharmony_ci		case USP:
10762306a36Sopenharmony_ci			pr_info("%s slot: %d\n", pfx,
10862306a36Sopenharmony_ci				prot_err->device_id.slot >> CPER_PCIE_SLOT_SHIFT);
10962306a36Sopenharmony_ci			pr_info("%s vendor_id: 0x%04x, device_id: 0x%04x\n",
11062306a36Sopenharmony_ci				pfx, prot_err->device_id.vendor_id,
11162306a36Sopenharmony_ci				prot_err->device_id.device_id);
11262306a36Sopenharmony_ci			pr_info("%s sub_vendor_id: 0x%04x, sub_device_id: 0x%04x\n",
11362306a36Sopenharmony_ci				pfx, prot_err->device_id.subsystem_vendor_id,
11462306a36Sopenharmony_ci				prot_err->device_id.subsystem_id);
11562306a36Sopenharmony_ci			class_code = prot_err->device_id.class_code;
11662306a36Sopenharmony_ci			pr_info("%s class_code: %02x%02x\n", pfx,
11762306a36Sopenharmony_ci				class_code[1], class_code[0]);
11862306a36Sopenharmony_ci			break;
11962306a36Sopenharmony_ci		default:
12062306a36Sopenharmony_ci			break;
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (prot_err->valid_bits & PROT_ERR_VALID_SERIAL_NUMBER) {
12562306a36Sopenharmony_ci		switch (prot_err->agent_type) {
12662306a36Sopenharmony_ci		case RCD:
12762306a36Sopenharmony_ci		case DEVICE:
12862306a36Sopenharmony_ci		case LD:
12962306a36Sopenharmony_ci		case FMLD:
13062306a36Sopenharmony_ci			pr_info("%s lower_dw: 0x%08x, upper_dw: 0x%08x\n", pfx,
13162306a36Sopenharmony_ci				prot_err->dev_serial_num.lower_dw,
13262306a36Sopenharmony_ci				prot_err->dev_serial_num.upper_dw);
13362306a36Sopenharmony_ci			break;
13462306a36Sopenharmony_ci		default:
13562306a36Sopenharmony_ci			break;
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci	}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (prot_err->valid_bits & PROT_ERR_VALID_CAPABILITY) {
14062306a36Sopenharmony_ci		switch (prot_err->agent_type) {
14162306a36Sopenharmony_ci		case RCD:
14262306a36Sopenharmony_ci		case DEVICE:
14362306a36Sopenharmony_ci		case LD:
14462306a36Sopenharmony_ci		case FMLD:
14562306a36Sopenharmony_ci		case RP:
14662306a36Sopenharmony_ci		case DSP:
14762306a36Sopenharmony_ci		case USP:
14862306a36Sopenharmony_ci			print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4,
14962306a36Sopenharmony_ci				       prot_err->capability,
15062306a36Sopenharmony_ci				       sizeof(prot_err->capability), 0);
15162306a36Sopenharmony_ci			break;
15262306a36Sopenharmony_ci		default:
15362306a36Sopenharmony_ci			break;
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (prot_err->valid_bits & PROT_ERR_VALID_DVSEC) {
15862306a36Sopenharmony_ci		pr_info("%s DVSEC length: 0x%04x\n", pfx, prot_err->dvsec_len);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		pr_info("%s CXL DVSEC:\n", pfx);
16162306a36Sopenharmony_ci		print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, (prot_err + 1),
16262306a36Sopenharmony_ci			       prot_err->dvsec_len, 0);
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (prot_err->valid_bits & PROT_ERR_VALID_ERROR_LOG) {
16662306a36Sopenharmony_ci		size_t size = sizeof(*prot_err) + prot_err->dvsec_len;
16762306a36Sopenharmony_ci		struct cxl_ras_capability_regs *cxl_ras;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci		pr_info("%s Error log length: 0x%04x\n", pfx, prot_err->err_len);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci		pr_info("%s CXL Error Log:\n", pfx);
17262306a36Sopenharmony_ci		cxl_ras = (struct cxl_ras_capability_regs *)((long)prot_err + size);
17362306a36Sopenharmony_ci		pr_info("%s cxl_ras_uncor_status: 0x%08x", pfx,
17462306a36Sopenharmony_ci			cxl_ras->uncor_status);
17562306a36Sopenharmony_ci		pr_info("%s cxl_ras_uncor_mask: 0x%08x\n", pfx,
17662306a36Sopenharmony_ci			cxl_ras->uncor_mask);
17762306a36Sopenharmony_ci		pr_info("%s cxl_ras_uncor_severity: 0x%08x\n", pfx,
17862306a36Sopenharmony_ci			cxl_ras->uncor_severity);
17962306a36Sopenharmony_ci		pr_info("%s cxl_ras_cor_status: 0x%08x", pfx,
18062306a36Sopenharmony_ci			cxl_ras->cor_status);
18162306a36Sopenharmony_ci		pr_info("%s cxl_ras_cor_mask: 0x%08x\n", pfx,
18262306a36Sopenharmony_ci			cxl_ras->cor_mask);
18362306a36Sopenharmony_ci		pr_info("%s cap_control: 0x%08x\n", pfx,
18462306a36Sopenharmony_ci			cxl_ras->cap_control);
18562306a36Sopenharmony_ci		pr_info("%s Header Log Registers:\n", pfx);
18662306a36Sopenharmony_ci		print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, cxl_ras->header_log,
18762306a36Sopenharmony_ci			       sizeof(cxl_ras->header_log), 0);
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci}
190