Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F173788025
D58026.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D58026.diff
View Options
diff --git a/usr.sbin/acpi/Makefile b/usr.sbin/acpi/Makefile
--- a/usr.sbin/acpi/Makefile
+++ b/usr.sbin/acpi/Makefile
@@ -1,6 +1,6 @@
# Makefile for acpi tools
# $Id: Makefile,v 1.1 2000/07/14 18:16:22 iwasaki Exp $
-SUBDIR= acpiconf acpidb acpidump iasl
+SUBDIR= acpiconf acpidb acpidump einj iasl
.include <bsd.subdir.mk>
diff --git a/usr.sbin/acpi/einj/Makefile b/usr.sbin/acpi/einj/Makefile
new file mode 100644
--- /dev/null
+++ b/usr.sbin/acpi/einj/Makefile
@@ -0,0 +1,4 @@
+PROG= einj
+MAN= einj.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acpi/einj/einj.8 b/usr.sbin/acpi/einj/einj.8
new file mode 100644
--- /dev/null
+++ b/usr.sbin/acpi/einj/einj.8
@@ -0,0 +1,73 @@
+.\"
+.\" Copyright (c) 2025 Netflix, Inc.
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.Dd December 17, 2025
+.Dt EINJ 8
+.Os
+.Sh NAME
+.Nm einj
+.Nd ACPI Platform Error Interface error injection
+.Sh SYNOPSIS
+.Nm
+.Cm list
+.Nm
+.Cm inject
+.Op Fl a Ar address
+.Op Fl C Ar APIC ID
+.Oo Fl P Ar device
+.Sm off
+.Cm pci Oo Ar domain \&: Oc Ar bus \&: Ar slot \&: Ar function
+.Sm on
+.Oc
+.Ar type
+.Sh DESCRIPTION
+The
+.Nm
+utility provides a command line interface to the ACPI Platform Error Interface
+.Pq APEI
+error injection facility provided by
+.Xr acpi_einj 4 .
+.Pp
+The
+.Cm list
+command outputs the set of platform errors that can be injected on the
+current host.
+.Pp
+The
+.Cm inject
+command injects a single platform error.
+The
+.Ar type
+argument specifies the type of error to inject,
+for example
+.Dq mem.cor
+to inject a correctable memory error.
+Vendor-specific errors are not supported.
+.Pp
+The
+.Ar address
+argument sets a specific address to use with memory errors.
+.Pp
+The
+.Ar APIC ID
+argument sets a physical processor ID for processor errors.
+.Pp
+The
+.Ar device
+argument sets the target for PCI device errors.
+The argument must follow the format
+.Sm off
+.Cm pci Oo Ar domain \&: Oc Ar bus \&: Ar slot \&: Ar function
+.Sm on .
+.Sh EXAMPLES
+Inject a fatal uncorrectable memory error for the physical address 0x12345678:
+.Pp
+.Dl einj inject -a 0x12345678 mem.fatal
+.Sh SEE ALSO
+.Xr acpi_einj 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 16.0 .
diff --git a/usr.sbin/acpi/einj/einj.c b/usr.sbin/acpi/einj/einj.c
new file mode 100644
--- /dev/null
+++ b/usr.sbin/acpi/einj/einj.c
@@ -0,0 +1,256 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Netflix, Inc.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <err.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/actbl1.h>
+#pragma GCC diagnostic pop
+
+#include <dev/acpica/acpiio.h>
+#include <dev/pci/pcireg.h>
+
+static int fd;
+
+static struct error_type_info {
+ const char *name;
+ uint32_t bit;
+ const char *description;
+} error_types[] = {
+ { "cpu.cor", ACPI_EINJ_PROCESSOR_CORRECTABLE, "Processor Correctable" },
+ { "cpu.uc", ACPI_EINJ_PROCESSOR_UNCORRECTABLE,
+ "Processor Uncorrectable Non-Fatal" },
+ { "cpu.fatal", ACPI_EINJ_PROCESSOR_FATAL,
+ "Processor Uncorrectable Fatal" },
+ { "mem.cor", ACPI_EINJ_MEMORY_CORRECTABLE, "Memory Correctable" },
+ { "mem.uc", ACPI_EINJ_MEMORY_UNCORRECTABLE,
+ "Memory Uncorrectable Non-Fatal" },
+ { "mem.fatal", ACPI_EINJ_MEMORY_FATAL, "Memory Uncorrectable Fatal" },
+ { "pcie.cor", ACPI_EINJ_PCIX_CORRECTABLE, "PCI Express Correctable" },
+ { "pcie.uc", ACPI_EINJ_PCIX_UNCORRECTABLE,
+ "PCI Express Uncorrectable Non-Fatal" },
+ { "pcie.fatal", ACPI_EINJ_PCIX_FATAL,
+ "PCI Express Uncorrectable Fatal" },
+ { "platform.cor", ACPI_EINJ_PLATFORM_CORRECTABLE,
+ "Platform Correctable" },
+ { "platform.uc", ACPI_EINJ_PLATFORM_UNCORRECTABLE,
+ "Platform Uncorrectable Non-Fatal" },
+ { "platform.fatal", ACPI_EINJ_PLATFORM_FATAL,
+ "Platform Uncorrectable Fatal" },
+ { "cxl.cache.cor", ACPI_EINJ_CXL_CACHE_CORRECTABLE,
+ "CXL.cache Correctable" },
+ { "cxl.cache.uc", ACPI_EINJ_CXL_CACHE_UNCORRECTABLE,
+ "CXL.cache Uncorrectable Non-Fatal" },
+ { "cxl.cache.fatal", ACPI_EINJ_CXL_CACHE_FATAL,
+ "CXL.cache Uncorrectable Fatal" },
+ { "cxl.mem.cor", ACPI_EINJ_CXL_MEM_CORRECTABLE, "CXL.mem Correctable" },
+ { "cxl.mem.uc", ACPI_EINJ_CXL_MEM_UNCORRECTABLE,
+ "CXL.mem Uncorrectable Non-Fatal" },
+ { "cxl.mem.fatal", ACPI_EINJ_CXL_MEM_FATAL,
+ "CXL.mem Uncorrectable Fatal" },
+ { "vendor", ACPI_EINJ_VENDOR_DEFINED, "Vendor Defined" },
+};
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage:\n"
+ "\teinj list\n"
+ "\teinj inject -a <address> -C <cpu> -P <dbsf> <type>\n");
+ exit(1);
+}
+
+static void
+print_vendor_struct(uint32_t vendor_length)
+{
+ struct acpi_einj_vendor_info vi;
+ ACPI_EINJ_VENDOR *vendor;
+ uint32_t dbsf;
+
+ vi.buf = malloc(vendor_length);
+ vi.len = vendor_length;
+ if (ioctl(fd, ACPIIO_EINJ_GET_VENDOR, &vi) == -1) {
+ warn("ACPIIO_EINJ_GET_VENDOR");
+ free(vi.buf);
+ return;
+ }
+
+ vendor = vi.buf;
+ dbsf = vendor->PcieId;
+
+ printf("Vendor Extension:\n");
+ printf("\tLength:\t%u\n", vendor->Length);
+ printf("\tPCIe Id:\t%u:%u:%u:%u\n", dbsf >> 24,
+ (dbsf >> 16) & 0xff, (dbsf >> 11) & 0x1f, (dbsf >> 8) & 7);
+ printf("\tVendor Id:\t%04x\n", vendor->VendorId);
+ printf("\tDevice Id:\t%04x\n", vendor->DeviceId);
+ printf("\tRevision:\t%02x\n", vendor->RevisionId);
+ free(vi.buf);
+}
+
+static void
+list_error_types(void)
+{
+ struct acpi_einj_info info;
+
+ if (ioctl(fd, ACPIIO_EINJ_GET_INFO, &info) == -1)
+ err(1, "ACPIIO_EINJ_GET_INFO");
+
+ printf("Supported errors <%#x>:\n", info.error_type);
+ for (u_int i = 0; i < nitems(error_types); i++) {
+ struct error_type_info *eti = &error_types[i];
+
+ if ((info.error_type & eti->bit) == 0)
+ continue;
+ printf("\t%s (%s)\n", eti->name, eti->description);
+ }
+
+ if ((info.error_type & ACPI_EINJ_VENDOR_DEFINED) != 0 &&
+ info.vendor_length != 0) {
+ printf("\n");
+ print_vendor_struct(info.vendor_length);
+ }
+}
+
+static uint32_t
+parse_dbsf(const char *arg)
+{
+ char *cp;
+ unsigned long val[4];
+ u_int i;
+
+ /*
+ * Permit the same format as pciconf. Eventually this should
+ * accept PCI device names, too.
+ */
+ if (strncmp(arg, "pci", 3) == 0)
+ arg += 3;
+
+ /* Read up to 4 colon-separated values. */
+ for (i = 0;; i++) {
+ val[i] = strtoul(arg, &cp, 10);
+ if (cp == arg || val[i] > 255)
+ goto error;
+ if (*cp == ':') {
+ if (i == nitems(val) - 1)
+ goto error;
+ arg = cp + 1;
+ continue;
+ }
+ if (*cp == '\0') {
+ if (i < 3)
+ goto error;
+ break;
+ }
+ goto error;
+ }
+
+ /* If no domain is given, assume a domain of 0. */
+ if (i == 3) {
+ val[3] = val[2];
+ val[2] = val[1];
+ val[1] = val[0];
+ val[0] = 0;
+ }
+
+ /*
+ * To handle ARI, only limit function numbers if the slot is
+ * non-zero.
+ */
+ if (val[2] != 0 && val[3] > PCI_FUNCMAX)
+ goto error;
+
+ return (val[0] << 24 | val[1] << 16 | val[2] << 3 | val[3]);
+
+error:
+ errx(1, "invalid PCIe ID");
+}
+
+static void
+inject_error(int argc, char *argv[])
+{
+ struct acpi_einj_error einj;
+ char *cp;
+ uintmax_t value;
+ int ch;
+
+ memset(&einj, 0, sizeof(einj));
+ while ((ch = getopt(argc, argv, "a:C:P:")) != -1) {
+ switch (ch) {
+ case 'a':
+ einj.memory_address = strtoumax(optarg, &cp, 0);
+ if (*cp != '\0')
+ errx(1, "invalid address");
+ einj.address_flags |= ACPI_EINJ_MEMADDRESS_VALID;
+ break;
+ case 'C':
+ value = strtoumax(optarg, &cp, 0);
+ if (*cp != '\0' || value > UINT32_MAX)
+ errx(1, "invalid APIC ID");
+ einj.apic_id = value;
+ einj.address_flags |= ACPI_EINJ_APICID_VALID;
+ break;
+ case 'P':
+ einj.pcie_id = parse_dbsf(optarg);
+ einj.address_flags |= ACPI_EINJ_PCIE_VALID;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (strcmp(argv[0], "vendor") == 0)
+ errx(1, "vendor errors are not supported");
+
+ for (u_int i = 0; i < nitems(error_types); i++) {
+ struct error_type_info *eti = &error_types[i];
+
+ if (strcmp(argv[0], eti->name) == 0) {
+ einj.error_type = eti->bit;
+ break;
+ }
+ }
+ if (einj.error_type == 0)
+ errx(1, "unknown error type");
+
+ if (ioctl(fd, ACPIIO_EINJ_SET_ERROR, &einj) == -1)
+ err(1, "ACPIIO_EINJ_SET_ERROR");
+}
+
+int
+main(int argc, char *argv[])
+{
+ if (argc < 2)
+ usage();
+
+ fd = open("/dev/acpi", O_RDWR);
+ if (fd == -1)
+ err(1, "/dev/acpi");
+
+ if (strcasecmp(argv[1], "list") == 0)
+ list_error_types();
+ else if (strcasecmp(argv[1], "inject") == 0)
+ inject_error(argc - 1, argv + 1);
+ else
+ usage();
+
+ close(fd);
+ return (0);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Sep 29, 10:26 AM (3 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39871712
Default Alt Text
D58026.diff (8 KB)
Attached To
Mode
D58026: einj: Tool to manage APEI error injection
Attached
Detach File
Event Timeline
Log In to Comment