xref: /linux/drivers/pci/pcie/tlp.c (revision 1a58c47fae2a3982b38ec6348c739a7c60833189)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * PCIe TLP Log handling
4  *
5  * Copyright (C) 2024 Intel Corporation
6  */
7 
8 #include <linux/aer.h>
9 #include <linux/array_size.h>
10 #include <linux/bitfield.h>
11 #include <linux/build_bug.h>
12 #include <linux/minmax.h>
13 #include <linux/pci.h>
14 #include <linux/string.h>
15 #include <linux/unaligned.h>
16 
17 #include "../pci.h"
18 
19 /**
20  * aer_tlp_log_len - Calculate AER Capability TLP Header/Prefix Log length
21  * @dev: PCIe device
22  * @aercc: AER Capabilities and Control register value
23  *
24  * Return: TLP Header/Prefix Log length
25  */
26 unsigned int aer_tlp_log_len(struct pci_dev *dev, u32 aercc)
27 {
28 	if (aercc & PCI_ERR_CAP_TLP_LOG_FLIT)
29 		return FIELD_GET(PCI_ERR_CAP_TLP_LOG_SIZE, aercc);
30 
31 	return PCIE_STD_NUM_TLP_HEADERLOG +
32 	       ((aercc & PCI_ERR_CAP_PREFIX_LOG_PRESENT) ?
33 		dev->eetlp_prefix_max : 0);
34 }
35 
36 #ifdef CONFIG_PCIE_DPC
37 /**
38  * dpc_tlp_log_len - Calculate DPC RP PIO TLP Header/Prefix Log length
39  * @dev: PCIe device
40  *
41  * Return: TLP Header/Prefix Log length
42  */
43 unsigned int dpc_tlp_log_len(struct pci_dev *dev)
44 {
45 	/* Remove ImpSpec Log register from the count */
46 	if (dev->dpc_rp_log_size >= PCIE_STD_NUM_TLP_HEADERLOG + 1)
47 		return dev->dpc_rp_log_size - 1;
48 
49 	return dev->dpc_rp_log_size;
50 }
51 #endif
52 
53 /**
54  * pcie_read_tlp_log - read TLP Header Log
55  * @dev: PCIe device
56  * @where: PCI Config offset of TLP Header Log
57  * @where2: PCI Config offset of TLP Prefix Log
58  * @tlp_len: TLP Log length (Header Log + TLP Prefix Log in DWORDs)
59  * @flit: TLP Logged in Flit mode
60  * @log: TLP Log structure to fill
61  *
62  * Fill @log from TLP Header Log registers, e.g., AER or DPC.
63  *
64  * Return: 0 on success and filled TLP Log structure, <0 on error.
65  */
66 int pcie_read_tlp_log(struct pci_dev *dev, int where, int where2,
67 		      unsigned int tlp_len, bool flit, struct pcie_tlp_log *log)
68 {
69 	unsigned int i;
70 	int off, ret;
71 
72 	if (tlp_len > ARRAY_SIZE(log->dw))
73 		tlp_len = ARRAY_SIZE(log->dw);
74 
75 	memset(log, 0, sizeof(*log));
76 
77 	for (i = 0; i < tlp_len; i++) {
78 		if (i < PCIE_STD_NUM_TLP_HEADERLOG)
79 			off = where + i * 4;
80 		else
81 			off = where2 + (i - PCIE_STD_NUM_TLP_HEADERLOG) * 4;
82 
83 		ret = pci_read_config_dword(dev, off, &log->dw[i]);
84 		if (ret)
85 			return pcibios_err_to_errno(ret);
86 	}
87 
88 	/*
89 	 * Hard-code non-Flit mode to 4 DWORDs, for now. The exact length
90 	 * can only be known if the TLP is parsed.
91 	 */
92 	log->header_len = flit ? tlp_len : 4;
93 	log->flit = flit;
94 
95 	return 0;
96 }
97 
98 /* The Prefix Log registers hold dw[4..13] in Flit mode. */
99 static_assert((PCI_ERR_PREFIX_LOG +
100 	       (PCIE_STD_MAX_TLP_HEADERLOG - PCIE_STD_NUM_TLP_HEADERLOG) *
101 	       sizeof(u32)) == PCIE_AER_CAP_HW_SIZE);
102 
103 /**
104  * aer_cap_regs_unpack - Convert a raw AER Capability image to the kernel layout
105  * @regs: Destination, fully initialised
106  * @raw: AER Capability register block in hardware order, little-endian
107  * @raw_len: Bytes readable at @raw
108  *
109  * struct aer_capability_regs is not the hardware layout: struct pcie_tlp_log
110  * spans 60 bytes where the Header Log it stands in for is 16, so a flat copy
111  * misplaces everything behind it. Map the registers one by one instead,
112  * taking the TLP Log layout from the Flit bit as pcie_read_tlp_log() does.
113  *
114  * @raw is not necessarily aligned. Registers past @raw_len are left zero, so
115  * a short image cannot be read past.
116  */
117 void aer_cap_regs_unpack(struct aer_capability_regs *regs, const void *raw,
118 			 size_t raw_len)
119 {
120 	unsigned int i, tlp_len;
121 	bool flit;
122 
123 	memset(regs, 0, sizeof(*regs));
124 
125 	if (raw_len < PCI_ERR_HEADER_LOG)
126 		return;
127 
128 	/* The Extended Capability Header, at offset 0, has no define */
129 	regs->header = get_unaligned_le32(raw);
130 	regs->uncor_status = get_unaligned_le32(raw + PCI_ERR_UNCOR_STATUS);
131 	regs->uncor_mask = get_unaligned_le32(raw + PCI_ERR_UNCOR_MASK);
132 	regs->uncor_severity = get_unaligned_le32(raw + PCI_ERR_UNCOR_SEVER);
133 	regs->cor_status = get_unaligned_le32(raw + PCI_ERR_COR_STATUS);
134 	regs->cor_mask = get_unaligned_le32(raw + PCI_ERR_COR_MASK);
135 	regs->cap_control = get_unaligned_le32(raw + PCI_ERR_CAP);
136 
137 	flit = FIELD_GET(PCI_ERR_CAP_TLP_LOG_FLIT, regs->cap_control);
138 	if (flit) {
139 		tlp_len = FIELD_GET(PCI_ERR_CAP_TLP_LOG_SIZE, regs->cap_control);
140 	} else {
141 		/*
142 		 * dw[4..7] alias the Prefix Log. Take all four whatever
143 		 * eetlp_prefix_max says; pcie_print_tlp_log() stops at the
144 		 * first zero one.
145 		 */
146 		tlp_len = PCIE_STD_NUM_TLP_HEADERLOG + PCIE_STD_MAX_TLP_PREFIXLOG;
147 	}
148 
149 	tlp_len = min(tlp_len, ARRAY_SIZE(regs->header_log.dw));
150 
151 	for (i = 0; i < tlp_len; i++) {
152 		unsigned int off;
153 
154 		if (i < PCIE_STD_NUM_TLP_HEADERLOG)
155 			off = PCI_ERR_HEADER_LOG + i * sizeof(u32);
156 		else
157 			off = PCI_ERR_PREFIX_LOG +
158 			      (i - PCIE_STD_NUM_TLP_HEADERLOG) * sizeof(u32);
159 
160 		if (off + sizeof(u32) > raw_len)
161 			break;
162 		regs->header_log.dw[i] = get_unaligned_le32(raw + off);
163 	}
164 
165 	/* @i may be short of tlp_len; non-Flit needs the TLP parsed, so cap at 4 */
166 	regs->header_log.header_len = flit ? i : min(i, PCIE_STD_NUM_TLP_HEADERLOG);
167 	regs->header_log.flit = flit;
168 
169 	if (raw_len >= PCI_ERR_ROOT_COMMAND + sizeof(u32))
170 		regs->root_command = get_unaligned_le32(raw + PCI_ERR_ROOT_COMMAND);
171 	if (raw_len >= PCI_ERR_ROOT_STATUS + sizeof(u32))
172 		regs->root_status = get_unaligned_le32(raw + PCI_ERR_ROOT_STATUS);
173 	if (raw_len >= PCI_ERR_ROOT_ERR_SRC + sizeof(u32)) {
174 		u32 src = get_unaligned_le32(raw + PCI_ERR_ROOT_ERR_SRC);
175 
176 		/* One register: ERR_COR in 15:0, ERR_FATAL/NONFATAL in 31:16 */
177 		regs->cor_err_source = src;
178 		regs->uncor_err_source = src >> 16;
179 	}
180 }
181 EXPORT_SYMBOL_GPL(aer_cap_regs_unpack);
182 
183 #define EE_PREFIX_STR " E-E Prefixes:"
184 
185 /**
186  * pcie_print_tlp_log - Print TLP Header / Prefix Log contents
187  * @dev: PCIe device
188  * @log: TLP Log structure
189  * @level: Printk log level
190  * @pfx: String prefix
191  *
192  * Prints TLP Header and Prefix Log information held by @log.
193  */
194 void pcie_print_tlp_log(const struct pci_dev *dev,
195 			const struct pcie_tlp_log *log, const char *level,
196 			const char *pfx)
197 {
198 	/* EE_PREFIX_STR fits the extended DW space needed for the Flit mode */
199 	char buf[11 * PCIE_STD_MAX_TLP_HEADERLOG + 1];
200 	unsigned int i;
201 	int len;
202 
203 	len = scnprintf(buf, sizeof(buf), "%#010x %#010x %#010x %#010x",
204 			log->dw[0], log->dw[1], log->dw[2], log->dw[3]);
205 
206 	if (log->flit) {
207 		for (i = PCIE_STD_NUM_TLP_HEADERLOG; i < log->header_len; i++) {
208 			len += scnprintf(buf + len, sizeof(buf) - len,
209 					 " %#010x", log->dw[i]);
210 		}
211 	} else {
212 		if (log->prefix[0])
213 			len += scnprintf(buf + len, sizeof(buf) - len,
214 					 EE_PREFIX_STR);
215 		for (i = 0; i < ARRAY_SIZE(log->prefix); i++) {
216 			if (!log->prefix[i])
217 				break;
218 			len += scnprintf(buf + len, sizeof(buf) - len,
219 					 " %#010x", log->prefix[i]);
220 		}
221 	}
222 
223 	dev_printk(level, &dev->dev, "%sTLP Header%s: %s\n", pfx,
224 		log->flit ? " (Flit)" : "", buf);
225 }
226