1*81bab70dSJohn Baldwin /*-
2*81bab70dSJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause
3*81bab70dSJohn Baldwin *
4*81bab70dSJohn Baldwin * Copyright (c) 2025 Netflix, Inc.
5*81bab70dSJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org>
6*81bab70dSJohn Baldwin */
7*81bab70dSJohn Baldwin
8*81bab70dSJohn Baldwin #include <sys/param.h>
9*81bab70dSJohn Baldwin #include <sys/ioctl.h>
10*81bab70dSJohn Baldwin #include <err.h>
11*81bab70dSJohn Baldwin #include <fcntl.h>
12*81bab70dSJohn Baldwin #include <inttypes.h>
13*81bab70dSJohn Baldwin #include <stdio.h>
14*81bab70dSJohn Baldwin #include <stdlib.h>
15*81bab70dSJohn Baldwin
16*81bab70dSJohn Baldwin #pragma GCC diagnostic push
17*81bab70dSJohn Baldwin #pragma GCC diagnostic ignored "-Wunused-parameter"
18*81bab70dSJohn Baldwin #include <contrib/dev/acpica/include/acpi.h>
19*81bab70dSJohn Baldwin #include <contrib/dev/acpica/include/actbl1.h>
20*81bab70dSJohn Baldwin #pragma GCC diagnostic pop
21*81bab70dSJohn Baldwin
22*81bab70dSJohn Baldwin #include <dev/acpica/acpiio.h>
23*81bab70dSJohn Baldwin #include <dev/pci/pcireg.h>
24*81bab70dSJohn Baldwin
25*81bab70dSJohn Baldwin static int fd;
26*81bab70dSJohn Baldwin
27*81bab70dSJohn Baldwin static struct error_type_info {
28*81bab70dSJohn Baldwin const char *name;
29*81bab70dSJohn Baldwin uint32_t bit;
30*81bab70dSJohn Baldwin const char *description;
31*81bab70dSJohn Baldwin } error_types[] = {
32*81bab70dSJohn Baldwin { "cpu.cor", ACPI_EINJ_PROCESSOR_CORRECTABLE, "Processor Correctable" },
33*81bab70dSJohn Baldwin { "cpu.uc", ACPI_EINJ_PROCESSOR_UNCORRECTABLE,
34*81bab70dSJohn Baldwin "Processor Uncorrectable Non-Fatal" },
35*81bab70dSJohn Baldwin { "cpu.fatal", ACPI_EINJ_PROCESSOR_FATAL,
36*81bab70dSJohn Baldwin "Processor Uncorrectable Fatal" },
37*81bab70dSJohn Baldwin { "mem.cor", ACPI_EINJ_MEMORY_CORRECTABLE, "Memory Correctable" },
38*81bab70dSJohn Baldwin { "mem.uc", ACPI_EINJ_MEMORY_UNCORRECTABLE,
39*81bab70dSJohn Baldwin "Memory Uncorrectable Non-Fatal" },
40*81bab70dSJohn Baldwin { "mem.fatal", ACPI_EINJ_MEMORY_FATAL, "Memory Uncorrectable Fatal" },
41*81bab70dSJohn Baldwin { "pcie.cor", ACPI_EINJ_PCIX_CORRECTABLE, "PCI Express Correctable" },
42*81bab70dSJohn Baldwin { "pcie.uc", ACPI_EINJ_PCIX_UNCORRECTABLE,
43*81bab70dSJohn Baldwin "PCI Express Uncorrectable Non-Fatal" },
44*81bab70dSJohn Baldwin { "pcie.fatal", ACPI_EINJ_PCIX_FATAL,
45*81bab70dSJohn Baldwin "PCI Express Uncorrectable Fatal" },
46*81bab70dSJohn Baldwin { "platform.cor", ACPI_EINJ_PLATFORM_CORRECTABLE,
47*81bab70dSJohn Baldwin "Platform Correctable" },
48*81bab70dSJohn Baldwin { "platform.uc", ACPI_EINJ_PLATFORM_UNCORRECTABLE,
49*81bab70dSJohn Baldwin "Platform Uncorrectable Non-Fatal" },
50*81bab70dSJohn Baldwin { "platform.fatal", ACPI_EINJ_PLATFORM_FATAL,
51*81bab70dSJohn Baldwin "Platform Uncorrectable Fatal" },
52*81bab70dSJohn Baldwin { "cxl.cache.cor", ACPI_EINJ_CXL_CACHE_CORRECTABLE,
53*81bab70dSJohn Baldwin "CXL.cache Correctable" },
54*81bab70dSJohn Baldwin { "cxl.cache.uc", ACPI_EINJ_CXL_CACHE_UNCORRECTABLE,
55*81bab70dSJohn Baldwin "CXL.cache Uncorrectable Non-Fatal" },
56*81bab70dSJohn Baldwin { "cxl.cache.fatal", ACPI_EINJ_CXL_CACHE_FATAL,
57*81bab70dSJohn Baldwin "CXL.cache Uncorrectable Fatal" },
58*81bab70dSJohn Baldwin { "cxl.mem.cor", ACPI_EINJ_CXL_MEM_CORRECTABLE, "CXL.mem Correctable" },
59*81bab70dSJohn Baldwin { "cxl.mem.uc", ACPI_EINJ_CXL_MEM_UNCORRECTABLE,
60*81bab70dSJohn Baldwin "CXL.mem Uncorrectable Non-Fatal" },
61*81bab70dSJohn Baldwin { "cxl.mem.fatal", ACPI_EINJ_CXL_MEM_FATAL,
62*81bab70dSJohn Baldwin "CXL.mem Uncorrectable Fatal" },
63*81bab70dSJohn Baldwin { "vendor", ACPI_EINJ_VENDOR_DEFINED, "Vendor Defined" },
64*81bab70dSJohn Baldwin };
65*81bab70dSJohn Baldwin
66*81bab70dSJohn Baldwin static void
usage(void)67*81bab70dSJohn Baldwin usage(void)
68*81bab70dSJohn Baldwin {
69*81bab70dSJohn Baldwin fprintf(stderr, "usage:\n"
70*81bab70dSJohn Baldwin "\teinj list\n"
71*81bab70dSJohn Baldwin "\teinj inject -a <address> -C <cpu> -P <dbsf> <type>\n");
72*81bab70dSJohn Baldwin exit(1);
73*81bab70dSJohn Baldwin }
74*81bab70dSJohn Baldwin
75*81bab70dSJohn Baldwin static void
print_vendor_struct(uint32_t vendor_length)76*81bab70dSJohn Baldwin print_vendor_struct(uint32_t vendor_length)
77*81bab70dSJohn Baldwin {
78*81bab70dSJohn Baldwin struct acpi_einj_vendor_info vi;
79*81bab70dSJohn Baldwin ACPI_EINJ_VENDOR *vendor;
80*81bab70dSJohn Baldwin uint32_t dbsf;
81*81bab70dSJohn Baldwin
82*81bab70dSJohn Baldwin vi.buf = malloc(vendor_length);
83*81bab70dSJohn Baldwin vi.len = vendor_length;
84*81bab70dSJohn Baldwin if (ioctl(fd, ACPIIO_EINJ_GET_VENDOR, &vi) == -1) {
85*81bab70dSJohn Baldwin warn("ACPIIO_EINJ_GET_VENDOR");
86*81bab70dSJohn Baldwin free(vi.buf);
87*81bab70dSJohn Baldwin return;
88*81bab70dSJohn Baldwin }
89*81bab70dSJohn Baldwin
90*81bab70dSJohn Baldwin vendor = vi.buf;
91*81bab70dSJohn Baldwin dbsf = vendor->PcieId;
92*81bab70dSJohn Baldwin
93*81bab70dSJohn Baldwin printf("Vendor Extension:\n");
94*81bab70dSJohn Baldwin printf("\tLength:\t%u\n", vendor->Length);
95*81bab70dSJohn Baldwin printf("\tPCIe Id:\t%u:%u:%u:%u\n", dbsf >> 24,
96*81bab70dSJohn Baldwin (dbsf >> 16) & 0xff, (dbsf >> 11) & 0x1f, (dbsf >> 8) & 7);
97*81bab70dSJohn Baldwin printf("\tVendor Id:\t%04x\n", vendor->VendorId);
98*81bab70dSJohn Baldwin printf("\tDevice Id:\t%04x\n", vendor->DeviceId);
99*81bab70dSJohn Baldwin printf("\tRevision:\t%02x\n", vendor->RevisionId);
100*81bab70dSJohn Baldwin free(vi.buf);
101*81bab70dSJohn Baldwin }
102*81bab70dSJohn Baldwin
103*81bab70dSJohn Baldwin static void
list_error_types(void)104*81bab70dSJohn Baldwin list_error_types(void)
105*81bab70dSJohn Baldwin {
106*81bab70dSJohn Baldwin struct acpi_einj_info info;
107*81bab70dSJohn Baldwin
108*81bab70dSJohn Baldwin if (ioctl(fd, ACPIIO_EINJ_GET_INFO, &info) == -1)
109*81bab70dSJohn Baldwin err(1, "ACPIIO_EINJ_GET_INFO");
110*81bab70dSJohn Baldwin
111*81bab70dSJohn Baldwin printf("Supported errors <%#x>:\n", info.error_type);
112*81bab70dSJohn Baldwin for (u_int i = 0; i < nitems(error_types); i++) {
113*81bab70dSJohn Baldwin struct error_type_info *eti = &error_types[i];
114*81bab70dSJohn Baldwin
115*81bab70dSJohn Baldwin if ((info.error_type & eti->bit) == 0)
116*81bab70dSJohn Baldwin continue;
117*81bab70dSJohn Baldwin printf("\t%s (%s)\n", eti->name, eti->description);
118*81bab70dSJohn Baldwin }
119*81bab70dSJohn Baldwin
120*81bab70dSJohn Baldwin if ((info.error_type & ACPI_EINJ_VENDOR_DEFINED) != 0 &&
121*81bab70dSJohn Baldwin info.vendor_length != 0) {
122*81bab70dSJohn Baldwin printf("\n");
123*81bab70dSJohn Baldwin print_vendor_struct(info.vendor_length);
124*81bab70dSJohn Baldwin }
125*81bab70dSJohn Baldwin }
126*81bab70dSJohn Baldwin
127*81bab70dSJohn Baldwin static uint32_t
parse_dbsf(const char * arg)128*81bab70dSJohn Baldwin parse_dbsf(const char *arg)
129*81bab70dSJohn Baldwin {
130*81bab70dSJohn Baldwin char *cp;
131*81bab70dSJohn Baldwin unsigned long val[4];
132*81bab70dSJohn Baldwin u_int i;
133*81bab70dSJohn Baldwin
134*81bab70dSJohn Baldwin /*
135*81bab70dSJohn Baldwin * Permit the same format as pciconf. Eventually this should
136*81bab70dSJohn Baldwin * accept PCI device names, too.
137*81bab70dSJohn Baldwin */
138*81bab70dSJohn Baldwin if (strncmp(arg, "pci", 3) == 0)
139*81bab70dSJohn Baldwin arg += 3;
140*81bab70dSJohn Baldwin
141*81bab70dSJohn Baldwin /* Read up to 4 colon-separated values. */
142*81bab70dSJohn Baldwin for (i = 0;; i++) {
143*81bab70dSJohn Baldwin val[i] = strtoul(arg, &cp, 10);
144*81bab70dSJohn Baldwin if (cp == arg || val[i] > 255)
145*81bab70dSJohn Baldwin goto error;
146*81bab70dSJohn Baldwin if (*cp == ':') {
147*81bab70dSJohn Baldwin if (i == nitems(val) - 1)
148*81bab70dSJohn Baldwin goto error;
149*81bab70dSJohn Baldwin arg = cp + 1;
150*81bab70dSJohn Baldwin continue;
151*81bab70dSJohn Baldwin }
152*81bab70dSJohn Baldwin if (*cp == '\0') {
153*81bab70dSJohn Baldwin if (i < 3)
154*81bab70dSJohn Baldwin goto error;
155*81bab70dSJohn Baldwin break;
156*81bab70dSJohn Baldwin }
157*81bab70dSJohn Baldwin goto error;
158*81bab70dSJohn Baldwin }
159*81bab70dSJohn Baldwin
160*81bab70dSJohn Baldwin /* If no domain is given, assume a domain of 0. */
161*81bab70dSJohn Baldwin if (i == 3) {
162*81bab70dSJohn Baldwin val[3] = val[2];
163*81bab70dSJohn Baldwin val[2] = val[1];
164*81bab70dSJohn Baldwin val[1] = val[0];
165*81bab70dSJohn Baldwin val[0] = 0;
166*81bab70dSJohn Baldwin }
167*81bab70dSJohn Baldwin
168*81bab70dSJohn Baldwin /*
169*81bab70dSJohn Baldwin * To handle ARI, only limit function numbers if the slot is
170*81bab70dSJohn Baldwin * non-zero.
171*81bab70dSJohn Baldwin */
172*81bab70dSJohn Baldwin if (val[2] != 0 && val[3] > PCI_FUNCMAX)
173*81bab70dSJohn Baldwin goto error;
174*81bab70dSJohn Baldwin
175*81bab70dSJohn Baldwin return (val[0] << 24 | val[1] << 16 | val[2] << 3 | val[3]);
176*81bab70dSJohn Baldwin
177*81bab70dSJohn Baldwin error:
178*81bab70dSJohn Baldwin errx(1, "invalid PCIe ID");
179*81bab70dSJohn Baldwin }
180*81bab70dSJohn Baldwin
181*81bab70dSJohn Baldwin static void
inject_error(int argc,char * argv[])182*81bab70dSJohn Baldwin inject_error(int argc, char *argv[])
183*81bab70dSJohn Baldwin {
184*81bab70dSJohn Baldwin struct acpi_einj_error einj;
185*81bab70dSJohn Baldwin char *cp;
186*81bab70dSJohn Baldwin uintmax_t value;
187*81bab70dSJohn Baldwin int ch;
188*81bab70dSJohn Baldwin
189*81bab70dSJohn Baldwin memset(&einj, 0, sizeof(einj));
190*81bab70dSJohn Baldwin while ((ch = getopt(argc, argv, "a:C:P:")) != -1) {
191*81bab70dSJohn Baldwin switch (ch) {
192*81bab70dSJohn Baldwin case 'a':
193*81bab70dSJohn Baldwin einj.memory_address = strtoumax(optarg, &cp, 0);
194*81bab70dSJohn Baldwin if (*cp != '\0')
195*81bab70dSJohn Baldwin errx(1, "invalid address");
196*81bab70dSJohn Baldwin einj.address_flags |= ACPI_EINJ_MEMADDRESS_VALID;
197*81bab70dSJohn Baldwin break;
198*81bab70dSJohn Baldwin case 'C':
199*81bab70dSJohn Baldwin value = strtoumax(optarg, &cp, 0);
200*81bab70dSJohn Baldwin if (*cp != '\0' || value > UINT32_MAX)
201*81bab70dSJohn Baldwin errx(1, "invalid APIC ID");
202*81bab70dSJohn Baldwin einj.apic_id = value;
203*81bab70dSJohn Baldwin einj.address_flags |= ACPI_EINJ_APICID_VALID;
204*81bab70dSJohn Baldwin break;
205*81bab70dSJohn Baldwin case 'P':
206*81bab70dSJohn Baldwin einj.pcie_id = parse_dbsf(optarg);
207*81bab70dSJohn Baldwin einj.address_flags |= ACPI_EINJ_PCIE_VALID;
208*81bab70dSJohn Baldwin break;
209*81bab70dSJohn Baldwin default:
210*81bab70dSJohn Baldwin usage();
211*81bab70dSJohn Baldwin }
212*81bab70dSJohn Baldwin }
213*81bab70dSJohn Baldwin argc -= optind;
214*81bab70dSJohn Baldwin argv += optind;
215*81bab70dSJohn Baldwin
216*81bab70dSJohn Baldwin if (argc != 1)
217*81bab70dSJohn Baldwin usage();
218*81bab70dSJohn Baldwin
219*81bab70dSJohn Baldwin if (strcmp(argv[0], "vendor") == 0)
220*81bab70dSJohn Baldwin errx(1, "vendor errors are not supported");
221*81bab70dSJohn Baldwin
222*81bab70dSJohn Baldwin for (u_int i = 0; i < nitems(error_types); i++) {
223*81bab70dSJohn Baldwin struct error_type_info *eti = &error_types[i];
224*81bab70dSJohn Baldwin
225*81bab70dSJohn Baldwin if (strcmp(argv[0], eti->name) == 0) {
226*81bab70dSJohn Baldwin einj.error_type = eti->bit;
227*81bab70dSJohn Baldwin break;
228*81bab70dSJohn Baldwin }
229*81bab70dSJohn Baldwin }
230*81bab70dSJohn Baldwin if (einj.error_type == 0)
231*81bab70dSJohn Baldwin errx(1, "unknown error type");
232*81bab70dSJohn Baldwin
233*81bab70dSJohn Baldwin if (ioctl(fd, ACPIIO_EINJ_SET_ERROR, &einj) == -1)
234*81bab70dSJohn Baldwin err(1, "ACPIIO_EINJ_SET_ERROR");
235*81bab70dSJohn Baldwin }
236*81bab70dSJohn Baldwin
237*81bab70dSJohn Baldwin int
main(int argc,char * argv[])238*81bab70dSJohn Baldwin main(int argc, char *argv[])
239*81bab70dSJohn Baldwin {
240*81bab70dSJohn Baldwin if (argc < 2)
241*81bab70dSJohn Baldwin usage();
242*81bab70dSJohn Baldwin
243*81bab70dSJohn Baldwin fd = open("/dev/acpi", O_RDWR);
244*81bab70dSJohn Baldwin if (fd == -1)
245*81bab70dSJohn Baldwin err(1, "/dev/acpi");
246*81bab70dSJohn Baldwin
247*81bab70dSJohn Baldwin if (strcasecmp(argv[1], "list") == 0)
248*81bab70dSJohn Baldwin list_error_types();
249*81bab70dSJohn Baldwin else if (strcasecmp(argv[1], "inject") == 0)
250*81bab70dSJohn Baldwin inject_error(argc - 1, argv + 1);
251*81bab70dSJohn Baldwin else
252*81bab70dSJohn Baldwin usage();
253*81bab70dSJohn Baldwin
254*81bab70dSJohn Baldwin close(fd);
255*81bab70dSJohn Baldwin return (0);
256*81bab70dSJohn Baldwin }
257