* [PATCH v2 RESEND 0/4] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoder and tests
@ 2026-07-24 12:20 Kai-Heng Feng
2026-07-24 12:20 ` [PATCH v2 RESEND 1/4] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
` (4 more replies)
0 siblings, 5 replies; 13+ messages in thread
From: Kai-Heng Feng @ 2026-07-24 12:20 UTC (permalink / raw)
To: rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, xueshuai,
linux-kernel, linux-acpi, linux-kselftest, linux-hardening, csoto,
mochs, Kai-Heng Feng
Vera is NVIDIA's next-generation server SoC. It reports hardware
errors via ACPI GHES using a CPER section format distinct from the
existing Grace section. Without this series, firmware-reported errors
on Vera platforms are received but not decoded.
Patch 1 refactors the Grace decoder to separate parsing from logging,
preparing it for testability and for sharing types with the new Vera
decoder.
Patches 2-4 add the Vera decoder, KUnit coverage for both decoders,
and an EINJ-based kselftest for end-to-end validation on real hardware.
Kai-Heng Feng (4):
ACPI: APEI: GHES: Refactor Grace decoder helpers
ACPI: APEI: GHES: Add NVIDIA Vera decoder
ACPI: APEI: GHES: Add Grace and Vera KUnit coverage
selftests: firmware: Add NVIDIA GHES EINJ selftest
drivers/acpi/apei/Kconfig | 17 +-
drivers/acpi/apei/Makefile | 1 +
drivers/acpi/apei/ghes-nvidia-test-fixtures.h | 233 ++++++
drivers/acpi/apei/ghes-nvidia-test.c | 704 ++++++++++++++++++
drivers/acpi/apei/ghes-nvidia.c | 480 ++++++++++--
drivers/acpi/apei/ghes-nvidia.h | 63 ++
tools/testing/selftests/firmware/Makefile | 4 +-
tools/testing/selftests/firmware/config | 5 +
tools/testing/selftests/firmware/einj_lib.sh | 189 +++++
.../selftests/firmware/ghes_nvidia_einj.sh | 144 ++++
.../firmware/ghes_nvidia_einj_profiles.sh | 46 ++
11 files changed, 1838 insertions(+), 48 deletions(-)
create mode 100644 drivers/acpi/apei/ghes-nvidia-test-fixtures.h
create mode 100644 drivers/acpi/apei/ghes-nvidia-test.c
create mode 100644 drivers/acpi/apei/ghes-nvidia.h
create mode 100644 tools/testing/selftests/firmware/einj_lib.sh
create mode 100755 tools/testing/selftests/firmware/ghes_nvidia_einj.sh
create mode 100755 tools/testing/selftests/firmware/ghes_nvidia_einj_profiles.sh
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v2 RESEND 1/4] ACPI: APEI: GHES: Refactor Grace decoder helpers
2026-07-24 12:20 [PATCH v2 RESEND 0/4] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoder and tests Kai-Heng Feng
@ 2026-07-24 12:20 ` Kai-Heng Feng
2026-07-26 8:10 ` Shuai Xue
2026-07-24 12:20 ` [PATCH v2 RESEND 2/4] ACPI: APEI: GHES: Add NVIDIA Vera decoder Kai-Heng Feng
` (3 subsequent siblings)
4 siblings, 1 reply; 13+ messages in thread
From: Kai-Heng Feng @ 2026-07-24 12:20 UTC (permalink / raw)
To: rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, xueshuai,
linux-kernel, linux-acpi, linux-kselftest, linux-hardening, csoto,
mochs, Kai-Heng Feng
Split the Grace CPER processing into a separate decode step and a
print step so the parser can be exercised by KUnit without a live
ACPI device. Introduce ghes-nvidia.h to hold shared types that the
Vera decoder added in the next commit will also reference.
Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
---
v2:
- No change.
drivers/acpi/apei/ghes-nvidia.c | 148 +++++++++++++++++++++-----------
drivers/acpi/apei/ghes-nvidia.h | 38 ++++++++
2 files changed, 137 insertions(+), 49 deletions(-)
create mode 100644 drivers/acpi/apei/ghes-nvidia.h
diff --git a/drivers/acpi/apei/ghes-nvidia.c b/drivers/acpi/apei/ghes-nvidia.c
index 597275d81de8..af445152def0 100644
--- a/drivers/acpi/apei/ghes-nvidia.c
+++ b/drivers/acpi/apei/ghes-nvidia.c
@@ -12,7 +12,10 @@
#include <linux/uuid.h>
#include <acpi/ghes.h>
-static const guid_t nvidia_sec_guid =
+#include <kunit/visibility.h>
+#include "ghes-nvidia.h"
+
+static const guid_t nvidia_grace_sec_guid =
GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
@@ -25,10 +28,7 @@ struct cper_sec_nvidia {
u8 number_regs;
u8 reserved;
__le64 instance_base;
- struct {
- __le64 addr;
- __le64 val;
- } regs[] __counted_by(number_regs);
+ struct nvidia_ghes_grace_reg regs[] __counted_by(number_regs);
};
struct nvidia_ghes_private {
@@ -36,73 +36,123 @@ struct nvidia_ghes_private {
struct device *dev;
};
-static void nvidia_ghes_print_error(struct device *dev,
- const struct cper_sec_nvidia *nvidia_err,
- size_t error_data_length, bool fatal)
+VISIBLE_IF_KUNIT
+int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded)
{
- const char *level = fatal ? KERN_ERR : KERN_INFO;
+ const struct cper_sec_nvidia *nvidia_err = buf;
size_t min_size;
- dev_printk(level, dev, "signature: %.16s\n", nvidia_err->signature);
- dev_printk(level, dev, "error_type: %u\n", le16_to_cpu(nvidia_err->error_type));
- dev_printk(level, dev, "error_instance: %u\n", le16_to_cpu(nvidia_err->error_instance));
- dev_printk(level, dev, "severity: %u\n", nvidia_err->severity);
- dev_printk(level, dev, "socket: %u\n", nvidia_err->socket);
- dev_printk(level, dev, "number_regs: %u\n", nvidia_err->number_regs);
- dev_printk(level, dev, "instance_base: 0x%016llx\n",
- le64_to_cpu(nvidia_err->instance_base));
-
- if (nvidia_err->number_regs == 0)
- return;
-
- /*
- * Validate that all registers fit within error_data_length.
- * Each register pair is two little-endian u64s.
- */
+ if (!buf || !decoded)
+ return -EINVAL;
+ if (len < sizeof(*nvidia_err)) {
+ if (dev)
+ dev_err(dev, "Section too small (%zu < %zu)\n",
+ len, sizeof(*nvidia_err));
+ return -ENODATA;
+ }
+
min_size = struct_size(nvidia_err, regs, nvidia_err->number_regs);
- if (error_data_length < min_size) {
- dev_err(dev, "Invalid number_regs %u (section size %zu, need %zu)\n",
- nvidia_err->number_regs, error_data_length, min_size);
- return;
+ if (len < min_size) {
+ if (dev)
+ dev_err(dev,
+ "Invalid number_regs %u (section size %zu, need %zu)\n",
+ nvidia_err->number_regs, len, min_size);
+ return -ENODATA;
}
- for (int i = 0; i < nvidia_err->number_regs; i++)
+ memset(decoded, 0, sizeof(*decoded));
+ decoded->format = NVIDIA_GHES_FORMAT_GRACE;
+ memcpy(decoded->signature, nvidia_err->signature, sizeof(nvidia_err->signature));
+ decoded->signature[sizeof(nvidia_err->signature)] = '\0';
+ decoded->error_type = le16_to_cpu(nvidia_err->error_type);
+ decoded->error_instance = le16_to_cpu(nvidia_err->error_instance);
+ decoded->severity = nvidia_err->severity;
+ decoded->socket = nvidia_err->socket;
+ decoded->number_regs = nvidia_err->number_regs;
+ decoded->instance_base = le64_to_cpu(nvidia_err->instance_base);
+ if (nvidia_err->number_regs)
+ decoded->grace_regs = nvidia_err->regs;
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_grace);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
+ unsigned int index, u64 *addr, u64 *val)
+{
+ const struct nvidia_ghes_grace_reg *regs;
+
+ if (!decoded || decoded->format != NVIDIA_GHES_FORMAT_GRACE || !addr || !val)
+ return -EINVAL;
+ if (index >= decoded->number_regs)
+ return -ERANGE;
+
+ regs = decoded->grace_regs;
+ *addr = le64_to_cpu(regs[index].addr);
+ *val = le64_to_cpu(regs[index].val);
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_grace_reg_pair);
+
+static void nvidia_ghes_print_grace(struct device *dev,
+ const struct nvidia_ghes_decoded *decoded,
+ bool fatal)
+{
+ const char *level = fatal ? KERN_ERR : KERN_INFO;
+ u64 addr, val;
+
+ dev_printk(level, dev, "signature: %s\n", decoded->signature);
+ dev_printk(level, dev, "error_type: %u\n", decoded->error_type);
+ dev_printk(level, dev, "error_instance: %u\n", decoded->error_instance);
+ dev_printk(level, dev, "severity: %u\n", decoded->severity);
+ dev_printk(level, dev, "socket: %u\n", decoded->socket);
+ dev_printk(level, dev, "number_regs: %u\n", decoded->number_regs);
+ dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
+
+ for (int i = 0; i < decoded->number_regs; i++) {
+ if (nvidia_ghes_grace_reg_pair(decoded, i, &addr, &val))
+ break;
dev_printk(level, dev, "register[%d]: address=0x%016llx value=0x%016llx\n",
- i, le64_to_cpu(nvidia_err->regs[i].addr),
- le64_to_cpu(nvidia_err->regs[i].val));
+ i, addr, val);
+ }
}
static int nvidia_ghes_notify(struct notifier_block *nb,
unsigned long event, void *data)
{
struct acpi_hest_generic_data *gdata = data;
+ struct nvidia_ghes_decoded decoded;
struct nvidia_ghes_private *priv;
- const struct cper_sec_nvidia *nvidia_err;
+ const void *payload;
guid_t sec_guid;
+ u32 len;
+ int ret;
+ bool fatal;
import_guid(&sec_guid, gdata->section_type);
- if (!guid_equal(&sec_guid, &nvidia_sec_guid))
+ if (!guid_equal(&sec_guid, &nvidia_grace_sec_guid))
return NOTIFY_DONE;
priv = container_of(nb, struct nvidia_ghes_private, nb);
-
- if (acpi_hest_get_error_length(gdata) < sizeof(*nvidia_err)) {
- dev_err(priv->dev, "Section too small (%d < %zu)\n",
- acpi_hest_get_error_length(gdata), sizeof(*nvidia_err));
+ len = acpi_hest_get_error_length(gdata);
+ payload = acpi_hest_get_payload(gdata);
+ fatal = event >= GHES_SEV_RECOVERABLE;
+
+ ret = nvidia_ghes_decode_grace(priv->dev, payload, len, &decoded);
+ if (ret) {
+ dev_err(priv->dev,
+ "Malformed NVIDIA CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
return NOTIFY_OK;
}
- nvidia_err = acpi_hest_get_payload(gdata);
-
- if (event >= GHES_SEV_RECOVERABLE)
- dev_err(priv->dev, "NVIDIA CPER section, error_data_length: %u\n",
- acpi_hest_get_error_length(gdata));
- else
- dev_info(priv->dev, "NVIDIA CPER section, error_data_length: %u\n",
- acpi_hest_get_error_length(gdata));
-
- nvidia_ghes_print_error(priv->dev, nvidia_err, acpi_hest_get_error_length(gdata),
- event >= GHES_SEV_RECOVERABLE);
+ dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
+ "NVIDIA CPER section, error_data_length: %u\n", len);
+ nvidia_ghes_print_grace(priv->dev, &decoded, fatal);
return NOTIFY_OK;
}
diff --git a/drivers/acpi/apei/ghes-nvidia.h b/drivers/acpi/apei/ghes-nvidia.h
new file mode 100644
index 000000000000..f0592fa41abf
--- /dev/null
+++ b/drivers/acpi/apei/ghes-nvidia.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef GHES_NVIDIA_H
+#define GHES_NVIDIA_H
+
+#include <linux/types.h>
+#include <kunit/visibility.h>
+
+struct device;
+
+enum nvidia_ghes_format {
+ NVIDIA_GHES_FORMAT_UNKNOWN,
+ NVIDIA_GHES_FORMAT_GRACE,
+};
+
+struct nvidia_ghes_grace_reg {
+ __le64 addr;
+ __le64 val;
+};
+
+struct nvidia_ghes_decoded {
+ enum nvidia_ghes_format format;
+ char signature[17];
+ u16 error_type;
+ u16 error_instance;
+ u8 severity;
+ u8 socket;
+ u8 number_regs;
+ u64 instance_base;
+ const struct nvidia_ghes_grace_reg *grace_regs;
+};
+
+VISIBLE_IF_KUNIT int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded);
+VISIBLE_IF_KUNIT int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
+ unsigned int index, u64 *addr, u64 *val);
+
+#endif
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 RESEND 2/4] ACPI: APEI: GHES: Add NVIDIA Vera decoder
2026-07-24 12:20 [PATCH v2 RESEND 0/4] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoder and tests Kai-Heng Feng
2026-07-24 12:20 ` [PATCH v2 RESEND 1/4] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
@ 2026-07-24 12:20 ` Kai-Heng Feng
2026-07-26 8:21 ` Shuai Xue
2026-07-24 12:20 ` [PATCH v2 RESEND 3/4] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage Kai-Heng Feng
` (2 subsequent siblings)
4 siblings, 1 reply; 13+ messages in thread
From: Kai-Heng Feng @ 2026-07-24 12:20 UTC (permalink / raw)
To: rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, xueshuai,
linux-kernel, linux-acpi, linux-kselftest, linux-hardening, csoto,
mochs, Kai-Heng Feng
Vera is NVIDIA's next-generation server SoC. Its CPER section uses a
different GUID and a different binary layout from Grace, so it needs
its own decoder. Without this, firmware-reported hardware errors on
Vera platforms are received but not decoded.
Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
---
drivers/acpi/apei/ghes-nvidia.c | 368 ++++++++++++++++++++++++++++++--
drivers/acpi/apei/ghes-nvidia.h | 29 ++-
2 files changed, 382 insertions(+), 15 deletions(-)
diff --git a/drivers/acpi/apei/ghes-nvidia.c b/drivers/acpi/apei/ghes-nvidia.c
index af445152def0..c74c155dd2ba 100644
--- a/drivers/acpi/apei/ghes-nvidia.c
+++ b/drivers/acpi/apei/ghes-nvidia.c
@@ -7,18 +7,27 @@
#include <linux/acpi.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/platform_device.h>
+#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/unaligned.h>
#include <linux/uuid.h>
+#include <kunit/visibility.h>
#include <acpi/ghes.h>
-#include <kunit/visibility.h>
#include "ghes-nvidia.h"
+#define NVIDIA_GHES_VERA_VERSION 1
+
static const guid_t nvidia_grace_sec_guid =
GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
+static const guid_t nvidia_vera_sec_guid =
+ GUID_INIT(0x9068e568, 0x6ca0, 0x11f0,
+ 0xae, 0xaf, 0x15, 0x93, 0x43, 0x59, 0x1e, 0xac);
+
struct cper_sec_nvidia {
char signature[16];
__le16 error_type;
@@ -31,11 +40,51 @@ struct cper_sec_nvidia {
struct nvidia_ghes_grace_reg regs[] __counted_by(number_regs);
};
+struct cper_sec_nvidia_vera_event {
+ u8 version;
+ u8 event_context_count;
+ u8 source_device_type;
+ u8 reserved;
+ __le16 event_type;
+ __le16 event_sub_type;
+ __le64 event_link_id;
+ char source_module_signature[16];
+} __packed;
+
+struct cper_sec_nvidia_vera_cpu_info {
+ __le16 info_version;
+ u8 info_size;
+ u8 socket_number;
+ __le32 architecture;
+ u8 chip_serial_number[16];
+ __le64 instance_base;
+} __packed;
+
+struct cper_sec_nvidia_vera_context {
+ __le32 context_size;
+ __le16 context_version;
+ __le16 reserved;
+ __le16 data_format_type;
+ __le16 data_format_version;
+ __le32 data_size;
+} __packed;
+
struct nvidia_ghes_private {
struct notifier_block nb;
struct device *dev;
};
+VISIBLE_IF_KUNIT
+enum nvidia_ghes_format nvidia_ghes_format_from_guid(const guid_t *guid)
+{
+ if (guid_equal(guid, &nvidia_grace_sec_guid))
+ return NVIDIA_GHES_FORMAT_GRACE;
+ if (guid_equal(guid, &nvidia_vera_sec_guid))
+ return NVIDIA_GHES_FORMAT_VERA;
+ return NVIDIA_GHES_FORMAT_UNKNOWN;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_format_from_guid);
+
VISIBLE_IF_KUNIT
int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
size_t len,
@@ -81,7 +130,7 @@ EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_grace);
VISIBLE_IF_KUNIT
int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
- unsigned int index, u64 *addr, u64 *val)
+ unsigned int index, u64 *addr, u64 *val)
{
const struct nvidia_ghes_grace_reg *regs;
@@ -98,6 +147,220 @@ int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
}
EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_grace_reg_pair);
+static int nvidia_ghes_vera_validate_context_data(u16 data_format_type,
+ u32 data_size)
+{
+ switch (data_format_type) {
+ case 0:
+ return 0;
+ case 1:
+ return data_size % 16 ? -EINVAL : 0;
+ case 2:
+ case 3:
+ return data_size % 8 ? -EINVAL : 0;
+ case 4:
+ return data_size % 4 ? -EINVAL : 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_decode_vera(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded)
+{
+ const struct cper_sec_nvidia_vera_event *event = buf;
+ const struct cper_sec_nvidia_vera_cpu_info *cpu_info;
+ const struct cper_sec_nvidia_vera_context *context;
+ const u8 *bytes = buf;
+ size_t data_end_advance;
+ size_t advance;
+ size_t offset;
+ int ret;
+
+ if (!buf || !decoded)
+ return -EINVAL;
+ if (len < sizeof(*event)) {
+ if (dev)
+ dev_err_ratelimited(dev, "Vera event header truncated (%zu < %zu)\n",
+ len, sizeof(*event));
+ return -ENODATA;
+ }
+ if (event->version != NVIDIA_GHES_VERA_VERSION)
+ return -EOPNOTSUPP;
+ if (event->source_device_type != 0)
+ return -EOPNOTSUPP;
+
+ offset = sizeof(*event);
+ if (len - offset < sizeof(*cpu_info)) {
+ if (dev)
+ dev_err_ratelimited(dev, "Vera CPU info truncated (%zu < %zu)\n",
+ len - offset, sizeof(*cpu_info));
+ return -ENODATA;
+ }
+
+ cpu_info = (const void *)(bytes + offset);
+ if (cpu_info->info_size < sizeof(*cpu_info)) {
+ if (dev)
+ dev_err_ratelimited(dev, "Vera CPU info size %u smaller than header %zu\n",
+ cpu_info->info_size, sizeof(*cpu_info));
+ return -EINVAL;
+ }
+ if (len - offset < cpu_info->info_size) {
+ if (dev)
+ dev_err_ratelimited(dev, "Vera CPU info extends past section (%u > %zu)\n",
+ cpu_info->info_size, len - offset);
+ return -ENODATA;
+ }
+
+ offset += cpu_info->info_size;
+ if (event->event_context_count > NVIDIA_GHES_MAX_CONTEXTS) {
+ if (dev)
+ dev_err_ratelimited(dev, "Vera context count %u exceeds maximum %u\n",
+ event->event_context_count,
+ NVIDIA_GHES_MAX_CONTEXTS);
+ return -E2BIG;
+ }
+
+ memset(decoded, 0, sizeof(*decoded));
+ decoded->format = NVIDIA_GHES_FORMAT_VERA;
+ memcpy(decoded->signature, event->source_module_signature,
+ sizeof(event->source_module_signature));
+ decoded->signature[sizeof(event->source_module_signature)] = '\0';
+ decoded->event_context_count = event->event_context_count;
+ decoded->source_device_type = event->source_device_type;
+ decoded->event_type = get_unaligned_le16(&event->event_type);
+ decoded->event_sub_type = get_unaligned_le16(&event->event_sub_type);
+ decoded->event_link_id = get_unaligned_le64(&event->event_link_id);
+ decoded->socket = cpu_info->socket_number;
+ decoded->architecture = get_unaligned_le32(&cpu_info->architecture);
+ memcpy(decoded->chip_serial_number, cpu_info->chip_serial_number,
+ sizeof(cpu_info->chip_serial_number));
+ decoded->instance_base = get_unaligned_le64(&cpu_info->instance_base);
+
+ for (int i = 0; i < event->event_context_count; i++) {
+ struct nvidia_ghes_vera_context *decoded_context = &decoded->contexts[i];
+ u32 context_size;
+ u32 data_size;
+ u16 data_format_type;
+
+ if (len - offset < sizeof(*context)) {
+ if (dev)
+ dev_err_ratelimited(dev, "Vera context[%d] header truncated (%zu < %zu)\n",
+ i, len - offset, sizeof(*context));
+ return -ENODATA;
+ }
+
+ context = (const void *)(bytes + offset);
+ context_size = get_unaligned_le32(&context->context_size);
+ data_format_type = get_unaligned_le16(&context->data_format_type);
+ data_size = get_unaligned_le32(&context->data_size);
+
+ if (context_size < sizeof(*context)) {
+ if (dev)
+ dev_err_ratelimited(dev,
+ "Vera context[%d] size %u smaller than header %zu\n",
+ i, context_size, sizeof(*context));
+ return -EINVAL;
+ }
+ if (data_format_type > 4) {
+ if (dev)
+ dev_dbg(dev,
+ "Vera context[%d] unsupported data format %u\n",
+ i, data_format_type);
+ return -EOPNOTSUPP;
+ }
+ if (check_add_overflow((size_t)data_size, sizeof(*context),
+ &data_end_advance)) {
+ if (dev)
+ dev_err_ratelimited(dev,
+ "Vera context[%d] data_size %u overflows section accounting\n",
+ i, data_size);
+ return -EOVERFLOW;
+ }
+
+ if (data_end_advance > len - offset) {
+ if (dev)
+ dev_err_ratelimited(dev,
+ "Vera context[%d] data extends past section (%zu > %zu)\n",
+ i, data_end_advance, len - offset);
+ return -ENODATA;
+ }
+
+ /*
+ * Some Vera payloads use only the header size here and
+ * place the format-specific payload immediately after it.
+ */
+ if (context_size == sizeof(*context))
+ advance = data_end_advance;
+ else if (data_size <= context_size - sizeof(*context))
+ advance = context_size;
+ else {
+ if (dev)
+ dev_err_ratelimited(dev,
+ "Vera context[%d] data_size %u exceeds context_size %u\n",
+ i, data_size, context_size);
+ return -EINVAL;
+ }
+
+ if (advance > len - offset) {
+ if (dev)
+ dev_err_ratelimited(dev,
+ "Vera context[%d] advance %zu extends past section (%zu)\n",
+ i, advance, len - offset);
+ return -ENODATA;
+ }
+
+ ret = nvidia_ghes_vera_validate_context_data(data_format_type, data_size);
+ if (ret) {
+ if (dev)
+ dev_err_ratelimited(dev,
+ "Vera context[%d] format %u rejected data_size %u (ret=%d)\n",
+ i, data_format_type, data_size, ret);
+ return ret;
+ }
+
+ decoded_context->context_size = context_size;
+ decoded_context->context_version =
+ get_unaligned_le16(&context->context_version);
+ decoded_context->data_format_type = data_format_type;
+ decoded_context->data_format_version =
+ get_unaligned_le16(&context->data_format_version);
+ decoded_context->data_size = data_size;
+ decoded_context->data = bytes + offset + sizeof(*context);
+ offset += advance;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_vera);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_entry_count(const struct nvidia_ghes_vera_context *ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+ if (ctx->data_size > INT_MAX)
+ return -EOVERFLOW;
+
+ switch (ctx->data_format_type) {
+ case 0:
+ return 0;
+ case 1:
+ return ctx->data_size / 16;
+ case 2:
+ return ctx->data_size / 8;
+ case 3:
+ return ctx->data_size / 8;
+ case 4:
+ return ctx->data_size / 4;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_entry_count);
+
static void nvidia_ghes_print_grace(struct device *dev,
const struct nvidia_ghes_decoded *decoded,
bool fatal)
@@ -111,7 +374,8 @@ static void nvidia_ghes_print_grace(struct device *dev,
dev_printk(level, dev, "severity: %u\n", decoded->severity);
dev_printk(level, dev, "socket: %u\n", decoded->socket);
dev_printk(level, dev, "number_regs: %u\n", decoded->number_regs);
- dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
+ dev_printk(level, dev, "instance_base: 0x%016llx\n",
+ decoded->instance_base);
for (int i = 0; i < decoded->number_regs; i++) {
if (nvidia_ghes_grace_reg_pair(decoded, i, &addr, &val))
@@ -121,12 +385,52 @@ static void nvidia_ghes_print_grace(struct device *dev,
}
}
+static void nvidia_ghes_print_vera(struct device *dev,
+ const struct nvidia_ghes_decoded *decoded,
+ bool fatal, unsigned long ghes_severity)
+{
+ const char *level = fatal ? KERN_ERR : KERN_INFO;
+
+ dev_printk(level, dev, "signature: %s\n", decoded->signature);
+ dev_printk(level, dev, "event_type: %u\n", decoded->event_type);
+ dev_printk(level, dev, "event_sub_type: %u\n", decoded->event_sub_type);
+ dev_printk(level, dev, "ghes_severity: %lu\n", ghes_severity);
+ dev_printk(level, dev, "event_link_id: 0x%016llx\n",
+ decoded->event_link_id);
+ dev_printk(level, dev, "socket: %u\n", decoded->socket);
+ dev_printk(level, dev, "architecture: 0x%x\n", decoded->architecture);
+ dev_printk(level, dev, "chip_serial_number: %*phN\n",
+ (int)sizeof(decoded->chip_serial_number),
+ decoded->chip_serial_number);
+ dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
+ dev_printk(level, dev, "event_context_count: %u\n", decoded->event_context_count);
+
+ for (int i = 0; i < decoded->event_context_count; i++) {
+ const struct nvidia_ghes_vera_context *ctx = &decoded->contexts[i];
+ int entries = nvidia_ghes_vera_context_entry_count(ctx);
+
+ dev_printk(level, dev,
+ "context[%d]: version=%u format=%u format_version=%u context_size=%u data_size=%u\n",
+ i, ctx->context_version, ctx->data_format_type,
+ ctx->data_format_version, ctx->context_size, ctx->data_size);
+ if (ctx->data_format_type == 0 && ctx->data_size > 0) {
+ int prefix_len = ctx->data_size > 16 ? 16 : ctx->data_size;
+
+ dev_printk(level, dev, "context[%d]_opaque_prefix: %*phN\n",
+ i, prefix_len, ctx->data);
+ } else if (entries >= 0) {
+ dev_printk(level, dev, "context[%d]_entries: %d\n", i, entries);
+ }
+ }
+}
+
static int nvidia_ghes_notify(struct notifier_block *nb,
unsigned long event, void *data)
{
struct acpi_hest_generic_data *gdata = data;
- struct nvidia_ghes_decoded decoded;
+ struct nvidia_ghes_decoded *decoded;
struct nvidia_ghes_private *priv;
+ enum nvidia_ghes_format format;
const void *payload;
guid_t sec_guid;
u32 len;
@@ -134,26 +438,64 @@ static int nvidia_ghes_notify(struct notifier_block *nb,
bool fatal;
import_guid(&sec_guid, gdata->section_type);
- if (!guid_equal(&sec_guid, &nvidia_grace_sec_guid))
+ format = nvidia_ghes_format_from_guid(&sec_guid);
+ if (format == NVIDIA_GHES_FORMAT_UNKNOWN)
return NOTIFY_DONE;
priv = container_of(nb, struct nvidia_ghes_private, nb);
len = acpi_hest_get_error_length(gdata);
+
payload = acpi_hest_get_payload(gdata);
fatal = event >= GHES_SEV_RECOVERABLE;
+ decoded = kzalloc_obj(*decoded);
+ if (!decoded) {
+ dev_err_ratelimited(priv->dev,
+ "Failed to allocate NVIDIA CPER decode buffer\n");
+ return NOTIFY_OK;
+ }
+
+ switch (format) {
+ case NVIDIA_GHES_FORMAT_GRACE:
+ ret = nvidia_ghes_decode_grace(priv->dev, payload, len, decoded);
+ break;
+ case NVIDIA_GHES_FORMAT_VERA:
+ ret = nvidia_ghes_decode_vera(priv->dev, payload, len, decoded);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
- ret = nvidia_ghes_decode_grace(priv->dev, payload, len, &decoded);
if (ret) {
- dev_err(priv->dev,
- "Malformed NVIDIA CPER section, error_data_length: %u, ret: %d\n",
- len, ret);
- return NOTIFY_OK;
+ if (ret == -EOPNOTSUPP && format == NVIDIA_GHES_FORMAT_VERA)
+ dev_info(priv->dev,
+ "Unsupported NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ else if (format == NVIDIA_GHES_FORMAT_GRACE)
+ dev_err(priv->dev,
+ "Malformed NVIDIA Grace CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ else
+ dev_err(priv->dev,
+ "Malformed NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ goto out;
}
- dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
- "NVIDIA CPER section, error_data_length: %u\n", len);
- nvidia_ghes_print_grace(priv->dev, &decoded, fatal);
+ if (format == NVIDIA_GHES_FORMAT_GRACE)
+ dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
+ "NVIDIA Grace CPER section, error_data_length: %u\n", len);
+ else
+ dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
+ "NVIDIA Vera CPER section, error_data_length: %u\n", len);
+
+ if (format == NVIDIA_GHES_FORMAT_VERA)
+ nvidia_ghes_print_vera(priv->dev, decoded, fatal, event);
+ else
+ nvidia_ghes_print_grace(priv->dev, decoded, fatal);
+out:
+ kfree(decoded);
return NOTIFY_OK;
}
diff --git a/drivers/acpi/apei/ghes-nvidia.h b/drivers/acpi/apei/ghes-nvidia.h
index f0592fa41abf..7fff088e1dc1 100644
--- a/drivers/acpi/apei/ghes-nvidia.h
+++ b/drivers/acpi/apei/ghes-nvidia.h
@@ -3,36 +3,61 @@
#define GHES_NVIDIA_H
#include <linux/types.h>
+#include <linux/uuid.h>
#include <kunit/visibility.h>
-struct device;
-
enum nvidia_ghes_format {
NVIDIA_GHES_FORMAT_UNKNOWN,
NVIDIA_GHES_FORMAT_GRACE,
+ NVIDIA_GHES_FORMAT_VERA,
};
+#define NVIDIA_GHES_MAX_CONTEXTS 16
+
struct nvidia_ghes_grace_reg {
__le64 addr;
__le64 val;
};
+struct nvidia_ghes_vera_context {
+ u32 context_size;
+ u16 context_version;
+ u16 data_format_type;
+ u16 data_format_version;
+ u32 data_size;
+ const u8 *data;
+};
+
struct nvidia_ghes_decoded {
enum nvidia_ghes_format format;
char signature[17];
u16 error_type;
u16 error_instance;
+ u16 event_type;
+ u16 event_sub_type;
u8 severity;
u8 socket;
u8 number_regs;
+ u8 source_device_type;
+ u8 event_context_count;
+ u32 architecture;
+ u64 event_link_id;
u64 instance_base;
+ u8 chip_serial_number[16];
const struct nvidia_ghes_grace_reg *grace_regs;
+ struct nvidia_ghes_vera_context contexts[NVIDIA_GHES_MAX_CONTEXTS];
};
+VISIBLE_IF_KUNIT enum nvidia_ghes_format nvidia_ghes_format_from_guid(const guid_t *guid);
VISIBLE_IF_KUNIT int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
size_t len,
struct nvidia_ghes_decoded *decoded);
VISIBLE_IF_KUNIT int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
unsigned int index, u64 *addr, u64 *val);
+VISIBLE_IF_KUNIT int nvidia_ghes_decode_vera(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_entry_count(const struct nvidia_ghes_vera_context *ctx);
#endif
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 RESEND 3/4] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage
2026-07-24 12:20 [PATCH v2 RESEND 0/4] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoder and tests Kai-Heng Feng
2026-07-24 12:20 ` [PATCH v2 RESEND 1/4] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
2026-07-24 12:20 ` [PATCH v2 RESEND 2/4] ACPI: APEI: GHES: Add NVIDIA Vera decoder Kai-Heng Feng
@ 2026-07-24 12:20 ` Kai-Heng Feng
2026-07-26 8:30 ` Shuai Xue
2026-07-24 12:20 ` [PATCH v2 RESEND 4/4] selftests: firmware: Add NVIDIA GHES EINJ selftest Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 0/3] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoding Kai-Heng Feng
4 siblings, 1 reply; 13+ messages in thread
From: Kai-Heng Feng @ 2026-07-24 12:20 UTC (permalink / raw)
To: rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, xueshuai,
linux-kernel, linux-acpi, linux-kselftest, linux-hardening, csoto,
mochs, Kai-Heng Feng
Verify the Grace and Vera decoders against real binary records
captured from firmware before deploying to production hardware.
Tests cover both the happy path and the rejection of malformed
inputs across all constraint categories.
Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
---
v2:
- Add !KUNIT_ALL_TESTS for tristate.
drivers/acpi/apei/Kconfig | 17 +-
drivers/acpi/apei/Makefile | 1 +
drivers/acpi/apei/ghes-nvidia-test-fixtures.h | 233 ++++++
drivers/acpi/apei/ghes-nvidia-test.c | 704 ++++++++++++++++++
4 files changed, 953 insertions(+), 2 deletions(-)
create mode 100644 drivers/acpi/apei/ghes-nvidia-test-fixtures.h
create mode 100644 drivers/acpi/apei/ghes-nvidia-test.c
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index 428458c623f0..22d88425f36c 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -80,14 +80,27 @@ config ACPI_APEI_GHES_NVIDIA
help
Support for decoding NVIDIA-specific CPER sections delivered via
the APEI GHES vendor record notifier chain. Registers a handler
- for the NVIDIA section GUID and logs error signatures, severity,
- socket, and diagnostic register address-value pairs.
+ for the NVIDIA Grace and Vera section GUIDs and logs the decoded
+ NVIDIA error records.
Enable on NVIDIA server platforms (e.g. DGX, HGX) that expose
ACPI device NVDA2012 in their firmware tables.
If unsure, say N.
+config ACPI_APEI_GHES_NVIDIA_KUNIT_TEST
+ tristate "NVIDIA GHES vendor record handler KUnit tests" if !KUNIT_ALL_TESTS
+ depends on KUNIT && ACPI_APEI_GHES_NVIDIA
+ default KUNIT_ALL_TESTS
+ help
+ KUnit tests for NVIDIA GHES vendor CPER parser helpers.
+ They cover Grace helper decoding, Vera payload parsing, and
+ section routing.
+ They are enabled only for KUnit test builds.
+ They are not intended for production firmware paths.
+
+ If unsure, say N.
+
config ACPI_APEI_ERST_DEBUG
tristate "APEI Error Record Serialization Table (ERST) Debug Support"
depends on ACPI_APEI
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
index 66588d6be56f..210a57242044 100644
--- a/drivers/acpi/apei/Makefile
+++ b/drivers/acpi/apei/Makefile
@@ -11,5 +11,6 @@ einj-y := einj-core.o
einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
obj-$(CONFIG_ACPI_APEI_GHES_NVIDIA) += ghes-nvidia.o
+obj-$(CONFIG_ACPI_APEI_GHES_NVIDIA_KUNIT_TEST) += ghes-nvidia-test.o
apei-y := apei-base.o hest.o erst.o bert.o
diff --git a/drivers/acpi/apei/ghes-nvidia-test-fixtures.h b/drivers/acpi/apei/ghes-nvidia-test-fixtures.h
new file mode 100644
index 000000000000..705c879c785e
--- /dev/null
+++ b/drivers/acpi/apei/ghes-nvidia-test-fixtures.h
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef GHES_NVIDIA_TEST_FIXTURES_H
+#define GHES_NVIDIA_TEST_FIXTURES_H
+
+#include <linux/types.h>
+
+/*
+ * Source: cper-tools/test_data/vera_msft.json
+ * cper-tools commit: bf18f75406422e73e11b8b1ff80e3c9d9cfb032a
+ * Boundary: CPER.Oem.Nvidia.Unknown.Data.
+ * Each payload equals the Vera GUID section body located in DiagnosticData.
+ */
+
+/* Redfish Id 10, signature L1 RESET, real sample. */
+/* Vera section index 0, offset 200, length 80. */
+/* sha256 fc11639e506eb8d79e7ff16a0556463ac4b1304bface935c0cd13efc0b3e317f. */
+static const u8 vera_l1_reset_id10[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x31, 0x20, 0x52, 0x45, 0x53, 0x45, 0x54,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x15, 0xfe, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+/* Redfish Id 40, signature CRASHDUMP-ID, real sample. */
+/* Vera section index 0, offset 200, length 304. */
+/* sha256 3ba93201740e6bd868b48ec40c3d52b235c1ebc59528fc81974079c21bf2a6a2. */
+static const u8 vera_crashdump_id_id40[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x41, 0x53, 0x48, 0x44, 0x55, 0x4d,
+ 0x50, 0x2d, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x83, 0x72, 0x65, 0x6c, 0x5f, 0x37, 0x33, 0x2e, 0x37,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x64, 0x69, 0x72, 0x74,
+ 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
+ 0x44, 0x24, 0x4e, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x07, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x07, 0x03, 0x03, 0x14, 0xfe, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x15, 0xfe, 0x00, 0x00,
+ 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x04, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00,
+ 0x40, 0x60, 0x30, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0a, 0x04, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00,
+ 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x0a, 0x04, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2c, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+/* Redfish Id 41, signature CRASHDUMP-S1, real sample. */
+/* Vera section index 0, offset 200, length 2008. */
+/* sha256 54db30564d3324240f088a1533c113407f1d81bcd652ef0d27d958c490aee5ce. */
+static const u8 vera_crashdump_s1_id41[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x41, 0x53, 0x48, 0x44, 0x55, 0x4d,
+ 0x50, 0x2d, 0x53, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x53, 0x56, 0x7f, 0xa1,
+ 0xd1, 0xd7, 0x7f, 0xe5, 0xaa, 0x34, 0xde, 0x7d, 0xe9, 0x3e, 0xc4, 0xd4,
+ 0x92, 0xe8, 0x10, 0xa1, 0x4c, 0xfc, 0xf9, 0x2e, 0x02, 0x87, 0xc5, 0x6e,
+ 0x44, 0xfb, 0xb5, 0x59, 0xac, 0xfa, 0xa8, 0x15, 0x75, 0x46, 0x2d, 0x67,
+ 0x84, 0xe0, 0x78, 0x14, 0xe4, 0x18, 0x8e, 0x30, 0x46, 0xff, 0xbe, 0x5e,
+ 0x15, 0x3e, 0x61, 0xf4, 0xcc, 0x3d, 0x2e, 0xdf, 0x1b, 0xf9, 0x1f, 0x1c,
+ 0xa3, 0xf8, 0x35, 0x34, 0xe1, 0x4c, 0xb7, 0xd0, 0xcc, 0xda, 0xf1, 0xbc,
+ 0x02, 0x8d, 0xc2, 0x6b, 0x5d, 0x56, 0xd4, 0x48, 0x89, 0x7f, 0xb2, 0x64,
+ 0xda, 0xa0, 0xf4, 0xed, 0xef, 0xf7, 0x5f, 0x29, 0x38, 0x51, 0x59, 0x35,
+ 0x1f, 0x52, 0x63, 0xa8, 0x45, 0x02, 0x7a, 0x24, 0xf6, 0x26, 0x83, 0x29,
+ 0x65, 0x01, 0x77, 0x0e, 0xb2, 0x5e, 0xac, 0xf2, 0xd4, 0xc0, 0x09, 0x94,
+ 0x5c, 0x4d, 0x55, 0xaf, 0xcf, 0xb7, 0x8c, 0xad, 0x47, 0xcf, 0x02, 0x2c,
+ 0x8c, 0x0f, 0x28, 0xf1, 0x4c, 0x15, 0x66, 0xb0, 0xb4, 0x7d, 0xd1, 0xf6,
+ 0x90, 0xc8, 0x5e, 0x64, 0x16, 0x9f, 0x5f, 0x3f, 0x50, 0xc9, 0x62, 0x91,
+ 0x87, 0xd8, 0xd7, 0xe9, 0x9f, 0x7b, 0xcd, 0xc9, 0xfe, 0xa1, 0xd3, 0xf2,
+ 0xc2, 0x95, 0xc7, 0x37, 0x34, 0x6c, 0xc4, 0xc0, 0x53, 0x56, 0x01, 0x2c,
+ 0x96, 0x63, 0xdb, 0x2c, 0xea, 0x09, 0xa0, 0xaa, 0xe5, 0xe2, 0x48, 0x18,
+ 0x3c, 0x68, 0xf9, 0xa0, 0x7f, 0x98, 0x5a, 0xe3, 0xe8, 0x8e, 0x83, 0x71,
+ 0x51, 0x79, 0xc3, 0x14, 0xf1, 0xf4, 0xac, 0xd5, 0xd9, 0x8c, 0xab, 0x33,
+ 0x4e, 0x9f, 0xc2, 0xb7, 0xed, 0x6f, 0x78, 0x81, 0x77, 0x34, 0xb8, 0xea,
+ 0xb0, 0x71, 0xa1, 0x4d, 0x08, 0x40, 0xad, 0x4c, 0x95, 0x6a, 0x21, 0x96,
+ 0x07, 0x12, 0x62, 0xed, 0x42, 0xa9, 0xe4, 0xa8, 0x88, 0xc7, 0x81, 0xff,
+ 0x88, 0x07, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x83, 0xbc, 0x15, 0xd9, 0x0b, 0x6b, 0x3b, 0x3b, 0xff, 0x77, 0xb2, 0x7c,
+ 0x0c, 0xdd, 0xfb, 0x00, 0x7e, 0x0a, 0x88, 0x47, 0x8a, 0xf2, 0xf8, 0xc5,
+ 0x07, 0x51, 0x0e, 0xd7, 0xcb, 0x78, 0xea, 0x89, 0x37, 0x2a, 0x44, 0x0d,
+ 0x74, 0x4f, 0x9e, 0x6d, 0x2c, 0xf9, 0x53, 0xed, 0x38, 0x34, 0xca, 0x38,
+ 0x7d, 0x96, 0xfe, 0x9a, 0x9b, 0x23, 0x3b, 0x17, 0x17, 0x69, 0xe6, 0x6c,
+ 0x0e, 0xe6, 0x71, 0xf5, 0x59, 0xf6, 0xc4, 0x82, 0xd6, 0x83, 0xb1, 0x76,
+ 0x41, 0x22, 0x53, 0x72, 0x9f, 0x85, 0x0d, 0xdc, 0x67, 0x22, 0x35, 0xd3,
+ 0xe3, 0xaf, 0x10, 0x50, 0xc8, 0xe1, 0xb0, 0xea, 0xc9, 0x1a, 0x3e, 0x02,
+ 0x36, 0xea, 0x94, 0xb1, 0x27, 0xfc, 0xc9, 0x61, 0xcb, 0x6f, 0xb0, 0x50,
+ 0xe1, 0x85, 0x1c, 0x2d, 0x5f, 0xb5, 0x63, 0x38, 0x69, 0x02, 0xb3, 0x7d,
+ 0xb3, 0xb5, 0xe0, 0xfe, 0x5a, 0xf1, 0x0d, 0x83, 0xd9, 0x51, 0xcc, 0xa2,
+ 0xb5, 0x41, 0x3b, 0xd1, 0xf5, 0x58, 0xe8, 0x54, 0x49, 0x78, 0xc1, 0x8b,
+ 0x70, 0xf1, 0x94, 0x45, 0x18, 0x4b, 0x19, 0x58, 0xe6, 0x0a, 0x08, 0x27,
+ 0xe7, 0x95, 0xd6, 0x82, 0xb7, 0x8c, 0xae, 0xae, 0x58, 0x33, 0xc2, 0x02,
+ 0xf8, 0xfb, 0x8a, 0x73, 0xe4, 0x0b, 0x75, 0x9b, 0x00, 0x76, 0x14, 0x31,
+ 0x5e, 0xf5, 0xf0, 0x8b, 0xb7, 0x6b, 0xaf, 0xdd, 0x8b, 0x52, 0xc9, 0x79,
+ 0xe7, 0xd3, 0x17, 0x90, 0xa4, 0xb0, 0xa0, 0x97, 0xcf, 0xba, 0x37, 0x1d,
+ 0xe0, 0xbf, 0x31, 0xff, 0xe9, 0x3a, 0x50, 0x5b, 0xf8, 0x94, 0xd0, 0x33,
+ 0xf9, 0x2d, 0x2e, 0x54, 0xfa, 0x11, 0x63, 0x06, 0xf2, 0x32, 0xb9, 0x7b,
+ 0xd9, 0xc6, 0x12, 0x60, 0xa6, 0x16, 0xac, 0xc8, 0x02, 0xf7, 0xd6, 0xd5,
+ 0xb6, 0x2e, 0xf0, 0xb3, 0x05, 0x39, 0x47, 0xd4, 0xa9, 0xed, 0x1a, 0xca,
+ 0x4c, 0x4f, 0x83, 0x2d, 0xcc, 0x5c, 0xa7, 0x02, 0x01, 0x84, 0xc9, 0x07,
+ 0xa5, 0x6a, 0xf7, 0xbe, 0x3d, 0xc2, 0x09, 0xe8, 0xc8, 0xf8, 0x4b, 0x71,
+ 0xa8, 0x79, 0x6c, 0x68, 0xda, 0x35, 0xe1, 0xd2, 0x9d, 0xa6, 0xb3, 0x70,
+ 0xf6, 0xcb, 0x66, 0x41, 0xde, 0x86, 0x11, 0x2a, 0xd0, 0xa1, 0x84, 0x99,
+ 0x5b, 0xdf, 0x74, 0x26, 0x18, 0xba, 0x6f, 0x3e, 0x46, 0xef, 0xe7, 0xd2,
+ 0xfd, 0xe5, 0xa8, 0xba, 0xff, 0xe4, 0xe0, 0x50, 0x9f, 0x23, 0xe5, 0x03,
+ 0x81, 0xc2, 0xab, 0x74, 0xd8, 0xc4, 0xe9, 0xd5, 0xf6, 0x7f, 0x86, 0x6b,
+ 0x06, 0xfc, 0x62, 0x0f, 0xeb, 0xa3, 0xc4, 0xa0, 0xad, 0x70, 0xad, 0x9b,
+ 0xdf, 0xd1, 0x75, 0x28, 0xdc, 0xf2, 0x24, 0x87, 0x6e, 0x85, 0x67, 0xfb,
+ 0xbb, 0x38, 0x86, 0xd8, 0x77, 0x15, 0x35, 0x41, 0x53, 0x69, 0x71, 0xd9,
+ 0x33, 0x00, 0x2c, 0x56, 0x45, 0xfd, 0xfc, 0xea, 0xf9, 0x8f, 0xd7, 0x04,
+ 0x6a, 0x8d, 0xf5, 0x4b, 0xd1, 0x5a, 0x66, 0x7e, 0xe0, 0xeb, 0x06, 0x80,
+ 0xc2, 0x14, 0x00, 0x49, 0x1c, 0x62, 0x54, 0xdb, 0x4b, 0x80, 0xc1, 0xa1,
+ 0xe3, 0x36, 0xc0, 0x58, 0x0f, 0x6c, 0x65, 0xfc, 0xc8, 0x4d, 0x85, 0xa5,
+ 0x5e, 0xb2, 0xbd, 0x5e, 0x2d, 0x47, 0x14, 0x77, 0x32, 0x7e, 0x39, 0x0b,
+ 0xba, 0xcd, 0x30, 0x4b, 0x8e, 0x3b, 0x6d, 0x66, 0x59, 0x73, 0x16, 0x0b,
+ 0x23, 0x99, 0xb5, 0x09, 0xb7, 0x8f, 0xb2, 0x96, 0x1f, 0x74, 0x08, 0x36,
+ 0x1f, 0xcd, 0x46, 0x90, 0x1b, 0xe9, 0xfe, 0xe1, 0xcb, 0x7e, 0x06, 0x98,
+ 0x74, 0x47, 0x2f, 0x57, 0x0b, 0x72, 0xea, 0x0d, 0x25, 0xed, 0x92, 0x70,
+ 0x0d, 0x00, 0x5f, 0x48, 0xe3, 0xff, 0x3f, 0x39, 0xfc, 0xa9, 0x66, 0xa9,
+ 0x95, 0x33, 0xcd, 0x8b, 0x22, 0x11, 0xd9, 0xff, 0x7e, 0x5c, 0x9d, 0x80,
+ 0xc5, 0xed, 0x99, 0x78, 0x5c, 0x33, 0xdd, 0x0e, 0xca, 0x6a, 0xe2, 0xb6,
+ 0x3a, 0x66, 0xbd, 0x1d, 0x60, 0x96, 0x28, 0x20, 0xbd, 0x8c, 0xf4, 0x6c,
+ 0xcb, 0xc3, 0x9b, 0x19, 0x78, 0xd7, 0x57, 0xa3, 0xc3, 0x12, 0x95, 0x11,
+ 0x3d, 0xab, 0x44, 0x0d, 0x78, 0xbb, 0x2e, 0x0b, 0x4e, 0x0c, 0x94, 0x75,
+ 0xe7, 0x8f, 0x5c, 0xad, 0xe4, 0x2f, 0x9c, 0x26, 0x00, 0x01, 0x71, 0xe1,
+ 0x36, 0xcf, 0xb1, 0x00, 0x8f, 0xf7, 0x86, 0x79, 0x2e, 0x79, 0x9e, 0xd5,
+ 0x8b, 0x53, 0x0d, 0x28, 0x6c, 0xb9, 0xa2, 0xdf, 0x04, 0xe3, 0x78, 0x6d,
+ 0xcb, 0xb7, 0xf9, 0xea, 0x20, 0x4a, 0x5d, 0x92, 0x64, 0x88, 0x7d, 0xc4,
+ 0x5a, 0x21, 0x86, 0x12, 0x46, 0x49, 0x6d, 0xbc, 0x03, 0xd5, 0xeb, 0xf6,
+ 0xe3, 0x38, 0x4a, 0xaa, 0xce, 0x45, 0x91, 0x6a, 0xc0, 0x59, 0x1b, 0x6c,
+ 0x98, 0xb2, 0xe0, 0x29, 0x91, 0x01, 0xb5, 0x44, 0x48, 0xa6, 0xd3, 0x78,
+ 0x5a, 0xe1, 0xe4, 0x8c, 0xd9, 0x33, 0xa0, 0xd5, 0xe1, 0x83, 0xd6, 0x5a,
+ 0xe3, 0x5f, 0xe6, 0xf8, 0xfd, 0x21, 0x12, 0xff, 0x9e, 0x71, 0x70, 0x7f,
+ 0x17, 0xe2, 0x41, 0xf2, 0xb1, 0x4a, 0x84, 0x6b, 0x88, 0xb6, 0xb9, 0xed,
+ 0xdb, 0x0b, 0x84, 0x6f, 0x07, 0x94, 0xc7, 0x8e, 0xcc, 0xc2, 0xb2, 0x49,
+ 0x3d, 0xf8, 0x70, 0x1a, 0x9b, 0x4d, 0x13, 0x0c, 0xe7, 0xeb, 0x75, 0x5d,
+ 0xfb, 0x10, 0x16, 0xf9, 0xf1, 0xac, 0x4b, 0xe3, 0x65, 0x5f, 0xaa, 0xbb,
+ 0x31, 0xac, 0x4d, 0x74, 0x11, 0x62, 0x64, 0xd1, 0x90, 0x6e, 0x45, 0xaf,
+ 0x2d, 0xb2, 0xa5, 0xfa, 0x57, 0x67, 0xaa, 0x4c, 0x3f, 0x81, 0x72, 0x93,
+ 0x8a, 0xfb, 0xeb, 0x19, 0x9d, 0x9a, 0x10, 0x8d, 0xce, 0xb8, 0xe9, 0x3f,
+ 0x97, 0x3e, 0xb2, 0x2d, 0x3e, 0xaf, 0x19, 0x39, 0xb0, 0xea, 0x33, 0x09,
+ 0x25, 0xbc, 0x6f, 0x88, 0x25, 0x75, 0x11, 0x2b, 0xd9, 0x42, 0x8d, 0x48,
+ 0x8e, 0xe7, 0x51, 0x4e, 0xd4, 0xee, 0xd4, 0xf1, 0xe1, 0x94, 0xfb, 0xbd,
+ 0x01, 0x87, 0xa5, 0xd6, 0x94, 0x36, 0x6f, 0x37, 0xcd, 0x70, 0x49, 0xfd,
+ 0x6a, 0x94, 0xb0, 0xc8, 0xe5, 0xb9, 0xdb, 0x83, 0x72, 0xa0, 0xd8, 0x5e,
+ 0x00, 0x3e, 0x3d, 0x56, 0xc9, 0xae, 0xf6, 0xe7, 0xb1, 0xba, 0xdd, 0x6d,
+ 0x59, 0x2a, 0x6a, 0x50, 0x66, 0xfd, 0xf1, 0x1f, 0x18, 0xe4, 0xf9, 0x59,
+ 0x16, 0xf1, 0x25, 0x42, 0x7c, 0xab, 0xc5, 0xea, 0x64, 0x31, 0xf7, 0xcf,
+ 0xdd, 0x56, 0xc4, 0xbd, 0xb5, 0xe6, 0xb4, 0xda, 0x41, 0xb7, 0xfa, 0x05,
+ 0x06, 0x57, 0x51, 0xd8, 0xc4, 0xf7, 0x3c, 0xc8, 0x54, 0x55, 0x5c, 0x2c,
+ 0xd2, 0x17, 0xec, 0x27, 0xd3, 0xbf, 0xf3, 0xc8, 0xba, 0xbf, 0x75, 0xcf,
+ 0x1f, 0x78, 0x42, 0x1a, 0x4f, 0xb0, 0xcb, 0xd7, 0x84, 0xc6, 0xbd, 0x40,
+ 0x07, 0xbf, 0x47, 0xe9, 0x17, 0x2f, 0xad, 0xec, 0x64, 0x8b, 0xca, 0x7c,
+ 0x49, 0x4c, 0xda, 0xf9, 0x5f, 0x28, 0x1d, 0x2d, 0xc3, 0x8d, 0x15, 0xa6,
+ 0x0f, 0x95, 0x76, 0x99, 0xb5, 0x76, 0x68, 0x97, 0x91, 0x93, 0xca, 0x9d,
+ 0x61, 0x50, 0xa7, 0xa7, 0xf7, 0xce, 0xd5, 0x14, 0xe5, 0xb2, 0x50, 0x50,
+ 0x21, 0x73, 0x00, 0x21, 0x4f, 0x12, 0x79, 0x7f, 0x68, 0x27, 0x52, 0xf2,
+ 0x2c, 0x9d, 0x2d, 0x4f, 0x66, 0x3e, 0xf1, 0xb5, 0x68, 0x77, 0xc9, 0xd5,
+ 0x16, 0x01, 0xb2, 0x6d, 0x46, 0x7c, 0xce, 0x8d, 0x67, 0x77, 0x27, 0xdd,
+ 0x4b, 0x16, 0xd7, 0x7b, 0x16, 0xfd, 0x6b, 0x18, 0x36, 0xeb, 0xa5, 0xd6,
+ 0xd1, 0x21, 0xdb, 0x27, 0x01, 0x9a, 0x22, 0xa1, 0xbf, 0x45, 0xda, 0xf7,
+ 0xe4, 0x07, 0x26, 0x0c, 0x84, 0xe1, 0x81, 0x1c, 0xf9, 0x38, 0x4a, 0x26,
+ 0xb3, 0xe3, 0x90, 0x13, 0x5e, 0xb7, 0xd5, 0xaf, 0xcb, 0x7d, 0xc1, 0xf7,
+ 0x23, 0x2d, 0x96, 0xc8, 0xe7, 0x3e, 0x21, 0x2c, 0xee, 0xa8, 0xd7, 0x83,
+ 0xf1, 0xd7, 0xf0, 0xce, 0xca, 0x80, 0xb5, 0xbd, 0x89, 0x10, 0x71, 0x38,
+ 0x8c, 0xa2, 0xa1, 0x07, 0x93, 0xa9, 0xd9, 0x4d, 0xa5, 0x9b, 0x66, 0x68,
+ 0x06, 0x65, 0x8d, 0x26, 0xff, 0xcb, 0xf2, 0xf5, 0x09, 0x7c, 0xf1, 0x7f,
+ 0x55, 0x3f, 0x61, 0x78, 0xb8, 0x27, 0x77, 0xb8, 0x30, 0x91, 0x10, 0x3f,
+ 0x57, 0xec, 0xfd, 0x82, 0xd2, 0xd3, 0x8a, 0x27, 0xa5, 0x26, 0x87, 0x01,
+ 0x8a, 0x20, 0x9a, 0x5d, 0xe9, 0x88, 0xa7, 0x70, 0xb0, 0x06, 0x04, 0xe8,
+ 0xa0, 0x15, 0x2c, 0x0b, 0xe5, 0xea, 0x79, 0x57, 0xad, 0x8c, 0xe2, 0x98,
+ 0xcd, 0xbf, 0x74, 0x1c, 0xb6, 0x06, 0xc6, 0x17, 0x12, 0x2b, 0xa4, 0xa6,
+ 0xfe, 0xe7, 0x94, 0xf9, 0xb6, 0x95, 0xf0, 0xfc, 0xe5, 0x57, 0xfe, 0xf9,
+ 0x1d, 0xd4, 0x29, 0xc0, 0x06, 0xf9, 0x5f, 0x14, 0x73, 0x14, 0x1d, 0x42,
+ 0x1f, 0x70, 0xa0, 0xd5, 0x3d, 0x0d, 0xbe, 0xfb, 0xb3, 0xe0, 0xae, 0x35,
+ 0x34, 0x40, 0xa9, 0xca, 0x5d, 0x6f, 0x4f, 0x3a, 0x3b, 0x9e, 0xe2, 0xed,
+ 0x85, 0xa9, 0x53, 0xb9, 0xb2, 0x43, 0x5e, 0x95, 0x7a, 0x30, 0x63, 0x2a,
+ 0x84, 0x40, 0x1b, 0x86, 0x2d, 0x74, 0xf6, 0xf3, 0x3b, 0x40, 0x5b, 0x16,
+ 0x51, 0xa1, 0xaa, 0x70, 0xed, 0xb3, 0x96, 0x66, 0x9b, 0x6b, 0x57, 0x4d,
+ 0x0d, 0x8b, 0x2a, 0x9b, 0x27, 0xb6, 0x04, 0xa5, 0x19, 0xb2, 0x17, 0xad,
+ 0x6f, 0x6f, 0xef, 0x8c, 0xae, 0x87, 0x58, 0xc9, 0xbd, 0x51, 0xf9, 0x6b,
+ 0x7d, 0x5c, 0xd1, 0x42, 0x10, 0xa1, 0x5c, 0x66, 0x13, 0xaa, 0xe0, 0xfc,
+ 0x91, 0x95, 0x63, 0x0a, 0x0b, 0x3d, 0x5c, 0xd1, 0x64, 0xda, 0xaf, 0x7b,
+ 0x1b, 0x0a, 0x09, 0x9f, 0xf8, 0xd9, 0x43, 0x7b, 0x7d, 0xe1, 0x34, 0x68,
+ 0x6a, 0xb2, 0x3a, 0x16, 0x99, 0x2f, 0x29, 0x5f, 0xa3, 0xe6, 0x03, 0x74,
+ 0x42, 0x49, 0x6a, 0x33, 0x15, 0x93, 0x5e, 0xa5, 0xab, 0x13, 0x77, 0x3a,
+ 0x1a, 0x78, 0x6d, 0xe5, 0x06, 0x1f, 0xa4, 0x6f, 0xdc, 0xa8, 0xc8, 0x93,
+ 0x9f, 0x4d, 0x99, 0xba, 0xb1, 0x6d, 0x8d, 0x1d, 0xc0, 0x6b, 0xff, 0x16,
+ 0x7d, 0xb3, 0xfc, 0xe0, 0xd7, 0xd4, 0xb2, 0x69, 0x32, 0x38, 0x64, 0xef,
+ 0x04, 0xba, 0x7b, 0xa5, 0x5e, 0xc6, 0x1f, 0x1d, 0xd0, 0xf8, 0x6b, 0xd8,
+ 0x35, 0xb9, 0x9e, 0x2f, 0x62, 0x36, 0xf5, 0x88, 0x41, 0x90, 0x1f, 0xc7,
+ 0xc3, 0x96, 0x1b, 0x4f, 0xc4, 0x87, 0x0c, 0xea, 0xdb, 0x8f, 0xea, 0x6a,
+ 0xe9, 0xdc, 0x30, 0xc4, 0x6c, 0x23, 0x9b, 0xac, 0x8b, 0xf6, 0xa6, 0xbf,
+ 0xc0, 0xd6, 0x00, 0xf6, 0x92, 0x3f, 0xd3, 0x83, 0xfe, 0x74, 0x7c, 0x19,
+ 0x74, 0xfb, 0xe7, 0x72, 0x81, 0x17, 0x32, 0x32, 0xb8, 0x4b, 0x5d, 0x70,
+ 0x67, 0x14, 0xe1, 0xee,
+};
+
+#endif
diff --git a/drivers/acpi/apei/ghes-nvidia-test.c b/drivers/acpi/apei/ghes-nvidia-test.c
new file mode 100644
index 000000000000..5c84da82f898
--- /dev/null
+++ b/drivers/acpi/apei/ghes-nvidia-test.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <kunit/test.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+#include <linux/uuid.h>
+
+#include "ghes-nvidia.h"
+#include "ghes-nvidia-test-fixtures.h"
+
+static const u8 grace_valid_two_regs[] = {
+ 'N', 'V', 'D', 'A', '-', 'G', 'R', 'A',
+ 'C', 'E', 0, 0, 0, 0, 0, 0,
+ 0x34, 0x12, 0x78, 0x56, 0x02, 0x03, 0x02, 0x00,
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 grace_valid_zero_regs[] = {
+ 'N', 'V', 'D', 'A', '-', 'G', 'R', 'A',
+ 'C', 'E', 0, 0, 0, 0, 0, 0,
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 grace_valid_full_signature[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void nvidia_ghes_grace_decodes_registers(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
+ sizeof(grace_valid_two_regs),
+ &decoded));
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE, decoded.format);
+ KUNIT_EXPECT_STREQ(test, "NVDA-GRACE", decoded.signature);
+ KUNIT_EXPECT_EQ(test, 0x1234, decoded.error_type);
+ KUNIT_EXPECT_EQ(test, 0x5678, decoded.error_instance);
+ KUNIT_EXPECT_EQ(test, 2, decoded.severity);
+ KUNIT_EXPECT_EQ(test, 3, decoded.socket);
+ KUNIT_EXPECT_EQ(test, 2, decoded.number_regs);
+ KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, decoded.instance_base);
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x1000ULL, addr);
+ KUNIT_EXPECT_EQ(test, 0x1111ULL, val);
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_grace_reg_pair(&decoded, 1, &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x2000ULL, addr);
+ KUNIT_EXPECT_EQ(test, 0x2222ULL, val);
+}
+
+static void nvidia_ghes_grace_accepts_zero_registers(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_grace(NULL, grace_valid_zero_regs,
+ sizeof(grace_valid_zero_regs),
+ &decoded));
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE, decoded.format);
+ KUNIT_EXPECT_EQ(test, 0, decoded.number_regs);
+ KUNIT_EXPECT_PTR_EQ(test, NULL, decoded.grace_regs);
+}
+
+static void nvidia_ghes_grace_copies_non_nul_signature(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_grace(NULL, grace_valid_full_signature,
+ sizeof(grace_valid_full_signature),
+ &decoded));
+ KUNIT_EXPECT_STREQ(test, "ABCDEFGHIJKLMNOP", decoded.signature);
+ KUNIT_EXPECT_EQ(test, '\0', decoded.signature[16]);
+}
+
+static void nvidia_ghes_grace_rejects_truncated_header(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
+ sizeof(grace_valid_zero_regs) - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_grace_rejects_truncated_registers(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
+ sizeof(grace_valid_two_regs) - 1,
+ &decoded));
+}
+
+static const u8 vera_chip_serial[] = {
+ 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00,
+};
+
+#define VERA_EVENT_HDR_SIZE 32
+#define VERA_CPU_INFO_SIZE 32
+#define VERA_CONTEXT_HDR_SIZE 16
+#define VERA_FIRST_CONTEXT_OFFSET (VERA_EVENT_HDR_SIZE + VERA_CPU_INFO_SIZE)
+
+static u8 *nvidia_ghes_kunit_memdup(struct kunit *test, const u8 *src, size_t len)
+{
+ u8 *buf;
+
+ buf = kunit_kmalloc(test, len, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ memcpy(buf, src, len);
+
+ return buf;
+}
+
+static u8 *vera_make_synthetic(struct kunit *test, u8 version, u8 info_size,
+ u16 format, const u8 *data, u32 data_size,
+ size_t *len)
+{
+ u8 *buf;
+ size_t context_offset;
+
+ KUNIT_ASSERT_TRUE(test, info_size >= VERA_CPU_INFO_SIZE);
+
+ context_offset = VERA_EVENT_HDR_SIZE + info_size;
+ *len = context_offset + VERA_CONTEXT_HDR_SIZE + data_size;
+ buf = kunit_kzalloc(test, *len, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+
+ buf[0] = version;
+ buf[1] = 1;
+ buf[2] = 0;
+ put_unaligned_le16(0x22, &buf[4]);
+ memcpy(&buf[16], "SYNTH", 5);
+ put_unaligned_le16(0, &buf[32]);
+ buf[34] = info_size;
+ buf[35] = 0;
+ put_unaligned_le32(0x14107, &buf[36]);
+ memcpy(&buf[40], vera_chip_serial, sizeof(vera_chip_serial));
+ put_unaligned_le64(0, &buf[56]);
+ put_unaligned_le32(VERA_CONTEXT_HDR_SIZE, &buf[context_offset]);
+ put_unaligned_le16(0, &buf[context_offset + 4]);
+ put_unaligned_le16(format, &buf[context_offset + 8]);
+ put_unaligned_le16(0, &buf[context_offset + 10]);
+ put_unaligned_le32(data_size, &buf[context_offset + 12]);
+ memcpy(&buf[context_offset + VERA_CONTEXT_HDR_SIZE], data, data_size);
+
+ return buf;
+}
+
+static u8 *vera_make_synthetic_default(struct kunit *test, u16 format,
+ const u8 *data, u32 data_size, size_t *len)
+{
+ return vera_make_synthetic(test, 1, VERA_CPU_INFO_SIZE, format, data,
+ data_size, len);
+}
+
+static int nvidia_ghes_vera_context_u64_pair(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u64 *addr, u64 *val)
+{
+ int count;
+
+ if (!ctx || !addr || !val || ctx->data_format_type != 1)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *addr = get_unaligned_le64(ctx->data + index * 16);
+ *val = get_unaligned_le64(ctx->data + index * 16 + 8);
+
+ return 0;
+}
+
+static int nvidia_ghes_vera_context_u32_pair(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u32 *addr, u32 *val)
+{
+ int count;
+
+ if (!ctx || !addr || !val || ctx->data_format_type != 2)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *addr = get_unaligned_le32(ctx->data + index * 8);
+ *val = get_unaligned_le32(ctx->data + index * 8 + 4);
+
+ return 0;
+}
+
+static int nvidia_ghes_vera_context_u64_value(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u64 *val)
+{
+ int count;
+
+ if (!ctx || !val || ctx->data_format_type != 3)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *val = get_unaligned_le64(ctx->data + index * 8);
+
+ return 0;
+}
+
+static int nvidia_ghes_vera_context_u32_value(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u32 *val)
+{
+ int count;
+
+ if (!ctx || !val || ctx->data_format_type != 4)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *val = get_unaligned_le32(ctx->data + index * 4);
+
+ return 0;
+}
+
+static void nvidia_ghes_vera_decodes_l1_reset(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ sizeof(vera_l1_reset_id10),
+ &decoded));
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_VERA, decoded.format);
+ KUNIT_EXPECT_STREQ(test, "L1 RESET", decoded.signature);
+ KUNIT_EXPECT_EQ(test, 1, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, 1, decoded.event_type);
+ KUNIT_EXPECT_EQ(test, 39, decoded.event_sub_type);
+ KUNIT_EXPECT_EQ(test, 0, decoded.source_device_type);
+ KUNIT_EXPECT_EQ(test, 0, decoded.socket);
+ KUNIT_EXPECT_EQ(test, 0x14107U, decoded.architecture);
+ KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
+ sizeof(vera_chip_serial));
+ KUNIT_EXPECT_EQ(test, 0xfe1500200000ULL, decoded.instance_base);
+ KUNIT_EXPECT_EQ(test, 16U, decoded.contexts[0].context_size);
+ KUNIT_EXPECT_EQ(test, 1, decoded.contexts[0].data_format_type);
+ KUNIT_EXPECT_EQ(test, 0U, decoded.contexts[0].data_size);
+}
+
+static void nvidia_ghes_vera_decodes_crashdump_id(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, vera_crashdump_id_id40,
+ sizeof(vera_crashdump_id_id40),
+ &decoded));
+ KUNIT_EXPECT_STREQ(test, "CRASHDUMP-ID", decoded.signature);
+ KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
+ sizeof(vera_chip_serial));
+ KUNIT_EXPECT_EQ(test, 240U, decoded.contexts[0].context_size);
+ KUNIT_EXPECT_EQ(test, 1, decoded.contexts[0].data_format_type);
+ KUNIT_EXPECT_EQ(test, 224U, decoded.contexts[0].data_size);
+ KUNIT_EXPECT_EQ(test, 14, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_pair(&decoded.contexts[0], 0,
+ &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x8300000000000000ULL, addr);
+ KUNIT_EXPECT_EQ(test, 0x372e33375f6c6572ULL, val);
+}
+
+static void nvidia_ghes_vera_decodes_crashdump_s1_opaque_context(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u64 val;
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, vera_crashdump_s1_id41,
+ sizeof(vera_crashdump_s1_id41),
+ &decoded));
+ KUNIT_EXPECT_STREQ(test, "CRASHDUMP-S1", decoded.signature);
+ KUNIT_EXPECT_EQ(test, 4096, decoded.event_type);
+ KUNIT_EXPECT_EQ(test, 2304, decoded.event_sub_type);
+ KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
+ sizeof(vera_chip_serial));
+ KUNIT_EXPECT_EQ(test, 16U, decoded.contexts[0].context_size);
+ KUNIT_EXPECT_EQ(test, 0, decoded.contexts[0].data_format_type);
+ KUNIT_EXPECT_EQ(test, 1928U, decoded.contexts[0].data_size);
+ KUNIT_EXPECT_NOT_NULL(test, decoded.contexts[0].data);
+ KUNIT_EXPECT_EQ(test, 0x53, decoded.contexts[0].data[0]);
+ KUNIT_EXPECT_EQ(test, 0x56, decoded.contexts[0].data[1]);
+ KUNIT_EXPECT_EQ(test, 0x7f, decoded.contexts[0].data[2]);
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_vera_context_u64_value(&decoded.contexts[0], 0, &val));
+}
+
+static void nvidia_ghes_vera_decodes_format2_u32_pairs(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+ u32 addr, val;
+
+ buf = vera_make_synthetic_default(test, 2, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_pair(&decoded.contexts[0], 0,
+ &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x12345678U, addr);
+ KUNIT_EXPECT_EQ(test, 0x9abcdef0U, val);
+}
+
+static void nvidia_ghes_vera_decodes_format3_u64_values(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+ u64 val;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_value(&decoded.contexts[0], 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, val);
+}
+
+static void nvidia_ghes_vera_decodes_format4_u32_values(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34, 0x12,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+ u32 val;
+
+ buf = vera_make_synthetic_default(test, 4, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_value(&decoded.contexts[0], 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x12345678U, val);
+}
+
+static void nvidia_ghes_vera_rejects_truncated_event_header(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ VERA_EVENT_HDR_SIZE - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_truncated_cpu_info(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ VERA_FIRST_CONTEXT_OFFSET - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_truncated_context_header(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ VERA_FIRST_CONTEXT_OFFSET +
+ VERA_CONTEXT_HDR_SIZE - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_too_many_contexts(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ buf[1] = NVIDIA_GHES_MAX_CONTEXTS + 1;
+
+ KUNIT_EXPECT_EQ(test, -E2BIG,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_bad_info_size(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ buf[34] = 31;
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unsupported_version(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ buf[0] = 2;
+
+ KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_accepts_extended_cpu_info(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+ u64 val;
+
+ buf = vera_make_synthetic(test, 1, VERA_CPU_INFO_SIZE + 4, 3,
+ data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE, decoded.contexts[0].context_size);
+ KUNIT_EXPECT_EQ(test, 3, decoded.contexts[0].data_format_type);
+ KUNIT_EXPECT_EQ(test, 8U, decoded.contexts[0].data_size);
+ KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_value(&decoded.contexts[0], 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, val);
+}
+
+static void nvidia_ghes_vera_rejects_short_context_size(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ put_unaligned_le32(15, &buf[VERA_FIRST_CONTEXT_OFFSET]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_ambiguous_context_size(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ put_unaligned_le32(20, &buf[VERA_FIRST_CONTEXT_OFFSET]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_data_beyond_section(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ put_unaligned_le32(16, &buf[VERA_FIRST_CONTEXT_OFFSET + 12]);
+
+ KUNIT_EXPECT_EQ(test, -ENODATA, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format1(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_crashdump_id_id40,
+ sizeof(vera_crashdump_id_id40));
+ put_unaligned_le32(15, &buf[VERA_FIRST_CONTEXT_OFFSET + 12]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_crashdump_id_id40), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format2(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 2, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format3(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format4(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 4, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unsupported_source_device(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ buf[2] = 1;
+
+ KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unsupported_data_format(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ put_unaligned_le16(5, &buf[VERA_FIRST_CONTEXT_OFFSET + 8]);
+
+ KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_copies_non_nul_signature(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ memcpy(&buf[16], "ABCDEFGHIJKLMNOP", 16);
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+ KUNIT_EXPECT_STREQ(test, "ABCDEFGHIJKLMNOP", decoded.signature);
+ KUNIT_EXPECT_EQ(test, '\0', decoded.signature[16]);
+}
+
+static void nvidia_ghes_guid_routes_grace(struct kunit *test)
+{
+ const guid_t guid = GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
+ 0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
+
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE,
+ nvidia_ghes_format_from_guid(&guid));
+}
+
+static void nvidia_ghes_guid_routes_vera(struct kunit *test)
+{
+ const guid_t guid = GUID_INIT(0x9068e568, 0x6ca0, 0x11f0,
+ 0xae, 0xaf, 0x15, 0x93, 0x43, 0x59, 0x1e, 0xac);
+
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_VERA,
+ nvidia_ghes_format_from_guid(&guid));
+}
+
+static void nvidia_ghes_guid_rejects_unknown(struct kunit *test)
+{
+ const guid_t guid = GUID_INIT(0x00000000, 0x0000, 0x0000,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_UNKNOWN,
+ nvidia_ghes_format_from_guid(&guid));
+}
+
+static void nvidia_ghes_grace_reg_pair_rejects_null_decoded(struct kunit *test)
+{
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_grace_reg_pair(NULL, 0, &addr, &val));
+}
+
+static void nvidia_ghes_grace_reg_pair_rejects_wrong_format(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {
+ .format = NVIDIA_GHES_FORMAT_VERA,
+ };
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+}
+
+static void nvidia_ghes_grace_reg_pair_rejects_out_of_range(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {
+ .format = NVIDIA_GHES_FORMAT_GRACE,
+ .number_regs = 0,
+ };
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, -ERANGE,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+}
+
+static struct kunit_case nvidia_ghes_test_cases[] = {
+ KUNIT_CASE(nvidia_ghes_grace_decodes_registers),
+ KUNIT_CASE(nvidia_ghes_grace_accepts_zero_registers),
+ KUNIT_CASE(nvidia_ghes_grace_copies_non_nul_signature),
+ KUNIT_CASE(nvidia_ghes_grace_rejects_truncated_header),
+ KUNIT_CASE(nvidia_ghes_grace_rejects_truncated_registers),
+ KUNIT_CASE(nvidia_ghes_grace_reg_pair_rejects_null_decoded),
+ KUNIT_CASE(nvidia_ghes_grace_reg_pair_rejects_wrong_format),
+ KUNIT_CASE(nvidia_ghes_grace_reg_pair_rejects_out_of_range),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_l1_reset),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_crashdump_id),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_crashdump_s1_opaque_context),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_format2_u32_pairs),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_format3_u64_values),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_format4_u32_values),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_truncated_event_header),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_truncated_cpu_info),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_truncated_context_header),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_too_many_contexts),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_bad_info_size),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unsupported_version),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_extended_cpu_info),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_short_context_size),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_ambiguous_context_size),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_data_beyond_section),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format1),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format2),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format3),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format4),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unsupported_source_device),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unsupported_data_format),
+ KUNIT_CASE(nvidia_ghes_vera_copies_non_nul_signature),
+ KUNIT_CASE(nvidia_ghes_guid_routes_grace),
+ KUNIT_CASE(nvidia_ghes_guid_routes_vera),
+ KUNIT_CASE(nvidia_ghes_guid_rejects_unknown),
+ {}
+};
+
+static struct kunit_suite nvidia_ghes_test_suite = {
+ .name = "ghes-nvidia",
+ .test_cases = nvidia_ghes_test_cases,
+};
+
+kunit_test_suite(nvidia_ghes_test_suite);
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_DESCRIPTION("KUnit tests for NVIDIA GHES CPER parser helpers");
+MODULE_LICENSE("GPL");
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v2 RESEND 4/4] selftests: firmware: Add NVIDIA GHES EINJ selftest
2026-07-24 12:20 [PATCH v2 RESEND 0/4] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoder and tests Kai-Heng Feng
` (2 preceding siblings ...)
2026-07-24 12:20 ` [PATCH v2 RESEND 3/4] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage Kai-Heng Feng
@ 2026-07-24 12:20 ` Kai-Heng Feng
2026-07-26 9:02 ` Shuai Xue
2026-08-04 12:23 ` [PATCH v3 0/3] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoding Kai-Heng Feng
4 siblings, 1 reply; 13+ messages in thread
From: Kai-Heng Feng @ 2026-07-24 12:20 UTC (permalink / raw)
To: rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, xueshuai,
linux-kernel, linux-acpi, linux-kselftest, linux-hardening, csoto,
mochs, Kai-Heng Feng
Exercise the full driver path on real Grace and Vera hardware using
ACPI EINJ to inject CPER sections and validate the kernel log output.
KUnit covers the parser in isolation; this test covers the path from
firmware notification through GUID dispatch to decoded output.
Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
---
v2:
- No change.
tools/testing/selftests/firmware/Makefile | 4 +-
tools/testing/selftests/firmware/config | 5 +
tools/testing/selftests/firmware/einj_lib.sh | 189 ++++++++++++++++++
.../selftests/firmware/ghes_nvidia_einj.sh | 144 +++++++++++++
.../firmware/ghes_nvidia_einj_profiles.sh | 46 +++++
5 files changed, 386 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/firmware/einj_lib.sh
create mode 100755 tools/testing/selftests/firmware/ghes_nvidia_einj.sh
create mode 100755 tools/testing/selftests/firmware/ghes_nvidia_einj_profiles.sh
diff --git a/tools/testing/selftests/firmware/Makefile b/tools/testing/selftests/firmware/Makefile
index 7992969deaa2..b753dd123860 100644
--- a/tools/testing/selftests/firmware/Makefile
+++ b/tools/testing/selftests/firmware/Makefile
@@ -3,8 +3,8 @@
CFLAGS = -Wall \
-O2
-TEST_PROGS := fw_run_tests.sh
-TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_upload.sh fw_lib.sh
+TEST_PROGS := fw_run_tests.sh ghes_nvidia_einj.sh
+TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_upload.sh fw_lib.sh einj_lib.sh ghes_nvidia_einj_profiles.sh
TEST_GEN_FILES := fw_namespace
include ../lib.mk
diff --git a/tools/testing/selftests/firmware/config b/tools/testing/selftests/firmware/config
index 6e402519b117..1b68e638d0b7 100644
--- a/tools/testing/selftests/firmware/config
+++ b/tools/testing/selftests/firmware/config
@@ -4,3 +4,8 @@ CONFIG_FW_LOADER_USER_HELPER=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_FW_UPLOAD=y
+CONFIG_DEBUG_FS=y
+CONFIG_ACPI_APEI=y
+CONFIG_ACPI_APEI_GHES=y
+CONFIG_ACPI_APEI_EINJ=y
+CONFIG_ACPI_APEI_GHES_NVIDIA=y
diff --git a/tools/testing/selftests/firmware/einj_lib.sh b/tools/testing/selftests/firmware/einj_lib.sh
new file mode 100644
index 000000000000..ca569a9fe5b0
--- /dev/null
+++ b/tools/testing/selftests/firmware/einj_lib.sh
@@ -0,0 +1,189 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+set -e
+
+# Kselftest framework requirement - SKIP code is 4.
+ksft_skip=4
+
+EINJ_TABLE=/sys/firmware/acpi/tables/EINJ
+EINJ_DEBUGFS=/sys/kernel/debug/apei/einj
+NVIDIA_PLATFORM_GLOB=/sys/bus/platform/devices/NVDA2012:*
+NVIDIA_DRIVER_DIR=/sys/bus/platform/drivers/nvidia-ghes
+
+einj_skip()
+{
+ echo "$0: $1" >&2
+ exit $ksft_skip
+}
+
+einj_require_root()
+{
+ [ "$(id -u)" -eq 0 ] || einj_skip "must be run as root"
+}
+
+einj_require_debugfs()
+{
+ [ -d /sys/kernel/debug ] || einj_skip "debugfs is not mounted at /sys/kernel/debug"
+}
+
+einj_require_einj()
+{
+ [ -e "$EINJ_TABLE" ] || einj_skip "ACPI EINJ table is missing"
+ if [ ! -d "$EINJ_DEBUGFS" ]; then
+ modprobe einj 2>/dev/null || true
+ fi
+ [ -d "$EINJ_DEBUGFS" ] || einj_skip "EINJ debugfs directory is missing"
+}
+
+einj_require_vendor_einj()
+{
+ [ -e "$EINJ_DEBUGFS/vendor" ] || einj_skip "NVIDIA vendor EINJ metadata is missing"
+ [ -e "$EINJ_DEBUGFS/vendor_flags" ] || einj_skip "NVIDIA vendor EINJ flags are missing"
+}
+
+einj_require_available_error_type()
+{
+ local available
+
+ available=$(einj_read_trimmed_value available_error_type)
+ [ -n "$available" ] || einj_skip "available_error_type is missing"
+}
+
+einj_read_trimmed_value()
+{
+ local file=$1
+
+ einj_read_value "$file" | tr -d '\n'
+}
+
+einj_require_writable_value()
+{
+ local file=$1
+
+ [ -w "$EINJ_DEBUGFS/$file" ] || einj_skip "$file is not writable"
+}
+
+einj_require_writable_profile()
+{
+ local file
+
+ for file in error_type flags vendor_flags param1 param2 param3 param4 notrigger; do
+ einj_require_writable_value "$file"
+ done
+}
+
+einj_find_bound_nvidia_device()
+{
+ local dev
+
+ for dev in $NVIDIA_PLATFORM_GLOB; do
+ [ -e "$dev" ] || continue
+ if [ "$(readlink -f "$dev/driver" 2>/dev/null)" = "$NVIDIA_DRIVER_DIR" ]; then
+ echo "$dev"
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+einj_require_bound_nvidia_device()
+{
+ local dev
+
+ dev=$(einj_find_bound_nvidia_device) || einj_skip "no bound NVIDIA GHES device"
+ echo "$dev"
+}
+
+einj_read_value()
+{
+ local file=$1
+
+ cat "$EINJ_DEBUGFS/$file"
+}
+
+einj_write_value()
+{
+ local file=$1
+ local value=$2
+
+ printf '%s\n' "$value" > "$EINJ_DEBUGFS/$file"
+}
+
+einj_restore_value()
+{
+ local file=$1
+ local value=$2
+
+ # Some EINJ controls read back as an empty string when unset, but the
+ # debugfs write handler has no matching "clear" operation.
+ [ -n "$value" ] || return 0
+ einj_write_value "$file" "$value"
+}
+
+einj_save_state()
+{
+ EINJ_SAVED_ERROR_TYPE=$(einj_read_value error_type)
+ EINJ_SAVED_FLAGS=$(einj_read_value flags)
+ EINJ_SAVED_PARAM1=$(einj_read_value param1)
+ EINJ_SAVED_PARAM2=$(einj_read_value param2)
+ EINJ_SAVED_PARAM3=$(einj_read_value param3)
+ EINJ_SAVED_PARAM4=$(einj_read_value param4)
+ EINJ_SAVED_VENDOR_FLAGS=$(einj_read_value vendor_flags)
+ EINJ_SAVED_NOTRIGGER=$(einj_read_value notrigger)
+}
+
+einj_restore_state()
+{
+ [ -n "${EINJ_SAVED_ERROR_TYPE+x}" ] || return 0
+
+ einj_restore_value error_type "$EINJ_SAVED_ERROR_TYPE"
+ einj_restore_value flags "$EINJ_SAVED_FLAGS"
+ einj_restore_value param1 "$EINJ_SAVED_PARAM1"
+ einj_restore_value param2 "$EINJ_SAVED_PARAM2"
+ einj_restore_value param3 "$EINJ_SAVED_PARAM3"
+ einj_restore_value param4 "$EINJ_SAVED_PARAM4"
+ einj_restore_value vendor_flags "$EINJ_SAVED_VENDOR_FLAGS"
+ einj_restore_value notrigger "$EINJ_SAVED_NOTRIGGER"
+}
+
+einj_emit_kmsg_marker()
+{
+ local tag=$1
+ local marker
+
+ marker="ghes-nvidia-einj:${tag}:$$:${RANDOM}"
+ printf '%s\n' "$marker" > /dev/kmsg
+ printf '%s\n' "$marker"
+}
+
+einj_capture_dmesg_after_marker()
+{
+ local marker=$1
+
+ dmesg | awk -v marker="$marker" '
+ found { print }
+ index($0, marker) { found = 1 }
+ '
+}
+
+einj_wait_for_dmesg_after_marker_contains()
+{
+ local marker=$1
+ local needle=$2
+ local timeout=${3:-10}
+ local i
+ local slice
+
+ for i in $(seq 1 "$timeout"); do
+ slice=$(einj_capture_dmesg_after_marker "$marker")
+ if printf '%s\n' "$slice" | grep -Fq "$needle"; then
+ printf '%s\n' "$slice"
+ return 0
+ fi
+ sleep 1
+ done
+
+ return 1
+}
diff --git a/tools/testing/selftests/firmware/ghes_nvidia_einj.sh b/tools/testing/selftests/firmware/ghes_nvidia_einj.sh
new file mode 100755
index 000000000000..6fc4d3189235
--- /dev/null
+++ b/tools/testing/selftests/firmware/ghes_nvidia_einj.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+set -e
+
+TEST_DIR=$(dirname "$0")
+source "$TEST_DIR/einj_lib.sh"
+source "$TEST_DIR/ghes_nvidia_einj_profiles.sh"
+
+einj_assert_nvidia_cper_output()
+{
+ local profile=$1
+ local output=$2
+
+ if printf '%s\n' "$output" | grep -Fq 'Malformed NVIDIA'; then
+ echo "$0: $profile produced malformed NVIDIA CPER output" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+
+ if printf '%s\n' "$output" | grep -Fq 'NVIDIA Grace CPER section'; then
+ if ! printf '%s\n' "$output" | grep -Fq 'signature:'; then
+ echo "$0: $profile Grace output missing signature line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ if ! printf '%s\n' "$output" | grep -Fq 'error_type:'; then
+ echo "$0: $profile Grace output missing error_type line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ if ! printf '%s\n' "$output" | grep -Fq 'number_regs:'; then
+ echo "$0: $profile Grace output missing number_regs line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ if ! printf '%s\n' "$output" | grep -Fq 'instance_base:'; then
+ echo "$0: $profile Grace output missing instance_base line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ return 0
+ fi
+
+ if printf '%s\n' "$output" | grep -Fq 'NVIDIA Vera CPER section'; then
+ if ! printf '%s\n' "$output" | grep -Fq 'signature:'; then
+ echo "$0: $profile Vera output missing signature line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ if ! printf '%s\n' "$output" | grep -Fq 'event_type:'; then
+ echo "$0: $profile Vera output missing event_type line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ if ! printf '%s\n' "$output" | grep -Fq 'event_sub_type:'; then
+ echo "$0: $profile Vera output missing event_sub_type line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ if ! printf '%s\n' "$output" | grep -Fq 'event_context_count:'; then
+ echo "$0: $profile Vera output missing event_context_count line" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+ fi
+ return 0
+ fi
+
+ echo "$0: $profile did not emit a recognized NVIDIA CPER section" >&2
+ printf '%s\n' "$output" >&2
+ return 1
+}
+
+einj_run_profile()
+{
+ local profile=$1
+ local marker
+ local output
+
+ if ! einj_select_profile "$profile"; then
+ echo "$0: unknown safe NVIDIA EINJ profile: $profile" >&2
+ return 1
+ fi
+
+ einj_require_writable_profile
+
+ printf '%s: running safe sample %s\n' "$0" "$profile"
+ marker=$(einj_emit_kmsg_marker "$profile")
+
+ einj_write_value error_type "$EINJ_PROFILE_ERROR_TYPE"
+ einj_write_value flags 0
+ einj_write_value vendor_flags "$EINJ_PROFILE_VENDOR_FLAGS"
+ einj_write_value param1 "$EINJ_PROFILE_PARAM1"
+ einj_write_value param2 "$EINJ_PROFILE_PARAM2"
+ einj_write_value param3 "$EINJ_PROFILE_PARAM3"
+ einj_write_value param4 "$EINJ_PROFILE_PARAM4"
+ einj_write_value notrigger 0
+ einj_write_value error_inject 1
+
+ output=$(einj_wait_for_dmesg_after_marker_contains "$marker" "$EINJ_PROFILE_BANNER" 10) || {
+ printf '%s: %s not supported on this platform\n' "$0" "$profile"
+ return "$ksft_skip"
+ }
+
+ einj_assert_nvidia_cper_output "$profile" "$output"
+}
+
+einj_cleanup()
+{
+ local status=$1
+
+ if ! einj_restore_state; then
+ echo "$0: failed to restore EINJ state" >&2
+ [ "$status" -eq 0 ] && status=1
+ fi
+
+ exit "$status"
+}
+
+main()
+{
+ local profile
+ local passed=0
+
+ einj_require_root
+ einj_require_debugfs
+ einj_require_einj
+ einj_require_vendor_einj
+ einj_require_available_error_type
+ einj_save_state
+ trap 'einj_cleanup "$?"' EXIT
+
+ einj_require_bound_nvidia_device
+
+ for profile in $(einj_list_profiles); do
+ einj_run_profile "$profile" && passed=$((passed + 1)) || {
+ [ "$?" -eq "$ksft_skip" ] || exit 1
+ }
+ done
+
+ [ "$passed" -gt 0 ] || einj_skip "no NVIDIA EINJ profiles produced output"
+}
+
+main "$@"
diff --git a/tools/testing/selftests/firmware/ghes_nvidia_einj_profiles.sh b/tools/testing/selftests/firmware/ghes_nvidia_einj_profiles.sh
new file mode 100755
index 000000000000..b25461d2238c
--- /dev/null
+++ b/tools/testing/selftests/firmware/ghes_nvidia_einj_profiles.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+
+set -e
+
+# Run both architecture profiles on every platform; firmware silently ignores
+# selectors it does not support, so a timeout just means "not this platform".
+EINJ_PROFILE_NAMES="cmet_dump_status_grace cmet_dump_status_vera"
+
+einj_list_profiles()
+{
+ printf '%s\n' $EINJ_PROFILE_NAMES
+}
+
+einj_select_profile()
+{
+ local profile=$1
+
+ case "$profile" in
+ cmet_dump_status_grace)
+ # Grace CMET dump/status: informational sample, selector 3.
+ EINJ_PROFILE_ERROR_TYPE=0x80000010
+ EINJ_PROFILE_VENDOR_FLAGS=1
+ EINJ_PROFILE_PARAM1=3
+ EINJ_PROFILE_PARAM2=0
+ EINJ_PROFILE_PARAM3=0
+ EINJ_PROFILE_PARAM4=0
+ EINJ_PROFILE_BANNER='NVIDIA Grace CPER section'
+ ;;
+ cmet_dump_status_vera)
+ # Vera CMET-NULL dump/status: informational sample, selector 0.
+ EINJ_PROFILE_ERROR_TYPE=0x80000010
+ EINJ_PROFILE_VENDOR_FLAGS=1
+ EINJ_PROFILE_PARAM1=0
+ EINJ_PROFILE_PARAM2=0
+ EINJ_PROFILE_PARAM3=0
+ EINJ_PROFILE_PARAM4=0
+ EINJ_PROFILE_BANNER='NVIDIA Vera CPER section'
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+
+ return 0
+}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH v2 RESEND 1/4] ACPI: APEI: GHES: Refactor Grace decoder helpers
2026-07-24 12:20 ` [PATCH v2 RESEND 1/4] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
@ 2026-07-26 8:10 ` Shuai Xue
0 siblings, 0 replies; 13+ messages in thread
From: Shuai Xue @ 2026-07-26 8:10 UTC (permalink / raw)
To: Kai-Heng Feng, rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, linux-kernel,
linux-acpi, linux-kselftest, linux-hardening, csoto, mochs
On 7/24/26 8:20 PM, Kai-Heng Feng wrote:
> Split the Grace CPER processing into a separate decode step and a
> print step so the parser can be exercised by KUnit without a live
> ACPI device. Introduce ghes-nvidia.h to hold shared types that the
> Vera decoder added in the next commit will also reference.
>
> Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
> ---
> v2:
> - No change.
>
> drivers/acpi/apei/ghes-nvidia.c | 148 +++++++++++++++++++++-----------
> drivers/acpi/apei/ghes-nvidia.h | 38 ++++++++
> 2 files changed, 137 insertions(+), 49 deletions(-)
> create mode 100644 drivers/acpi/apei/ghes-nvidia.h
>
> diff --git a/drivers/acpi/apei/ghes-nvidia.c b/drivers/acpi/apei/ghes-nvidia.c
> index 597275d81de8..af445152def0 100644
> --- a/drivers/acpi/apei/ghes-nvidia.c
> +++ b/drivers/acpi/apei/ghes-nvidia.c
> @@ -12,7 +12,10 @@
> #include <linux/uuid.h>
> #include <acpi/ghes.h>
>
> -static const guid_t nvidia_sec_guid =
> +#include <kunit/visibility.h>
> +#include "ghes-nvidia.h"
> +
> +static const guid_t nvidia_grace_sec_guid =
> GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
> 0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
>
> @@ -25,10 +28,7 @@ struct cper_sec_nvidia {
> u8 number_regs;
> u8 reserved;
> __le64 instance_base;
> - struct {
> - __le64 addr;
> - __le64 val;
> - } regs[] __counted_by(number_regs);
> + struct nvidia_ghes_grace_reg regs[] __counted_by(number_regs);
> };
The Vera side treats the payload as a packed wire format and uses
get_unaligned_leXX(), but the Grace side still casts the CPER payload to
struct cper_sec_nvidia and directly dereferences __le16/__le64 fields.
Unless the GHES vendor payload is guaranteed to be naturally aligned here,
please make the Grace decoder follow the same rule as Vera and use
get_unaligned_le16()/get_unaligned_le64() for all multi-byte fields,
including the register pairs.
>
> struct nvidia_ghes_private {
> @@ -36,73 +36,123 @@ struct nvidia_ghes_private {
> struct device *dev;
> };
>
> -static void nvidia_ghes_print_error(struct device *dev,
> - const struct cper_sec_nvidia *nvidia_err,
> - size_t error_data_length, bool fatal)
> +VISIBLE_IF_KUNIT
> +int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
> + size_t len,
> + struct nvidia_ghes_decoded *decoded)
> {
> - const char *level = fatal ? KERN_ERR : KERN_INFO;
> + const struct cper_sec_nvidia *nvidia_err = buf;
> size_t min_size;
>
> - dev_printk(level, dev, "signature: %.16s\n", nvidia_err->signature);
> - dev_printk(level, dev, "error_type: %u\n", le16_to_cpu(nvidia_err->error_type));
> - dev_printk(level, dev, "error_instance: %u\n", le16_to_cpu(nvidia_err->error_instance));
> - dev_printk(level, dev, "severity: %u\n", nvidia_err->severity);
> - dev_printk(level, dev, "socket: %u\n", nvidia_err->socket);
> - dev_printk(level, dev, "number_regs: %u\n", nvidia_err->number_regs);
> - dev_printk(level, dev, "instance_base: 0x%016llx\n",
> - le64_to_cpu(nvidia_err->instance_base));
> -
> - if (nvidia_err->number_regs == 0)
> - return;
> -
> - /*
> - * Validate that all registers fit within error_data_length.
> - * Each register pair is two little-endian u64s.
> - */
> + if (!buf || !decoded)
> + return -EINVAL;
> + if (len < sizeof(*nvidia_err)) {
> + if (dev)
> + dev_err(dev, "Section too small (%zu < %zu)\n",
> + len, sizeof(*nvidia_err));
> + return -ENODATA;
> + }
> +
> min_size = struct_size(nvidia_err, regs, nvidia_err->number_regs);
> - if (error_data_length < min_size) {
> - dev_err(dev, "Invalid number_regs %u (section size %zu, need %zu)\n",
> - nvidia_err->number_regs, error_data_length, min_size);
> - return;
> + if (len < min_size) {
> + if (dev)
> + dev_err(dev,
> + "Invalid number_regs %u (section size %zu, need %zu)\n",
> + nvidia_err->number_regs, len, min_size);
> + return -ENODATA;
> }
>
> - for (int i = 0; i < nvidia_err->number_regs; i++)
> + memset(decoded, 0, sizeof(*decoded));
> + decoded->format = NVIDIA_GHES_FORMAT_GRACE;
> + memcpy(decoded->signature, nvidia_err->signature, sizeof(nvidia_err->signature));
> + decoded->signature[sizeof(nvidia_err->signature)] = '\0';
> + decoded->error_type = le16_to_cpu(nvidia_err->error_type);
> + decoded->error_instance = le16_to_cpu(nvidia_err->error_instance);
> + decoded->severity = nvidia_err->severity;
> + decoded->socket = nvidia_err->socket;
> + decoded->number_regs = nvidia_err->number_regs;
> + decoded->instance_base = le64_to_cpu(nvidia_err->instance_base);
> + if (nvidia_err->number_regs)
> + decoded->grace_regs = nvidia_err->regs;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_grace);
> +
> +VISIBLE_IF_KUNIT
> +int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
> + unsigned int index, u64 *addr, u64 *val)
> +{
> + const struct nvidia_ghes_grace_reg *regs;
> +
> + if (!decoded || decoded->format != NVIDIA_GHES_FORMAT_GRACE || !addr || !val)
> + return -EINVAL;
> + if (index >= decoded->number_regs)
> + return -ERANGE;
> +
> + regs = decoded->grace_regs;
This is fine for objects produced by nvidia_ghes_decode_grace(), but now
that the helper is visible to KUnit, please either document that contract
or reject number_regs != 0 && !decoded->grace_regs here.
Thanks.
Shuai
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 RESEND 2/4] ACPI: APEI: GHES: Add NVIDIA Vera decoder
2026-07-24 12:20 ` [PATCH v2 RESEND 2/4] ACPI: APEI: GHES: Add NVIDIA Vera decoder Kai-Heng Feng
@ 2026-07-26 8:21 ` Shuai Xue
0 siblings, 0 replies; 13+ messages in thread
From: Shuai Xue @ 2026-07-26 8:21 UTC (permalink / raw)
To: Kai-Heng Feng, rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, linux-kernel,
linux-acpi, linux-kselftest, linux-hardening, csoto, mochs
On 7/24/26 8:20 PM, Kai-Heng Feng wrote:
> Vera is NVIDIA's next-generation server SoC. Its CPER section uses a
> different GUID and a different binary layout from Grace, so it needs
> its own decoder. Without this, firmware-reported hardware errors on
> Vera platforms are received but not decoded.
>
> Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
> ---
> drivers/acpi/apei/ghes-nvidia.c | 368 ++++++++++++++++++++++++++++++--
> drivers/acpi/apei/ghes-nvidia.h | 29 ++-
> 2 files changed, 382 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/acpi/apei/ghes-nvidia.c b/drivers/acpi/apei/ghes-nvidia.c
> index af445152def0..c74c155dd2ba 100644
> --- a/drivers/acpi/apei/ghes-nvidia.c
> +++ b/drivers/acpi/apei/ghes-nvidia.c
> @@ -7,18 +7,27 @@
>
> #include <linux/acpi.h>
> #include <linux/module.h>
> +#include <linux/overflow.h>
> #include <linux/platform_device.h>
> +#include <linux/slab.h>
> #include <linux/types.h>
> +#include <linux/unaligned.h>
> #include <linux/uuid.h>
> +#include <kunit/visibility.h>
> #include <acpi/ghes.h>
>
> -#include <kunit/visibility.h>
> #include "ghes-nvidia.h"
>
> +#define NVIDIA_GHES_VERA_VERSION 1
> +
> static const guid_t nvidia_grace_sec_guid =
> GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
> 0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
>
> +static const guid_t nvidia_vera_sec_guid =
> + GUID_INIT(0x9068e568, 0x6ca0, 0x11f0,
> + 0xae, 0xaf, 0x15, 0x93, 0x43, 0x59, 0x1e, 0xac);
> +
> struct cper_sec_nvidia {
> char signature[16];
> __le16 error_type;
> @@ -31,11 +40,51 @@ struct cper_sec_nvidia {
> struct nvidia_ghes_grace_reg regs[] __counted_by(number_regs);
> };
>
> +struct cper_sec_nvidia_vera_event {
> + u8 version;
> + u8 event_context_count;
> + u8 source_device_type;
> + u8 reserved;
> + __le16 event_type;
> + __le16 event_sub_type;
> + __le64 event_link_id;
> + char source_module_signature[16];
> +} __packed;
> +
> +struct cper_sec_nvidia_vera_cpu_info {
> + __le16 info_version;
> + u8 info_size;
> + u8 socket_number;
> + __le32 architecture;
> + u8 chip_serial_number[16];
> + __le64 instance_base;
> +} __packed;
> +
> +struct cper_sec_nvidia_vera_context {
> + __le32 context_size;
> + __le16 context_version;
> + __le16 reserved;
> + __le16 data_format_type;
> + __le16 data_format_version;
> + __le32 data_size;
> +} __packed;
> +
> struct nvidia_ghes_private {
> struct notifier_block nb;
> struct device *dev;
> };
>
> +VISIBLE_IF_KUNIT
> +enum nvidia_ghes_format nvidia_ghes_format_from_guid(const guid_t *guid)
> +{
> + if (guid_equal(guid, &nvidia_grace_sec_guid))
> + return NVIDIA_GHES_FORMAT_GRACE;
> + if (guid_equal(guid, &nvidia_vera_sec_guid))
> + return NVIDIA_GHES_FORMAT_VERA;
> + return NVIDIA_GHES_FORMAT_UNKNOWN;
> +}
> +EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_format_from_guid);
> +
> VISIBLE_IF_KUNIT
> int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
> size_t len,
> @@ -81,7 +130,7 @@ EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_grace);
>
> VISIBLE_IF_KUNIT
> int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
> - unsigned int index, u64 *addr, u64 *val)
> + unsigned int index, u64 *addr, u64 *val)
> {
> const struct nvidia_ghes_grace_reg *regs;
>
> @@ -98,6 +147,220 @@ int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
> }
> EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_grace_reg_pair);
>
> +static int nvidia_ghes_vera_validate_context_data(u16 data_format_type,
> + u32 data_size)
> +{
> + switch (data_format_type) {
> + case 0:
> + return 0;
> + case 1:
> + return data_size % 16 ? -EINVAL : 0;
> + case 2:
> + case 3:
> + return data_size % 8 ? -EINVAL : 0;
> + case 4:
> + return data_size % 4 ? -EINVAL : 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> +}
> +
> +VISIBLE_IF_KUNIT
> +int nvidia_ghes_decode_vera(struct device *dev, const void *buf,
> + size_t len,
> + struct nvidia_ghes_decoded *decoded)
> +{
> + const struct cper_sec_nvidia_vera_event *event = buf;
> + const struct cper_sec_nvidia_vera_cpu_info *cpu_info;
> + const struct cper_sec_nvidia_vera_context *context;
> + const u8 *bytes = buf;
> + size_t data_end_advance;
> + size_t advance;
> + size_t offset;
> + int ret;
> +
> + if (!buf || !decoded)
> + return -EINVAL;
> + if (len < sizeof(*event)) {
> + if (dev)
> + dev_err_ratelimited(dev, "Vera event header truncated (%zu < %zu)\n",
> + len, sizeof(*event));
> + return -ENODATA;
> + }
> + if (event->version != NVIDIA_GHES_VERA_VERSION)
> + return -EOPNOTSUPP;
> + if (event->source_device_type != 0)
> + return -EOPNOTSUPP;
> +
> + offset = sizeof(*event);
> + if (len - offset < sizeof(*cpu_info)) {
> + if (dev)
> + dev_err_ratelimited(dev, "Vera CPU info truncated (%zu < %zu)\n",
> + len - offset, sizeof(*cpu_info));
> + return -ENODATA;
> + }
> +
> + cpu_info = (const void *)(bytes + offset);
> + if (cpu_info->info_size < sizeof(*cpu_info)) {
> + if (dev)
> + dev_err_ratelimited(dev, "Vera CPU info size %u smaller than header %zu\n",
> + cpu_info->info_size, sizeof(*cpu_info));
> + return -EINVAL;
> + }
> + if (len - offset < cpu_info->info_size) {
> + if (dev)
> + dev_err_ratelimited(dev, "Vera CPU info extends past section (%u > %zu)\n",
> + cpu_info->info_size, len - offset);
> + return -ENODATA;
> + }
> +
> + offset += cpu_info->info_size;
> + if (event->event_context_count > NVIDIA_GHES_MAX_CONTEXTS) {
> + if (dev)
> + dev_err_ratelimited(dev, "Vera context count %u exceeds maximum %u\n",
> + event->event_context_count,
> + NVIDIA_GHES_MAX_CONTEXTS);
> + return -E2BIG;
> + }
> +
> + memset(decoded, 0, sizeof(*decoded));
> + decoded->format = NVIDIA_GHES_FORMAT_VERA;
> + memcpy(decoded->signature, event->source_module_signature,
> + sizeof(event->source_module_signature));
> + decoded->signature[sizeof(event->source_module_signature)] = '\0';
> + decoded->event_context_count = event->event_context_count;
> + decoded->source_device_type = event->source_device_type;
> + decoded->event_type = get_unaligned_le16(&event->event_type);
> + decoded->event_sub_type = get_unaligned_le16(&event->event_sub_type);
> + decoded->event_link_id = get_unaligned_le64(&event->event_link_id);
> + decoded->socket = cpu_info->socket_number;
> + decoded->architecture = get_unaligned_le32(&cpu_info->architecture);
> + memcpy(decoded->chip_serial_number, cpu_info->chip_serial_number,
> + sizeof(cpu_info->chip_serial_number));
> + decoded->instance_base = get_unaligned_le64(&cpu_info->instance_base);
> +
> + for (int i = 0; i < event->event_context_count; i++) {
> + struct nvidia_ghes_vera_context *decoded_context = &decoded->contexts[i];
> + u32 context_size;
> + u32 data_size;
> + u16 data_format_type;
> +
> + if (len - offset < sizeof(*context)) {
> + if (dev)
> + dev_err_ratelimited(dev, "Vera context[%d] header truncated (%zu < %zu)\n",
> + i, len - offset, sizeof(*context));
> + return -ENODATA;
> + }
> +
> + context = (const void *)(bytes + offset);
> + context_size = get_unaligned_le32(&context->context_size);
> + data_format_type = get_unaligned_le16(&context->data_format_type);
> + data_size = get_unaligned_le32(&context->data_size);
> +
> + if (context_size < sizeof(*context)) {
> + if (dev)
> + dev_err_ratelimited(dev,
> + "Vera context[%d] size %u smaller than header %zu\n",
> + i, context_size, sizeof(*context));
> + return -EINVAL;
> + }
> + if (data_format_type > 4) {
> + if (dev)
> + dev_dbg(dev,
> + "Vera context[%d] unsupported data format %u\n",
> + i, data_format_type);
> + return -EOPNOTSUPP;
> + }
> + if (check_add_overflow((size_t)data_size, sizeof(*context),
> + &data_end_advance)) {
> + if (dev)
> + dev_err_ratelimited(dev,
> + "Vera context[%d] data_size %u overflows section accounting\n",
> + i, data_size);
> + return -EOVERFLOW;
> + }
> +
> + if (data_end_advance > len - offset) {
> + if (dev)
> + dev_err_ratelimited(dev,
> + "Vera context[%d] data extends past section (%zu > %zu)\n",
> + i, data_end_advance, len - offset);
> + return -ENODATA;
> + }
> +
> + /*
> + * Some Vera payloads use only the header size here and
> + * place the format-specific payload immediately after it.
> + */
> + if (context_size == sizeof(*context))
> + advance = data_end_advance;
> + else if (data_size <= context_size - sizeof(*context))
> + advance = context_size;
> + else {
> + if (dev)
> + dev_err_ratelimited(dev,
> + "Vera context[%d] data_size %u exceeds context_size %u\n",
> + i, data_size, context_size);
> + return -EINVAL;
> + }
This is the part that I would like to understand better before relying on
this parser in production.
The code accepts two different encodings:
- context_size == sizeof(*context), with the payload length coming from
data_size, and
- context_size covering both the context header and the payload.
If both forms are part of the Vera CPER ABI, please make that explicit in
the comment, preferably with the terminology used by the firmware
specification. Otherwise this looks like the parser is guessing around
ambiguous firmware data.
Please also add a KUnit case with more than one context, ideally covering
the second form where context_size is larger than the header and the next
context is found via offset += context_size. That would test the most
important offset accounting rule here.
> +
> + if (advance > len - offset) {
> + if (dev)
> + dev_err_ratelimited(dev,
> + "Vera context[%d] advance %zu extends past section (%zu)\n",
> + i, advance, len - offset);
> + return -ENODATA;
> + }
> +
> + ret = nvidia_ghes_vera_validate_context_data(data_format_type, data_size);
> + if (ret) {
> + if (dev)
> + dev_err_ratelimited(dev,
> + "Vera context[%d] format %u rejected data_size %u (ret=%d)\n",
> + i, data_format_type, data_size, ret);
> + return ret;
> + }
> +
> + decoded_context->context_size = context_size;
> + decoded_context->context_version =
> + get_unaligned_le16(&context->context_version);
> + decoded_context->data_format_type = data_format_type;
> + decoded_context->data_format_version =
> + get_unaligned_le16(&context->data_format_version);
> + decoded_context->data_size = data_size;
> + decoded_context->data = bytes + offset + sizeof(*context);
> + offset += advance;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_vera);
> +
> +VISIBLE_IF_KUNIT
> +int nvidia_ghes_vera_context_entry_count(const struct nvidia_ghes_vera_context *ctx)
> +{
> + if (!ctx)
> + return -EINVAL;
> + if (ctx->data_size > INT_MAX)
> + return -EOVERFLOW;
> +
> + switch (ctx->data_format_type) {
> + case 0:
> + return 0;
> + case 1:
> + return ctx->data_size / 16;
> + case 2:
> + return ctx->data_size / 8;
> + case 3:
> + return ctx->data_size / 8;
> + case 4:
> + return ctx->data_size / 4;
> + default:
> + return -EINVAL;
> + }
> +}
> +EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_entry_count);
> +
> static void nvidia_ghes_print_grace(struct device *dev,
> const struct nvidia_ghes_decoded *decoded,
> bool fatal)
> @@ -111,7 +374,8 @@ static void nvidia_ghes_print_grace(struct device *dev,
> dev_printk(level, dev, "severity: %u\n", decoded->severity);
> dev_printk(level, dev, "socket: %u\n", decoded->socket);
> dev_printk(level, dev, "number_regs: %u\n", decoded->number_regs);
> - dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
> + dev_printk(level, dev, "instance_base: 0x%016llx\n",
> + decoded->instance_base);
>
> for (int i = 0; i < decoded->number_regs; i++) {
> if (nvidia_ghes_grace_reg_pair(decoded, i, &addr, &val))
> @@ -121,12 +385,52 @@ static void nvidia_ghes_print_grace(struct device *dev,
> }
> }
>
> +static void nvidia_ghes_print_vera(struct device *dev,
> + const struct nvidia_ghes_decoded *decoded,
> + bool fatal, unsigned long ghes_severity)
> +{
> + const char *level = fatal ? KERN_ERR : KERN_INFO;
> +
> + dev_printk(level, dev, "signature: %s\n", decoded->signature);
> + dev_printk(level, dev, "event_type: %u\n", decoded->event_type);
> + dev_printk(level, dev, "event_sub_type: %u\n", decoded->event_sub_type);
> + dev_printk(level, dev, "ghes_severity: %lu\n", ghes_severity);
> + dev_printk(level, dev, "event_link_id: 0x%016llx\n",
> + decoded->event_link_id);
> + dev_printk(level, dev, "socket: %u\n", decoded->socket);
> + dev_printk(level, dev, "architecture: 0x%x\n", decoded->architecture);
> + dev_printk(level, dev, "chip_serial_number: %*phN\n",
> + (int)sizeof(decoded->chip_serial_number),
> + decoded->chip_serial_number);
> + dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
> + dev_printk(level, dev, "event_context_count: %u\n", decoded->event_context_count);
> +
> + for (int i = 0; i < decoded->event_context_count; i++) {
> + const struct nvidia_ghes_vera_context *ctx = &decoded->contexts[i];
> + int entries = nvidia_ghes_vera_context_entry_count(ctx);
> +
> + dev_printk(level, dev,
> + "context[%d]: version=%u format=%u format_version=%u context_size=%u data_size=%u\n",
> + i, ctx->context_version, ctx->data_format_type,
> + ctx->data_format_version, ctx->context_size, ctx->data_size);
> + if (ctx->data_format_type == 0 && ctx->data_size > 0) {
> + int prefix_len = ctx->data_size > 16 ? 16 : ctx->data_size;
> +
> + dev_printk(level, dev, "context[%d]_opaque_prefix: %*phN\n",
> + i, prefix_len, ctx->data);
> + } else if (entries >= 0) {
> + dev_printk(level, dev, "context[%d]_entries: %d\n", i, entries);
> + }
> + }
> +}
> +
> static int nvidia_ghes_notify(struct notifier_block *nb,
> unsigned long event, void *data)
> {
> struct acpi_hest_generic_data *gdata = data;
> - struct nvidia_ghes_decoded decoded;
> + struct nvidia_ghes_decoded *decoded;
> struct nvidia_ghes_private *priv;
> + enum nvidia_ghes_format format;
> const void *payload;
> guid_t sec_guid;
> u32 len;
> @@ -134,26 +438,64 @@ static int nvidia_ghes_notify(struct notifier_block *nb,
> bool fatal;
>
> import_guid(&sec_guid, gdata->section_type);
> - if (!guid_equal(&sec_guid, &nvidia_grace_sec_guid))
> + format = nvidia_ghes_format_from_guid(&sec_guid);
> + if (format == NVIDIA_GHES_FORMAT_UNKNOWN)
> return NOTIFY_DONE;
>
> priv = container_of(nb, struct nvidia_ghes_private, nb);
> len = acpi_hest_get_error_length(gdata);
> +
> payload = acpi_hest_get_payload(gdata);
> fatal = event >= GHES_SEV_RECOVERABLE;
> + decoded = kzalloc_obj(*decoded);
> + if (!decoded) {
> + dev_err_ratelimited(priv->dev,
> + "Failed to allocate NVIDIA CPER decode buffer\n");
> + return NOTIFY_OK;
> + }
This runs from the GHES vendor workqueue, so sleeping allocation is not
the issue. But this is still the error reporting path, and struct
nvidia_ghes_decoded looks small enough to avoid a per-event allocation.
Please consider keeping it on the stack, or explain why heap allocation
is needed here.
> +
> + switch (format) {
> + case NVIDIA_GHES_FORMAT_GRACE:
> + ret = nvidia_ghes_decode_grace(priv->dev, payload, len, decoded);
> + break;
> + case NVIDIA_GHES_FORMAT_VERA:
> + ret = nvidia_ghes_decode_vera(priv->dev, payload, len, decoded);
> + break;
> + default:
> + ret = -EOPNOTSUPP;
> + break;
> + }
>
> - ret = nvidia_ghes_decode_grace(priv->dev, payload, len, &decoded);
> if (ret) {
> - dev_err(priv->dev,
> - "Malformed NVIDIA CPER section, error_data_length: %u, ret: %d\n",
> - len, ret);
> - return NOTIFY_OK;
> + if (ret == -EOPNOTSUPP && format == NVIDIA_GHES_FORMAT_VERA)
> + dev_info(priv->dev,
> + "Unsupported NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
> + len, ret);
> + else if (format == NVIDIA_GHES_FORMAT_GRACE)
> + dev_err(priv->dev,
> + "Malformed NVIDIA Grace CPER section, error_data_length: %u, ret: %d\n",
> + len, ret);
> + else
> + dev_err(priv->dev,
> + "Malformed NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
> + len, ret);
> + goto out;
Most of the detailed parser errors are already rate limited inside the
decoder, but this outer message is not. If firmware repeatedly reports an
unsupported or malformed vendor section, this can still generate one line
per event.
Please make the outer message rate limited as well, or avoid the second
message when the decoder has already emitted a detailed diagnostic.
Thanks.
Shuai
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 RESEND 3/4] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage
2026-07-24 12:20 ` [PATCH v2 RESEND 3/4] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage Kai-Heng Feng
@ 2026-07-26 8:30 ` Shuai Xue
0 siblings, 0 replies; 13+ messages in thread
From: Shuai Xue @ 2026-07-26 8:30 UTC (permalink / raw)
To: Kai-Heng Feng, rafael, shuah, kees
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, linux-kernel,
linux-acpi, linux-kselftest, linux-hardening, csoto, mochs
On 7/24/26 8:20 PM, Kai-Heng Feng wrote:
> Verify the Grace and Vera decoders against real binary records
> captured from firmware before deploying to production hardware.
> Tests cover both the happy path and the rejection of malformed
> inputs across all constraint categories.
>
> Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
> ---
> v2:
> - Add !KUNIT_ALL_TESTS for tristate.
>
> drivers/acpi/apei/Kconfig | 17 +-
> drivers/acpi/apei/Makefile | 1 +
> drivers/acpi/apei/ghes-nvidia-test-fixtures.h | 233 ++++++
> drivers/acpi/apei/ghes-nvidia-test.c | 704 ++++++++++++++++++
> 4 files changed, 953 insertions(+), 2 deletions(-)
> create mode 100644 drivers/acpi/apei/ghes-nvidia-test-fixtures.h
> create mode 100644 drivers/acpi/apei/ghes-nvidia-test.c
>
> diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
> index 428458c623f0..22d88425f36c 100644
> --- a/drivers/acpi/apei/Kconfig
> +++ b/drivers/acpi/apei/Kconfig
> @@ -80,14 +80,27 @@ config ACPI_APEI_GHES_NVIDIA
> help
> Support for decoding NVIDIA-specific CPER sections delivered via
> the APEI GHES vendor record notifier chain. Registers a handler
> - for the NVIDIA section GUID and logs error signatures, severity,
> - socket, and diagnostic register address-value pairs.
> + for the NVIDIA Grace and Vera section GUIDs and logs the decoded
> + NVIDIA error records.
>
> Enable on NVIDIA server platforms (e.g. DGX, HGX) that expose
> ACPI device NVDA2012 in their firmware tables.
>
> If unsure, say N.
>
> +config ACPI_APEI_GHES_NVIDIA_KUNIT_TEST
> + tristate "NVIDIA GHES vendor record handler KUnit tests" if !KUNIT_ALL_TESTS
> + depends on KUNIT && ACPI_APEI_GHES_NVIDIA
> + default KUNIT_ALL_TESTS
> + help
> + KUnit tests for NVIDIA GHES vendor CPER parser helpers.
> + They cover Grace helper decoding, Vera payload parsing, and
> + section routing.
> + They are enabled only for KUnit test builds.
> + They are not intended for production firmware paths.
> +
> + If unsure, say N.
> +
> config ACPI_APEI_ERST_DEBUG
> tristate "APEI Error Record Serialization Table (ERST) Debug Support"
> depends on ACPI_APEI
> diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
> index 66588d6be56f..210a57242044 100644
> --- a/drivers/acpi/apei/Makefile
> +++ b/drivers/acpi/apei/Makefile
> @@ -11,5 +11,6 @@ einj-y := einj-core.o
> einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o
> obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
> obj-$(CONFIG_ACPI_APEI_GHES_NVIDIA) += ghes-nvidia.o
> +obj-$(CONFIG_ACPI_APEI_GHES_NVIDIA_KUNIT_TEST) += ghes-nvidia-test.o
>
> apei-y := apei-base.o hest.o erst.o bert.o
> diff --git a/drivers/acpi/apei/ghes-nvidia-test-fixtures.h b/drivers/acpi/apei/ghes-nvidia-test-fixtures.h
> new file mode 100644
> index 000000000000..705c879c785e
> --- /dev/null
> +++ b/drivers/acpi/apei/ghes-nvidia-test-fixtures.h
> @@ -0,0 +1,233 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef GHES_NVIDIA_TEST_FIXTURES_H
> +#define GHES_NVIDIA_TEST_FIXTURES_H
> +
> +#include <linux/types.h>
> +
> +/*
> + * Source: cper-tools/test_data/vera_msft.json
> + * cper-tools commit: bf18f75406422e73e11b8b1ff80e3c9d9cfb032a
> + * Boundary: CPER.Oem.Nvidia.Unknown.Data.
> + * Each payload equals the Vera GUID section body located in DiagnosticData.
> + */
> +
> +/* Redfish Id 10, signature L1 RESET, real sample. */
> +/* Vera section index 0, offset 200, length 80. */
> +/* sha256 fc11639e506eb8d79e7ff16a0556463ac4b1304bface935c0cd13efc0b3e317f. */
> +static const u8 vera_l1_reset_id10[] = {
> + 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x31, 0x20, 0x52, 0x45, 0x53, 0x45, 0x54,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
> + 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
> + 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
> + 0x15, 0xfe, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +};
> +
> +/* Redfish Id 40, signature CRASHDUMP-ID, real sample. */
> +/* Vera section index 0, offset 200, length 304. */
> +/* sha256 3ba93201740e6bd868b48ec40c3d52b235c1ebc59528fc81974079c21bf2a6a2. */
> +static const u8 vera_crashdump_id_id40[] = {
> + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x41, 0x53, 0x48, 0x44, 0x55, 0x4d,
> + 0x50, 0x2d, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
> + 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
> + 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x01, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x83, 0x72, 0x65, 0x6c, 0x5f, 0x37, 0x33, 0x2e, 0x37,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x64, 0x69, 0x72, 0x74,
> + 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
> + 0x44, 0x24, 0x4e, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00,
> + 0x14, 0xfe, 0x00, 0x00, 0x07, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x04, 0x07, 0x03, 0x03, 0x14, 0xfe, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x15, 0xfe, 0x00, 0x00,
> + 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x04, 0x00,
> + 0x14, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x14, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x18, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00,
> + 0x40, 0x60, 0x30, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0a, 0x04, 0x00,
> + 0x14, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x20, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x24, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00,
> + 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x0a, 0x04, 0x00,
> + 0x14, 0xfe, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x2c, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00,
> +};
> +
> +/* Redfish Id 41, signature CRASHDUMP-S1, real sample. */
> +/* Vera section index 0, offset 200, length 2008. */
> +/* sha256 54db30564d3324240f088a1533c113407f1d81bcd652ef0d27d958c490aee5ce. */
> +static const u8 vera_crashdump_s1_id41[] = {
> + 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x41, 0x53, 0x48, 0x44, 0x55, 0x4d,
> + 0x50, 0x2d, 0x53, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
> + 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
> + 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x53, 0x56, 0x7f, 0xa1,
> + 0xd1, 0xd7, 0x7f, 0xe5, 0xaa, 0x34, 0xde, 0x7d, 0xe9, 0x3e, 0xc4, 0xd4,
> + 0x92, 0xe8, 0x10, 0xa1, 0x4c, 0xfc, 0xf9, 0x2e, 0x02, 0x87, 0xc5, 0x6e,
> + 0x44, 0xfb, 0xb5, 0x59, 0xac, 0xfa, 0xa8, 0x15, 0x75, 0x46, 0x2d, 0x67,
> + 0x84, 0xe0, 0x78, 0x14, 0xe4, 0x18, 0x8e, 0x30, 0x46, 0xff, 0xbe, 0x5e,
> + 0x15, 0x3e, 0x61, 0xf4, 0xcc, 0x3d, 0x2e, 0xdf, 0x1b, 0xf9, 0x1f, 0x1c,
> + 0xa3, 0xf8, 0x35, 0x34, 0xe1, 0x4c, 0xb7, 0xd0, 0xcc, 0xda, 0xf1, 0xbc,
> + 0x02, 0x8d, 0xc2, 0x6b, 0x5d, 0x56, 0xd4, 0x48, 0x89, 0x7f, 0xb2, 0x64,
> + 0xda, 0xa0, 0xf4, 0xed, 0xef, 0xf7, 0x5f, 0x29, 0x38, 0x51, 0x59, 0x35,
> + 0x1f, 0x52, 0x63, 0xa8, 0x45, 0x02, 0x7a, 0x24, 0xf6, 0x26, 0x83, 0x29,
> + 0x65, 0x01, 0x77, 0x0e, 0xb2, 0x5e, 0xac, 0xf2, 0xd4, 0xc0, 0x09, 0x94,
> + 0x5c, 0x4d, 0x55, 0xaf, 0xcf, 0xb7, 0x8c, 0xad, 0x47, 0xcf, 0x02, 0x2c,
> + 0x8c, 0x0f, 0x28, 0xf1, 0x4c, 0x15, 0x66, 0xb0, 0xb4, 0x7d, 0xd1, 0xf6,
> + 0x90, 0xc8, 0x5e, 0x64, 0x16, 0x9f, 0x5f, 0x3f, 0x50, 0xc9, 0x62, 0x91,
> + 0x87, 0xd8, 0xd7, 0xe9, 0x9f, 0x7b, 0xcd, 0xc9, 0xfe, 0xa1, 0xd3, 0xf2,
> + 0xc2, 0x95, 0xc7, 0x37, 0x34, 0x6c, 0xc4, 0xc0, 0x53, 0x56, 0x01, 0x2c,
> + 0x96, 0x63, 0xdb, 0x2c, 0xea, 0x09, 0xa0, 0xaa, 0xe5, 0xe2, 0x48, 0x18,
> + 0x3c, 0x68, 0xf9, 0xa0, 0x7f, 0x98, 0x5a, 0xe3, 0xe8, 0x8e, 0x83, 0x71,
> + 0x51, 0x79, 0xc3, 0x14, 0xf1, 0xf4, 0xac, 0xd5, 0xd9, 0x8c, 0xab, 0x33,
> + 0x4e, 0x9f, 0xc2, 0xb7, 0xed, 0x6f, 0x78, 0x81, 0x77, 0x34, 0xb8, 0xea,
> + 0xb0, 0x71, 0xa1, 0x4d, 0x08, 0x40, 0xad, 0x4c, 0x95, 0x6a, 0x21, 0x96,
> + 0x07, 0x12, 0x62, 0xed, 0x42, 0xa9, 0xe4, 0xa8, 0x88, 0xc7, 0x81, 0xff,
> + 0x88, 0x07, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x83, 0xbc, 0x15, 0xd9, 0x0b, 0x6b, 0x3b, 0x3b, 0xff, 0x77, 0xb2, 0x7c,
> + 0x0c, 0xdd, 0xfb, 0x00, 0x7e, 0x0a, 0x88, 0x47, 0x8a, 0xf2, 0xf8, 0xc5,
> + 0x07, 0x51, 0x0e, 0xd7, 0xcb, 0x78, 0xea, 0x89, 0x37, 0x2a, 0x44, 0x0d,
> + 0x74, 0x4f, 0x9e, 0x6d, 0x2c, 0xf9, 0x53, 0xed, 0x38, 0x34, 0xca, 0x38,
> + 0x7d, 0x96, 0xfe, 0x9a, 0x9b, 0x23, 0x3b, 0x17, 0x17, 0x69, 0xe6, 0x6c,
> + 0x0e, 0xe6, 0x71, 0xf5, 0x59, 0xf6, 0xc4, 0x82, 0xd6, 0x83, 0xb1, 0x76,
> + 0x41, 0x22, 0x53, 0x72, 0x9f, 0x85, 0x0d, 0xdc, 0x67, 0x22, 0x35, 0xd3,
> + 0xe3, 0xaf, 0x10, 0x50, 0xc8, 0xe1, 0xb0, 0xea, 0xc9, 0x1a, 0x3e, 0x02,
> + 0x36, 0xea, 0x94, 0xb1, 0x27, 0xfc, 0xc9, 0x61, 0xcb, 0x6f, 0xb0, 0x50,
> + 0xe1, 0x85, 0x1c, 0x2d, 0x5f, 0xb5, 0x63, 0x38, 0x69, 0x02, 0xb3, 0x7d,
> + 0xb3, 0xb5, 0xe0, 0xfe, 0x5a, 0xf1, 0x0d, 0x83, 0xd9, 0x51, 0xcc, 0xa2,
> + 0xb5, 0x41, 0x3b, 0xd1, 0xf5, 0x58, 0xe8, 0x54, 0x49, 0x78, 0xc1, 0x8b,
> + 0x70, 0xf1, 0x94, 0x45, 0x18, 0x4b, 0x19, 0x58, 0xe6, 0x0a, 0x08, 0x27,
> + 0xe7, 0x95, 0xd6, 0x82, 0xb7, 0x8c, 0xae, 0xae, 0x58, 0x33, 0xc2, 0x02,
> + 0xf8, 0xfb, 0x8a, 0x73, 0xe4, 0x0b, 0x75, 0x9b, 0x00, 0x76, 0x14, 0x31,
> + 0x5e, 0xf5, 0xf0, 0x8b, 0xb7, 0x6b, 0xaf, 0xdd, 0x8b, 0x52, 0xc9, 0x79,
> + 0xe7, 0xd3, 0x17, 0x90, 0xa4, 0xb0, 0xa0, 0x97, 0xcf, 0xba, 0x37, 0x1d,
> + 0xe0, 0xbf, 0x31, 0xff, 0xe9, 0x3a, 0x50, 0x5b, 0xf8, 0x94, 0xd0, 0x33,
> + 0xf9, 0x2d, 0x2e, 0x54, 0xfa, 0x11, 0x63, 0x06, 0xf2, 0x32, 0xb9, 0x7b,
> + 0xd9, 0xc6, 0x12, 0x60, 0xa6, 0x16, 0xac, 0xc8, 0x02, 0xf7, 0xd6, 0xd5,
> + 0xb6, 0x2e, 0xf0, 0xb3, 0x05, 0x39, 0x47, 0xd4, 0xa9, 0xed, 0x1a, 0xca,
> + 0x4c, 0x4f, 0x83, 0x2d, 0xcc, 0x5c, 0xa7, 0x02, 0x01, 0x84, 0xc9, 0x07,
> + 0xa5, 0x6a, 0xf7, 0xbe, 0x3d, 0xc2, 0x09, 0xe8, 0xc8, 0xf8, 0x4b, 0x71,
> + 0xa8, 0x79, 0x6c, 0x68, 0xda, 0x35, 0xe1, 0xd2, 0x9d, 0xa6, 0xb3, 0x70,
> + 0xf6, 0xcb, 0x66, 0x41, 0xde, 0x86, 0x11, 0x2a, 0xd0, 0xa1, 0x84, 0x99,
> + 0x5b, 0xdf, 0x74, 0x26, 0x18, 0xba, 0x6f, 0x3e, 0x46, 0xef, 0xe7, 0xd2,
> + 0xfd, 0xe5, 0xa8, 0xba, 0xff, 0xe4, 0xe0, 0x50, 0x9f, 0x23, 0xe5, 0x03,
> + 0x81, 0xc2, 0xab, 0x74, 0xd8, 0xc4, 0xe9, 0xd5, 0xf6, 0x7f, 0x86, 0x6b,
> + 0x06, 0xfc, 0x62, 0x0f, 0xeb, 0xa3, 0xc4, 0xa0, 0xad, 0x70, 0xad, 0x9b,
> + 0xdf, 0xd1, 0x75, 0x28, 0xdc, 0xf2, 0x24, 0x87, 0x6e, 0x85, 0x67, 0xfb,
> + 0xbb, 0x38, 0x86, 0xd8, 0x77, 0x15, 0x35, 0x41, 0x53, 0x69, 0x71, 0xd9,
> + 0x33, 0x00, 0x2c, 0x56, 0x45, 0xfd, 0xfc, 0xea, 0xf9, 0x8f, 0xd7, 0x04,
> + 0x6a, 0x8d, 0xf5, 0x4b, 0xd1, 0x5a, 0x66, 0x7e, 0xe0, 0xeb, 0x06, 0x80,
> + 0xc2, 0x14, 0x00, 0x49, 0x1c, 0x62, 0x54, 0xdb, 0x4b, 0x80, 0xc1, 0xa1,
> + 0xe3, 0x36, 0xc0, 0x58, 0x0f, 0x6c, 0x65, 0xfc, 0xc8, 0x4d, 0x85, 0xa5,
> + 0x5e, 0xb2, 0xbd, 0x5e, 0x2d, 0x47, 0x14, 0x77, 0x32, 0x7e, 0x39, 0x0b,
> + 0xba, 0xcd, 0x30, 0x4b, 0x8e, 0x3b, 0x6d, 0x66, 0x59, 0x73, 0x16, 0x0b,
> + 0x23, 0x99, 0xb5, 0x09, 0xb7, 0x8f, 0xb2, 0x96, 0x1f, 0x74, 0x08, 0x36,
> + 0x1f, 0xcd, 0x46, 0x90, 0x1b, 0xe9, 0xfe, 0xe1, 0xcb, 0x7e, 0x06, 0x98,
> + 0x74, 0x47, 0x2f, 0x57, 0x0b, 0x72, 0xea, 0x0d, 0x25, 0xed, 0x92, 0x70,
> + 0x0d, 0x00, 0x5f, 0x48, 0xe3, 0xff, 0x3f, 0x39, 0xfc, 0xa9, 0x66, 0xa9,
> + 0x95, 0x33, 0xcd, 0x8b, 0x22, 0x11, 0xd9, 0xff, 0x7e, 0x5c, 0x9d, 0x80,
> + 0xc5, 0xed, 0x99, 0x78, 0x5c, 0x33, 0xdd, 0x0e, 0xca, 0x6a, 0xe2, 0xb6,
> + 0x3a, 0x66, 0xbd, 0x1d, 0x60, 0x96, 0x28, 0x20, 0xbd, 0x8c, 0xf4, 0x6c,
> + 0xcb, 0xc3, 0x9b, 0x19, 0x78, 0xd7, 0x57, 0xa3, 0xc3, 0x12, 0x95, 0x11,
> + 0x3d, 0xab, 0x44, 0x0d, 0x78, 0xbb, 0x2e, 0x0b, 0x4e, 0x0c, 0x94, 0x75,
> + 0xe7, 0x8f, 0x5c, 0xad, 0xe4, 0x2f, 0x9c, 0x26, 0x00, 0x01, 0x71, 0xe1,
> + 0x36, 0xcf, 0xb1, 0x00, 0x8f, 0xf7, 0x86, 0x79, 0x2e, 0x79, 0x9e, 0xd5,
> + 0x8b, 0x53, 0x0d, 0x28, 0x6c, 0xb9, 0xa2, 0xdf, 0x04, 0xe3, 0x78, 0x6d,
> + 0xcb, 0xb7, 0xf9, 0xea, 0x20, 0x4a, 0x5d, 0x92, 0x64, 0x88, 0x7d, 0xc4,
> + 0x5a, 0x21, 0x86, 0x12, 0x46, 0x49, 0x6d, 0xbc, 0x03, 0xd5, 0xeb, 0xf6,
> + 0xe3, 0x38, 0x4a, 0xaa, 0xce, 0x45, 0x91, 0x6a, 0xc0, 0x59, 0x1b, 0x6c,
> + 0x98, 0xb2, 0xe0, 0x29, 0x91, 0x01, 0xb5, 0x44, 0x48, 0xa6, 0xd3, 0x78,
> + 0x5a, 0xe1, 0xe4, 0x8c, 0xd9, 0x33, 0xa0, 0xd5, 0xe1, 0x83, 0xd6, 0x5a,
> + 0xe3, 0x5f, 0xe6, 0xf8, 0xfd, 0x21, 0x12, 0xff, 0x9e, 0x71, 0x70, 0x7f,
> + 0x17, 0xe2, 0x41, 0xf2, 0xb1, 0x4a, 0x84, 0x6b, 0x88, 0xb6, 0xb9, 0xed,
> + 0xdb, 0x0b, 0x84, 0x6f, 0x07, 0x94, 0xc7, 0x8e, 0xcc, 0xc2, 0xb2, 0x49,
> + 0x3d, 0xf8, 0x70, 0x1a, 0x9b, 0x4d, 0x13, 0x0c, 0xe7, 0xeb, 0x75, 0x5d,
> + 0xfb, 0x10, 0x16, 0xf9, 0xf1, 0xac, 0x4b, 0xe3, 0x65, 0x5f, 0xaa, 0xbb,
> + 0x31, 0xac, 0x4d, 0x74, 0x11, 0x62, 0x64, 0xd1, 0x90, 0x6e, 0x45, 0xaf,
> + 0x2d, 0xb2, 0xa5, 0xfa, 0x57, 0x67, 0xaa, 0x4c, 0x3f, 0x81, 0x72, 0x93,
> + 0x8a, 0xfb, 0xeb, 0x19, 0x9d, 0x9a, 0x10, 0x8d, 0xce, 0xb8, 0xe9, 0x3f,
> + 0x97, 0x3e, 0xb2, 0x2d, 0x3e, 0xaf, 0x19, 0x39, 0xb0, 0xea, 0x33, 0x09,
> + 0x25, 0xbc, 0x6f, 0x88, 0x25, 0x75, 0x11, 0x2b, 0xd9, 0x42, 0x8d, 0x48,
> + 0x8e, 0xe7, 0x51, 0x4e, 0xd4, 0xee, 0xd4, 0xf1, 0xe1, 0x94, 0xfb, 0xbd,
> + 0x01, 0x87, 0xa5, 0xd6, 0x94, 0x36, 0x6f, 0x37, 0xcd, 0x70, 0x49, 0xfd,
> + 0x6a, 0x94, 0xb0, 0xc8, 0xe5, 0xb9, 0xdb, 0x83, 0x72, 0xa0, 0xd8, 0x5e,
> + 0x00, 0x3e, 0x3d, 0x56, 0xc9, 0xae, 0xf6, 0xe7, 0xb1, 0xba, 0xdd, 0x6d,
> + 0x59, 0x2a, 0x6a, 0x50, 0x66, 0xfd, 0xf1, 0x1f, 0x18, 0xe4, 0xf9, 0x59,
> + 0x16, 0xf1, 0x25, 0x42, 0x7c, 0xab, 0xc5, 0xea, 0x64, 0x31, 0xf7, 0xcf,
> + 0xdd, 0x56, 0xc4, 0xbd, 0xb5, 0xe6, 0xb4, 0xda, 0x41, 0xb7, 0xfa, 0x05,
> + 0x06, 0x57, 0x51, 0xd8, 0xc4, 0xf7, 0x3c, 0xc8, 0x54, 0x55, 0x5c, 0x2c,
> + 0xd2, 0x17, 0xec, 0x27, 0xd3, 0xbf, 0xf3, 0xc8, 0xba, 0xbf, 0x75, 0xcf,
> + 0x1f, 0x78, 0x42, 0x1a, 0x4f, 0xb0, 0xcb, 0xd7, 0x84, 0xc6, 0xbd, 0x40,
> + 0x07, 0xbf, 0x47, 0xe9, 0x17, 0x2f, 0xad, 0xec, 0x64, 0x8b, 0xca, 0x7c,
> + 0x49, 0x4c, 0xda, 0xf9, 0x5f, 0x28, 0x1d, 0x2d, 0xc3, 0x8d, 0x15, 0xa6,
> + 0x0f, 0x95, 0x76, 0x99, 0xb5, 0x76, 0x68, 0x97, 0x91, 0x93, 0xca, 0x9d,
> + 0x61, 0x50, 0xa7, 0xa7, 0xf7, 0xce, 0xd5, 0x14, 0xe5, 0xb2, 0x50, 0x50,
> + 0x21, 0x73, 0x00, 0x21, 0x4f, 0x12, 0x79, 0x7f, 0x68, 0x27, 0x52, 0xf2,
> + 0x2c, 0x9d, 0x2d, 0x4f, 0x66, 0x3e, 0xf1, 0xb5, 0x68, 0x77, 0xc9, 0xd5,
> + 0x16, 0x01, 0xb2, 0x6d, 0x46, 0x7c, 0xce, 0x8d, 0x67, 0x77, 0x27, 0xdd,
> + 0x4b, 0x16, 0xd7, 0x7b, 0x16, 0xfd, 0x6b, 0x18, 0x36, 0xeb, 0xa5, 0xd6,
> + 0xd1, 0x21, 0xdb, 0x27, 0x01, 0x9a, 0x22, 0xa1, 0xbf, 0x45, 0xda, 0xf7,
> + 0xe4, 0x07, 0x26, 0x0c, 0x84, 0xe1, 0x81, 0x1c, 0xf9, 0x38, 0x4a, 0x26,
> + 0xb3, 0xe3, 0x90, 0x13, 0x5e, 0xb7, 0xd5, 0xaf, 0xcb, 0x7d, 0xc1, 0xf7,
> + 0x23, 0x2d, 0x96, 0xc8, 0xe7, 0x3e, 0x21, 0x2c, 0xee, 0xa8, 0xd7, 0x83,
> + 0xf1, 0xd7, 0xf0, 0xce, 0xca, 0x80, 0xb5, 0xbd, 0x89, 0x10, 0x71, 0x38,
> + 0x8c, 0xa2, 0xa1, 0x07, 0x93, 0xa9, 0xd9, 0x4d, 0xa5, 0x9b, 0x66, 0x68,
> + 0x06, 0x65, 0x8d, 0x26, 0xff, 0xcb, 0xf2, 0xf5, 0x09, 0x7c, 0xf1, 0x7f,
> + 0x55, 0x3f, 0x61, 0x78, 0xb8, 0x27, 0x77, 0xb8, 0x30, 0x91, 0x10, 0x3f,
> + 0x57, 0xec, 0xfd, 0x82, 0xd2, 0xd3, 0x8a, 0x27, 0xa5, 0x26, 0x87, 0x01,
> + 0x8a, 0x20, 0x9a, 0x5d, 0xe9, 0x88, 0xa7, 0x70, 0xb0, 0x06, 0x04, 0xe8,
> + 0xa0, 0x15, 0x2c, 0x0b, 0xe5, 0xea, 0x79, 0x57, 0xad, 0x8c, 0xe2, 0x98,
> + 0xcd, 0xbf, 0x74, 0x1c, 0xb6, 0x06, 0xc6, 0x17, 0x12, 0x2b, 0xa4, 0xa6,
> + 0xfe, 0xe7, 0x94, 0xf9, 0xb6, 0x95, 0xf0, 0xfc, 0xe5, 0x57, 0xfe, 0xf9,
> + 0x1d, 0xd4, 0x29, 0xc0, 0x06, 0xf9, 0x5f, 0x14, 0x73, 0x14, 0x1d, 0x42,
> + 0x1f, 0x70, 0xa0, 0xd5, 0x3d, 0x0d, 0xbe, 0xfb, 0xb3, 0xe0, 0xae, 0x35,
> + 0x34, 0x40, 0xa9, 0xca, 0x5d, 0x6f, 0x4f, 0x3a, 0x3b, 0x9e, 0xe2, 0xed,
> + 0x85, 0xa9, 0x53, 0xb9, 0xb2, 0x43, 0x5e, 0x95, 0x7a, 0x30, 0x63, 0x2a,
> + 0x84, 0x40, 0x1b, 0x86, 0x2d, 0x74, 0xf6, 0xf3, 0x3b, 0x40, 0x5b, 0x16,
> + 0x51, 0xa1, 0xaa, 0x70, 0xed, 0xb3, 0x96, 0x66, 0x9b, 0x6b, 0x57, 0x4d,
> + 0x0d, 0x8b, 0x2a, 0x9b, 0x27, 0xb6, 0x04, 0xa5, 0x19, 0xb2, 0x17, 0xad,
> + 0x6f, 0x6f, 0xef, 0x8c, 0xae, 0x87, 0x58, 0xc9, 0xbd, 0x51, 0xf9, 0x6b,
> + 0x7d, 0x5c, 0xd1, 0x42, 0x10, 0xa1, 0x5c, 0x66, 0x13, 0xaa, 0xe0, 0xfc,
> + 0x91, 0x95, 0x63, 0x0a, 0x0b, 0x3d, 0x5c, 0xd1, 0x64, 0xda, 0xaf, 0x7b,
> + 0x1b, 0x0a, 0x09, 0x9f, 0xf8, 0xd9, 0x43, 0x7b, 0x7d, 0xe1, 0x34, 0x68,
> + 0x6a, 0xb2, 0x3a, 0x16, 0x99, 0x2f, 0x29, 0x5f, 0xa3, 0xe6, 0x03, 0x74,
> + 0x42, 0x49, 0x6a, 0x33, 0x15, 0x93, 0x5e, 0xa5, 0xab, 0x13, 0x77, 0x3a,
> + 0x1a, 0x78, 0x6d, 0xe5, 0x06, 0x1f, 0xa4, 0x6f, 0xdc, 0xa8, 0xc8, 0x93,
> + 0x9f, 0x4d, 0x99, 0xba, 0xb1, 0x6d, 0x8d, 0x1d, 0xc0, 0x6b, 0xff, 0x16,
> + 0x7d, 0xb3, 0xfc, 0xe0, 0xd7, 0xd4, 0xb2, 0x69, 0x32, 0x38, 0x64, 0xef,
> + 0x04, 0xba, 0x7b, 0xa5, 0x5e, 0xc6, 0x1f, 0x1d, 0xd0, 0xf8, 0x6b, 0xd8,
> + 0x35, 0xb9, 0x9e, 0x2f, 0x62, 0x36, 0xf5, 0x88, 0x41, 0x90, 0x1f, 0xc7,
> + 0xc3, 0x96, 0x1b, 0x4f, 0xc4, 0x87, 0x0c, 0xea, 0xdb, 0x8f, 0xea, 0x6a,
> + 0xe9, 0xdc, 0x30, 0xc4, 0x6c, 0x23, 0x9b, 0xac, 0x8b, 0xf6, 0xa6, 0xbf,
> + 0xc0, 0xd6, 0x00, 0xf6, 0x92, 0x3f, 0xd3, 0x83, 0xfe, 0x74, 0x7c, 0x19,
> + 0x74, 0xfb, 0xe7, 0x72, 0x81, 0x17, 0x32, 0x32, 0xb8, 0x4b, 0x5d, 0x70,
> + 0x67, 0x14, 0xe1, 0xee,
> +};
> +
> +#endif
> diff --git a/drivers/acpi/apei/ghes-nvidia-test.c b/drivers/acpi/apei/ghes-nvidia-test.c
> new file mode 100644
> index 000000000000..5c84da82f898
> --- /dev/null
> +++ b/drivers/acpi/apei/ghes-nvidia-test.c
> @@ -0,0 +1,704 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <kunit/test.h>
> +#include <linux/errno.h>
> +#include <linux/slab.h>
> +#include <linux/unaligned.h>
> +#include <linux/uuid.h>
> +
> +#include "ghes-nvidia.h"
> +#include "ghes-nvidia-test-fixtures.h"
> +
> +static const u8 grace_valid_two_regs[] = {
> + 'N', 'V', 'D', 'A', '-', 'G', 'R', 'A',
> + 'C', 'E', 0, 0, 0, 0, 0, 0,
> + 0x34, 0x12, 0x78, 0x56, 0x02, 0x03, 0x02, 0x00,
> + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
> + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> + 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
> +};
> +
> +static const u8 grace_valid_zero_regs[] = {
> + 'N', 'V', 'D', 'A', '-', 'G', 'R', 'A',
> + 'C', 'E', 0, 0, 0, 0, 0, 0,
> + 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00,
> + 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
> +};
> +
> +static const u8 grace_valid_full_signature[] = {
> + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
> + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
> + 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00,
> + 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
> +};
> +
> +static void nvidia_ghes_grace_decodes_registers(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u64 addr, val;
> +
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
> + sizeof(grace_valid_two_regs),
> + &decoded));
Given that the CPER payload is a firmware-provided byte stream, please add
a Grace test where the valid payload starts at an unaligned address. That
would catch direct dereferences of __le16/__le64 fields in the Grace parser
and keep the Grace and Vera decoders under the same robustness expectation.
> + KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE, decoded.format);
> + KUNIT_EXPECT_STREQ(test, "NVDA-GRACE", decoded.signature);
> + KUNIT_EXPECT_EQ(test, 0x1234, decoded.error_type);
> + KUNIT_EXPECT_EQ(test, 0x5678, decoded.error_instance);
> + KUNIT_EXPECT_EQ(test, 2, decoded.severity);
> + KUNIT_EXPECT_EQ(test, 3, decoded.socket);
> + KUNIT_EXPECT_EQ(test, 2, decoded.number_regs);
> + KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, decoded.instance_base);
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
> + KUNIT_EXPECT_EQ(test, 0x1000ULL, addr);
> + KUNIT_EXPECT_EQ(test, 0x1111ULL, val);
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_grace_reg_pair(&decoded, 1, &addr, &val));
> + KUNIT_EXPECT_EQ(test, 0x2000ULL, addr);
> + KUNIT_EXPECT_EQ(test, 0x2222ULL, val);
> +}
> +
> +static void nvidia_ghes_grace_accepts_zero_registers(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_decode_grace(NULL, grace_valid_zero_regs,
> + sizeof(grace_valid_zero_regs),
> + &decoded));
> + KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE, decoded.format);
> + KUNIT_EXPECT_EQ(test, 0, decoded.number_regs);
> + KUNIT_EXPECT_PTR_EQ(test, NULL, decoded.grace_regs);
> +}
> +
> +static void nvidia_ghes_grace_copies_non_nul_signature(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_decode_grace(NULL, grace_valid_full_signature,
> + sizeof(grace_valid_full_signature),
> + &decoded));
> + KUNIT_EXPECT_STREQ(test, "ABCDEFGHIJKLMNOP", decoded.signature);
> + KUNIT_EXPECT_EQ(test, '\0', decoded.signature[16]);
> +}
> +
> +static void nvidia_ghes_grace_rejects_truncated_header(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, -ENODATA,
> + nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
> + sizeof(grace_valid_zero_regs) - 1,
> + &decoded));
> +}
> +
> +static void nvidia_ghes_grace_rejects_truncated_registers(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, -ENODATA,
> + nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
> + sizeof(grace_valid_two_regs) - 1,
> + &decoded));
> +}
> +
> +static const u8 vera_chip_serial[] = {
> + 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
> + 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00,
> +};
> +
> +#define VERA_EVENT_HDR_SIZE 32
> +#define VERA_CPU_INFO_SIZE 32
> +#define VERA_CONTEXT_HDR_SIZE 16
> +#define VERA_FIRST_CONTEXT_OFFSET (VERA_EVENT_HDR_SIZE + VERA_CPU_INFO_SIZE)
> +
> +static u8 *nvidia_ghes_kunit_memdup(struct kunit *test, const u8 *src, size_t len)
> +{
> + u8 *buf;
> +
> + buf = kunit_kmalloc(test, len, GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, buf);
> + memcpy(buf, src, len);
> +
> + return buf;
> +}
> +
> +static u8 *vera_make_synthetic(struct kunit *test, u8 version, u8 info_size,
> + u16 format, const u8 *data, u32 data_size,
> + size_t *len)
> +{
> + u8 *buf;
> + size_t context_offset;
> +
> + KUNIT_ASSERT_TRUE(test, info_size >= VERA_CPU_INFO_SIZE);
> +
> + context_offset = VERA_EVENT_HDR_SIZE + info_size;
> + *len = context_offset + VERA_CONTEXT_HDR_SIZE + data_size;
> + buf = kunit_kzalloc(test, *len, GFP_KERNEL);
> + KUNIT_ASSERT_NOT_NULL(test, buf);
> +
> + buf[0] = version;
> + buf[1] = 1;
> + buf[2] = 0;
> + put_unaligned_le16(0x22, &buf[4]);
> + memcpy(&buf[16], "SYNTH", 5);
> + put_unaligned_le16(0, &buf[32]);
> + buf[34] = info_size;
> + buf[35] = 0;
> + put_unaligned_le32(0x14107, &buf[36]);
> + memcpy(&buf[40], vera_chip_serial, sizeof(vera_chip_serial));
> + put_unaligned_le64(0, &buf[56]);
> + put_unaligned_le32(VERA_CONTEXT_HDR_SIZE, &buf[context_offset]);
> + put_unaligned_le16(0, &buf[context_offset + 4]);
> + put_unaligned_le16(format, &buf[context_offset + 8]);
> + put_unaligned_le16(0, &buf[context_offset + 10]);
> + put_unaligned_le32(data_size, &buf[context_offset + 12]);
> + memcpy(&buf[context_offset + VERA_CONTEXT_HDR_SIZE], data, data_size);
> +
> + return buf;
> +}
> +
> +static u8 *vera_make_synthetic_default(struct kunit *test, u16 format,
> + const u8 *data, u32 data_size, size_t *len)
> +{
> + return vera_make_synthetic(test, 1, VERA_CPU_INFO_SIZE, format, data,
> + data_size, len);
> +}
> +
> +static int nvidia_ghes_vera_context_u64_pair(const struct nvidia_ghes_vera_context *ctx,
> + unsigned int index, u64 *addr, u64 *val)
> +{
> + int count;
> +
> + if (!ctx || !addr || !val || ctx->data_format_type != 1)
> + return -EINVAL;
> +
> + count = nvidia_ghes_vera_context_entry_count(ctx);
> + if (count < 0)
> + return count;
> + if (index >= count)
> + return -ERANGE;
> +
> + *addr = get_unaligned_le64(ctx->data + index * 16);
> + *val = get_unaligned_le64(ctx->data + index * 16 + 8);
> +
> + return 0;
> +}
> +
> +static int nvidia_ghes_vera_context_u32_pair(const struct nvidia_ghes_vera_context *ctx,
> + unsigned int index, u32 *addr, u32 *val)
> +{
> + int count;
> +
> + if (!ctx || !addr || !val || ctx->data_format_type != 2)
> + return -EINVAL;
> +
> + count = nvidia_ghes_vera_context_entry_count(ctx);
> + if (count < 0)
> + return count;
> + if (index >= count)
> + return -ERANGE;
> +
> + *addr = get_unaligned_le32(ctx->data + index * 8);
> + *val = get_unaligned_le32(ctx->data + index * 8 + 4);
> +
> + return 0;
> +}
> +
> +static int nvidia_ghes_vera_context_u64_value(const struct nvidia_ghes_vera_context *ctx,
> + unsigned int index, u64 *val)
> +{
> + int count;
> +
> + if (!ctx || !val || ctx->data_format_type != 3)
> + return -EINVAL;
> +
> + count = nvidia_ghes_vera_context_entry_count(ctx);
> + if (count < 0)
> + return count;
> + if (index >= count)
> + return -ERANGE;
> +
> + *val = get_unaligned_le64(ctx->data + index * 8);
> +
> + return 0;
> +}
> +
> +static int nvidia_ghes_vera_context_u32_value(const struct nvidia_ghes_vera_context *ctx,
> + unsigned int index, u32 *val)
> +{
> + int count;
> +
> + if (!ctx || !val || ctx->data_format_type != 4)
> + return -EINVAL;
> +
> + count = nvidia_ghes_vera_context_entry_count(ctx);
> + if (count < 0)
> + return count;
> + if (index >= count)
> + return -ERANGE;
> +
> + *val = get_unaligned_le32(ctx->data + index * 4);
> +
> + return 0;
> +}
> +
> +static void nvidia_ghes_vera_decodes_l1_reset(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
> + sizeof(vera_l1_reset_id10),
> + &decoded));
> + KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_VERA, decoded.format);
> + KUNIT_EXPECT_STREQ(test, "L1 RESET", decoded.signature);
> + KUNIT_EXPECT_EQ(test, 1, decoded.event_context_count);
> + KUNIT_EXPECT_EQ(test, 1, decoded.event_type);
> + KUNIT_EXPECT_EQ(test, 39, decoded.event_sub_type);
> + KUNIT_EXPECT_EQ(test, 0, decoded.source_device_type);
> + KUNIT_EXPECT_EQ(test, 0, decoded.socket);
> + KUNIT_EXPECT_EQ(test, 0x14107U, decoded.architecture);
> + KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
> + sizeof(vera_chip_serial));
> + KUNIT_EXPECT_EQ(test, 0xfe1500200000ULL, decoded.instance_base);
> + KUNIT_EXPECT_EQ(test, 16U, decoded.contexts[0].context_size);
> + KUNIT_EXPECT_EQ(test, 1, decoded.contexts[0].data_format_type);
> + KUNIT_EXPECT_EQ(test, 0U, decoded.contexts[0].data_size);
> +}
> +
> +static void nvidia_ghes_vera_decodes_crashdump_id(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u64 addr, val;
> +
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_decode_vera(NULL, vera_crashdump_id_id40,
> + sizeof(vera_crashdump_id_id40),
> + &decoded));
> + KUNIT_EXPECT_STREQ(test, "CRASHDUMP-ID", decoded.signature);
> + KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
> + sizeof(vera_chip_serial));
> + KUNIT_EXPECT_EQ(test, 240U, decoded.contexts[0].context_size);
> + KUNIT_EXPECT_EQ(test, 1, decoded.contexts[0].data_format_type);
> + KUNIT_EXPECT_EQ(test, 224U, decoded.contexts[0].data_size);
> + KUNIT_EXPECT_EQ(test, 14, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_vera_context_u64_pair(&decoded.contexts[0], 0,
> + &addr, &val));
> + KUNIT_EXPECT_EQ(test, 0x8300000000000000ULL, addr);
> + KUNIT_EXPECT_EQ(test, 0x372e33375f6c6572ULL, val);
> +}
> +
> +static void nvidia_ghes_vera_decodes_crashdump_s1_opaque_context(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u64 val;
> +
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_decode_vera(NULL, vera_crashdump_s1_id41,
> + sizeof(vera_crashdump_s1_id41),
> + &decoded));
> + KUNIT_EXPECT_STREQ(test, "CRASHDUMP-S1", decoded.signature);
> + KUNIT_EXPECT_EQ(test, 4096, decoded.event_type);
> + KUNIT_EXPECT_EQ(test, 2304, decoded.event_sub_type);
> + KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
> + sizeof(vera_chip_serial));
> + KUNIT_EXPECT_EQ(test, 16U, decoded.contexts[0].context_size);
> + KUNIT_EXPECT_EQ(test, 0, decoded.contexts[0].data_format_type);
> + KUNIT_EXPECT_EQ(test, 1928U, decoded.contexts[0].data_size);
> + KUNIT_EXPECT_NOT_NULL(test, decoded.contexts[0].data);
Should this use KUNIT_ASSERT_NOT_NULL() instead?
KUNIT_EXPECT_NOT_NULL() will register a test failure if the pointer is NULL,
but it will not abort the test case execution. This means if the pointer
is NULL, the very next lines will trigger a NULL pointer dereference and
crash the test thread.
> + KUNIT_EXPECT_EQ(test, 0x53, decoded.contexts[0].data[0]);
> + KUNIT_EXPECT_EQ(test, 0x56, decoded.contexts[0].data[1]);
> + KUNIT_EXPECT_EQ(test, 0x7f, decoded.contexts[0].data[2]);
> + KUNIT_EXPECT_EQ(test, -EINVAL,
> + nvidia_ghes_vera_context_u64_value(&decoded.contexts[0], 0, &val));
> +}
> +
> +static void nvidia_ghes_vera_decodes_format2_u32_pairs(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> + u32 addr, val;
> +
> + buf = vera_make_synthetic_default(test, 2, data, sizeof(data), &len);
> + KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> + KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_vera_context_u32_pair(&decoded.contexts[0], 0,
> + &addr, &val));
> + KUNIT_EXPECT_EQ(test, 0x12345678U, addr);
> + KUNIT_EXPECT_EQ(test, 0x9abcdef0U, val);
> +}
> +
> +static void nvidia_ghes_vera_decodes_format3_u64_values(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> + u64 val;
> +
> + buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
> + KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> + KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_vera_context_u64_value(&decoded.contexts[0], 0, &val));
> + KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, val);
> +}
> +
> +static void nvidia_ghes_vera_decodes_format4_u32_values(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x78, 0x56, 0x34, 0x12,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> + u32 val;
> +
> + buf = vera_make_synthetic_default(test, 4, data, sizeof(data), &len);
> + KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> + KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_vera_context_u32_value(&decoded.contexts[0], 0, &val));
> + KUNIT_EXPECT_EQ(test, 0x12345678U, val);
> +}
> +
> +static void nvidia_ghes_vera_rejects_truncated_event_header(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, -ENODATA,
> + nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
> + VERA_EVENT_HDR_SIZE - 1,
> + &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_truncated_cpu_info(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, -ENODATA,
> + nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
> + VERA_FIRST_CONTEXT_OFFSET - 1,
> + &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_truncated_context_header(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> +
> + KUNIT_EXPECT_EQ(test, -ENODATA,
> + nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
> + VERA_FIRST_CONTEXT_OFFSET +
> + VERA_CONTEXT_HDR_SIZE - 1,
> + &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_too_many_contexts(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
> + buf[1] = NVIDIA_GHES_MAX_CONTEXTS + 1;
> +
> + KUNIT_EXPECT_EQ(test, -E2BIG,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
> +}
This covers the upper bound check, but I do not see a positive test with
event_context_count > 1.
The Vera decoder has non-trivial offset accounting:
offset += advance;
and accepts two different context encodings depending on whether
context_size is equal to the context header size or covers the payload as
well. Please add a multi-context test, ideally with the first context using
context_size > sizeof(header), so the test proves that the second context is
found via the advertised context_size.
That would make the coverage match the parser logic we care about for
production use.
> +
> +static void nvidia_ghes_vera_rejects_bad_info_size(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
> + buf[34] = 31;
> +
> + KUNIT_EXPECT_EQ(test, -EINVAL,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_unsupported_version(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
> + buf[0] = 2;
> +
> + KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
> +}
> +
> +static void nvidia_ghes_vera_accepts_extended_cpu_info(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> + u64 val;
> +
> + buf = vera_make_synthetic(test, 1, VERA_CPU_INFO_SIZE + 4, 3,
> + data, sizeof(data), &len);
> + KUNIT_EXPECT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> + KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE, decoded.contexts[0].context_size);
> + KUNIT_EXPECT_EQ(test, 3, decoded.contexts[0].data_format_type);
> + KUNIT_EXPECT_EQ(test, 8U, decoded.contexts[0].data_size);
> + KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&decoded.contexts[0]));
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_vera_context_u64_value(&decoded.contexts[0], 0, &val));
> + KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, val);
> +}
> +
> +static void nvidia_ghes_vera_rejects_short_context_size(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
> + put_unaligned_le32(15, &buf[VERA_FIRST_CONTEXT_OFFSET]);
> +
> + KUNIT_EXPECT_EQ(test, -EINVAL,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_ambiguous_context_size(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> +
> + buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
> + put_unaligned_le32(20, &buf[VERA_FIRST_CONTEXT_OFFSET]);
> +
> + KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> +}
This is useful, but it only tests the rejection case. Please also add the
matching accepted case where context_size is larger than the header and
data_size fits inside it, including any padding before the next context.
> +
> +static void nvidia_ghes_vera_rejects_data_beyond_section(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> +
> + buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
> + put_unaligned_le32(16, &buf[VERA_FIRST_CONTEXT_OFFSET + 12]);
> +
> + KUNIT_EXPECT_EQ(test, -ENODATA, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_mis_sized_format1(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_crashdump_id_id40,
> + sizeof(vera_crashdump_id_id40));
> + put_unaligned_le32(15, &buf[VERA_FIRST_CONTEXT_OFFSET + 12]);
> +
> + KUNIT_EXPECT_EQ(test, -EINVAL,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_crashdump_id_id40), &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_mis_sized_format2(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> +
> + buf = vera_make_synthetic_default(test, 2, data, sizeof(data), &len);
> + KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_mis_sized_format3(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> +
> + buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
> + KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_mis_sized_format4(struct kunit *test)
> +{
> + static const u8 data[] = {
> + 0x78, 0x56, 0x34,
> + };
> + struct nvidia_ghes_decoded decoded = {};
> + size_t len;
> + u8 *buf;
> +
> + buf = vera_make_synthetic_default(test, 4, data, sizeof(data), &len);
> + KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_unsupported_source_device(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
> + buf[2] = 1;
> +
> + KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
> +}
> +
> +static void nvidia_ghes_vera_rejects_unsupported_data_format(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
> + put_unaligned_le16(5, &buf[VERA_FIRST_CONTEXT_OFFSET + 8]);
> +
> + KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
> +}
> +
> +static void nvidia_ghes_vera_copies_non_nul_signature(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {};
> + u8 *buf;
> +
> + buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
> + memcpy(&buf[16], "ABCDEFGHIJKLMNOP", 16);
> +
> + KUNIT_EXPECT_EQ(test, 0,
> + nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
> + KUNIT_EXPECT_STREQ(test, "ABCDEFGHIJKLMNOP", decoded.signature);
> + KUNIT_EXPECT_EQ(test, '\0', decoded.signature[16]);
> +}
> +
> +static void nvidia_ghes_guid_routes_grace(struct kunit *test)
> +{
> + const guid_t guid = GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
> + 0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
> +
> + KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE,
> + nvidia_ghes_format_from_guid(&guid));
> +}
> +
> +static void nvidia_ghes_guid_routes_vera(struct kunit *test)
> +{
> + const guid_t guid = GUID_INIT(0x9068e568, 0x6ca0, 0x11f0,
> + 0xae, 0xaf, 0x15, 0x93, 0x43, 0x59, 0x1e, 0xac);
> +
> + KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_VERA,
> + nvidia_ghes_format_from_guid(&guid));
> +}
> +
> +static void nvidia_ghes_guid_rejects_unknown(struct kunit *test)
> +{
> + const guid_t guid = GUID_INIT(0x00000000, 0x0000, 0x0000,
> + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
> +
> + KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_UNKNOWN,
> + nvidia_ghes_format_from_guid(&guid));
> +}
> +
> +static void nvidia_ghes_grace_reg_pair_rejects_null_decoded(struct kunit *test)
> +{
> + u64 addr, val;
> +
> + KUNIT_EXPECT_EQ(test, -EINVAL,
> + nvidia_ghes_grace_reg_pair(NULL, 0, &addr, &val));
> +}
> +
> +static void nvidia_ghes_grace_reg_pair_rejects_wrong_format(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {
> + .format = NVIDIA_GHES_FORMAT_VERA,
> + };
> + u64 addr, val;
> +
> + KUNIT_EXPECT_EQ(test, -EINVAL,
> + nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
> +}
> +
> +static void nvidia_ghes_grace_reg_pair_rejects_out_of_range(struct kunit *test)
> +{
> + struct nvidia_ghes_decoded decoded = {
> + .format = NVIDIA_GHES_FORMAT_GRACE,
> + .number_regs = 0,
> + };
> + u64 addr, val;
> +
> + KUNIT_EXPECT_EQ(test, -ERANGE,
> + nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
Please also test the malformed helper input where format is GRACE,
number_regs is non-zero, but grace_regs is NULL. If the helper is visible
to KUnit, its contract should be covered explicitly.
Thanks.
Shuai
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v2 RESEND 4/4] selftests: firmware: Add NVIDIA GHES EINJ selftest
2026-07-24 12:20 ` [PATCH v2 RESEND 4/4] selftests: firmware: Add NVIDIA GHES EINJ selftest Kai-Heng Feng
@ 2026-07-26 9:02 ` Shuai Xue
0 siblings, 0 replies; 13+ messages in thread
From: Shuai Xue @ 2026-07-26 9:02 UTC (permalink / raw)
To: Kai-Heng Feng, rafael, shuah, kees, Luck, Tony
Cc: julianbraha, tony.luck, bp, guohanjun, mchehab, linux-kernel,
linux-acpi, linux-kselftest, linux-hardening, csoto, mochs
On 7/24/26 8:20 PM, Kai-Heng Feng wrote:
> Exercise the full driver path on real Grace and Vera hardware using
> ACPI EINJ to inject CPER sections and validate the kernel log output.
> KUnit covers the parser in isolation; this test covers the path from
> firmware notification through GUID dispatch to decoded output.
>
> Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
> ---
> v2:
> - No change.
>
> tools/testing/selftests/firmware/Makefile | 4 +-
> tools/testing/selftests/firmware/config | 5 +
> tools/testing/selftests/firmware/einj_lib.sh | 189 ++++++++++++++++++
> .../selftests/firmware/ghes_nvidia_einj.sh | 144 +++++++++++++
> .../firmware/ghes_nvidia_einj_profiles.sh | 46 +++++
> 5 files changed, 386 insertions(+), 2 deletions(-)
> create mode 100644 tools/testing/selftests/firmware/einj_lib.sh
> create mode 100755 tools/testing/selftests/firmware/ghes_nvidia_einj.sh
> create mode 100755 tools/testing/selftests/firmware/ghes_nvidia_einj_profiles.sh
>
> diff --git a/tools/testing/selftests/firmware/Makefile b/tools/testing/selftests/firmware/Makefile
> index 7992969deaa2..b753dd123860 100644
> --- a/tools/testing/selftests/firmware/Makefile
> +++ b/tools/testing/selftests/firmware/Makefile
> @@ -3,8 +3,8 @@
> CFLAGS = -Wall \
> -O2
>
> -TEST_PROGS := fw_run_tests.sh
> -TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_upload.sh fw_lib.sh
> +TEST_PROGS := fw_run_tests.sh ghes_nvidia_einj.sh
> +TEST_FILES := fw_fallback.sh fw_filesystem.sh fw_upload.sh fw_lib.sh einj_lib.sh ghes_nvidia_einj_profiles.sh
> TEST_GEN_FILES := fw_namespace
I am not sure this belongs in the generic firmware selftests.
This test requires root, debugfs, ACPI EINJ, NVIDIA-specific vendor EINJ
metadata, a bound NVDA2012 device, and it writes EINJ controls before
triggering error_inject. Even if the selected profiles are intended to be
informational, this looks more like a platform RAS validation tool than a
generic kselftest that should run as part of the firmware selftest suite.
Would ras-tools be a better place for this? Tony Luck maintains that tree
for RAS-related test and injection tooling, and this script seems to fit
there better than tools/testing/selftests/firmware/.
Please see:
https://git.kernel.org/pub/scm/linux/kernel/git/aegl/ras-tools.git
Thanks,
Shuai
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 0/3] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoding
2026-07-24 12:20 [PATCH v2 RESEND 0/4] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoder and tests Kai-Heng Feng
` (3 preceding siblings ...)
2026-07-24 12:20 ` [PATCH v2 RESEND 4/4] selftests: firmware: Add NVIDIA GHES EINJ selftest Kai-Heng Feng
@ 2026-08-04 12:23 ` Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 1/3] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
` (2 more replies)
4 siblings, 3 replies; 13+ messages in thread
From: Kai-Heng Feng @ 2026-08-04 12:23 UTC (permalink / raw)
To: rafael, linux-acpi
Cc: tony.luck, bp, guohanjun, mchehab, xueshuai, lenb, kees,
gustavoars, linux-hardening, linux-kernel, Kai-Heng Feng
The NVIDIA GHES driver currently decodes the vendor CPER section used by
Grace systems. Vera uses a different section GUID and event layout.
Refactor the Grace path into testable decode and print helpers, add the Vera
event decoder, and cover both formats with KUnit. The Vera parser follows
the extensible CPU information and event-context wire contracts: it accepts
new minor versions and extended information headers, validates every one of
the wire format's 0 to 255 contexts without allocation, and retains a valid
prefix when a later context is malformed.
Printing walks the validated raw context span a second time. It limits
verbose output to 16 contexts and 32 key/value entries per section while
still validating the complete record. Existing one-context output remains
unchanged.
The companion ras-tools series adds a fail-closed NVIDIA EINJ validator for
the non-fatal Grace CMET-INFO and Vera CMET-NULL samples.
Changes since v2:
- Move EINJ tooling out of the kernel series and into ras-tools.
- Follow the Vera CtxSize padding and alignment contract while retaining
the legacy opaque-context encoding.
- Accept compatible CPU information extensions and validate the complete
8-bit context count with an allocation-free cursor.
- Bound verbose context and entry output independently, while reporting
omitted contexts and entries.
- Add wire-layout assertions, partial-context handling, severity-aware
diagnostics, and 52 Grace/Vera KUnit cases.
Verification:
- x86_64 KUnit: 52/52 passed.
- ARM64 KASAN+UBSAN KUnit under QEMU/HVF: 52/52 passed.
- ARM64 GCC and Clang, built-in and module configurations, W=1: passed.
- ARM64 GCC full Image link: passed.
- ARM64 sparse C=2 with __CHECK_ENDIAN__: passed (one unrelated existing
arm64 vDSO declaration warning).
- Strict source checkpatch and git diff --check: passed.
- The raw live sections were decoded independently by libcper at commit
b83878a4aca9: Vera as an 80-byte CPU CMET-NULL event with one empty
type-1 context, and Grace as a 544-byte CMET-INFO section with 32
registers.
- Vera live EINJ: a real 80-byte CMET-NULL CPER from GHES source 8193,
event type/subtype 0, one 16-byte context, and zero entries (not the
firmware-rejection record seen with the old selector).
- Grace live EINJ: a real 544-byte CMET-INFO CPER from GHES source 896,
severity 3, with all 32 register addresses decoded in order.
- Both live runs reached the runner's PASS gate with empty tool stderr,
captured the raw tracepoint record, and restored every EINJ control
byte-for-byte.
Kai-Heng Feng (3):
ACPI: APEI: GHES: Refactor Grace decoder helpers
ACPI: APEI: GHES: Add NVIDIA Vera decoder
ACPI: APEI: GHES: Add Grace and Vera KUnit coverage
MAINTAINERS | 2 +-
drivers/acpi/apei/Kconfig | 24 +-
drivers/acpi/apei/Makefile | 1 +
drivers/acpi/apei/ghes-nvidia-test-fixtures.h | 233 +++
drivers/acpi/apei/ghes-nvidia-test.c | 1274 +++++++++++++++++
drivers/acpi/apei/ghes-nvidia.c | 767 +++++++++-
drivers/acpi/apei/ghes-nvidia.h | 109 ++
7 files changed, 2364 insertions(+), 46 deletions(-)
create mode 100644 drivers/acpi/apei/ghes-nvidia-test-fixtures.h
create mode 100644 drivers/acpi/apei/ghes-nvidia-test.c
create mode 100644 drivers/acpi/apei/ghes-nvidia.h
base-commit: 848acc8ffe1b7cd5f1bf427b93069becfebc2c9d
--
2.50.1 (Apple Git-155)
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 1/3] ACPI: APEI: GHES: Refactor Grace decoder helpers
2026-08-04 12:23 ` [PATCH v3 0/3] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoding Kai-Heng Feng
@ 2026-08-04 12:23 ` Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 2/3] ACPI: APEI: GHES: Add NVIDIA Vera decoder Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 3/3] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage Kai-Heng Feng
2 siblings, 0 replies; 13+ messages in thread
From: Kai-Heng Feng @ 2026-08-04 12:23 UTC (permalink / raw)
To: rafael, linux-acpi
Cc: tony.luck, bp, guohanjun, mchehab, xueshuai, lenb, kees,
gustavoars, linux-hardening, linux-kernel, Kai-Heng Feng
Split the Grace CPER processing into a separate decode step and a
print step so the parser can be exercised by KUnit without a live
ACPI device. Introduce ghes-nvidia.h to hold shared types that the
Vera decoder added in the next commit will also reference.
Parse the Grace wire payload with __packed structs and
get_unaligned_le*() for multi-byte fields, matching the Vera
decoder style. Reject nvidia_ghes_grace_reg_pair() calls with a
non-zero register count and a NULL grace_regs pointer, and
rate-limit Grace decoder diagnostics.
Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
---
v3:
- Use __packed + get_unaligned_le*() for Grace multi-byte fields,
including register pairs (Shuai Xue).
- Keep __counted_by / struct_size; reject NULL grace_regs when
number_regs != 0; document the helper contract.
- Rate-limit Grace decoder and notify error messages.
v2:
- No change.
---
MAINTAINERS | 2 +-
drivers/acpi/apei/ghes-nvidia.c | 160 ++++++++++++++++++++++----------
drivers/acpi/apei/ghes-nvidia.h | 50 ++++++++++
3 files changed, 160 insertions(+), 52 deletions(-)
create mode 100644 drivers/acpi/apei/ghes-nvidia.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 5114e6db7307..aa9c2ae58ac5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19348,7 +19348,7 @@ NVIDIA GHES VENDOR CPER RECORD HANDLER
M: Kai-Heng Feng <kaihengf@nvidia.com>
L: linux-acpi@vger.kernel.org
S: Maintained
-F: drivers/acpi/apei/ghes-nvidia.c
+F: drivers/acpi/apei/ghes-nvidia*
NVIDIA VRS RTC DRIVER
M: Shubhi Garg <shgarg@nvidia.com>
diff --git a/drivers/acpi/apei/ghes-nvidia.c b/drivers/acpi/apei/ghes-nvidia.c
index 597275d81de8..e7cc912344fd 100644
--- a/drivers/acpi/apei/ghes-nvidia.c
+++ b/drivers/acpi/apei/ghes-nvidia.c
@@ -9,13 +9,18 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
+#include <linux/unaligned.h>
#include <linux/uuid.h>
#include <acpi/ghes.h>
-static const guid_t nvidia_sec_guid =
+#include <kunit/visibility.h>
+#include "ghes-nvidia.h"
+
+static const guid_t nvidia_grace_sec_guid =
GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
+/* Grace CPER section wire layout (header without flexible register array). */
struct cper_sec_nvidia {
char signature[16];
__le16 error_type;
@@ -25,84 +30,137 @@ struct cper_sec_nvidia {
u8 number_regs;
u8 reserved;
__le64 instance_base;
- struct {
- __le64 addr;
- __le64 val;
- } regs[] __counted_by(number_regs);
-};
+ struct nvidia_ghes_grace_reg regs[] __counted_by(number_regs);
+} __packed;
+
+static_assert(sizeof(struct cper_sec_nvidia) == 32);
struct nvidia_ghes_private {
struct notifier_block nb;
struct device *dev;
};
-static void nvidia_ghes_print_error(struct device *dev,
- const struct cper_sec_nvidia *nvidia_err,
- size_t error_data_length, bool fatal)
+VISIBLE_IF_KUNIT
+int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded)
{
- const char *level = fatal ? KERN_ERR : KERN_INFO;
+ const struct cper_sec_nvidia *nvidia_err = buf;
size_t min_size;
+ u8 number_regs;
+
+ if (!buf || !decoded)
+ return -EINVAL;
+ if (len < sizeof(*nvidia_err)) {
+ if (dev)
+ dev_err_ratelimited(dev, "Section too small (%zu < %zu)\n",
+ len, sizeof(*nvidia_err));
+ return -ENODATA;
+ }
- dev_printk(level, dev, "signature: %.16s\n", nvidia_err->signature);
- dev_printk(level, dev, "error_type: %u\n", le16_to_cpu(nvidia_err->error_type));
- dev_printk(level, dev, "error_instance: %u\n", le16_to_cpu(nvidia_err->error_instance));
- dev_printk(level, dev, "severity: %u\n", nvidia_err->severity);
- dev_printk(level, dev, "socket: %u\n", nvidia_err->socket);
- dev_printk(level, dev, "number_regs: %u\n", nvidia_err->number_regs);
- dev_printk(level, dev, "instance_base: 0x%016llx\n",
- le64_to_cpu(nvidia_err->instance_base));
-
- if (nvidia_err->number_regs == 0)
- return;
-
- /*
- * Validate that all registers fit within error_data_length.
- * Each register pair is two little-endian u64s.
- */
- min_size = struct_size(nvidia_err, regs, nvidia_err->number_regs);
- if (error_data_length < min_size) {
- dev_err(dev, "Invalid number_regs %u (section size %zu, need %zu)\n",
- nvidia_err->number_regs, error_data_length, min_size);
- return;
+ number_regs = nvidia_err->number_regs;
+ min_size = struct_size(nvidia_err, regs, number_regs);
+ if (len < min_size) {
+ if (dev)
+ dev_err_ratelimited(dev,
+ "Invalid number_regs %u (section size %zu, need %zu)\n",
+ number_regs, len, min_size);
+ return -ENODATA;
}
- for (int i = 0; i < nvidia_err->number_regs; i++)
+ memset(decoded, 0, sizeof(*decoded));
+ decoded->format = NVIDIA_GHES_FORMAT_GRACE;
+ memcpy(decoded->signature, nvidia_err->signature, sizeof(nvidia_err->signature));
+ decoded->signature[sizeof(nvidia_err->signature)] = '\0';
+ decoded->error_type = get_unaligned_le16(&nvidia_err->error_type);
+ decoded->error_instance = get_unaligned_le16(&nvidia_err->error_instance);
+ decoded->severity = nvidia_err->severity;
+ decoded->socket = nvidia_err->socket;
+ decoded->number_regs = number_regs;
+ decoded->instance_base = get_unaligned_le64(&nvidia_err->instance_base);
+ if (number_regs)
+ decoded->grace_regs = nvidia_err->regs;
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_grace);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
+ unsigned int index, u64 *addr, u64 *val)
+{
+ const struct nvidia_ghes_grace_reg *regs;
+
+ if (!decoded || decoded->format != NVIDIA_GHES_FORMAT_GRACE || !addr || !val)
+ return -EINVAL;
+ if (decoded->number_regs && !decoded->grace_regs)
+ return -EINVAL;
+ if (index >= decoded->number_regs)
+ return -ERANGE;
+
+ regs = decoded->grace_regs;
+ *addr = get_unaligned_le64(®s[index].addr);
+ *val = get_unaligned_le64(®s[index].val);
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_grace_reg_pair);
+
+static void nvidia_ghes_print_grace(struct device *dev,
+ const struct nvidia_ghes_decoded *decoded,
+ bool fatal)
+{
+ const char *level = fatal ? KERN_ERR : KERN_INFO;
+ u64 addr, val;
+
+ dev_printk(level, dev, "signature: %s\n", decoded->signature);
+ dev_printk(level, dev, "error_type: %u\n", decoded->error_type);
+ dev_printk(level, dev, "error_instance: %u\n", decoded->error_instance);
+ dev_printk(level, dev, "severity: %u\n", decoded->severity);
+ dev_printk(level, dev, "socket: %u\n", decoded->socket);
+ dev_printk(level, dev, "number_regs: %u\n", decoded->number_regs);
+ dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
+
+ for (int i = 0; i < decoded->number_regs; i++) {
+ if (nvidia_ghes_grace_reg_pair(decoded, i, &addr, &val))
+ break;
dev_printk(level, dev, "register[%d]: address=0x%016llx value=0x%016llx\n",
- i, le64_to_cpu(nvidia_err->regs[i].addr),
- le64_to_cpu(nvidia_err->regs[i].val));
+ i, addr, val);
+ }
}
static int nvidia_ghes_notify(struct notifier_block *nb,
unsigned long event, void *data)
{
struct acpi_hest_generic_data *gdata = data;
+ struct nvidia_ghes_decoded decoded = {};
struct nvidia_ghes_private *priv;
- const struct cper_sec_nvidia *nvidia_err;
+ const void *payload;
guid_t sec_guid;
+ u32 len;
+ int ret;
+ bool fatal;
import_guid(&sec_guid, gdata->section_type);
- if (!guid_equal(&sec_guid, &nvidia_sec_guid))
+ if (!guid_equal(&sec_guid, &nvidia_grace_sec_guid))
return NOTIFY_DONE;
priv = container_of(nb, struct nvidia_ghes_private, nb);
-
- if (acpi_hest_get_error_length(gdata) < sizeof(*nvidia_err)) {
- dev_err(priv->dev, "Section too small (%d < %zu)\n",
- acpi_hest_get_error_length(gdata), sizeof(*nvidia_err));
+ len = acpi_hest_get_error_length(gdata);
+ payload = acpi_hest_get_payload(gdata);
+ fatal = event >= GHES_SEV_RECOVERABLE;
+
+ ret = nvidia_ghes_decode_grace(priv->dev, payload, len, &decoded);
+ if (ret) {
+ dev_err_ratelimited(priv->dev,
+ "Malformed NVIDIA CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
return NOTIFY_OK;
}
- nvidia_err = acpi_hest_get_payload(gdata);
-
- if (event >= GHES_SEV_RECOVERABLE)
- dev_err(priv->dev, "NVIDIA CPER section, error_data_length: %u\n",
- acpi_hest_get_error_length(gdata));
- else
- dev_info(priv->dev, "NVIDIA CPER section, error_data_length: %u\n",
- acpi_hest_get_error_length(gdata));
-
- nvidia_ghes_print_error(priv->dev, nvidia_err, acpi_hest_get_error_length(gdata),
- event >= GHES_SEV_RECOVERABLE);
+ dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
+ "NVIDIA CPER section, error_data_length: %u\n", len);
+ nvidia_ghes_print_grace(priv->dev, &decoded, fatal);
return NOTIFY_OK;
}
diff --git a/drivers/acpi/apei/ghes-nvidia.h b/drivers/acpi/apei/ghes-nvidia.h
new file mode 100644
index 000000000000..965abe3d5c49
--- /dev/null
+++ b/drivers/acpi/apei/ghes-nvidia.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef GHES_NVIDIA_H
+#define GHES_NVIDIA_H
+
+#include <linux/build_bug.h>
+#include <linux/types.h>
+#include <kunit/visibility.h>
+
+struct device;
+
+enum nvidia_ghes_format {
+ NVIDIA_GHES_FORMAT_UNKNOWN,
+ NVIDIA_GHES_FORMAT_GRACE,
+};
+
+struct nvidia_ghes_grace_reg {
+ __le64 addr;
+ __le64 val;
+} __packed;
+
+struct nvidia_ghes_decoded {
+ enum nvidia_ghes_format format;
+ char signature[17];
+ u16 error_type;
+ u16 error_instance;
+ u8 severity;
+ u8 socket;
+ u8 number_regs;
+ u64 instance_base;
+ const struct nvidia_ghes_grace_reg *grace_regs;
+};
+
+/**
+ * nvidia_ghes_grace_reg_pair() - Read one Grace register address/value pair
+ * @decoded: Decoded Grace section; format must be NVIDIA_GHES_FORMAT_GRACE
+ * @index: Register index in [0, number_regs)
+ * @addr: Output register address
+ * @val: Output register value
+ *
+ * When number_regs is non-zero, decoded->grace_regs must be non-NULL.
+ * Returns -EINVAL for bad arguments / missing grace_regs, -ERANGE for
+ * index >= number_regs, and 0 on success.
+ */
+VISIBLE_IF_KUNIT int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded);
+VISIBLE_IF_KUNIT int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
+ unsigned int index, u64 *addr, u64 *val);
+
+#endif
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 2/3] ACPI: APEI: GHES: Add NVIDIA Vera decoder
2026-08-04 12:23 ` [PATCH v3 0/3] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoding Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 1/3] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
@ 2026-08-04 12:23 ` Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 3/3] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage Kai-Heng Feng
2 siblings, 0 replies; 13+ messages in thread
From: Kai-Heng Feng @ 2026-08-04 12:23 UTC (permalink / raw)
To: rafael, linux-acpi
Cc: tony.luck, bp, guohanjun, mchehab, xueshuai, lenb, kees,
gustavoars, linux-hardening, linux-kernel, Kai-Heng Feng
Vera is the next-generation NVIDIA server SoC. Its CPER section uses a
different GUID and binary layout from Grace, so firmware-reported hardware
errors need a dedicated decoder.
Follow the NVIDIA event-section CtxSize contract. Require CtxSize to be
16-byte aligned, validate that each context covers its header and data, and
advance by CtxSize so alignment padding is skipped. Preserve the legacy
format-0 header-only CtxSize used by existing opaque firmware records.
Assert all three Vera wire-header sizes and keep the decoded object on the
stack. Print at most 32 key/value entries across the entire section, report
omitted entries, and retain successfully decoded contexts when a later
context is malformed.
Keep detailed fatal decode errors unratelimited while rate-limiting those
diagnostics for non-fatal records.
Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
---
v3:
- Validate and traverse CtxSize according to the firmware contract and
libcper implementation; accept aligned padding.
- Preserve the legacy format-0 header-only CtxSize encoding.
- Add wire-layout assertions and use Vera key/value terminology.
- Bound entry output to 32 entries per section and report omissions.
- Print partial Vera contexts when decode fails mid-section.
- Keep fatal diagnostics unratelimited and non-fatal diagnostics
ratelimited.
---
drivers/acpi/apei/Kconfig | 4 +-
drivers/acpi/apei/ghes-nvidia.c | 665 +++++++++++++++++++++++++++++++-
drivers/acpi/apei/ghes-nvidia.h | 69 +++-
3 files changed, 710 insertions(+), 28 deletions(-)
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index 428458c623f0..2dcea74c1677 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -80,8 +80,8 @@ config ACPI_APEI_GHES_NVIDIA
help
Support for decoding NVIDIA-specific CPER sections delivered via
the APEI GHES vendor record notifier chain. Registers a handler
- for the NVIDIA section GUID and logs error signatures, severity,
- socket, and diagnostic register address-value pairs.
+ for the NVIDIA Grace and Vera section GUIDs and logs decoded
+ metadata and per-context key/value entries (bounded).
Enable on NVIDIA server platforms (e.g. DGX, HGX) that expose
ACPI device NVDA2012 in their firmware tables.
diff --git a/drivers/acpi/apei/ghes-nvidia.c b/drivers/acpi/apei/ghes-nvidia.c
index e7cc912344fd..adf298dc722b 100644
--- a/drivers/acpi/apei/ghes-nvidia.c
+++ b/drivers/acpi/apei/ghes-nvidia.c
@@ -6,20 +6,54 @@
*/
#include <linux/acpi.h>
+#include <linux/align.h>
+#include <linux/limits.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/unaligned.h>
#include <linux/uuid.h>
+#include <kunit/visibility.h>
#include <acpi/ghes.h>
-#include <kunit/visibility.h>
#include "ghes-nvidia.h"
+#define NVIDIA_GHES_VERA_VERSION 1
+#define NVIDIA_GHES_VERA_CPU_INFO_MAJOR 0
+/* Cap section-wide entry log lines so a large dump cannot flood the console. */
+#define NVIDIA_GHES_MAX_PRINT_ENTRIES 32
+#define NVIDIA_GHES_MAX_PRINT_CONTEXTS 16
+
+static __printf(3, 4) void nvidia_ghes_decode_err(struct device *dev,
+ bool fatal, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (!dev)
+ return;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (fatal)
+ dev_err(dev, "%pV", &vaf);
+ else
+ dev_err_ratelimited(dev, "%pV", &vaf);
+
+ va_end(args);
+}
+
static const guid_t nvidia_grace_sec_guid =
GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
+static const guid_t nvidia_vera_sec_guid =
+ GUID_INIT(0x9068e568, 0x6ca0, 0x11f0,
+ 0xae, 0xaf, 0x15, 0x93, 0x43, 0x59, 0x1e, 0xac);
+
/* Grace CPER section wire layout (header without flexible register array). */
struct cper_sec_nvidia {
char signature[16];
@@ -35,15 +69,61 @@ struct cper_sec_nvidia {
static_assert(sizeof(struct cper_sec_nvidia) == 32);
+struct cper_sec_nvidia_vera_event {
+ u8 version;
+ u8 event_context_count;
+ u8 source_device_type;
+ u8 reserved;
+ __le16 event_type;
+ __le16 event_sub_type;
+ __le64 event_link_id;
+ char source_module_signature[16];
+} __packed;
+
+static_assert(sizeof(struct cper_sec_nvidia_vera_event) == 32);
+
+struct cper_sec_nvidia_vera_cpu_info {
+ __le16 info_version;
+ u8 info_size;
+ u8 socket_number;
+ __le32 architecture;
+ u8 chip_serial_number[16];
+ __le64 instance_base;
+} __packed;
+
+static_assert(sizeof(struct cper_sec_nvidia_vera_cpu_info) == 32);
+
+struct cper_sec_nvidia_vera_context {
+ __le32 context_size;
+ __le16 context_version;
+ __le16 reserved;
+ __le16 data_format_type;
+ __le16 data_format_version;
+ __le32 data_size;
+} __packed;
+
+static_assert(sizeof(struct cper_sec_nvidia_vera_context) == 16);
+
struct nvidia_ghes_private {
struct notifier_block nb;
struct device *dev;
};
VISIBLE_IF_KUNIT
-int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
- size_t len,
- struct nvidia_ghes_decoded *decoded)
+enum nvidia_ghes_format nvidia_ghes_format_from_guid(const guid_t *guid)
+{
+ if (guid_equal(guid, &nvidia_grace_sec_guid))
+ return NVIDIA_GHES_FORMAT_GRACE;
+ if (guid_equal(guid, &nvidia_vera_sec_guid))
+ return NVIDIA_GHES_FORMAT_VERA;
+ return NVIDIA_GHES_FORMAT_UNKNOWN;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_format_from_guid);
+
+static int __nvidia_ghes_decode_grace(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded,
+ bool fatal)
{
const struct cper_sec_nvidia *nvidia_err = buf;
size_t min_size;
@@ -52,19 +132,18 @@ int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
if (!buf || !decoded)
return -EINVAL;
if (len < sizeof(*nvidia_err)) {
- if (dev)
- dev_err_ratelimited(dev, "Section too small (%zu < %zu)\n",
- len, sizeof(*nvidia_err));
+ nvidia_ghes_decode_err(dev, fatal,
+ "Section too small (%zu < %zu)\n",
+ len, sizeof(*nvidia_err));
return -ENODATA;
}
number_regs = nvidia_err->number_regs;
min_size = struct_size(nvidia_err, regs, number_regs);
if (len < min_size) {
- if (dev)
- dev_err_ratelimited(dev,
- "Invalid number_regs %u (section size %zu, need %zu)\n",
- number_regs, len, min_size);
+ nvidia_ghes_decode_err(dev, fatal,
+ "Invalid number_regs %u (section size %zu, need %zu)\n",
+ number_regs, len, min_size);
return -ENODATA;
}
@@ -83,6 +162,14 @@ int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
return 0;
}
+
+VISIBLE_IF_KUNIT
+__maybe_unused int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded)
+{
+ return __nvidia_ghes_decode_grace(dev, buf, len, decoded, false);
+}
EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_grace);
VISIBLE_IF_KUNIT
@@ -106,6 +193,361 @@ int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
}
EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_grace_reg_pair);
+static int nvidia_ghes_vera_validate_context_data(u16 data_format_type,
+ u32 data_size)
+{
+ switch (data_format_type) {
+ case 0:
+ return 0;
+ case 1:
+ return data_size % 16 ? -EINVAL : 0;
+ case 2:
+ case 3:
+ return data_size % 8 ? -EINVAL : 0;
+ case 4:
+ return data_size % 4 ? -EINVAL : 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static void
+__nvidia_ghes_vera_cursor_init(const struct nvidia_ghes_decoded *decoded,
+ struct nvidia_ghes_vera_cursor *cursor, u8 count)
+{
+ *cursor = (struct nvidia_ghes_vera_cursor) {
+ .pos = decoded->vera_contexts,
+ .remaining = decoded->vera_contexts_len,
+ .contexts_left = count,
+ };
+}
+
+VISIBLE_IF_KUNIT
+void nvidia_ghes_vera_cursor_init(const struct nvidia_ghes_decoded *decoded,
+ struct nvidia_ghes_vera_cursor *cursor)
+{
+ if (!cursor)
+ return;
+
+ memset(cursor, 0, sizeof(*cursor));
+ if (!decoded || decoded->format != NVIDIA_GHES_FORMAT_VERA)
+ return;
+
+ __nvidia_ghes_vera_cursor_init(decoded, cursor,
+ decoded->valid_context_count);
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_cursor_init);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_cursor_next(struct nvidia_ghes_vera_cursor *cursor,
+ struct nvidia_ghes_vera_context *ctx)
+{
+ const struct cper_sec_nvidia_vera_context *wire;
+ size_t data_end_advance;
+ size_t advance;
+ u32 context_size;
+ u32 data_size;
+ u16 data_format_type;
+ int ret;
+
+ if (!cursor || !ctx)
+ return -EINVAL;
+ if (!cursor->contexts_left)
+ return 0;
+ if (!cursor->pos || cursor->remaining < sizeof(*wire))
+ return -ENODATA;
+
+ wire = (const void *)cursor->pos;
+ context_size = get_unaligned_le32(&wire->context_size);
+ data_format_type = get_unaligned_le16(&wire->data_format_type);
+ data_size = get_unaligned_le32(&wire->data_size);
+
+ if (context_size < sizeof(*wire) || !IS_ALIGNED(context_size, 16))
+ return -EINVAL;
+
+ ret = nvidia_ghes_vera_validate_context_data(data_format_type, data_size);
+ if (ret)
+ return ret;
+ if (check_add_overflow((size_t)data_size, sizeof(*wire),
+ &data_end_advance))
+ return -EOVERFLOW;
+ if (data_end_advance > cursor->remaining)
+ return -ENODATA;
+
+ /*
+ * CtxSize covers the header, data, and alignment padding. Older opaque
+ * records used a header-only CtxSize, so advance over their data directly.
+ */
+ if (data_format_type == 0 && context_size == sizeof(*wire)) {
+ advance = data_end_advance;
+ } else {
+ if (data_end_advance > context_size)
+ return -EINVAL;
+ if (context_size > cursor->remaining)
+ return -ENODATA;
+ advance = context_size;
+ }
+
+ *ctx = (struct nvidia_ghes_vera_context) {
+ .context_size = context_size,
+ .context_version = get_unaligned_le16(&wire->context_version),
+ .data_format_type = data_format_type,
+ .data_format_version = get_unaligned_le16(&wire->data_format_version),
+ .data_size = data_size,
+ .data = cursor->pos + sizeof(*wire),
+ };
+ cursor->pos += advance;
+ cursor->remaining -= advance;
+ cursor->contexts_left--;
+
+ return 1;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_cursor_next);
+
+static int __nvidia_ghes_decode_vera(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded,
+ bool fatal)
+{
+ const struct cper_sec_nvidia_vera_event *event = buf;
+ const struct cper_sec_nvidia_vera_cpu_info *cpu_info;
+ struct nvidia_ghes_vera_cursor cursor;
+ struct nvidia_ghes_vera_context context;
+ const u8 *bytes = buf;
+ size_t offset;
+ u16 info_version;
+ int ret;
+
+ if (!buf || !decoded)
+ return -EINVAL;
+ if (len < sizeof(*event)) {
+ nvidia_ghes_decode_err(dev, fatal,
+ "Vera event header truncated (%zu < %zu)\n",
+ len, sizeof(*event));
+ return -ENODATA;
+ }
+ if (event->version != NVIDIA_GHES_VERA_VERSION)
+ return -EOPNOTSUPP;
+ if (event->source_device_type != 0)
+ return -EOPNOTSUPP;
+
+ offset = sizeof(*event);
+ if (len - offset < sizeof(*cpu_info)) {
+ nvidia_ghes_decode_err(dev, fatal,
+ "Vera CPU info truncated (%zu < %zu)\n",
+ len - offset, sizeof(*cpu_info));
+ return -ENODATA;
+ }
+
+ cpu_info = (const void *)(bytes + offset);
+ info_version = get_unaligned_le16(&cpu_info->info_version);
+ if ((info_version >> 8) != NVIDIA_GHES_VERA_CPU_INFO_MAJOR)
+ return -EOPNOTSUPP;
+ if (cpu_info->info_size < sizeof(*cpu_info)) {
+ nvidia_ghes_decode_err(dev, fatal,
+ "Vera CPU info size %u smaller than header %zu\n",
+ cpu_info->info_size, sizeof(*cpu_info));
+ return -EINVAL;
+ }
+ if (len - offset < cpu_info->info_size) {
+ nvidia_ghes_decode_err(dev, fatal,
+ "Vera CPU info extends past section (%u > %zu)\n",
+ cpu_info->info_size, len - offset);
+ return -ENODATA;
+ }
+
+ offset += cpu_info->info_size;
+
+ memset(decoded, 0, sizeof(*decoded));
+ decoded->format = NVIDIA_GHES_FORMAT_VERA;
+ memcpy(decoded->signature, event->source_module_signature,
+ sizeof(event->source_module_signature));
+ decoded->signature[sizeof(event->source_module_signature)] = '\0';
+ decoded->event_context_count = event->event_context_count;
+ decoded->source_device_type = event->source_device_type;
+ decoded->event_type = get_unaligned_le16(&event->event_type);
+ decoded->event_sub_type = get_unaligned_le16(&event->event_sub_type);
+ decoded->event_link_id = get_unaligned_le64(&event->event_link_id);
+ decoded->socket = cpu_info->socket_number;
+ decoded->architecture = get_unaligned_le32(&cpu_info->architecture);
+ memcpy(decoded->chip_serial_number, cpu_info->chip_serial_number,
+ sizeof(cpu_info->chip_serial_number));
+ decoded->instance_base = get_unaligned_le64(&cpu_info->instance_base);
+ decoded->vera_contexts = bytes + offset;
+ decoded->vera_contexts_len = len - offset;
+ __nvidia_ghes_vera_cursor_init(decoded, &cursor,
+ event->event_context_count);
+ while ((ret = nvidia_ghes_vera_cursor_next(&cursor, &context)) > 0)
+ decoded->valid_context_count++;
+ if (ret < 0) {
+ nvidia_ghes_decode_err(dev, fatal,
+ "Vera context[%u] is malformed (ret=%d)\n",
+ decoded->valid_context_count, ret);
+ return ret;
+ }
+
+ if (cursor.remaining && dev)
+ dev_dbg(dev,
+ "Vera section has %zu trailing byte(s) after %u context(s)\n",
+ cursor.remaining, decoded->valid_context_count);
+
+ return 0;
+}
+
+VISIBLE_IF_KUNIT
+__maybe_unused int nvidia_ghes_decode_vera(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded)
+{
+ return __nvidia_ghes_decode_vera(dev, buf, len, decoded, false);
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_decode_vera);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_entry_count(const struct nvidia_ghes_vera_context *ctx)
+{
+ if (!ctx)
+ return -EINVAL;
+ if (ctx->data_size && !ctx->data)
+ return -EINVAL;
+ if (ctx->data_size > INT_MAX)
+ return -EOVERFLOW;
+
+ switch (ctx->data_format_type) {
+ case 0:
+ return 0;
+ case 1:
+ return ctx->data_size / 16;
+ case 2:
+ return ctx->data_size / 8;
+ case 3:
+ return ctx->data_size / 8;
+ case 4:
+ return ctx->data_size / 4;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_entry_count);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u64_pair(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u64 *key, u64 *val)
+{
+ int count;
+
+ if (!ctx || !key || !val || ctx->data_format_type != 1)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *key = get_unaligned_le64(ctx->data + index * 16);
+ *val = get_unaligned_le64(ctx->data + index * 16 + 8);
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_u64_pair);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u32_pair(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u32 *key, u32 *val)
+{
+ int count;
+
+ if (!ctx || !key || !val || ctx->data_format_type != 2)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *key = get_unaligned_le32(ctx->data + index * 8);
+ *val = get_unaligned_le32(ctx->data + index * 8 + 4);
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_u32_pair);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u64_value(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u64 *val)
+{
+ int count;
+
+ if (!ctx || !val || ctx->data_format_type != 3)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *val = get_unaligned_le64(ctx->data + index * 8);
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_u64_value);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u32_value(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u32 *val)
+{
+ int count;
+
+ if (!ctx || !val || ctx->data_format_type != 4)
+ return -EINVAL;
+
+ count = nvidia_ghes_vera_context_entry_count(ctx);
+ if (count < 0)
+ return count;
+ if (index >= count)
+ return -ERANGE;
+
+ *val = get_unaligned_le32(ctx->data + index * 4);
+
+ return 0;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_u32_value);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_print_count(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int *remaining)
+{
+ int entries;
+ int count;
+
+ if (!remaining)
+ return -EINVAL;
+
+ entries = nvidia_ghes_vera_context_entry_count(ctx);
+ if (entries < 0)
+ return entries;
+
+ count = min_t(unsigned int, entries, *remaining);
+ *remaining -= count;
+
+ return count;
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_context_print_count);
+
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_print_context_count(const struct nvidia_ghes_decoded *decoded)
+{
+ if (!decoded || decoded->format != NVIDIA_GHES_FORMAT_VERA)
+ return -EINVAL;
+
+ return min_t(unsigned int, decoded->valid_context_count,
+ NVIDIA_GHES_MAX_PRINT_CONTEXTS);
+}
+EXPORT_SYMBOL_IF_KUNIT(nvidia_ghes_vera_print_context_count);
+
static void nvidia_ghes_print_grace(struct device *dev,
const struct nvidia_ghes_decoded *decoded,
bool fatal)
@@ -113,13 +555,16 @@ static void nvidia_ghes_print_grace(struct device *dev,
const char *level = fatal ? KERN_ERR : KERN_INFO;
u64 addr, val;
- dev_printk(level, dev, "signature: %s\n", decoded->signature);
+ dev_printk(level, dev, "signature: %*pE\n",
+ (int)strnlen(decoded->signature, sizeof(decoded->signature)),
+ decoded->signature);
dev_printk(level, dev, "error_type: %u\n", decoded->error_type);
dev_printk(level, dev, "error_instance: %u\n", decoded->error_instance);
dev_printk(level, dev, "severity: %u\n", decoded->severity);
dev_printk(level, dev, "socket: %u\n", decoded->socket);
dev_printk(level, dev, "number_regs: %u\n", decoded->number_regs);
- dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
+ dev_printk(level, dev, "instance_base: 0x%016llx\n",
+ decoded->instance_base);
for (int i = 0; i < decoded->number_regs; i++) {
if (nvidia_ghes_grace_reg_pair(decoded, i, &addr, &val))
@@ -129,12 +574,157 @@ static void nvidia_ghes_print_grace(struct device *dev,
}
}
+static void nvidia_ghes_print_vera(struct device *dev,
+ const struct nvidia_ghes_decoded *decoded,
+ bool fatal, unsigned long ghes_severity)
+{
+ struct nvidia_ghes_vera_cursor cursor;
+ struct nvidia_ghes_vera_context context;
+ const char *level = fatal ? KERN_ERR : KERN_INFO;
+ unsigned int remaining = NVIDIA_GHES_MAX_PRINT_ENTRIES;
+ unsigned int print_contexts;
+
+ dev_printk(level, dev, "signature: %*pE\n",
+ (int)strnlen(decoded->signature, sizeof(decoded->signature)),
+ decoded->signature);
+ dev_printk(level, dev, "event_type: %u\n", decoded->event_type);
+ dev_printk(level, dev, "event_sub_type: %u\n", decoded->event_sub_type);
+ dev_printk(level, dev, "ghes_severity: %lu\n", ghes_severity);
+ dev_printk(level, dev, "event_link_id: 0x%016llx\n",
+ decoded->event_link_id);
+ dev_printk(level, dev, "socket: %u\n", decoded->socket);
+ dev_printk(level, dev, "architecture: 0x%x\n", decoded->architecture);
+ dev_printk(level, dev, "chip_serial_number: %*phN\n",
+ (int)sizeof(decoded->chip_serial_number),
+ decoded->chip_serial_number);
+ dev_printk(level, dev, "instance_base: 0x%016llx\n", decoded->instance_base);
+ dev_printk(level, dev, "event_context_count: %u\n", decoded->event_context_count);
+ if (decoded->valid_context_count != decoded->event_context_count)
+ dev_printk(level, dev, "valid_context_count: %u\n",
+ decoded->valid_context_count);
+
+ print_contexts = nvidia_ghes_vera_print_context_count(decoded);
+ nvidia_ghes_vera_cursor_init(decoded, &cursor);
+ for (int i = 0; i < print_contexts; i++) {
+ const struct nvidia_ghes_vera_context *ctx = &context;
+ int entries;
+ int print_n;
+
+ if (nvidia_ghes_vera_cursor_next(&cursor, &context) != 1)
+ break;
+ entries = nvidia_ghes_vera_context_entry_count(ctx);
+ dev_printk(level, dev,
+ "context[%d]: version=%u format=%u format_version=%u context_size=%u data_size=%u\n",
+ i, ctx->context_version, ctx->data_format_type,
+ ctx->data_format_version, ctx->context_size, ctx->data_size);
+ if (ctx->data_format_type == 0 && ctx->data_size > 0) {
+ int prefix_len = ctx->data_size > 16 ? 16 : ctx->data_size;
+
+ dev_printk(level, dev, "context[%d]_opaque_prefix: %*phN\n",
+ i, prefix_len, ctx->data);
+ continue;
+ }
+ if (entries < 0)
+ continue;
+
+ dev_printk(level, dev, "context[%d]_entries: %d\n", i, entries);
+ print_n = nvidia_ghes_vera_context_print_count(ctx, &remaining);
+ if (print_n < 0)
+ continue;
+ for (int j = 0; j < print_n; j++) {
+ u64 key64, val64;
+ u32 key32, val32;
+
+ switch (ctx->data_format_type) {
+ case 1:
+ if (nvidia_ghes_vera_context_u64_pair(ctx, j, &key64, &val64))
+ break;
+ dev_printk(level, dev,
+ "context[%d]_entry[%d]: key=0x%016llx value=0x%016llx\n",
+ i, j, key64, val64);
+ break;
+ case 2:
+ if (nvidia_ghes_vera_context_u32_pair(ctx, j, &key32, &val32))
+ break;
+ dev_printk(level, dev,
+ "context[%d]_entry[%d]: key=0x%08x value=0x%08x\n",
+ i, j, key32, val32);
+ break;
+ case 3:
+ if (nvidia_ghes_vera_context_u64_value(ctx, j, &val64))
+ break;
+ dev_printk(level, dev,
+ "context[%d]_entry[%d]: value=0x%016llx\n",
+ i, j, val64);
+ break;
+ case 4:
+ if (nvidia_ghes_vera_context_u32_value(ctx, j, &val32))
+ break;
+ dev_printk(level, dev,
+ "context[%d]_entry[%d]: value=0x%08x\n",
+ i, j, val32);
+ break;
+ default:
+ break;
+ }
+ }
+ if (entries > print_n)
+ dev_printk(level, dev,
+ "context[%d]_entries_omitted: %d\n",
+ i, entries - print_n);
+ }
+ if (decoded->valid_context_count > print_contexts)
+ dev_printk(level, dev, "contexts_omitted: %u\n",
+ decoded->valid_context_count - print_contexts);
+}
+
+static void nvidia_ghes_log_decode_failure(struct device *dev,
+ enum nvidia_ghes_format format,
+ bool fatal, u32 len, int ret)
+{
+ const char *level = fatal ? KERN_ERR : KERN_INFO;
+
+ if (ret == -EOPNOTSUPP && format == NVIDIA_GHES_FORMAT_VERA) {
+ if (fatal)
+ dev_printk(level, dev,
+ "Unsupported NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ else
+ dev_info_ratelimited(dev,
+ "Unsupported NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ return;
+ }
+
+ if (format == NVIDIA_GHES_FORMAT_GRACE) {
+ if (fatal)
+ dev_printk(level, dev,
+ "Malformed NVIDIA Grace CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ else
+ dev_err_ratelimited(dev,
+ "Malformed NVIDIA Grace CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ return;
+ }
+
+ if (fatal)
+ dev_printk(level, dev,
+ "Malformed NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+ else
+ dev_err_ratelimited(dev,
+ "Malformed NVIDIA Vera CPER section, error_data_length: %u, ret: %d\n",
+ len, ret);
+}
+
static int nvidia_ghes_notify(struct notifier_block *nb,
unsigned long event, void *data)
{
struct acpi_hest_generic_data *gdata = data;
struct nvidia_ghes_decoded decoded = {};
struct nvidia_ghes_private *priv;
+ enum nvidia_ghes_format format;
const void *payload;
guid_t sec_guid;
u32 len;
@@ -142,25 +732,58 @@ static int nvidia_ghes_notify(struct notifier_block *nb,
bool fatal;
import_guid(&sec_guid, gdata->section_type);
- if (!guid_equal(&sec_guid, &nvidia_grace_sec_guid))
+ format = nvidia_ghes_format_from_guid(&sec_guid);
+ if (format == NVIDIA_GHES_FORMAT_UNKNOWN)
return NOTIFY_DONE;
priv = container_of(nb, struct nvidia_ghes_private, nb);
len = acpi_hest_get_error_length(gdata);
+
payload = acpi_hest_get_payload(gdata);
fatal = event >= GHES_SEV_RECOVERABLE;
- ret = nvidia_ghes_decode_grace(priv->dev, payload, len, &decoded);
+ switch (format) {
+ case NVIDIA_GHES_FORMAT_GRACE:
+ ret = __nvidia_ghes_decode_grace(priv->dev, payload, len,
+ &decoded, fatal);
+ break;
+ case NVIDIA_GHES_FORMAT_VERA:
+ ret = __nvidia_ghes_decode_vera(priv->dev, payload, len,
+ &decoded, fatal);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
if (ret) {
- dev_err_ratelimited(priv->dev,
- "Malformed NVIDIA CPER section, error_data_length: %u, ret: %d\n",
- len, ret);
+ nvidia_ghes_log_decode_failure(priv->dev, format, fatal, len, ret);
+ /*
+ * Print any contexts successfully decoded before the failure.
+ * Raw section bytes remain available via log_non_standard_event().
+ */
+ if (format == NVIDIA_GHES_FORMAT_VERA &&
+ decoded.format == NVIDIA_GHES_FORMAT_VERA &&
+ decoded.valid_context_count > 0) {
+ dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
+ "NVIDIA Vera CPER section (partial), error_data_length: %u\n",
+ len);
+ nvidia_ghes_print_vera(priv->dev, &decoded, fatal, event);
+ }
return NOTIFY_OK;
}
- dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
- "NVIDIA CPER section, error_data_length: %u\n", len);
- nvidia_ghes_print_grace(priv->dev, &decoded, fatal);
+ if (format == NVIDIA_GHES_FORMAT_GRACE)
+ dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
+ "NVIDIA Grace CPER section, error_data_length: %u\n", len);
+ else
+ dev_printk(fatal ? KERN_ERR : KERN_INFO, priv->dev,
+ "NVIDIA Vera CPER section, error_data_length: %u\n", len);
+
+ if (format == NVIDIA_GHES_FORMAT_VERA)
+ nvidia_ghes_print_vera(priv->dev, &decoded, fatal, event);
+ else
+ nvidia_ghes_print_grace(priv->dev, &decoded, fatal);
return NOTIFY_OK;
}
diff --git a/drivers/acpi/apei/ghes-nvidia.h b/drivers/acpi/apei/ghes-nvidia.h
index 965abe3d5c49..b5f0312499fc 100644
--- a/drivers/acpi/apei/ghes-nvidia.h
+++ b/drivers/acpi/apei/ghes-nvidia.h
@@ -4,13 +4,13 @@
#include <linux/build_bug.h>
#include <linux/types.h>
+#include <linux/uuid.h>
#include <kunit/visibility.h>
-struct device;
-
enum nvidia_ghes_format {
NVIDIA_GHES_FORMAT_UNKNOWN,
NVIDIA_GHES_FORMAT_GRACE,
+ NVIDIA_GHES_FORMAT_VERA,
};
struct nvidia_ghes_grace_reg {
@@ -18,18 +18,52 @@ struct nvidia_ghes_grace_reg {
__le64 val;
} __packed;
+struct nvidia_ghes_vera_context {
+ u32 context_size;
+ u16 context_version;
+ u16 data_format_type;
+ u16 data_format_version;
+ u32 data_size;
+ const u8 *data;
+};
+
+struct nvidia_ghes_vera_cursor {
+ const u8 *pos;
+ size_t remaining;
+ u8 contexts_left;
+};
+
struct nvidia_ghes_decoded {
enum nvidia_ghes_format format;
char signature[17];
u16 error_type;
u16 error_instance;
+ u16 event_type;
+ u16 event_sub_type;
u8 severity;
u8 socket;
u8 number_regs;
+ u8 source_device_type;
+ u8 event_context_count;
+ u8 valid_context_count;
+ u32 architecture;
+ u64 event_link_id;
u64 instance_base;
+ u8 chip_serial_number[16];
const struct nvidia_ghes_grace_reg *grace_regs;
+ const u8 *vera_contexts;
+ size_t vera_contexts_len;
};
+/* Bound the notifier stack frame. */
+static_assert(sizeof(struct nvidia_ghes_decoded) <= 128);
+
+struct device;
+
+VISIBLE_IF_KUNIT enum nvidia_ghes_format nvidia_ghes_format_from_guid(const guid_t *guid);
+VISIBLE_IF_KUNIT int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded);
/**
* nvidia_ghes_grace_reg_pair() - Read one Grace register address/value pair
* @decoded: Decoded Grace section; format must be NVIDIA_GHES_FORMAT_GRACE
@@ -41,10 +75,35 @@ struct nvidia_ghes_decoded {
* Returns -EINVAL for bad arguments / missing grace_regs, -ERANGE for
* index >= number_regs, and 0 on success.
*/
-VISIBLE_IF_KUNIT int nvidia_ghes_decode_grace(struct device *dev, const void *buf,
- size_t len,
- struct nvidia_ghes_decoded *decoded);
VISIBLE_IF_KUNIT int nvidia_ghes_grace_reg_pair(const struct nvidia_ghes_decoded *decoded,
unsigned int index, u64 *addr, u64 *val);
+VISIBLE_IF_KUNIT int nvidia_ghes_decode_vera(struct device *dev, const void *buf,
+ size_t len,
+ struct nvidia_ghes_decoded *decoded);
+VISIBLE_IF_KUNIT
+void nvidia_ghes_vera_cursor_init(const struct nvidia_ghes_decoded *decoded,
+ struct nvidia_ghes_vera_cursor *cursor);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_cursor_next(struct nvidia_ghes_vera_cursor *cursor,
+ struct nvidia_ghes_vera_context *ctx);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_print_context_count(const struct nvidia_ghes_decoded *decoded);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_entry_count(const struct nvidia_ghes_vera_context *ctx);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u64_pair(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u64 *key, u64 *val);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u32_pair(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u32 *key, u32 *val);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u64_value(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u64 *val);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_u32_value(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int index, u32 *val);
+VISIBLE_IF_KUNIT
+int nvidia_ghes_vera_context_print_count(const struct nvidia_ghes_vera_context *ctx,
+ unsigned int *remaining);
#endif
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH v3 3/3] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage
2026-08-04 12:23 ` [PATCH v3 0/3] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoding Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 1/3] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 2/3] ACPI: APEI: GHES: Add NVIDIA Vera decoder Kai-Heng Feng
@ 2026-08-04 12:23 ` Kai-Heng Feng
2 siblings, 0 replies; 13+ messages in thread
From: Kai-Heng Feng @ 2026-08-04 12:23 UTC (permalink / raw)
To: rafael, linux-acpi
Cc: tony.luck, bp, guohanjun, mchehab, xueshuai, lenb, kees,
gustavoars, linux-hardening, linux-kernel, Kai-Heng Feng
Verify the Grace and Vera decoders against binary records captured from
firmware and synthetic records for contract boundaries. Cover successful
decoding, malformed inputs, unaligned Grace payloads, accessors, and GUID
routing.
Exercise Vera CtxSize alignment, padding traversal into the next context,
section bounds, retained contexts before a later failure, and the legacy
format-0 header-only encoding. Verify that the 32-entry print budget is
shared across the complete Vera section.
Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com>
---
v3:
- Add unaligned Grace decode and grace_reg_pair coverage.
- Cover aligned Vera padding, next-context traversal, bounds, retained
contexts before failure, and the legacy opaque encoding.
- Exercise driver-exported Vera accessors rather than test-only copies.
- Reject accessor inputs with positive data size and no backing data.
- Verify section-wide print-budget accounting.
- Document the ACPI-capable kunit.py invocation.
- Keep EINJ firmware validation in ras-tools.
---
drivers/acpi/apei/Kconfig | 20 +
drivers/acpi/apei/Makefile | 1 +
drivers/acpi/apei/ghes-nvidia-test-fixtures.h | 233 +++
drivers/acpi/apei/ghes-nvidia-test.c | 1274 +++++++++++++++++
4 files changed, 1528 insertions(+)
create mode 100644 drivers/acpi/apei/ghes-nvidia-test-fixtures.h
create mode 100644 drivers/acpi/apei/ghes-nvidia-test.c
diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig
index 2dcea74c1677..3374b4f0aa6e 100644
--- a/drivers/acpi/apei/Kconfig
+++ b/drivers/acpi/apei/Kconfig
@@ -88,6 +88,26 @@ config ACPI_APEI_GHES_NVIDIA
If unsure, say N.
+config ACPI_APEI_GHES_NVIDIA_KUNIT_TEST
+ tristate "NVIDIA GHES vendor record handler KUnit tests" if !KUNIT_ALL_TESTS
+ depends on KUNIT && ACPI_APEI_GHES_NVIDIA
+ default KUNIT_ALL_TESTS
+ help
+ KUnit tests for NVIDIA GHES vendor CPER parser helpers.
+ They cover Grace helper decoding, Vera context parsing/accessors,
+ and GUID section routing.
+ They are enabled only for KUnit test builds.
+ They are not intended for production firmware paths.
+
+ Run with an ACPI-capable architecture, e.g.:
+ tools/testing/kunit/kunit.py run --arch=x86_64 \
+ --kconfig_add CONFIG_ACPI=y --kconfig_add CONFIG_ACPI_APEI=y \
+ --kconfig_add CONFIG_ACPI_APEI_GHES=y \
+ --kconfig_add CONFIG_ACPI_APEI_GHES_NVIDIA=y \
+ --kconfig_add CONFIG_ACPI_APEI_GHES_NVIDIA_KUNIT_TEST=y
+
+ If unsure, say N.
+
config ACPI_APEI_ERST_DEBUG
tristate "APEI Error Record Serialization Table (ERST) Debug Support"
depends on ACPI_APEI
diff --git a/drivers/acpi/apei/Makefile b/drivers/acpi/apei/Makefile
index 66588d6be56f..210a57242044 100644
--- a/drivers/acpi/apei/Makefile
+++ b/drivers/acpi/apei/Makefile
@@ -11,5 +11,6 @@ einj-y := einj-core.o
einj-$(CONFIG_ACPI_APEI_EINJ_CXL) += einj-cxl.o
obj-$(CONFIG_ACPI_APEI_ERST_DEBUG) += erst-dbg.o
obj-$(CONFIG_ACPI_APEI_GHES_NVIDIA) += ghes-nvidia.o
+obj-$(CONFIG_ACPI_APEI_GHES_NVIDIA_KUNIT_TEST) += ghes-nvidia-test.o
apei-y := apei-base.o hest.o erst.o bert.o
diff --git a/drivers/acpi/apei/ghes-nvidia-test-fixtures.h b/drivers/acpi/apei/ghes-nvidia-test-fixtures.h
new file mode 100644
index 000000000000..705c879c785e
--- /dev/null
+++ b/drivers/acpi/apei/ghes-nvidia-test-fixtures.h
@@ -0,0 +1,233 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef GHES_NVIDIA_TEST_FIXTURES_H
+#define GHES_NVIDIA_TEST_FIXTURES_H
+
+#include <linux/types.h>
+
+/*
+ * Source: cper-tools/test_data/vera_msft.json
+ * cper-tools commit: bf18f75406422e73e11b8b1ff80e3c9d9cfb032a
+ * Boundary: CPER.Oem.Nvidia.Unknown.Data.
+ * Each payload equals the Vera GUID section body located in DiagnosticData.
+ */
+
+/* Redfish Id 10, signature L1 RESET, real sample. */
+/* Vera section index 0, offset 200, length 80. */
+/* sha256 fc11639e506eb8d79e7ff16a0556463ac4b1304bface935c0cd13efc0b3e317f. */
+static const u8 vera_l1_reset_id10[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x4c, 0x31, 0x20, 0x52, 0x45, 0x53, 0x45, 0x54,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x15, 0xfe, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+/* Redfish Id 40, signature CRASHDUMP-ID, real sample. */
+/* Vera section index 0, offset 200, length 304. */
+/* sha256 3ba93201740e6bd868b48ec40c3d52b235c1ebc59528fc81974079c21bf2a6a2. */
+static const u8 vera_crashdump_id_id40[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x41, 0x53, 0x48, 0x44, 0x55, 0x4d,
+ 0x50, 0x2d, 0x49, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x83, 0x72, 0x65, 0x6c, 0x5f, 0x37, 0x33, 0x2e, 0x37,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x64, 0x69, 0x72, 0x74,
+ 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85,
+ 0x44, 0x24, 0x4e, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x10, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x07, 0x41, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x07, 0x03, 0x03, 0x14, 0xfe, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x20, 0x00, 0x15, 0xfe, 0x00, 0x00,
+ 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0a, 0x04, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x18, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00,
+ 0x40, 0x60, 0x30, 0x06, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0a, 0x04, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x24, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00,
+ 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x0a, 0x04, 0x00,
+ 0x14, 0xfe, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x2c, 0x0a, 0x04, 0x00, 0x14, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+/* Redfish Id 41, signature CRASHDUMP-S1, real sample. */
+/* Vera section index 0, offset 200, length 2008. */
+/* sha256 54db30564d3324240f088a1533c113407f1d81bcd652ef0d27d958c490aee5ce. */
+static const u8 vera_crashdump_s1_id41[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x41, 0x53, 0x48, 0x44, 0x55, 0x4d,
+ 0x50, 0x2d, 0x53, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x07, 0x41, 0x01, 0x00, 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x88, 0x07, 0x00, 0x00, 0x53, 0x56, 0x7f, 0xa1,
+ 0xd1, 0xd7, 0x7f, 0xe5, 0xaa, 0x34, 0xde, 0x7d, 0xe9, 0x3e, 0xc4, 0xd4,
+ 0x92, 0xe8, 0x10, 0xa1, 0x4c, 0xfc, 0xf9, 0x2e, 0x02, 0x87, 0xc5, 0x6e,
+ 0x44, 0xfb, 0xb5, 0x59, 0xac, 0xfa, 0xa8, 0x15, 0x75, 0x46, 0x2d, 0x67,
+ 0x84, 0xe0, 0x78, 0x14, 0xe4, 0x18, 0x8e, 0x30, 0x46, 0xff, 0xbe, 0x5e,
+ 0x15, 0x3e, 0x61, 0xf4, 0xcc, 0x3d, 0x2e, 0xdf, 0x1b, 0xf9, 0x1f, 0x1c,
+ 0xa3, 0xf8, 0x35, 0x34, 0xe1, 0x4c, 0xb7, 0xd0, 0xcc, 0xda, 0xf1, 0xbc,
+ 0x02, 0x8d, 0xc2, 0x6b, 0x5d, 0x56, 0xd4, 0x48, 0x89, 0x7f, 0xb2, 0x64,
+ 0xda, 0xa0, 0xf4, 0xed, 0xef, 0xf7, 0x5f, 0x29, 0x38, 0x51, 0x59, 0x35,
+ 0x1f, 0x52, 0x63, 0xa8, 0x45, 0x02, 0x7a, 0x24, 0xf6, 0x26, 0x83, 0x29,
+ 0x65, 0x01, 0x77, 0x0e, 0xb2, 0x5e, 0xac, 0xf2, 0xd4, 0xc0, 0x09, 0x94,
+ 0x5c, 0x4d, 0x55, 0xaf, 0xcf, 0xb7, 0x8c, 0xad, 0x47, 0xcf, 0x02, 0x2c,
+ 0x8c, 0x0f, 0x28, 0xf1, 0x4c, 0x15, 0x66, 0xb0, 0xb4, 0x7d, 0xd1, 0xf6,
+ 0x90, 0xc8, 0x5e, 0x64, 0x16, 0x9f, 0x5f, 0x3f, 0x50, 0xc9, 0x62, 0x91,
+ 0x87, 0xd8, 0xd7, 0xe9, 0x9f, 0x7b, 0xcd, 0xc9, 0xfe, 0xa1, 0xd3, 0xf2,
+ 0xc2, 0x95, 0xc7, 0x37, 0x34, 0x6c, 0xc4, 0xc0, 0x53, 0x56, 0x01, 0x2c,
+ 0x96, 0x63, 0xdb, 0x2c, 0xea, 0x09, 0xa0, 0xaa, 0xe5, 0xe2, 0x48, 0x18,
+ 0x3c, 0x68, 0xf9, 0xa0, 0x7f, 0x98, 0x5a, 0xe3, 0xe8, 0x8e, 0x83, 0x71,
+ 0x51, 0x79, 0xc3, 0x14, 0xf1, 0xf4, 0xac, 0xd5, 0xd9, 0x8c, 0xab, 0x33,
+ 0x4e, 0x9f, 0xc2, 0xb7, 0xed, 0x6f, 0x78, 0x81, 0x77, 0x34, 0xb8, 0xea,
+ 0xb0, 0x71, 0xa1, 0x4d, 0x08, 0x40, 0xad, 0x4c, 0x95, 0x6a, 0x21, 0x96,
+ 0x07, 0x12, 0x62, 0xed, 0x42, 0xa9, 0xe4, 0xa8, 0x88, 0xc7, 0x81, 0xff,
+ 0x88, 0x07, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x83, 0xbc, 0x15, 0xd9, 0x0b, 0x6b, 0x3b, 0x3b, 0xff, 0x77, 0xb2, 0x7c,
+ 0x0c, 0xdd, 0xfb, 0x00, 0x7e, 0x0a, 0x88, 0x47, 0x8a, 0xf2, 0xf8, 0xc5,
+ 0x07, 0x51, 0x0e, 0xd7, 0xcb, 0x78, 0xea, 0x89, 0x37, 0x2a, 0x44, 0x0d,
+ 0x74, 0x4f, 0x9e, 0x6d, 0x2c, 0xf9, 0x53, 0xed, 0x38, 0x34, 0xca, 0x38,
+ 0x7d, 0x96, 0xfe, 0x9a, 0x9b, 0x23, 0x3b, 0x17, 0x17, 0x69, 0xe6, 0x6c,
+ 0x0e, 0xe6, 0x71, 0xf5, 0x59, 0xf6, 0xc4, 0x82, 0xd6, 0x83, 0xb1, 0x76,
+ 0x41, 0x22, 0x53, 0x72, 0x9f, 0x85, 0x0d, 0xdc, 0x67, 0x22, 0x35, 0xd3,
+ 0xe3, 0xaf, 0x10, 0x50, 0xc8, 0xe1, 0xb0, 0xea, 0xc9, 0x1a, 0x3e, 0x02,
+ 0x36, 0xea, 0x94, 0xb1, 0x27, 0xfc, 0xc9, 0x61, 0xcb, 0x6f, 0xb0, 0x50,
+ 0xe1, 0x85, 0x1c, 0x2d, 0x5f, 0xb5, 0x63, 0x38, 0x69, 0x02, 0xb3, 0x7d,
+ 0xb3, 0xb5, 0xe0, 0xfe, 0x5a, 0xf1, 0x0d, 0x83, 0xd9, 0x51, 0xcc, 0xa2,
+ 0xb5, 0x41, 0x3b, 0xd1, 0xf5, 0x58, 0xe8, 0x54, 0x49, 0x78, 0xc1, 0x8b,
+ 0x70, 0xf1, 0x94, 0x45, 0x18, 0x4b, 0x19, 0x58, 0xe6, 0x0a, 0x08, 0x27,
+ 0xe7, 0x95, 0xd6, 0x82, 0xb7, 0x8c, 0xae, 0xae, 0x58, 0x33, 0xc2, 0x02,
+ 0xf8, 0xfb, 0x8a, 0x73, 0xe4, 0x0b, 0x75, 0x9b, 0x00, 0x76, 0x14, 0x31,
+ 0x5e, 0xf5, 0xf0, 0x8b, 0xb7, 0x6b, 0xaf, 0xdd, 0x8b, 0x52, 0xc9, 0x79,
+ 0xe7, 0xd3, 0x17, 0x90, 0xa4, 0xb0, 0xa0, 0x97, 0xcf, 0xba, 0x37, 0x1d,
+ 0xe0, 0xbf, 0x31, 0xff, 0xe9, 0x3a, 0x50, 0x5b, 0xf8, 0x94, 0xd0, 0x33,
+ 0xf9, 0x2d, 0x2e, 0x54, 0xfa, 0x11, 0x63, 0x06, 0xf2, 0x32, 0xb9, 0x7b,
+ 0xd9, 0xc6, 0x12, 0x60, 0xa6, 0x16, 0xac, 0xc8, 0x02, 0xf7, 0xd6, 0xd5,
+ 0xb6, 0x2e, 0xf0, 0xb3, 0x05, 0x39, 0x47, 0xd4, 0xa9, 0xed, 0x1a, 0xca,
+ 0x4c, 0x4f, 0x83, 0x2d, 0xcc, 0x5c, 0xa7, 0x02, 0x01, 0x84, 0xc9, 0x07,
+ 0xa5, 0x6a, 0xf7, 0xbe, 0x3d, 0xc2, 0x09, 0xe8, 0xc8, 0xf8, 0x4b, 0x71,
+ 0xa8, 0x79, 0x6c, 0x68, 0xda, 0x35, 0xe1, 0xd2, 0x9d, 0xa6, 0xb3, 0x70,
+ 0xf6, 0xcb, 0x66, 0x41, 0xde, 0x86, 0x11, 0x2a, 0xd0, 0xa1, 0x84, 0x99,
+ 0x5b, 0xdf, 0x74, 0x26, 0x18, 0xba, 0x6f, 0x3e, 0x46, 0xef, 0xe7, 0xd2,
+ 0xfd, 0xe5, 0xa8, 0xba, 0xff, 0xe4, 0xe0, 0x50, 0x9f, 0x23, 0xe5, 0x03,
+ 0x81, 0xc2, 0xab, 0x74, 0xd8, 0xc4, 0xe9, 0xd5, 0xf6, 0x7f, 0x86, 0x6b,
+ 0x06, 0xfc, 0x62, 0x0f, 0xeb, 0xa3, 0xc4, 0xa0, 0xad, 0x70, 0xad, 0x9b,
+ 0xdf, 0xd1, 0x75, 0x28, 0xdc, 0xf2, 0x24, 0x87, 0x6e, 0x85, 0x67, 0xfb,
+ 0xbb, 0x38, 0x86, 0xd8, 0x77, 0x15, 0x35, 0x41, 0x53, 0x69, 0x71, 0xd9,
+ 0x33, 0x00, 0x2c, 0x56, 0x45, 0xfd, 0xfc, 0xea, 0xf9, 0x8f, 0xd7, 0x04,
+ 0x6a, 0x8d, 0xf5, 0x4b, 0xd1, 0x5a, 0x66, 0x7e, 0xe0, 0xeb, 0x06, 0x80,
+ 0xc2, 0x14, 0x00, 0x49, 0x1c, 0x62, 0x54, 0xdb, 0x4b, 0x80, 0xc1, 0xa1,
+ 0xe3, 0x36, 0xc0, 0x58, 0x0f, 0x6c, 0x65, 0xfc, 0xc8, 0x4d, 0x85, 0xa5,
+ 0x5e, 0xb2, 0xbd, 0x5e, 0x2d, 0x47, 0x14, 0x77, 0x32, 0x7e, 0x39, 0x0b,
+ 0xba, 0xcd, 0x30, 0x4b, 0x8e, 0x3b, 0x6d, 0x66, 0x59, 0x73, 0x16, 0x0b,
+ 0x23, 0x99, 0xb5, 0x09, 0xb7, 0x8f, 0xb2, 0x96, 0x1f, 0x74, 0x08, 0x36,
+ 0x1f, 0xcd, 0x46, 0x90, 0x1b, 0xe9, 0xfe, 0xe1, 0xcb, 0x7e, 0x06, 0x98,
+ 0x74, 0x47, 0x2f, 0x57, 0x0b, 0x72, 0xea, 0x0d, 0x25, 0xed, 0x92, 0x70,
+ 0x0d, 0x00, 0x5f, 0x48, 0xe3, 0xff, 0x3f, 0x39, 0xfc, 0xa9, 0x66, 0xa9,
+ 0x95, 0x33, 0xcd, 0x8b, 0x22, 0x11, 0xd9, 0xff, 0x7e, 0x5c, 0x9d, 0x80,
+ 0xc5, 0xed, 0x99, 0x78, 0x5c, 0x33, 0xdd, 0x0e, 0xca, 0x6a, 0xe2, 0xb6,
+ 0x3a, 0x66, 0xbd, 0x1d, 0x60, 0x96, 0x28, 0x20, 0xbd, 0x8c, 0xf4, 0x6c,
+ 0xcb, 0xc3, 0x9b, 0x19, 0x78, 0xd7, 0x57, 0xa3, 0xc3, 0x12, 0x95, 0x11,
+ 0x3d, 0xab, 0x44, 0x0d, 0x78, 0xbb, 0x2e, 0x0b, 0x4e, 0x0c, 0x94, 0x75,
+ 0xe7, 0x8f, 0x5c, 0xad, 0xe4, 0x2f, 0x9c, 0x26, 0x00, 0x01, 0x71, 0xe1,
+ 0x36, 0xcf, 0xb1, 0x00, 0x8f, 0xf7, 0x86, 0x79, 0x2e, 0x79, 0x9e, 0xd5,
+ 0x8b, 0x53, 0x0d, 0x28, 0x6c, 0xb9, 0xa2, 0xdf, 0x04, 0xe3, 0x78, 0x6d,
+ 0xcb, 0xb7, 0xf9, 0xea, 0x20, 0x4a, 0x5d, 0x92, 0x64, 0x88, 0x7d, 0xc4,
+ 0x5a, 0x21, 0x86, 0x12, 0x46, 0x49, 0x6d, 0xbc, 0x03, 0xd5, 0xeb, 0xf6,
+ 0xe3, 0x38, 0x4a, 0xaa, 0xce, 0x45, 0x91, 0x6a, 0xc0, 0x59, 0x1b, 0x6c,
+ 0x98, 0xb2, 0xe0, 0x29, 0x91, 0x01, 0xb5, 0x44, 0x48, 0xa6, 0xd3, 0x78,
+ 0x5a, 0xe1, 0xe4, 0x8c, 0xd9, 0x33, 0xa0, 0xd5, 0xe1, 0x83, 0xd6, 0x5a,
+ 0xe3, 0x5f, 0xe6, 0xf8, 0xfd, 0x21, 0x12, 0xff, 0x9e, 0x71, 0x70, 0x7f,
+ 0x17, 0xe2, 0x41, 0xf2, 0xb1, 0x4a, 0x84, 0x6b, 0x88, 0xb6, 0xb9, 0xed,
+ 0xdb, 0x0b, 0x84, 0x6f, 0x07, 0x94, 0xc7, 0x8e, 0xcc, 0xc2, 0xb2, 0x49,
+ 0x3d, 0xf8, 0x70, 0x1a, 0x9b, 0x4d, 0x13, 0x0c, 0xe7, 0xeb, 0x75, 0x5d,
+ 0xfb, 0x10, 0x16, 0xf9, 0xf1, 0xac, 0x4b, 0xe3, 0x65, 0x5f, 0xaa, 0xbb,
+ 0x31, 0xac, 0x4d, 0x74, 0x11, 0x62, 0x64, 0xd1, 0x90, 0x6e, 0x45, 0xaf,
+ 0x2d, 0xb2, 0xa5, 0xfa, 0x57, 0x67, 0xaa, 0x4c, 0x3f, 0x81, 0x72, 0x93,
+ 0x8a, 0xfb, 0xeb, 0x19, 0x9d, 0x9a, 0x10, 0x8d, 0xce, 0xb8, 0xe9, 0x3f,
+ 0x97, 0x3e, 0xb2, 0x2d, 0x3e, 0xaf, 0x19, 0x39, 0xb0, 0xea, 0x33, 0x09,
+ 0x25, 0xbc, 0x6f, 0x88, 0x25, 0x75, 0x11, 0x2b, 0xd9, 0x42, 0x8d, 0x48,
+ 0x8e, 0xe7, 0x51, 0x4e, 0xd4, 0xee, 0xd4, 0xf1, 0xe1, 0x94, 0xfb, 0xbd,
+ 0x01, 0x87, 0xa5, 0xd6, 0x94, 0x36, 0x6f, 0x37, 0xcd, 0x70, 0x49, 0xfd,
+ 0x6a, 0x94, 0xb0, 0xc8, 0xe5, 0xb9, 0xdb, 0x83, 0x72, 0xa0, 0xd8, 0x5e,
+ 0x00, 0x3e, 0x3d, 0x56, 0xc9, 0xae, 0xf6, 0xe7, 0xb1, 0xba, 0xdd, 0x6d,
+ 0x59, 0x2a, 0x6a, 0x50, 0x66, 0xfd, 0xf1, 0x1f, 0x18, 0xe4, 0xf9, 0x59,
+ 0x16, 0xf1, 0x25, 0x42, 0x7c, 0xab, 0xc5, 0xea, 0x64, 0x31, 0xf7, 0xcf,
+ 0xdd, 0x56, 0xc4, 0xbd, 0xb5, 0xe6, 0xb4, 0xda, 0x41, 0xb7, 0xfa, 0x05,
+ 0x06, 0x57, 0x51, 0xd8, 0xc4, 0xf7, 0x3c, 0xc8, 0x54, 0x55, 0x5c, 0x2c,
+ 0xd2, 0x17, 0xec, 0x27, 0xd3, 0xbf, 0xf3, 0xc8, 0xba, 0xbf, 0x75, 0xcf,
+ 0x1f, 0x78, 0x42, 0x1a, 0x4f, 0xb0, 0xcb, 0xd7, 0x84, 0xc6, 0xbd, 0x40,
+ 0x07, 0xbf, 0x47, 0xe9, 0x17, 0x2f, 0xad, 0xec, 0x64, 0x8b, 0xca, 0x7c,
+ 0x49, 0x4c, 0xda, 0xf9, 0x5f, 0x28, 0x1d, 0x2d, 0xc3, 0x8d, 0x15, 0xa6,
+ 0x0f, 0x95, 0x76, 0x99, 0xb5, 0x76, 0x68, 0x97, 0x91, 0x93, 0xca, 0x9d,
+ 0x61, 0x50, 0xa7, 0xa7, 0xf7, 0xce, 0xd5, 0x14, 0xe5, 0xb2, 0x50, 0x50,
+ 0x21, 0x73, 0x00, 0x21, 0x4f, 0x12, 0x79, 0x7f, 0x68, 0x27, 0x52, 0xf2,
+ 0x2c, 0x9d, 0x2d, 0x4f, 0x66, 0x3e, 0xf1, 0xb5, 0x68, 0x77, 0xc9, 0xd5,
+ 0x16, 0x01, 0xb2, 0x6d, 0x46, 0x7c, 0xce, 0x8d, 0x67, 0x77, 0x27, 0xdd,
+ 0x4b, 0x16, 0xd7, 0x7b, 0x16, 0xfd, 0x6b, 0x18, 0x36, 0xeb, 0xa5, 0xd6,
+ 0xd1, 0x21, 0xdb, 0x27, 0x01, 0x9a, 0x22, 0xa1, 0xbf, 0x45, 0xda, 0xf7,
+ 0xe4, 0x07, 0x26, 0x0c, 0x84, 0xe1, 0x81, 0x1c, 0xf9, 0x38, 0x4a, 0x26,
+ 0xb3, 0xe3, 0x90, 0x13, 0x5e, 0xb7, 0xd5, 0xaf, 0xcb, 0x7d, 0xc1, 0xf7,
+ 0x23, 0x2d, 0x96, 0xc8, 0xe7, 0x3e, 0x21, 0x2c, 0xee, 0xa8, 0xd7, 0x83,
+ 0xf1, 0xd7, 0xf0, 0xce, 0xca, 0x80, 0xb5, 0xbd, 0x89, 0x10, 0x71, 0x38,
+ 0x8c, 0xa2, 0xa1, 0x07, 0x93, 0xa9, 0xd9, 0x4d, 0xa5, 0x9b, 0x66, 0x68,
+ 0x06, 0x65, 0x8d, 0x26, 0xff, 0xcb, 0xf2, 0xf5, 0x09, 0x7c, 0xf1, 0x7f,
+ 0x55, 0x3f, 0x61, 0x78, 0xb8, 0x27, 0x77, 0xb8, 0x30, 0x91, 0x10, 0x3f,
+ 0x57, 0xec, 0xfd, 0x82, 0xd2, 0xd3, 0x8a, 0x27, 0xa5, 0x26, 0x87, 0x01,
+ 0x8a, 0x20, 0x9a, 0x5d, 0xe9, 0x88, 0xa7, 0x70, 0xb0, 0x06, 0x04, 0xe8,
+ 0xa0, 0x15, 0x2c, 0x0b, 0xe5, 0xea, 0x79, 0x57, 0xad, 0x8c, 0xe2, 0x98,
+ 0xcd, 0xbf, 0x74, 0x1c, 0xb6, 0x06, 0xc6, 0x17, 0x12, 0x2b, 0xa4, 0xa6,
+ 0xfe, 0xe7, 0x94, 0xf9, 0xb6, 0x95, 0xf0, 0xfc, 0xe5, 0x57, 0xfe, 0xf9,
+ 0x1d, 0xd4, 0x29, 0xc0, 0x06, 0xf9, 0x5f, 0x14, 0x73, 0x14, 0x1d, 0x42,
+ 0x1f, 0x70, 0xa0, 0xd5, 0x3d, 0x0d, 0xbe, 0xfb, 0xb3, 0xe0, 0xae, 0x35,
+ 0x34, 0x40, 0xa9, 0xca, 0x5d, 0x6f, 0x4f, 0x3a, 0x3b, 0x9e, 0xe2, 0xed,
+ 0x85, 0xa9, 0x53, 0xb9, 0xb2, 0x43, 0x5e, 0x95, 0x7a, 0x30, 0x63, 0x2a,
+ 0x84, 0x40, 0x1b, 0x86, 0x2d, 0x74, 0xf6, 0xf3, 0x3b, 0x40, 0x5b, 0x16,
+ 0x51, 0xa1, 0xaa, 0x70, 0xed, 0xb3, 0x96, 0x66, 0x9b, 0x6b, 0x57, 0x4d,
+ 0x0d, 0x8b, 0x2a, 0x9b, 0x27, 0xb6, 0x04, 0xa5, 0x19, 0xb2, 0x17, 0xad,
+ 0x6f, 0x6f, 0xef, 0x8c, 0xae, 0x87, 0x58, 0xc9, 0xbd, 0x51, 0xf9, 0x6b,
+ 0x7d, 0x5c, 0xd1, 0x42, 0x10, 0xa1, 0x5c, 0x66, 0x13, 0xaa, 0xe0, 0xfc,
+ 0x91, 0x95, 0x63, 0x0a, 0x0b, 0x3d, 0x5c, 0xd1, 0x64, 0xda, 0xaf, 0x7b,
+ 0x1b, 0x0a, 0x09, 0x9f, 0xf8, 0xd9, 0x43, 0x7b, 0x7d, 0xe1, 0x34, 0x68,
+ 0x6a, 0xb2, 0x3a, 0x16, 0x99, 0x2f, 0x29, 0x5f, 0xa3, 0xe6, 0x03, 0x74,
+ 0x42, 0x49, 0x6a, 0x33, 0x15, 0x93, 0x5e, 0xa5, 0xab, 0x13, 0x77, 0x3a,
+ 0x1a, 0x78, 0x6d, 0xe5, 0x06, 0x1f, 0xa4, 0x6f, 0xdc, 0xa8, 0xc8, 0x93,
+ 0x9f, 0x4d, 0x99, 0xba, 0xb1, 0x6d, 0x8d, 0x1d, 0xc0, 0x6b, 0xff, 0x16,
+ 0x7d, 0xb3, 0xfc, 0xe0, 0xd7, 0xd4, 0xb2, 0x69, 0x32, 0x38, 0x64, 0xef,
+ 0x04, 0xba, 0x7b, 0xa5, 0x5e, 0xc6, 0x1f, 0x1d, 0xd0, 0xf8, 0x6b, 0xd8,
+ 0x35, 0xb9, 0x9e, 0x2f, 0x62, 0x36, 0xf5, 0x88, 0x41, 0x90, 0x1f, 0xc7,
+ 0xc3, 0x96, 0x1b, 0x4f, 0xc4, 0x87, 0x0c, 0xea, 0xdb, 0x8f, 0xea, 0x6a,
+ 0xe9, 0xdc, 0x30, 0xc4, 0x6c, 0x23, 0x9b, 0xac, 0x8b, 0xf6, 0xa6, 0xbf,
+ 0xc0, 0xd6, 0x00, 0xf6, 0x92, 0x3f, 0xd3, 0x83, 0xfe, 0x74, 0x7c, 0x19,
+ 0x74, 0xfb, 0xe7, 0x72, 0x81, 0x17, 0x32, 0x32, 0xb8, 0x4b, 0x5d, 0x70,
+ 0x67, 0x14, 0xe1, 0xee,
+};
+
+#endif
diff --git a/drivers/acpi/apei/ghes-nvidia-test.c b/drivers/acpi/apei/ghes-nvidia-test.c
new file mode 100644
index 000000000000..38eacdaafff9
--- /dev/null
+++ b/drivers/acpi/apei/ghes-nvidia-test.c
@@ -0,0 +1,1274 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <kunit/test.h>
+#include <linux/align.h>
+#include <linux/errno.h>
+#include <linux/limits.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+#include <linux/uuid.h>
+
+#include "ghes-nvidia.h"
+#include "ghes-nvidia-test-fixtures.h"
+
+static const u8 grace_valid_two_regs[] = {
+ 'N', 'V', 'D', 'A', '-', 'G', 'R', 'A',
+ 'C', 'E', 0, 0, 0, 0, 0, 0,
+ 0x34, 0x12, 0x78, 0x56, 0x02, 0x03, 0x02, 0x00,
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 grace_valid_zero_regs[] = {
+ 'N', 'V', 'D', 'A', '-', 'G', 'R', 'A',
+ 'C', 'E', 0, 0, 0, 0, 0, 0,
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
+};
+
+static const u8 grace_valid_full_signature[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00,
+ 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00,
+};
+
+static void nvidia_ghes_grace_decodes_registers(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u64 addr, val;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
+ sizeof(grace_valid_two_regs),
+ &decoded));
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE, decoded.format);
+ KUNIT_EXPECT_STREQ(test, "NVDA-GRACE", decoded.signature);
+ KUNIT_EXPECT_EQ(test, 0x1234, decoded.error_type);
+ KUNIT_EXPECT_EQ(test, 0x5678, decoded.error_instance);
+ KUNIT_EXPECT_EQ(test, 2, decoded.severity);
+ KUNIT_EXPECT_EQ(test, 3, decoded.socket);
+ KUNIT_EXPECT_EQ(test, 2, decoded.number_regs);
+ KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, decoded.instance_base);
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x1000ULL, addr);
+ KUNIT_EXPECT_EQ(test, 0x1111ULL, val);
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_grace_reg_pair(&decoded, 1, &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x2000ULL, addr);
+ KUNIT_EXPECT_EQ(test, 0x2222ULL, val);
+}
+
+static void nvidia_ghes_grace_decodes_unaligned_payload(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+ u64 addr, val;
+
+ buf = kunit_kzalloc(test, sizeof(grace_valid_two_regs) + 1, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ memcpy(buf + 1, grace_valid_two_regs, sizeof(grace_valid_two_regs));
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_grace(NULL, buf + 1,
+ sizeof(grace_valid_two_regs),
+ &decoded));
+ KUNIT_EXPECT_EQ(test, 0x1234, decoded.error_type);
+ KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, decoded.instance_base);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x1000ULL, addr);
+ KUNIT_EXPECT_EQ(test, 0x1111ULL, val);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_grace_reg_pair(&decoded, 1, &addr, &val));
+ KUNIT_EXPECT_EQ(test, 0x2000ULL, addr);
+ KUNIT_EXPECT_EQ(test, 0x2222ULL, val);
+}
+
+static void nvidia_ghes_grace_accepts_zero_registers(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_grace(NULL, grace_valid_zero_regs,
+ sizeof(grace_valid_zero_regs),
+ &decoded));
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE, decoded.format);
+ KUNIT_EXPECT_EQ(test, 0, decoded.number_regs);
+ KUNIT_EXPECT_PTR_EQ(test, NULL, decoded.grace_regs);
+}
+
+static void nvidia_ghes_grace_copies_non_nul_signature(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_grace(NULL, grace_valid_full_signature,
+ sizeof(grace_valid_full_signature),
+ &decoded));
+ KUNIT_EXPECT_STREQ(test, "ABCDEFGHIJKLMNOP", decoded.signature);
+ KUNIT_EXPECT_EQ(test, '\0', decoded.signature[16]);
+}
+
+static void nvidia_ghes_grace_rejects_truncated_header(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
+ sizeof(grace_valid_zero_regs) - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_grace_rejects_truncated_registers(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_grace(NULL, grace_valid_two_regs,
+ sizeof(grace_valid_two_regs) - 1,
+ &decoded));
+}
+
+static const u8 vera_chip_serial[] = {
+ 0xc0, 0x01, 0xfe, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x81, 0xc1, 0x18, 0x78, 0x01, 0x00, 0x00, 0x00,
+};
+
+#define VERA_EVENT_HDR_SIZE 32
+#define VERA_CPU_INFO_SIZE 32
+#define VERA_CONTEXT_HDR_SIZE 16
+#define VERA_FIRST_CONTEXT_OFFSET (VERA_EVENT_HDR_SIZE + VERA_CPU_INFO_SIZE)
+
+struct vera_synth_context {
+ u32 context_size;
+ u16 context_version;
+ u16 format;
+ u16 data_format_version;
+ const u8 *data;
+ u32 data_size;
+};
+
+static u8 *nvidia_ghes_kunit_memdup(struct kunit *test, const u8 *src, size_t len)
+{
+ u8 *buf;
+
+ buf = kunit_kmalloc(test, len, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+ memcpy(buf, src, len);
+
+ return buf;
+}
+
+static u8 *vera_make_synthetic_contexts(struct kunit *test, u8 version, u8 info_size,
+ const struct vera_synth_context *contexts,
+ u8 count, size_t *len)
+{
+ u8 *buf;
+ size_t offset;
+ u8 i;
+
+ KUNIT_ASSERT_TRUE(test, info_size >= VERA_CPU_INFO_SIZE);
+ *len = VERA_EVENT_HDR_SIZE + info_size;
+ for (i = 0; i < count; i++) {
+ u32 context_size = contexts[i].context_size;
+ u32 payload;
+
+ KUNIT_ASSERT_TRUE(test, context_size >= VERA_CONTEXT_HDR_SIZE);
+ if (context_size == VERA_CONTEXT_HDR_SIZE)
+ payload = contexts[i].data_size;
+ else
+ payload = context_size - VERA_CONTEXT_HDR_SIZE;
+ *len += VERA_CONTEXT_HDR_SIZE + payload;
+ }
+
+ buf = kunit_kzalloc(test, *len, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+
+ buf[0] = version;
+ buf[1] = count;
+ buf[2] = 0;
+ put_unaligned_le16(0x22, &buf[4]);
+ memcpy(&buf[16], "SYNTH", 5);
+ put_unaligned_le16(0, &buf[32]);
+ buf[34] = info_size;
+ buf[35] = 0;
+ put_unaligned_le32(0x14107, &buf[36]);
+ memcpy(&buf[40], vera_chip_serial, sizeof(vera_chip_serial));
+ put_unaligned_le64(0, &buf[56]);
+
+ offset = VERA_EVENT_HDR_SIZE + info_size;
+ for (i = 0; i < count; i++) {
+ u32 context_size = contexts[i].context_size;
+ u32 payload;
+
+ put_unaligned_le32(context_size, &buf[offset]);
+ put_unaligned_le16(contexts[i].context_version,
+ &buf[offset + 4]);
+ put_unaligned_le16(contexts[i].format, &buf[offset + 8]);
+ put_unaligned_le16(contexts[i].data_format_version,
+ &buf[offset + 10]);
+ put_unaligned_le32(contexts[i].data_size, &buf[offset + 12]);
+ if (contexts[i].data && contexts[i].data_size)
+ memcpy(&buf[offset + VERA_CONTEXT_HDR_SIZE],
+ contexts[i].data, contexts[i].data_size);
+
+ if (context_size == VERA_CONTEXT_HDR_SIZE)
+ payload = contexts[i].data_size;
+ else
+ payload = context_size - VERA_CONTEXT_HDR_SIZE;
+ offset += VERA_CONTEXT_HDR_SIZE + payload;
+ }
+
+ return buf;
+}
+
+static u8 *vera_make_synthetic(struct kunit *test, u8 version, u8 info_size,
+ u16 format, const u8 *data, u32 data_size,
+ size_t *len)
+{
+ struct vera_synth_context ctx = {
+ .context_size = format == 0 ? VERA_CONTEXT_HDR_SIZE :
+ ALIGN(VERA_CONTEXT_HDR_SIZE + data_size, 16),
+ .format = format,
+ .data = data,
+ .data_size = data_size,
+ };
+
+ return vera_make_synthetic_contexts(test, version, info_size, &ctx, 1, len);
+}
+
+static u8 *vera_make_synthetic_default(struct kunit *test, u16 format,
+ const u8 *data, u32 data_size, size_t *len)
+{
+ return vera_make_synthetic(test, 1, VERA_CPU_INFO_SIZE, format, data,
+ data_size, len);
+}
+
+static int vera_context_at(const struct nvidia_ghes_decoded *decoded,
+ unsigned int index,
+ struct nvidia_ghes_vera_context *context)
+{
+ struct nvidia_ghes_vera_cursor cursor;
+ int ret;
+
+ nvidia_ghes_vera_cursor_init(decoded, &cursor);
+ for (unsigned int i = 0; i <= index; i++) {
+ ret = nvidia_ghes_vera_cursor_next(&cursor, context);
+ if (ret != 1)
+ return ret ?: -ERANGE;
+ }
+
+ return 0;
+}
+
+static void nvidia_ghes_vera_decodes_l1_reset(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ sizeof(vera_l1_reset_id10),
+ &decoded));
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_VERA, decoded.format);
+ KUNIT_EXPECT_STREQ(test, "L1 RESET", decoded.signature);
+ KUNIT_EXPECT_EQ(test, 1, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, 1, decoded.event_type);
+ KUNIT_EXPECT_EQ(test, 39, decoded.event_sub_type);
+ KUNIT_EXPECT_EQ(test, 0, decoded.source_device_type);
+ KUNIT_EXPECT_EQ(test, 0, decoded.socket);
+ KUNIT_EXPECT_EQ(test, 0x14107U, decoded.architecture);
+ KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
+ sizeof(vera_chip_serial));
+ KUNIT_EXPECT_EQ(test, 0xfe1500200000ULL, decoded.instance_base);
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_EXPECT_EQ(test, 16U, context.context_size);
+ KUNIT_EXPECT_EQ(test, 1, context.data_format_type);
+ KUNIT_EXPECT_EQ(test, 0U, context.data_size);
+}
+
+static void nvidia_ghes_vera_decodes_crashdump_id(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+ u64 key, val;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, vera_crashdump_id_id40,
+ sizeof(vera_crashdump_id_id40),
+ &decoded));
+ KUNIT_EXPECT_STREQ(test, "CRASHDUMP-ID", decoded.signature);
+ KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
+ sizeof(vera_chip_serial));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_EXPECT_EQ(test, 240U, context.context_size);
+ KUNIT_EXPECT_EQ(test, 1, context.data_format_type);
+ KUNIT_EXPECT_EQ(test, 224U, context.data_size);
+ KUNIT_ASSERT_EQ(test, 14, nvidia_ghes_vera_context_entry_count(&context));
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_pair(&context, 0, &key, &val));
+ KUNIT_EXPECT_EQ(test, 0x8300000000000000ULL, key);
+ KUNIT_EXPECT_EQ(test, 0x372e33375f6c6572ULL, val);
+}
+
+static void nvidia_ghes_vera_decodes_crashdump_s1_opaque_context(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+ u64 val;
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, vera_crashdump_s1_id41,
+ sizeof(vera_crashdump_s1_id41),
+ &decoded));
+ KUNIT_EXPECT_STREQ(test, "CRASHDUMP-S1", decoded.signature);
+ KUNIT_EXPECT_EQ(test, 4096, decoded.event_type);
+ KUNIT_EXPECT_EQ(test, 2304, decoded.event_sub_type);
+ KUNIT_EXPECT_MEMEQ(test, decoded.chip_serial_number, vera_chip_serial,
+ sizeof(vera_chip_serial));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_EXPECT_EQ(test, 16U, context.context_size);
+ KUNIT_EXPECT_EQ(test, 0, context.data_format_type);
+ KUNIT_EXPECT_EQ(test, 1928U, context.data_size);
+ KUNIT_ASSERT_NOT_NULL(test, context.data);
+ KUNIT_EXPECT_EQ(test, 0x53, context.data[0]);
+ KUNIT_EXPECT_EQ(test, 0x56, context.data[1]);
+ KUNIT_EXPECT_EQ(test, 0x7f, context.data[2]);
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_vera_context_u64_value(&context, 0, &val));
+}
+
+static void nvidia_ghes_vera_decodes_multi_context_aligned(struct kunit *test)
+{
+ static const u8 first_data[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ };
+ static const u8 second_data[] = {
+ 0xa0, 0xb0, 0xc0, 0xd0,
+ };
+ struct vera_synth_context contexts[] = {
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 3,
+ .data = first_data,
+ .data_size = sizeof(first_data),
+ },
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 4,
+ .data = second_data,
+ .data_size = sizeof(second_data),
+ },
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context parsed[2];
+ size_t len;
+ u8 *buf;
+ u64 val64;
+ u32 val32;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ ARRAY_SIZE(contexts), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 2, decoded.event_context_count);
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &parsed[0]));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 1, &parsed[1]));
+ KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE + 16,
+ parsed[0].context_size);
+ KUNIT_EXPECT_EQ(test, 3, parsed[0].data_format_type);
+ KUNIT_EXPECT_EQ(test, sizeof(first_data), parsed[0].data_size);
+ KUNIT_ASSERT_NOT_NULL(test, parsed[0].data);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_value(&parsed[0], 0, &val64));
+ KUNIT_EXPECT_EQ(test, 0x8877665544332211ULL, val64);
+ KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE + 16,
+ parsed[1].context_size);
+ KUNIT_EXPECT_EQ(test, 4, parsed[1].data_format_type);
+ KUNIT_EXPECT_EQ(test, sizeof(second_data), parsed[1].data_size);
+ KUNIT_ASSERT_NOT_NULL(test, parsed[1].data);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_value(&parsed[1], 0, &val32));
+ KUNIT_EXPECT_EQ(test, 0xd0c0b0a0U, val32);
+}
+
+static void nvidia_ghes_vera_decodes_multi_context_covering_first(struct kunit *test)
+{
+ static const u8 first_data[] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
+ };
+ static const u8 second_data[] = {
+ 0xa0, 0xb0, 0xc0, 0xd0,
+ };
+ struct vera_synth_context contexts[] = {
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + sizeof(first_data),
+ .format = 1,
+ .data = first_data,
+ .data_size = sizeof(first_data),
+ },
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 4,
+ .data = second_data,
+ .data_size = sizeof(second_data),
+ },
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context parsed[2];
+ size_t len;
+ u8 *buf;
+ u64 key, val;
+ u32 val32;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ ARRAY_SIZE(contexts), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 2, decoded.event_context_count);
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &parsed[0]));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 1, &parsed[1]));
+ KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE + sizeof(first_data),
+ parsed[0].context_size);
+ KUNIT_EXPECT_EQ(test, 1, parsed[0].data_format_type);
+ KUNIT_EXPECT_EQ(test, sizeof(first_data), parsed[0].data_size);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_pair(&parsed[0], 0, &key, &val));
+ KUNIT_EXPECT_EQ(test, 0x8877665544332211ULL, key);
+ KUNIT_EXPECT_EQ(test, 0x00ffeeddccbbaa99ULL, val);
+ KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE + 16,
+ parsed[1].context_size);
+ KUNIT_EXPECT_EQ(test, 4, parsed[1].data_format_type);
+ KUNIT_EXPECT_EQ(test, sizeof(second_data), parsed[1].data_size);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_value(&parsed[1], 0, &val32));
+ KUNIT_EXPECT_EQ(test, 0xd0c0b0a0U, val32);
+}
+
+static void nvidia_ghes_vera_decodes_format2_u32_pairs(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+ size_t len;
+ u8 *buf;
+ u32 key, val;
+
+ buf = vera_make_synthetic_default(test, 2, data, sizeof(data), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_ASSERT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&context));
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_pair(&context, 0, &key, &val));
+ KUNIT_EXPECT_EQ(test, 0x12345678U, key);
+ KUNIT_EXPECT_EQ(test, 0x9abcdef0U, val);
+}
+
+static void nvidia_ghes_vera_decodes_format3_u64_values(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+ size_t len;
+ u8 *buf;
+ u64 val;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_ASSERT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&context));
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_value(&context, 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, val);
+}
+
+static void nvidia_ghes_vera_decodes_format4_u32_values(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34, 0x12,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+ size_t len;
+ u8 *buf;
+ u32 val;
+
+ buf = vera_make_synthetic_default(test, 4, data, sizeof(data), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_ASSERT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&context));
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_value(&context, 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x12345678U, val);
+}
+
+static void nvidia_ghes_vera_rejects_truncated_event_header(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ VERA_EVENT_HDR_SIZE - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_truncated_cpu_info(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ VERA_FIRST_CONTEXT_OFFSET - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_truncated_context_header(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_vera(NULL, vera_l1_reset_id10,
+ VERA_FIRST_CONTEXT_OFFSET +
+ VERA_CONTEXT_HDR_SIZE - 1,
+ &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_bad_info_size(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ buf[34] = 31;
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unsupported_version(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ buf[0] = 2;
+
+ KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unsupported_info_major_version(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+ int ret;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10,
+ sizeof(vera_l1_reset_id10));
+ put_unaligned_le16(0x0100, &buf[VERA_EVENT_HDR_SIZE]);
+
+ ret = nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10),
+ &decoded);
+ KUNIT_EXPECT_EQ(test, -EOPNOTSUPP, ret);
+}
+
+static void nvidia_ghes_vera_accepts_newer_info_minor_version(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10,
+ sizeof(vera_l1_reset_id10));
+ put_unaligned_le16(0x00ff, &buf[VERA_EVENT_HDR_SIZE]);
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, buf,
+ sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_accepts_extended_cpu_info(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+ size_t len;
+ u8 *buf;
+ u64 val;
+
+ buf = vera_make_synthetic(test, 1, VERA_CPU_INFO_SIZE + 4, 3,
+ data, sizeof(data), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE + 16,
+ context.context_size);
+ KUNIT_EXPECT_EQ(test, 3, context.data_format_type);
+ KUNIT_EXPECT_EQ(test, 8U, context.data_size);
+ KUNIT_EXPECT_EQ(test, 1, nvidia_ghes_vera_context_entry_count(&context));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_context_u64_value(&context, 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x0102030405060708ULL, val);
+}
+
+static void nvidia_ghes_vera_rejects_short_context_size(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ put_unaligned_le32(15, &buf[VERA_FIRST_CONTEXT_OFFSET]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_data_size_exceeding_context_size(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ put_unaligned_le32(VERA_CONTEXT_HDR_SIZE,
+ &buf[VERA_FIRST_CONTEXT_OFFSET]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unaligned_context_size(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ put_unaligned_le32(VERA_CONTEXT_HDR_SIZE + sizeof(data),
+ &buf[VERA_FIRST_CONTEXT_OFFSET]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_accepts_padded_context_size(struct kunit *test)
+{
+ static const u8 first_data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ static const u8 second_data[] = {
+ 0x78, 0x56, 0x34, 0x12,
+ };
+ struct vera_synth_context contexts[] = {
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 3,
+ .data = first_data,
+ .data_size = sizeof(first_data),
+ },
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 4,
+ .data = second_data,
+ .data_size = sizeof(second_data),
+ },
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context parsed[2];
+ size_t len;
+ u8 *buf;
+ u32 val;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ ARRAY_SIZE(contexts), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 2, decoded.event_context_count);
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &parsed[0]));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 1, &parsed[1]));
+ KUNIT_EXPECT_EQ(test, VERA_CONTEXT_HDR_SIZE + 16,
+ parsed[0].context_size);
+ KUNIT_EXPECT_EQ(test, sizeof(first_data), parsed[0].data_size);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_value(&parsed[1], 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x12345678U, val);
+}
+
+static void nvidia_ghes_vera_rejects_context_beyond_section(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, -ENODATA,
+ nvidia_ghes_decode_vera(NULL, buf, len - 8, &decoded));
+}
+
+static void nvidia_ghes_vera_preserves_legacy_opaque_advance(struct kunit *test)
+{
+ static const u8 opaque_data[] = { 0xaa, 0xbb, 0xcc };
+ static const u8 value_data[] = { 0x78, 0x56, 0x34, 0x12 };
+ struct vera_synth_context contexts[] = {
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE,
+ .format = 0,
+ .data = opaque_data,
+ .data_size = sizeof(opaque_data),
+ },
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 4,
+ .data = value_data,
+ .data_size = sizeof(value_data),
+ },
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context parsed[2];
+ size_t len;
+ u8 *buf;
+ u32 val;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ ARRAY_SIZE(contexts), &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 2, decoded.event_context_count);
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &parsed[0]));
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 1, &parsed[1]));
+ KUNIT_EXPECT_EQ(test, sizeof(opaque_data), parsed[0].data_size);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_value(&parsed[1], 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x12345678U, val);
+}
+
+static void nvidia_ghes_vera_retains_contexts_before_failure(struct kunit *test)
+{
+ static const u8 first_data[] = { 0x78, 0x56, 0x34, 0x12 };
+ static const u8 second_data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct vera_synth_context contexts[] = {
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 4,
+ .data = first_data,
+ .data_size = sizeof(first_data),
+ },
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .format = 3,
+ .data = second_data,
+ .data_size = sizeof(second_data),
+ },
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ struct nvidia_ghes_vera_context context;
+ size_t second_offset = VERA_FIRST_CONTEXT_OFFSET + contexts[0].context_size;
+ size_t len;
+ u8 *buf;
+ u32 val;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ ARRAY_SIZE(contexts), &len);
+ put_unaligned_le32(VERA_CONTEXT_HDR_SIZE + sizeof(second_data),
+ &buf[second_offset]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 2, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, 1, decoded.valid_context_count);
+ KUNIT_ASSERT_EQ(test, 0, vera_context_at(&decoded, 0, &context));
+ KUNIT_EXPECT_EQ(test, sizeof(first_data), context.data_size);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_vera_context_u32_value(&context, 0, &val));
+ KUNIT_EXPECT_EQ(test, 0x12345678U, val);
+}
+
+static void nvidia_ghes_vera_accepts_all_wire_contexts(struct kunit *test)
+{
+ struct nvidia_ghes_vera_cursor cursor;
+ struct nvidia_ghes_vera_context context;
+ struct vera_synth_context *contexts;
+ struct nvidia_ghes_decoded decoded = {};
+ static const u8 empty;
+ size_t len;
+ u8 *buf;
+ int i;
+
+ contexts = kunit_kcalloc(test, U8_MAX, sizeof(*contexts), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, contexts);
+ for (i = 0; i < U8_MAX; i++) {
+ contexts[i].context_size = VERA_CONTEXT_HDR_SIZE;
+ contexts[i].format = 0;
+ contexts[i].data = ∅
+ contexts[i].data_size = 0;
+ }
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ U8_MAX, &len);
+ KUNIT_ASSERT_EQ(test, 0, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, U8_MAX, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, U8_MAX, decoded.valid_context_count);
+
+ nvidia_ghes_vera_cursor_init(&decoded, &cursor);
+ for (i = 0; i < U8_MAX; i++)
+ KUNIT_ASSERT_EQ(test, 1,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+ KUNIT_EXPECT_EQ(test, 16,
+ nvidia_ghes_vera_print_context_count(&decoded));
+}
+
+static void nvidia_ghes_vera_accepts_zero_contexts(struct kunit *test)
+{
+ struct nvidia_ghes_vera_cursor cursor;
+ struct nvidia_ghes_vera_context context;
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, NULL, 0,
+ &len);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 0, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, 0, decoded.valid_context_count);
+ nvidia_ghes_vera_cursor_init(&decoded, &cursor);
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+}
+
+static void nvidia_ghes_vera_accepts_seventeen_contexts(struct kunit *test)
+{
+ struct nvidia_ghes_vera_cursor cursor;
+ struct nvidia_ghes_vera_context context;
+ struct vera_synth_context *contexts;
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+ int i;
+
+ contexts = kunit_kcalloc(test, 17, sizeof(*contexts), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, contexts);
+ for (i = 0; i < 17; i++)
+ contexts[i].context_size = VERA_CONTEXT_HDR_SIZE;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ 17, &len);
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 17, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, 17, decoded.valid_context_count);
+ nvidia_ghes_vera_cursor_init(&decoded, &cursor);
+ for (i = 0; i < 17; i++)
+ KUNIT_ASSERT_EQ(test, 1,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+}
+
+static void nvidia_ghes_vera_accepts_schema_extensions_together(struct kunit *test)
+{
+ static const u8 first_data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct vera_synth_context contexts[] = {
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE + 16,
+ .context_version = 2,
+ .format = 3,
+ .data_format_version = 3,
+ .data = first_data,
+ .data_size = sizeof(first_data),
+ },
+ {
+ .context_size = VERA_CONTEXT_HDR_SIZE,
+ .context_version = 4,
+ .format = 0,
+ .data_format_version = 5,
+ },
+ };
+ struct nvidia_ghes_vera_cursor cursor;
+ struct nvidia_ghes_vera_context context;
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *section;
+ u8 *buf;
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE + 4,
+ contexts, ARRAY_SIZE(contexts),
+ &len);
+ put_unaligned_le16(0x00ff, &buf[VERA_EVENT_HDR_SIZE]);
+ section = kunit_kzalloc(test, len + 7, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, section);
+ memcpy(section, buf, len);
+
+ KUNIT_ASSERT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, section, len + 7, &decoded));
+ KUNIT_EXPECT_EQ(test, 2, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, 2, decoded.valid_context_count);
+ nvidia_ghes_vera_cursor_init(&decoded, &cursor);
+ KUNIT_ASSERT_EQ(test, 1,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+ KUNIT_EXPECT_EQ(test, 2, context.context_version);
+ KUNIT_EXPECT_EQ(test, 3, context.data_format_version);
+ KUNIT_ASSERT_EQ(test, 1,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+ KUNIT_EXPECT_EQ(test, 4, context.context_version);
+ KUNIT_EXPECT_EQ(test, 5, context.data_format_version);
+ KUNIT_EXPECT_EQ(test, 7, cursor.remaining);
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+}
+
+static void nvidia_ghes_vera_retains_large_valid_prefix(struct kunit *test)
+{
+ struct nvidia_ghes_vera_cursor cursor;
+ struct nvidia_ghes_vera_context context;
+ struct vera_synth_context *contexts;
+ struct nvidia_ghes_decoded decoded = {};
+ static const u8 empty;
+ size_t bad_offset;
+ size_t len;
+ u8 *buf;
+ int i;
+
+ contexts = kunit_kcalloc(test, 18, sizeof(*contexts), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, contexts);
+ for (i = 0; i < 18; i++) {
+ contexts[i].context_size = VERA_CONTEXT_HDR_SIZE;
+ contexts[i].format = 0;
+ contexts[i].data = ∅
+ }
+
+ buf = vera_make_synthetic_contexts(test, 1, VERA_CPU_INFO_SIZE, contexts,
+ 18, &len);
+ bad_offset = VERA_FIRST_CONTEXT_OFFSET + 17 * VERA_CONTEXT_HDR_SIZE;
+ put_unaligned_le32(VERA_CONTEXT_HDR_SIZE - 1, &buf[bad_offset]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+ KUNIT_EXPECT_EQ(test, 18, decoded.event_context_count);
+ KUNIT_EXPECT_EQ(test, 17, decoded.valid_context_count);
+ KUNIT_EXPECT_EQ(test, 16,
+ nvidia_ghes_vera_print_context_count(&decoded));
+
+ nvidia_ghes_vera_cursor_init(&decoded, &cursor);
+ for (i = 0; i < 17; i++)
+ KUNIT_ASSERT_EQ(test, 1,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_cursor_next(&cursor, &context));
+}
+
+static void nvidia_ghes_vera_rejects_data_beyond_section(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ put_unaligned_le32(24, &buf[VERA_FIRST_CONTEXT_OFFSET + 12]);
+
+ KUNIT_EXPECT_EQ(test, -ENODATA, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format1(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_crashdump_id_id40,
+ sizeof(vera_crashdump_id_id40));
+ put_unaligned_le32(15, &buf[VERA_FIRST_CONTEXT_OFFSET + 12]);
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_decode_vera(NULL, buf,
+ sizeof(vera_crashdump_id_id40),
+ &decoded));
+}
+
+static void nvidia_ghes_vera_print_budget_is_section_wide(struct kunit *test)
+{
+ static const u8 data;
+ struct nvidia_ghes_vera_context ctx = {
+ .data_format_type = 1,
+ .data_size = 24 * 16,
+ .data = &data,
+ };
+ unsigned int remaining = 32;
+
+ KUNIT_EXPECT_EQ(test, 24,
+ nvidia_ghes_vera_context_print_count(&ctx, &remaining));
+ KUNIT_EXPECT_EQ(test, 8U, remaining);
+ KUNIT_EXPECT_EQ(test, 8,
+ nvidia_ghes_vera_context_print_count(&ctx, &remaining));
+ KUNIT_EXPECT_EQ(test, 0U, remaining);
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_vera_context_print_count(&ctx, &remaining));
+}
+
+static void nvidia_ghes_vera_print_budget_rejects_null_remaining(struct kunit *test)
+{
+ struct nvidia_ghes_vera_context ctx = {};
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_vera_context_print_count(&ctx, NULL));
+}
+
+static void nvidia_ghes_vera_accessors_reject_null_data(struct kunit *test)
+{
+ struct nvidia_ghes_vera_context ctx = {
+ .data_size = 16,
+ };
+ u64 key64, val64;
+ u32 key32, val32;
+
+ ctx.data_format_type = 1;
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_vera_context_u64_pair(&ctx, 0, &key64,
+ &val64));
+ ctx.data_format_type = 2;
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_vera_context_u32_pair(&ctx, 0, &key32,
+ &val32));
+ ctx.data_format_type = 3;
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_vera_context_u64_value(&ctx, 0, &val64));
+ ctx.data_format_type = 4;
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_vera_context_u32_value(&ctx, 0, &val32));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format2(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 2, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format3(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 3, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_mis_sized_format4(struct kunit *test)
+{
+ static const u8 data[] = {
+ 0x78, 0x56, 0x34,
+ };
+ struct nvidia_ghes_decoded decoded = {};
+ size_t len;
+ u8 *buf;
+
+ buf = vera_make_synthetic_default(test, 4, data, sizeof(data), &len);
+ KUNIT_EXPECT_EQ(test, -EINVAL, nvidia_ghes_decode_vera(NULL, buf, len, &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unsupported_source_device(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ buf[2] = 1;
+
+ KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_rejects_unsupported_data_format(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ put_unaligned_le16(5, &buf[VERA_FIRST_CONTEXT_OFFSET + 8]);
+
+ KUNIT_EXPECT_EQ(test, -EOPNOTSUPP,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+}
+
+static void nvidia_ghes_vera_copies_non_nul_signature(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {};
+ u8 *buf;
+
+ buf = nvidia_ghes_kunit_memdup(test, vera_l1_reset_id10, sizeof(vera_l1_reset_id10));
+ memcpy(&buf[16], "ABCDEFGHIJKLMNOP", 16);
+
+ KUNIT_EXPECT_EQ(test, 0,
+ nvidia_ghes_decode_vera(NULL, buf, sizeof(vera_l1_reset_id10), &decoded));
+ KUNIT_EXPECT_STREQ(test, "ABCDEFGHIJKLMNOP", decoded.signature);
+ KUNIT_EXPECT_EQ(test, '\0', decoded.signature[16]);
+}
+
+static void nvidia_ghes_guid_routes_grace(struct kunit *test)
+{
+ const guid_t guid = GUID_INIT(0x6d5244f2, 0x2712, 0x11ec,
+ 0xbe, 0xa7, 0xcb, 0x3f, 0xdb, 0x95, 0xc7, 0x86);
+
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_GRACE,
+ nvidia_ghes_format_from_guid(&guid));
+}
+
+static void nvidia_ghes_guid_routes_vera(struct kunit *test)
+{
+ const guid_t guid = GUID_INIT(0x9068e568, 0x6ca0, 0x11f0,
+ 0xae, 0xaf, 0x15, 0x93, 0x43, 0x59, 0x1e, 0xac);
+
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_VERA,
+ nvidia_ghes_format_from_guid(&guid));
+}
+
+static void nvidia_ghes_guid_rejects_unknown(struct kunit *test)
+{
+ const guid_t guid = GUID_INIT(0x00000000, 0x0000, 0x0000,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
+
+ KUNIT_EXPECT_EQ(test, NVIDIA_GHES_FORMAT_UNKNOWN,
+ nvidia_ghes_format_from_guid(&guid));
+}
+
+static void nvidia_ghes_grace_reg_pair_rejects_null_decoded(struct kunit *test)
+{
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_grace_reg_pair(NULL, 0, &addr, &val));
+}
+
+static void nvidia_ghes_grace_reg_pair_rejects_wrong_format(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {
+ .format = NVIDIA_GHES_FORMAT_VERA,
+ };
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+}
+
+static void nvidia_ghes_grace_reg_pair_rejects_null_regs(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {
+ .format = NVIDIA_GHES_FORMAT_GRACE,
+ .number_regs = 1,
+ .grace_regs = NULL,
+ };
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, -EINVAL,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+}
+
+static void nvidia_ghes_grace_reg_pair_rejects_out_of_range(struct kunit *test)
+{
+ struct nvidia_ghes_decoded decoded = {
+ .format = NVIDIA_GHES_FORMAT_GRACE,
+ .number_regs = 0,
+ };
+ u64 addr, val;
+
+ KUNIT_EXPECT_EQ(test, -ERANGE,
+ nvidia_ghes_grace_reg_pair(&decoded, 0, &addr, &val));
+}
+
+static struct kunit_case nvidia_ghes_test_cases[] = {
+ KUNIT_CASE(nvidia_ghes_grace_decodes_registers),
+ KUNIT_CASE(nvidia_ghes_grace_decodes_unaligned_payload),
+ KUNIT_CASE(nvidia_ghes_grace_accepts_zero_registers),
+ KUNIT_CASE(nvidia_ghes_grace_copies_non_nul_signature),
+ KUNIT_CASE(nvidia_ghes_grace_rejects_truncated_header),
+ KUNIT_CASE(nvidia_ghes_grace_rejects_truncated_registers),
+ KUNIT_CASE(nvidia_ghes_grace_reg_pair_rejects_null_decoded),
+ KUNIT_CASE(nvidia_ghes_grace_reg_pair_rejects_wrong_format),
+ KUNIT_CASE(nvidia_ghes_grace_reg_pair_rejects_null_regs),
+ KUNIT_CASE(nvidia_ghes_grace_reg_pair_rejects_out_of_range),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_l1_reset),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_crashdump_id),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_crashdump_s1_opaque_context),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_multi_context_aligned),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_multi_context_covering_first),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_format2_u32_pairs),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_format3_u64_values),
+ KUNIT_CASE(nvidia_ghes_vera_decodes_format4_u32_values),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_truncated_event_header),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_truncated_cpu_info),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_truncated_context_header),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_bad_info_size),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unsupported_version),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unsupported_info_major_version),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_newer_info_minor_version),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_extended_cpu_info),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_short_context_size),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_data_size_exceeding_context_size),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unaligned_context_size),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_padded_context_size),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_context_beyond_section),
+ KUNIT_CASE(nvidia_ghes_vera_preserves_legacy_opaque_advance),
+ KUNIT_CASE(nvidia_ghes_vera_retains_contexts_before_failure),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_all_wire_contexts),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_zero_contexts),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_seventeen_contexts),
+ KUNIT_CASE(nvidia_ghes_vera_accepts_schema_extensions_together),
+ KUNIT_CASE(nvidia_ghes_vera_retains_large_valid_prefix),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_data_beyond_section),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format1),
+ KUNIT_CASE(nvidia_ghes_vera_print_budget_is_section_wide),
+ KUNIT_CASE(nvidia_ghes_vera_print_budget_rejects_null_remaining),
+ KUNIT_CASE(nvidia_ghes_vera_accessors_reject_null_data),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format2),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format3),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_mis_sized_format4),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unsupported_source_device),
+ KUNIT_CASE(nvidia_ghes_vera_rejects_unsupported_data_format),
+ KUNIT_CASE(nvidia_ghes_vera_copies_non_nul_signature),
+ KUNIT_CASE(nvidia_ghes_guid_routes_grace),
+ KUNIT_CASE(nvidia_ghes_guid_routes_vera),
+ KUNIT_CASE(nvidia_ghes_guid_rejects_unknown),
+ {}
+};
+
+static struct kunit_suite nvidia_ghes_test_suite = {
+ .name = "acpi_apei_ghes_nvidia",
+ .test_cases = nvidia_ghes_test_cases,
+};
+
+kunit_test_suite(nvidia_ghes_test_suite);
+
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+MODULE_DESCRIPTION("KUnit tests for NVIDIA GHES CPER parser helpers");
+MODULE_LICENSE("GPL");
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-08-04 12:23 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-24 12:20 [PATCH v2 RESEND 0/4] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoder and tests Kai-Heng Feng
2026-07-24 12:20 ` [PATCH v2 RESEND 1/4] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
2026-07-26 8:10 ` Shuai Xue
2026-07-24 12:20 ` [PATCH v2 RESEND 2/4] ACPI: APEI: GHES: Add NVIDIA Vera decoder Kai-Heng Feng
2026-07-26 8:21 ` Shuai Xue
2026-07-24 12:20 ` [PATCH v2 RESEND 3/4] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage Kai-Heng Feng
2026-07-26 8:30 ` Shuai Xue
2026-07-24 12:20 ` [PATCH v2 RESEND 4/4] selftests: firmware: Add NVIDIA GHES EINJ selftest Kai-Heng Feng
2026-07-26 9:02 ` Shuai Xue
2026-08-04 12:23 ` [PATCH v3 0/3] ACPI: APEI: GHES: Add NVIDIA Vera CPER decoding Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 1/3] ACPI: APEI: GHES: Refactor Grace decoder helpers Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 2/3] ACPI: APEI: GHES: Add NVIDIA Vera decoder Kai-Heng Feng
2026-08-04 12:23 ` [PATCH v3 3/3] ACPI: APEI: GHES: Add Grace and Vera KUnit coverage Kai-Heng Feng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox