xref: /freebsd/usr.sbin/acpi/einj/einj.c (revision 81bab70dc6205032e58d15a87ccc8549f900316d)
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