Skip to content

Commit 12fb28e

Browse files
benhartcheathamdjbw
authored andcommitted
EINJ: Add CXL error type support
Move CXL protocol error types from einj.c (now einj-core.c) to einj-cxl.c. einj-cxl.c implements the necessary handling for CXL protocol error injection and exposes an API for the CXL core to use said functionality, while also allowing the EINJ module to be built without CXL support. Because CXL error types targeting CXL 1.0/1.1 ports require special handling, only allow them to be injected through the new cxl debugfs interface (next commit) and return an error when attempting to inject through the legacy interface. Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> Signed-off-by: Ben Cheatham <Benjamin.Cheatham@amd.com> Link: https://lore.kernel.org/r/20240311142508.31717-3-Benjamin.Cheatham@amd.com Signed-off-by: Dan Williams <dan.j.williams@intel.com>
1 parent 5621faf commit 12fb28e

File tree

7 files changed

+249
-18
lines changed

7 files changed

+249
-18
lines changed

MAINTAINERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5289,6 +5289,7 @@ M: Dan Williams <dan.j.williams@intel.com>
52895289
L: linux-cxl@vger.kernel.org
52905290
S: Maintained
52915291
F: drivers/cxl/
5292+
F: include/linux/cxl-einj.h
52925293
F: include/linux/cxl-event.h
52935294
F: include/uapi/linux/cxl_mem.h
52945295
F: tools/testing/cxl/

drivers/acpi/apei/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ config ACPI_APEI_EINJ
6060
mainly used for debugging and testing the other parts of
6161
APEI and some other RAS features.
6262

63+
config ACPI_APEI_EINJ_CXL
64+
bool "CXL Error INJection Support"
65+
default ACPI_APEI_EINJ
66+
depends on ACPI_APEI_EINJ
67+
depends on CXL_BUS && CXL_BUS <= ACPI_APEI_EINJ
68+
help
69+
Support for CXL protocol Error INJection through debugfs/cxl.
70+
Availability and which errors are supported is dependent on
71+
the host platform. Look to ACPI v6.5 section 18.6.4 and kernel
72+
EINJ documentation for more information.
73+
74+
If unsure say 'n'
75+
6376
config ACPI_APEI_ERST_DEBUG
6477
tristate "APEI Error Record Serialization Table (ERST) Debug Support"
6578
depends on ACPI_APEI

drivers/acpi/apei/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
obj-$(CONFIG_ACPI_APEI) += apei.o
33
obj-$(CONFIG_ACPI_APEI_GHES) += ghes.o
44
obj-$(CONFIG_ACPI_APEI_EINJ) += einj.o
5+
einj-y := einj-core.o
6+
einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o
57
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
68

79
apei-y := apei-base.o hest.o erst.o bert.o

drivers/acpi/apei/apei-internal.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,22 @@ static inline u32 cper_estatus_len(struct acpi_hest_generic_status *estatus)
130130
}
131131

132132
int apei_osc_setup(void);
133+
134+
int einj_get_available_error_type(u32 *type);
135+
int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
136+
u64 param4);
137+
int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
138+
u64 param3, u64 param4);
139+
bool einj_is_cxl_error_type(u64 type);
140+
int einj_validate_error_type(u64 type);
141+
142+
#ifndef ACPI_EINJ_CXL_CACHE_CORRECTABLE
143+
#define ACPI_EINJ_CXL_CACHE_CORRECTABLE BIT(12)
144+
#define ACPI_EINJ_CXL_CACHE_UNCORRECTABLE BIT(13)
145+
#define ACPI_EINJ_CXL_CACHE_FATAL BIT(14)
146+
#define ACPI_EINJ_CXL_MEM_CORRECTABLE BIT(15)
147+
#define ACPI_EINJ_CXL_MEM_UNCORRECTABLE BIT(16)
148+
#define ACPI_EINJ_CXL_MEM_FATAL BIT(17)
149+
#endif
150+
133151
#endif

drivers/acpi/apei/einj.c renamed to drivers/acpi/apei/einj-core.c

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@
3737
#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
3838
ACPI_EINJ_MEMORY_UNCORRECTABLE | \
3939
ACPI_EINJ_MEMORY_FATAL)
40+
#define CXL_ERROR_MASK (ACPI_EINJ_CXL_CACHE_CORRECTABLE | \
41+
ACPI_EINJ_CXL_CACHE_UNCORRECTABLE | \
42+
ACPI_EINJ_CXL_CACHE_FATAL | \
43+
ACPI_EINJ_CXL_MEM_CORRECTABLE | \
44+
ACPI_EINJ_CXL_MEM_UNCORRECTABLE | \
45+
ACPI_EINJ_CXL_MEM_FATAL)
4046

4147
/*
4248
* ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
@@ -141,7 +147,7 @@ static DEFINE_MUTEX(einj_mutex);
141147
/*
142148
* Exported APIs use this flag to exit early if einj_probe() failed.
143149
*/
144-
static bool einj_initialized __ro_after_init;
150+
bool einj_initialized __ro_after_init;
145151

146152
static void *einj_param;
147153

@@ -166,7 +172,7 @@ static int __einj_get_available_error_type(u32 *type)
166172
}
167173

168174
/* Get error injection capabilities of the platform */
169-
static int einj_get_available_error_type(u32 *type)
175+
int einj_get_available_error_type(u32 *type)
170176
{
171177
int rc;
172178

@@ -536,8 +542,8 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
536542
}
537543

538544
/* Inject the specified hardware error */
539-
static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
540-
u64 param3, u64 param4)
545+
int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3,
546+
u64 param4)
541547
{
542548
int rc;
543549
u64 base_addr, size;
@@ -560,8 +566,17 @@ static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
560566
if (type & ACPI5_VENDOR_BIT) {
561567
if (vendor_flags != SETWA_FLAGS_MEM)
562568
goto inject;
563-
} else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM))
569+
} else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) {
564570
goto inject;
571+
}
572+
573+
/*
574+
* Injections targeting a CXL 1.0/1.1 port have to be injected
575+
* via the einj_cxl_rch_error_inject() path as that does the proper
576+
* validation of the given RCRB base (MMIO) address.
577+
*/
578+
if (einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM))
579+
return -EINVAL;
565580

566581
/*
567582
* Disallow crazy address masks that give BIOS leeway to pick
@@ -593,6 +608,21 @@ static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
593608
return rc;
594609
}
595610

611+
int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
612+
u64 param3, u64 param4)
613+
{
614+
int rc;
615+
616+
if (!(einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM)))
617+
return -EINVAL;
618+
619+
mutex_lock(&einj_mutex);
620+
rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
621+
mutex_unlock(&einj_mutex);
622+
623+
return rc;
624+
}
625+
596626
static u32 error_type;
597627
static u32 error_flags;
598628
static u64 error_param1;
@@ -613,12 +643,6 @@ static struct { u32 mask; const char *str; } const einj_error_type_string[] = {
613643
{ BIT(9), "Platform Correctable" },
614644
{ BIT(10), "Platform Uncorrectable non-fatal" },
615645
{ BIT(11), "Platform Uncorrectable fatal"},
616-
{ BIT(12), "CXL.cache Protocol Correctable" },
617-
{ BIT(13), "CXL.cache Protocol Uncorrectable non-fatal" },
618-
{ BIT(14), "CXL.cache Protocol Uncorrectable fatal" },
619-
{ BIT(15), "CXL.mem Protocol Correctable" },
620-
{ BIT(16), "CXL.mem Protocol Uncorrectable non-fatal" },
621-
{ BIT(17), "CXL.mem Protocol Uncorrectable fatal" },
622646
{ BIT(31), "Vendor Defined Error Types" },
623647
};
624648

@@ -647,22 +671,26 @@ static int error_type_get(void *data, u64 *val)
647671
return 0;
648672
}
649673

650-
static int error_type_set(void *data, u64 val)
674+
bool einj_is_cxl_error_type(u64 type)
675+
{
676+
return (type & CXL_ERROR_MASK) && (!(type & ACPI5_VENDOR_BIT));
677+
}
678+
679+
int einj_validate_error_type(u64 type)
651680
{
681+
u32 tval, vendor, available_error_type = 0;
652682
int rc;
653-
u32 available_error_type = 0;
654-
u32 tval, vendor;
655683

656684
/* Only low 32 bits for error type are valid */
657-
if (val & GENMASK_ULL(63, 32))
685+
if (type & GENMASK_ULL(63, 32))
658686
return -EINVAL;
659687

660688
/*
661689
* Vendor defined types have 0x80000000 bit set, and
662690
* are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
663691
*/
664-
vendor = val & ACPI5_VENDOR_BIT;
665-
tval = val & 0x7fffffff;
692+
vendor = type & ACPI5_VENDOR_BIT;
693+
tval = type & GENMASK(30, 0);
666694

667695
/* Only one error type can be specified */
668696
if (tval & (tval - 1))
@@ -671,9 +699,21 @@ static int error_type_set(void *data, u64 val)
671699
rc = einj_get_available_error_type(&available_error_type);
672700
if (rc)
673701
return rc;
674-
if (!(val & available_error_type))
702+
if (!(type & available_error_type))
675703
return -EINVAL;
676704
}
705+
706+
return 0;
707+
}
708+
709+
static int error_type_set(void *data, u64 val)
710+
{
711+
int rc;
712+
713+
rc = einj_validate_error_type(val);
714+
if (rc)
715+
return rc;
716+
677717
error_type = val;
678718

679719
return 0;

drivers/acpi/apei/einj-cxl.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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(&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, "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(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, 0x2, rcrb, GENMASK_ULL(63, 0),
84+
0, 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, &param4);
102+
if (rc)
103+
return rc;
104+
105+
return einj_error_inject(type, 0x4, 0, 0, 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);

include/linux/einj-cxl.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* SPDX-License-Identifier: GPL-2.0-or-later */
2+
/*
3+
* CXL protocol Error INJection support.
4+
*
5+
* Copyright (c) 2023 Advanced Micro Devices, Inc.
6+
* All Rights Reserved.
7+
*
8+
* Author: Ben Cheatham <benjamin.cheatham@amd.com>
9+
*/
10+
#ifndef EINJ_CXL_H
11+
#define EINJ_CXL_H
12+
13+
#include <linux/errno.h>
14+
#include <linux/types.h>
15+
16+
struct pci_dev;
17+
struct seq_file;
18+
19+
#if IS_ENABLED(CONFIG_ACPI_APEI_EINJ_CXL)
20+
int einj_cxl_available_error_type_show(struct seq_file *m, void *v);
21+
int einj_cxl_inject_error(struct pci_dev *dport_dev, u64 type);
22+
int einj_cxl_inject_rch_error(u64 rcrb, u64 type);
23+
bool einj_cxl_is_initialized(void);
24+
#else /* !IS_ENABLED(CONFIG_ACPI_APEI_EINJ_CXL) */
25+
static inline int einj_cxl_available_error_type_show(struct seq_file *m,
26+
void *v)
27+
{
28+
return -ENXIO;
29+
}
30+
31+
static inline int einj_cxl_inject_error(struct pci_dev *dport_dev, u64 type)
32+
{
33+
return -ENXIO;
34+
}
35+
36+
static inline int einj_cxl_inject_rch_error(u64 rcrb, u64 type)
37+
{
38+
return -ENXIO;
39+
}
40+
41+
static inline bool einj_cxl_is_initialized(void) { return false; }
42+
#endif /* CONFIG_ACPI_APEI_EINJ_CXL */
43+
44+
#endif /* EINJ_CXL_H */

0 commit comments

Comments
 (0)