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