1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * CXL Error INJection support. Used by CXL core to inject |
4 | * protocol errors into CXL ports. |
5 | * |
6 | * Copyright (C) 2023 Advanced Micro Devices, Inc. |
7 | * |
8 | * Author: Ben Cheatham <benjamin.cheatham@amd.com> |
9 | */ |
10 | #include <linux/einj-cxl.h> |
11 | #include <linux/seq_file.h> |
12 | #include <linux/pci.h> |
13 | |
14 | #include "apei-internal.h" |
15 | |
16 | /* Defined in einj-core.c */ |
17 | extern bool einj_initialized; |
18 | |
19 | static struct { u32 mask; const char *str; } const einj_cxl_error_type_string[] = { |
20 | { ACPI_EINJ_CXL_CACHE_CORRECTABLE, "CXL.cache Protocol Correctable" }, |
21 | { ACPI_EINJ_CXL_CACHE_UNCORRECTABLE, "CXL.cache Protocol Uncorrectable non-fatal" }, |
22 | { ACPI_EINJ_CXL_CACHE_FATAL, "CXL.cache Protocol Uncorrectable fatal" }, |
23 | { ACPI_EINJ_CXL_MEM_CORRECTABLE, "CXL.mem Protocol Correctable" }, |
24 | { ACPI_EINJ_CXL_MEM_UNCORRECTABLE, "CXL.mem Protocol Uncorrectable non-fatal" }, |
25 | { ACPI_EINJ_CXL_MEM_FATAL, "CXL.mem Protocol Uncorrectable fatal" }, |
26 | }; |
27 | |
28 | int einj_cxl_available_error_type_show(struct seq_file *m, void *v) |
29 | { |
30 | int cxl_err, rc; |
31 | u32 available_error_type = 0; |
32 | |
33 | rc = einj_get_available_error_type(type: &available_error_type); |
34 | if (rc) |
35 | return rc; |
36 | |
37 | for (int pos = 0; pos < ARRAY_SIZE(einj_cxl_error_type_string); pos++) { |
38 | cxl_err = ACPI_EINJ_CXL_CACHE_CORRECTABLE << pos; |
39 | |
40 | if (available_error_type & cxl_err) |
41 | seq_printf(m, fmt: "0x%08x\t%s\n" , |
42 | einj_cxl_error_type_string[pos].mask, |
43 | einj_cxl_error_type_string[pos].str); |
44 | } |
45 | |
46 | return 0; |
47 | } |
48 | EXPORT_SYMBOL_NS_GPL(einj_cxl_available_error_type_show, CXL); |
49 | |
50 | static int cxl_dport_get_sbdf(struct pci_dev *dport_dev, u64 *sbdf) |
51 | { |
52 | struct pci_bus *pbus; |
53 | struct pci_host_bridge *bridge; |
54 | u64 seg = 0, bus; |
55 | |
56 | pbus = dport_dev->bus; |
57 | bridge = pci_find_host_bridge(bus: pbus); |
58 | |
59 | if (!bridge) |
60 | return -ENODEV; |
61 | |
62 | if (bridge->domain_nr != PCI_DOMAIN_NR_NOT_SET) |
63 | seg = bridge->domain_nr; |
64 | |
65 | bus = pbus->number; |
66 | *sbdf = (seg << 24) | (bus << 16) | dport_dev->devfn; |
67 | |
68 | return 0; |
69 | } |
70 | |
71 | int einj_cxl_inject_rch_error(u64 rcrb, u64 type) |
72 | { |
73 | int rc; |
74 | |
75 | /* Only CXL error types can be specified */ |
76 | if (!einj_is_cxl_error_type(type)) |
77 | return -EINVAL; |
78 | |
79 | rc = einj_validate_error_type(type); |
80 | if (rc) |
81 | return rc; |
82 | |
83 | return einj_cxl_rch_error_inject(type, flags: 0x2, param1: rcrb, GENMASK_ULL(63, 0), |
84 | param3: 0, param4: 0); |
85 | } |
86 | EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_rch_error, CXL); |
87 | |
88 | int einj_cxl_inject_error(struct pci_dev *dport, u64 type) |
89 | { |
90 | u64 param4 = 0; |
91 | int rc; |
92 | |
93 | /* Only CXL error types can be specified */ |
94 | if (!einj_is_cxl_error_type(type)) |
95 | return -EINVAL; |
96 | |
97 | rc = einj_validate_error_type(type); |
98 | if (rc) |
99 | return rc; |
100 | |
101 | rc = cxl_dport_get_sbdf(dport_dev: dport, sbdf: ¶m4); |
102 | if (rc) |
103 | return rc; |
104 | |
105 | return einj_error_inject(type, flags: 0x4, param1: 0, param2: 0, param3: 0, param4); |
106 | } |
107 | EXPORT_SYMBOL_NS_GPL(einj_cxl_inject_error, CXL); |
108 | |
109 | bool einj_cxl_is_initialized(void) |
110 | { |
111 | return einj_initialized; |
112 | } |
113 | EXPORT_SYMBOL_NS_GPL(einj_cxl_is_initialized, CXL); |
114 | |