All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Michael S. Tsirkin" <mst@redhat.com>
To: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Cc: Igor Mammedov <imammedo@redhat.com>,
	Jonathan Cameron <Jonathan.Cameron@huawei.com>,
	Shiju Jose <shiju.jose@huawei.com>,
	qemu-arm@nongnu.org, qemu-devel@nongnu.org,
	Cleber Rosa <crosa@redhat.com>, John Snow <jsnow@redhat.com>
Subject: Re: [PATCH v11 17/17] scripts/ghes_inject: add a script to generate GHES error inject
Date: Mon, 22 Sep 2025 08:48:03 -0400	[thread overview]
Message-ID: <20250922084417-mutt-send-email-mst@kernel.org> (raw)
In-Reply-To: <41cfda8170605a25a795b555cdb49402af4ef5b0.1757084668.git.mchehab+huawei@kernel.org>

On Fri, Sep 05, 2025 at 05:09:24PM +0200, Mauro Carvalho Chehab wrote:
> Using the QMP GHESv2 API requires preparing a raw data array
> containing a CPER record.
> 
> Add a helper script with subcommands to prepare such data.
> 
> Currently, only ARM Processor error CPER record is supported, by
> using:
> 	$ ghes_inject.py arm
> 
> which produces those warnings on Linux:
> 
> [  705.032426] [Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
> [  774.866308] {4}[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1
> [  774.866583] {4}[Hardware Error]: event severity: recoverable
> [  774.866738] {4}[Hardware Error]:  Error 0, type: recoverable
> [  774.866889] {4}[Hardware Error]:   section_type: ARM processor error
> [  774.867048] {4}[Hardware Error]:   MIDR: 0x00000000000f0510
> [  774.867189] {4}[Hardware Error]:   running state: 0x0
> [  774.867321] {4}[Hardware Error]:   Power State Coordination Interface state: 0
> [  774.867511] {4}[Hardware Error]:   Error info structure 0:
> [  774.867679] {4}[Hardware Error]:   num errors: 2
> [  774.867801] {4}[Hardware Error]:    error_type: 0x02: cache error
> [  774.867962] {4}[Hardware Error]:    error_info: 0x000000000091000f
> [  774.868124] {4}[Hardware Error]:     transaction type: Data Access
> [  774.868280] {4}[Hardware Error]:     cache error, operation type: Data write
> [  774.868465] {4}[Hardware Error]:     cache level: 2
> [  774.868592] {4}[Hardware Error]:     processor context not corrupted
> [  774.868774] [Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
> 
> Such script allows customizing the error data, allowing to change
> all fields at the record. Please use:
> 
> 	$ ghes_inject.py arm -h
> 
> For more details about its usage.
> 
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
>  MAINTAINERS                    |   3 +
>  scripts/arm_processor_error.py | 476 ++++++++++++++++++++++
>  scripts/ghes_inject.py         |  51 +++
>  scripts/qmp_helper.py          | 703 +++++++++++++++++++++++++++++++++
>  4 files changed, 1233 insertions(+)
>  create mode 100644 scripts/arm_processor_error.py
>  create mode 100755 scripts/ghes_inject.py
>  create mode 100755 scripts/qmp_helper.py
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e609678ba9d6..0e503f40037c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2171,6 +2171,9 @@ S: Maintained
>  F: hw/arm/ghes_cper.c
>  F: hw/acpi/ghes_cper_stub.c
>  F: qapi/acpi-hest.json
> +F: scripts/ghes_inject.py
> +F: scripts/arm_processor_error.py
> +F: scripts/qmp_helper.py
>  
>  ppc4xx
>  L: qemu-ppc@nongnu.org
> diff --git a/scripts/arm_processor_error.py b/scripts/arm_processor_error.py
> new file mode 100644
> index 000000000000..1dd42e42a877
> --- /dev/null
> +++ b/scripts/arm_processor_error.py
> @@ -0,0 +1,476 @@

Malformed patch: script only has 474 lines.


> +#!/usr/bin/env python3
> +#
> +# pylint: disable=C0301,C0114,R0903,R0912,R0913,R0914,R0915,W0511
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> +
> +# TODO: current implementation has dummy defaults.
> +#
> +# For a better implementation, a QMP addition/call is needed to
> +# retrieve some data for ARM Processor Error injection:
> +#
> +#   - ARM registers: power_state, mpidr.
> +
> +"""
> +Generates an ARM processor error CPER, compatible with

Generates -> Generate

prefer imperative mood

> +UEFI 2.9A Errata.
> +
> +Injecting such errors can be done using:
> +
> +    $ ./scripts/ghes_inject.py arm
> +    Error injected.
> +
> +Produces a simple CPER register, as detected on a Linux guest:
> +
> +[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1
> +[Hardware Error]: event severity: recoverable
> +[Hardware Error]:  Error 0, type: recoverable
> +[Hardware Error]:   section_type: ARM processor error
> +[Hardware Error]:   MIDR: 0x0000000000000000
> +[Hardware Error]:   running state: 0x0
> +[Hardware Error]:   Power State Coordination Interface state: 0
> +[Hardware Error]:   Error info structure 0:
> +[Hardware Error]:   num errors: 2
> +[Hardware Error]:    error_type: 0x02: cache error
> +[Hardware Error]:    error_info: 0x000000000091000f
> +[Hardware Error]:     transaction type: Data Access
> +[Hardware Error]:     cache error, operation type: Data write
> +[Hardware Error]:     cache level: 2
> +[Hardware Error]:     processor context not corrupted
> +[Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
> +
> +The ARM Processor Error message can be customized via command line
> +parameters. For instance:
> +
> +    $ ./scripts/ghes_inject.py arm --mpidr 0x444 --running --affinity 1 +        --error-info 12345678 --vendor 0x13,123,4,5,1 --ctx-array 0,1,2,3,4,5 +        -t cache tlb bus micro-arch tlb,micro-arch

Weird formatting. Are these + signs intentional here?
Oh, maybe that is where the 2 missing lines are hiding.


> +    Error injected.
> +
> +Injects this error, as detected on a Linux guest:
> +
> +[Hardware Error]: Hardware error from APEI Generic Hardware Error Source: 1
> +[Hardware Error]: event severity: recoverable
> +[Hardware Error]:  Error 0, type: recoverable
> +[Hardware Error]:   section_type: ARM processor error
> +[Hardware Error]:   MIDR: 0x0000000000000000
> +[Hardware Error]:   Multiprocessor Affinity Register (MPIDR): 0x0000000000000000
> +[Hardware Error]:   error affinity level: 0
> +[Hardware Error]:   running state: 0x1
> +[Hardware Error]:   Power State Coordination Interface state: 0
> +[Hardware Error]:   Error info structure 0:
> +[Hardware Error]:   num errors: 2
> +[Hardware Error]:    error_type: 0x02: cache error
> +[Hardware Error]:    error_info: 0x0000000000bc614e
> +[Hardware Error]:     cache level: 2
> +[Hardware Error]:     processor context not corrupted
> +[Hardware Error]:   Error info structure 1:
> +[Hardware Error]:   num errors: 2
> +[Hardware Error]:    error_type: 0x04: TLB error
> +[Hardware Error]:    error_info: 0x000000000054007f
> +[Hardware Error]:     transaction type: Instruction
> +[Hardware Error]:     TLB error, operation type: Instruction fetch
> +[Hardware Error]:     TLB level: 1
> +[Hardware Error]:     processor context not corrupted
> +[Hardware Error]:     the error has not been corrected
> +[Hardware Error]:     PC is imprecise
> +[Hardware Error]:   Error info structure 2:
> +[Hardware Error]:   num errors: 2
> +[Hardware Error]:    error_type: 0x08: bus error
> +[Hardware Error]:    error_info: 0x00000080d6460fff
> +[Hardware Error]:     transaction type: Generic
> +[Hardware Error]:     bus error, operation type: Generic read (type of instruction or data request cannot be determined)
> +[Hardware Error]:     affinity level at which the bus error occurred: 1
> +[Hardware Error]:     processor context corrupted
> +[Hardware Error]:     the error has been corrected
> +[Hardware Error]:     PC is imprecise
> +[Hardware Error]:     Program execution can be restarted reliably at the PC associated with the error.
> +[Hardware Error]:     participation type: Local processor observed
> +[Hardware Error]:     request timed out
> +[Hardware Error]:     address space: External Memory Access
> +[Hardware Error]:     memory access attributes:0x20
> +[Hardware Error]:     access mode: secure
> +[Hardware Error]:   Error info structure 3:
> +[Hardware Error]:   num errors: 2
> +[Hardware Error]:    error_type: 0x10: micro-architectural error
> +[Hardware Error]:    error_info: 0x0000000078da03ff
> +[Hardware Error]:   Error info structure 4:
> +[Hardware Error]:   num errors: 2
> +[Hardware Error]:    error_type: 0x14: TLB error|micro-architectural error
> +[Hardware Error]:   Context info structure 0:
> +[Hardware Error]:    register context type: AArch64 EL1 context registers
> +[Hardware Error]:    00000000: 00000000 00000000
> +[Hardware Error]:   Vendor specific error info has 5 bytes:
> +[Hardware Error]:    00000000: 13 7b 04 05 01                                   .{...
> +[Firmware Warn]: GHES: Unhandled processor error type 0x02: cache error
> +[Firmware Warn]: GHES: Unhandled processor error type 0x04: TLB error
> +[Firmware Warn]: GHES: Unhandled processor error type 0x08: bus error
> +[Firmware Warn]: GHES: Unhandled processor error type 0x10: micro-architectural error
> +[Firmware Warn]: GHES: Unhandled processor error type 0x14: TLB error|micro-architectural error
> +"""
> +
> +import argparse
> +import re
> +
> +from qmp_helper import qmp, util, cper_guid
> +
> +
> +class ArmProcessorEinj:
> +    """
> +    Implements ARM Processor Error injection via GHES
> +    """
> +
> +    DESC = """
> +    Generates an ARM processor error CPER, compatible with
> +    UEFI 2.9A Errata.
> +    """
> +
> +    ACPI_GHES_ARM_CPER_LENGTH = 40
> +    ACPI_GHES_ARM_CPER_PEI_LENGTH = 32
> +
> +    # Context types
> +    CONTEXT_AARCH32_EL1 = 1
> +    CONTEXT_AARCH64_EL1 = 5
> +    CONTEXT_MISC_REG = 8
> +
> +    def __init__(self, subparsers):
> +        """Initialize the error injection class and add subparser"""
> +
> +        # Valid choice values
> +        self.arm_valid_bits = {
> +            "mpidr":    util.bit(0),
> +            "affinity": util.bit(1),
> +            "running":  util.bit(2),
> +            "vendor":   util.bit(3),
> +        }
> +
> +        self.pei_flags = {
> +            "first":        util.bit(0),
> +            "last":         util.bit(1),
> +            "propagated":   util.bit(2),
> +            "overflow":     util.bit(3),
> +        }
> +
> +        self.pei_error_types = {
> +            "cache":        util.bit(1),
> +            "tlb":          util.bit(2),
> +            "bus":          util.bit(3),
> +            "micro-arch":   util.bit(4),
> +        }
> +
> +        self.pei_valid_bits = {
> +            "multiple-error":   util.bit(0),
> +            "flags":            util.bit(1),
> +            "error-info":       util.bit(2),
> +            "virt-addr":        util.bit(3),
> +            "phy-addr":         util.bit(4),
> +        }
> +
> +        self.data = bytearray()
> +
> +        parser = subparsers.add_parser("arm", description=self.DESC)
> +
> +        arm_valid_bits = ",".join(self.arm_valid_bits.keys())
> +        flags = ",".join(self.pei_flags.keys())
> +        error_types = ",".join(self.pei_error_types.keys())
> +        pei_valid_bits = ",".join(self.pei_valid_bits.keys())
> +
> +        # UEFI N.16 ARM Validation bits
> +        g_arm = parser.add_argument_group("ARM processor")
> +        g_arm.add_argument("--arm", "--arm-valid",
> +                           help=f"ARM valid bits: {arm_valid_bits}")
> +        g_arm.add_argument("-a", "--affinity",  "--level", "--affinity-level",
> +                           type=lambda x: int(x, 0),
> +                           help="Affinity level (when multiple levels apply)")
> +        g_arm.add_argument("-l", "--mpidr", type=lambda x: int(x, 0),
> +                           help="Multiprocessor Affinity Register")
> +        g_arm.add_argument("-i", "--midr", type=lambda x: int(x, 0),
> +                           help="Main ID Register")
> +        g_arm.add_argument("-r", "--running",
> +                           action=argparse.BooleanOptionalAction,
> +                           default=None,
> +                           help="Indicates if the processor is running or not")
> +        g_arm.add_argument("--psci", "--psci-state",
> +                           type=lambda x: int(x, 0),
> +                           help="Power State Coordination Interface - PSCI state")
> +
> +        # TODO: Add vendor-specific support
> +
> +        # UEFI N.17 bitmaps (type and flags)
> +        g_pei = parser.add_argument_group("ARM Processor Error Info (PEI)")
> +        g_pei.add_argument("-t", "--type", nargs="+",
> +                        help=f"one or more error types: {error_types}")
> +        g_pei.add_argument("-f", "--flags", nargs="*",
> +                        help=f"zero or more error flags: {flags}")
> +        g_pei.add_argument("-V", "--pei-valid", "--error-valid", nargs="*",
> +                        help=f"zero or more PEI valid bits: {pei_valid_bits}")
> +
> +        # UEFI N.17 Integer values
> +        g_pei.add_argument("-m", "--multiple-error", nargs="+",
> +                        help="Number of errors: 0: Single error, 1: Multiple errors, 2-65535: Error count if known")
> +        g_pei.add_argument("-e", "--error-info", nargs="+",
> +                        help="Error information (UEFI 2.10 tables N.18 to N.20)")
> +        g_pei.add_argument("-p", "--physical-address",  nargs="+",
> +                        help="Physical address")
> +        g_pei.add_argument("-v", "--virtual-address",  nargs="+",
> +                        help="Virtual address")
> +
> +        # UEFI N.21 Context
> +        g_ctx = parser.add_argument_group("Processor Context")
> +        g_ctx.add_argument("--ctx-type", "--context-type", nargs="*",
> +                        help="Type of the context (0=ARM32 GPR, 5=ARM64 EL1, other values supported)")
> +        g_ctx.add_argument("--ctx-size", "--context-size", nargs="*",
> +                        help="Minimal size of the context")
> +        g_ctx.add_argument("--ctx-array", "--context-array", nargs="*",
> +                        help="Comma-separated arrays for each context")
> +
> +        # Vendor-specific data
> +        g_vendor = parser.add_argument_group("Vendor-specific data")
> +        g_vendor.add_argument("--vendor", "--vendor-specific", nargs="+",
> +                        help="Vendor-specific byte arrays of data")
> +
> +        # Add arguments for Generic Error Data
> +        qmp.argparse(parser)
> +
> +        parser.set_defaults(func=self.send_cper)
> +
> +    def send_cper(self, args):
> +        """Parse subcommand arguments and send a CPER via QMP"""
> +
> +        qmp_cmd = qmp(args.host, args.port, args.debug)
> +
> +        # Handle Generic Error Data arguments if any
> +        qmp_cmd.set_args(args)
> +
> +        is_cpu_type = re.compile(r"^([\w+]+\-)?arm\-cpu$")
> +        cpus = qmp_cmd.search_qom("/machine/unattached/device",
> +                                  "type", is_cpu_type)
> +
> +        cper = {}
> +        pei = {}
> +        ctx = {}
> +        vendor = {}
> +
> +        arg = vars(args)
> +
> +        # Handle global parameters
> +        if args.arm:
> +            arm_valid_init = False
> +            cper["valid"] = util.get_choice(name="valid",
> +                                       value=args.arm,
> +                                       choices=self.arm_valid_bits,
> +                                       suffixes=["-error", "-err"])
> +        else:
> +            cper["valid"] = 0
> +            arm_valid_init = True
> +
> +        if "running" in arg:
> +            if args.running:
> +                cper["running-state"] = util.bit(0)
> +            else:
> +                cper["running-state"] = 0
> +        else:
> +            cper["running-state"] = 0
> +
> +        if arm_valid_init:
> +            if args.affinity:
> +                cper["valid"] |= self.arm_valid_bits["affinity"]
> +
> +            if args.mpidr:
> +                cper["valid"] |= self.arm_valid_bits["mpidr"]
> +
> +            if "running-state" in cper:
> +                cper["valid"] |= self.arm_valid_bits["running"]
> +
> +            if args.psci:
> +                cper["valid"] |= self.arm_valid_bits["running"]
> +
> +        # Handle PEI
> +        if not args.type:
> +            args.type = ["cache-error"]
> +
> +        util.get_mult_choices(
> +            pei,
> +            name="valid",
> +            values=args.pei_valid,
> +            choices=self.pei_valid_bits,
> +            suffixes=["-valid", "--addr"],
> +        )
> +        util.get_mult_choices(
> +            pei,
> +            name="type",
> +            values=args.type,
> +            choices=self.pei_error_types,
> +            suffixes=["-error", "-err"],
> +        )
> +        util.get_mult_choices(
> +            pei,
> +            name="flags",
> +            values=args.flags,
> +            choices=self.pei_flags,
> +            suffixes=["-error", "-cap"],
> +        )
> +        util.get_mult_int(pei, "error-info", args.error_info)
> +        util.get_mult_int(pei, "multiple-error", args.multiple_error)
> +        util.get_mult_int(pei, "phy-addr", args.physical_address)
> +        util.get_mult_int(pei, "virt-addr", args.virtual_address)
> +
> +        # Handle context
> +        util.get_mult_int(ctx, "type", args.ctx_type, allow_zero=True)
> +        util.get_mult_int(ctx, "minimal-size", args.ctx_size, allow_zero=True)
> +        util.get_mult_array(ctx, "register", args.ctx_array, allow_zero=True)
> +
> +        util.get_mult_array(vendor, "bytes", args.vendor, max_val=255)
> +
> +        # Store PEI
> +        pei_data = bytearray()
> +        default_flags  = self.pei_flags["first"]
> +        default_flags |= self.pei_flags["last"]
> +
> +        error_info_num = 0
> +
> +        for i, p in pei.items():        # pylint: disable=W0612
> +            error_info_num += 1
> +
> +            # UEFI 2.10 doesn't define how to encode error information
> +            # when multiple types are raised. So, provide a default only
> +            # if a single type is there
> +            if "error-info" not in p:
> +                if p["type"] == util.bit(1):
> +                    p["error-info"] = 0x0091000F
> +                if p["type"] == util.bit(2):
> +                    p["error-info"] = 0x0054007F
> +                if p["type"] == util.bit(3):
> +                    p["error-info"] = 0x80D6460FFF
> +                if p["type"] == util.bit(4):
> +                    p["error-info"] = 0x78DA03FF
> +
> +            if "valid" not in p:
> +                p["valid"] = 0
> +                if "multiple-error" in p:
> +                    p["valid"] |= self.pei_valid_bits["multiple-error"]
> +
> +                if "flags" in p:
> +                    p["valid"] |= self.pei_valid_bits["flags"]
> +
> +                if "error-info" in p:
> +                    p["valid"] |= self.pei_valid_bits["error-info"]
> +
> +                if "phy-addr" in p:
> +                    p["valid"] |= self.pei_valid_bits["phy-addr"]
> +
> +                if "virt-addr" in p:
> +                    p["valid"] |= self.pei_valid_bits["virt-addr"]
> +
> +            # Version
> +            util.data_add(pei_data, 0, 1)
> +
> +            util.data_add(pei_data,
> +                         self.ACPI_GHES_ARM_CPER_PEI_LENGTH, 1)
> +
> +            util.data_add(pei_data, p["valid"], 2)
> +            util.data_add(pei_data, p["type"], 1)
> +            util.data_add(pei_data, p.get("multiple-error", 1), 2)
> +            util.data_add(pei_data, p.get("flags", default_flags), 1)
> +            util.data_add(pei_data, p.get("error-info", 0), 8)
> +            util.data_add(pei_data, p.get("virt-addr", 0xDEADBEEF), 8)
> +            util.data_add(pei_data, p.get("phy-addr", 0xABBA0BAD), 8)
> +
> +        # Store Context
> +        ctx_data = bytearray()
> +        context_info_num = 0
> +
> +        if ctx:
> +            ret = qmp_cmd.send_cmd("query-target", may_open=True)
> +
> +            default_ctx = self.CONTEXT_MISC_REG
> +
> +            if "arch" in ret:
> +                if ret["arch"] == "aarch64":
> +                    default_ctx = self.CONTEXT_AARCH64_EL1
> +                elif ret["arch"] == "arm":
> +                    default_ctx = self.CONTEXT_AARCH32_EL1
> +
> +            for k in sorted(ctx.keys()):
> +                context_info_num += 1
> +
> +                if "type" not in ctx[k]:
> +                    ctx[k]["type"] = default_ctx
> +
> +                if "register" not in ctx[k]:
> +                    ctx[k]["register"] = []
> +
> +                reg_size = len(ctx[k]["register"])
> +                size = 0
> +
> +                if "minimal-size" in ctx:
> +                    size = ctx[k]["minimal-size"]
> +
> +                size = max(size, reg_size)
> +
> +                size = (size + 1) % 0xFFFE
> +
> +                # Version
> +                util.data_add(ctx_data, 0, 2)
> +
> +                util.data_add(ctx_data, ctx[k]["type"], 2)
> +
> +                util.data_add(ctx_data, 8 * size, 4)
> +
> +                for r in ctx[k]["register"]:
> +                    util.data_add(ctx_data, r, 8)
> +
> +                for i in range(reg_size, size):   # pylint: disable=W0612
> +                    util.data_add(ctx_data, 0, 8)
> +
> +        # Vendor-specific bytes are not grouped
> +        vendor_data = bytearray()
> +        if vendor:
> +            for k in sorted(vendor.keys()):
> +                for b in vendor[k]["bytes"]:
> +                    util.data_add(vendor_data, b, 1)
> +
> +        # Encode ARM Processor Error
> +        data = bytearray()
> +
> +        util.data_add(data, cper["valid"], 4)
> +
> +        util.data_add(data, error_info_num, 2)
> +        util.data_add(data, context_info_num, 2)
> +
> +        # Calculate the length of the CPER data
> +        cper_length = self.ACPI_GHES_ARM_CPER_LENGTH
> +        cper_length += len(pei_data)
> +        cper_length += len(vendor_data)
> +        cper_length += len(ctx_data)
> +        util.data_add(data, cper_length, 4)
> +
> +        util.data_add(data, arg.get("affinity-level", 0), 1)
> +
> +        # Reserved
> +        util.data_add(data, 0, 3)
> +
> +        if "midr-el1" not in arg:
> +            if cpus:
> +                cmd_arg = {
> +                    'path': cpus[0],
> +                    'property': "midr"
> +                }
> +                ret = qmp_cmd.send_cmd("qom-get", cmd_arg, may_open=True)
> +                if isinstance(ret, int):
> +                    arg["midr-el1"] = ret
> +
> +        util.data_add(data, arg.get("mpidr-el1", 0), 8)
> +        util.data_add(data, arg.get("midr-el1", 0), 8)
> +        util.data_add(data, cper["running-state"], 4)
> +        util.data_add(data, arg.get("psci-state", 0), 4)
> +
> +        # Add PEI
> +        data.extend(pei_data)
> +        data.extend(ctx_data)
> +        data.extend(vendor_data)
> +
> +        self.data = data
> +
> +        qmp_cmd.send_cper(cper_guid.CPER_PROC_ARM, self.data)
> diff --git a/scripts/ghes_inject.py b/scripts/ghes_inject.py
> new file mode 100755
> index 000000000000..9a235201418b
> --- /dev/null
> +++ b/scripts/ghes_inject.py
> @@ -0,0 +1,51 @@
> +#!/usr/bin/env python3
> +#
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> +
> +"""
> +Handle ACPI GHESv2 error injection logic QEMU QMP interface.
> +"""
> +
> +import argparse
> +import sys
> +
> +from arm_processor_error import ArmProcessorEinj
> +
> +EINJ_DESC = """
> +Handle ACPI GHESv2 error injection logic QEMU QMP interface.
> +
> +It allows using UEFI BIOS EINJ features to generate GHES records.
> +
> +It helps testing CPER and GHES drivers at the guest OS and how
> +userspace applications at the guest handle them.
> +"""
> +
> +def main():
> +    """Main program"""
> +
> +    # Main parser - handle generic args like QEMU QMP TCP socket options
> +    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
> +                                     usage="%(prog)s [options]",
> +                                     description=EINJ_DESC)
> +
> +    g_options = parser.add_argument_group("QEMU QMP socket options")
> +    g_options.add_argument("-H", "--host", default="localhost", type=str,
> +                           help="host name")
> +    g_options.add_argument("-P", "--port", default=4445, type=int,
> +                           help="TCP port number")
> +    g_options.add_argument('-d', '--debug', action='store_true')
> +
> +    subparsers = parser.add_subparsers()
> +
> +    ArmProcessorEinj(subparsers)
> +
> +    args = parser.parse_args()
> +    if "func" in args:
> +        args.func(args)
> +    else:
> +        sys.exit(f"Please specify a valid command for {sys.argv[0]}")
> +
> +if __name__ == "__main__":
> +    main()
> diff --git a/scripts/qmp_helper.py b/scripts/qmp_helper.py
> new file mode 100755
> index 000000000000..c1e7e0fd80ce
> --- /dev/null
> +++ b/scripts/qmp_helper.py
> @@ -0,0 +1,703 @@
> +#!/usr/bin/env python3
> +#
> +# pylint: disable=C0103,E0213,E1135,E1136,E1137,R0902,R0903,R0912,R0913,R0917
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Copyright (C) 2024-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> +
> +"""
> +Helper classes to be used by ghes_inject command classes.
> +"""
> +
> +import json
> +import sys
> +
> +from datetime import datetime
> +from os import path as os_path
> +
> +try:
> +    qemu_dir = os_path.abspath(os_path.dirname(os_path.dirname(__file__)))
> +    sys.path.append(os_path.join(qemu_dir, 'python'))
> +
> +    from qemu.qmp.legacy import QEMUMonitorProtocol
> +
> +except ModuleNotFoundError as exc:
> +    print(f"Module '{exc.name}' not found.")
> +    print("Try export PYTHONPATH=top-qemu-dir/python or run from top-qemu-dir")
> +    sys.exit(1)
> +
> +from base64 import b64encode
> +
> +class util:
> +    """
> +    Ancillary functions to deal with bitmaps, parse arguments,
> +    generate GUID and encode data on a bytearray buffer.
> +    """
> +
> +    #
> +    # Helper routines to handle multiple choice arguments
> +    #
> +    def get_choice(name, value, choices, suffixes=None, bitmask=True):
> +        """Produce a list from multiple choice argument"""
> +
> +        new_values = 0
> +
> +        if not value:
> +            return new_values
> +
> +        for val in value.split(","):
> +            val = val.lower()
> +
> +            if suffixes:
> +                for suffix in suffixes:
> +                    val = val.removesuffix(suffix)
> +
> +            if val not in choices.keys():
> +                if suffixes:
> +                    for suffix in suffixes:
> +                        if val + suffix in choices.keys():
> +                            val += suffix
> +                            break
> +
> +            if val not in choices.keys():
> +                sys.exit(f"Error on '{name}': choice '{val}' is invalid.")
> +
> +            val = choices[val]
> +
> +            if bitmask:
> +                new_values |= val
> +            else:
> +                if new_values:
> +                    sys.exit(f"Error on '{name}': only one value is accepted.")
> +
> +                new_values = val
> +
> +        return new_values
> +
> +    def get_array(name, values, max_val=None):
> +        """Add numbered hashes from integer lists into an array"""
> +
> +        array = []
> +
> +        for value in values:
> +            for val in value.split(","):
> +                try:
> +                    val = int(val, 0)
> +                except ValueError:
> +                    sys.exit(f"Error on '{name}': {val} is not an integer")
> +
> +                if val < 0:
> +                    sys.exit(f"Error on '{name}': {val} is not unsigned")
> +
> +                if max_val and val > max_val:
> +                    sys.exit(f"Error on '{name}': {val} is too little")

you mean too large?


> +
> +                array.append(val)
> +
> +        return array
> +
> +    def get_mult_array(mult, name, values, allow_zero=False, max_val=None):
> +        """Add numbered hashes from integer lists"""
> +
> +        if not allow_zero:
> +            if not values:
> +                return
> +        else:
> +            if values is None:
> +                return
> +
> +            if not values:
> +                i = 0
> +                if i not in mult:
> +                    mult[i] = {}
> +
> +                mult[i][name] = []
> +                return
> +
> +        i = 0
> +        for value in values:
> +            for val in value.split(","):
> +                try:
> +                    val = int(val, 0)
> +                except ValueError:
> +                    sys.exit(f"Error on '{name}': {val} is not an integer")
> +
> +                if val < 0:
> +                    sys.exit(f"Error on '{name}': {val} is not unsigned")
> +
> +                if max_val and val > max_val:
> +                    sys.exit(f"Error on '{name}': {val} is too little")
> +
> +                if i not in mult:
> +                    mult[i] = {}
> +
> +                if name not in mult[i]:
> +                    mult[i][name] = []
> +
> +                mult[i][name].append(val)
> +
> +            i += 1
> +
> +
> +    def get_mult_choices(mult, name, values, choices,
> +                        suffixes=None, allow_zero=False):
> +        """Add numbered hashes from multiple choice arguments"""
> +
> +        if not allow_zero:
> +            if not values:
> +                return
> +        else:
> +            if values is None:
> +                return
> +
> +        i = 0
> +        for val in values:
> +            new_values = util.get_choice(name, val, choices, suffixes)
> +
> +            if i not in mult:
> +                mult[i] = {}
> +
> +            mult[i][name] = new_values
> +            i += 1
> +
> +
> +    def get_mult_int(mult, name, values, allow_zero=False):
> +        """Add numbered hashes from integer arguments"""
> +        if not allow_zero:
> +            if not values:
> +                return
> +        else:
> +            if values is None:
> +                return
> +
> +        i = 0
> +        for val in values:
> +            try:
> +                val = int(val, 0)
> +            except ValueError:
> +                sys.exit(f"Error on '{name}': {val} is not an integer")
> +
> +            if val < 0:
> +                sys.exit(f"Error on '{name}': {val} is not unsigned")
> +
> +            if i not in mult:
> +                mult[i] = {}
> +
> +            mult[i][name] = val
> +            i += 1
> +
> +
> +    #
> +    # Data encode helper functions
> +    #
> +    def bit(b):
> +        """Simple macro to define a bit on a bitmask"""
> +        return 1 << b
> +
> +
> +    def data_add(data, value, num_bytes):
> +        """Adds bytes from value inside a bitarray"""
> +
> +        data.extend(value.to_bytes(num_bytes, byteorder="little"))  # pylint: disable=E1101
> +
> +    def dump_bytearray(name, data):
> +        """Does an hexdump of a byte array, grouping in bytes"""
> +
> +        print(f"{name} ({len(data)} bytes):")
> +
> +        for ln_start in range(0, len(data), 16):
> +            ln_end = min(ln_start + 16, len(data))
> +            print(f"      {ln_start:08x}  ", end="")
> +            for i in range(ln_start, ln_end):
> +                print(f"{data[i]:02x} ", end="")
> +            for i in range(ln_end, ln_start + 16):
> +                print("   ", end="")
> +            print("  ", end="")
> +            for i in range(ln_start, ln_end):
> +                if data[i] >= 32 and data[i] < 127:
> +                    print(chr(data[i]), end="")
> +                else:
> +                    print(".", end="")
> +
> +            print()
> +        print()
> +
> +    def time(string):
> +        """Handle BCD timestamps used on Generic Error Data Block"""
> +
> +        time = None
> +
> +        # Formats to be used when parsing time stamps
> +        formats = [
> +            "%Y-%m-%d %H:%M:%S",
> +        ]
> +
> +        if string == "now":
> +            time = datetime.now()
> +
> +        if time is None:
> +            for fmt in formats:
> +                try:
> +                    time = datetime.strptime(string, fmt)
> +                    break
> +                except ValueError:
> +                    pass
> +
> +            if time is None:
> +                raise ValueError("Invalid time format")
> +
> +        return time
> +
> +class guid:
> +    """
> +    Simple class to handle GUID fields.
> +    """
> +
> +    def __init__(self, time_low, time_mid, time_high, nodes):
> +        """Initialize a GUID value"""
> +
> +        assert len(nodes) == 8
> +
> +        self.time_low = time_low
> +        self.time_mid = time_mid
> +        self.time_high = time_high
> +        self.nodes = nodes
> +
> +    @classmethod
> +    def UUID(cls, guid_str):
> +        """Initialize a GUID using a string on its standard format"""
> +
> +        if len(guid_str) != 36:
> +            print("Size not 36")
> +            raise ValueError('Invalid GUID size')
> +
> +        # It is easier to parse without separators. So, drop them
> +        guid_str = guid_str.replace('-', '')
> +
> +        if len(guid_str) != 32:
> +            print("Size not 32", guid_str, len(guid_str))
> +            raise ValueError('Invalid GUID hex size')
> +
> +        time_low = 0
> +        time_mid = 0
> +        time_high = 0
> +        nodes = []
> +
> +        for i in reversed(range(16, 32, 2)):
> +            h = guid_str[i:i + 2]
> +            value = int(h, 16)
> +            nodes.insert(0, value)
> +
> +        time_high = int(guid_str[12:16], 16)
> +        time_mid = int(guid_str[8:12], 16)
> +        time_low = int(guid_str[0:8], 16)
> +
> +        return cls(time_low, time_mid, time_high, nodes)
> +
> +    def __str__(self):
> +        """Output a GUID value on its default string representation"""
> +
> +        clock = self.nodes[0] << 8 | self.nodes[1]
> +
> +        node = 0
> +        for i in range(2, len(self.nodes)):
> +            node = node << 8 | self.nodes[i]
> +
> +        s = f"{self.time_low:08x}-{self.time_mid:04x}-"
> +        s += f"{self.time_high:04x}-{clock:04x}-{node:012x}"
> +        return s
> +
> +    def to_bytes(self):
> +        """Output a GUID value in bytes"""
> +
> +        data = bytearray()
> +
> +        util.data_add(data, self.time_low, 4)
> +        util.data_add(data, self.time_mid, 2)
> +        util.data_add(data, self.time_high, 2)
> +        data.extend(bytearray(self.nodes))
> +
> +        return data
> +
> +class qmp:
> +    """
> +    Opens a connection and send/receive QMP commands.
> +    """
> +
> +    def send_cmd(self, command, args=None, may_open=False, return_error=True):
> +        """Send a command to QMP, optinally opening a connection"""
> +
> +        if may_open:
> +            self._connect()
> +        elif not self.connected:
> +            return False
> +
> +        msg = { 'execute': command }
> +        if args:
> +            msg['arguments'] = args
> +
> +        try:
> +            obj = self.qmp_monitor.cmd_obj(msg)
> +        # Can we use some other exception class here?
> +        except Exception as e:                         # pylint: disable=W0718
> +            print(f"Command: {command}")
> +            print(f"Failed to inject error: {e}.")
> +            return None
> +
> +        if "return" in obj:
> +            if isinstance(obj.get("return"), dict):
> +                if obj["return"]:
> +                    return obj["return"]
> +                return "OK"
> +
> +            return obj["return"]
> +
> +        if isinstance(obj.get("error"), dict):
> +            error = obj["error"]
> +            if return_error:
> +                print(f"Command: {msg}")
> +                print(f'{error["class"]}: {error["desc"]}')
> +        else:
> +            print(json.dumps(obj))
> +
> +        return None
> +
> +    def _close(self):
> +        """Shutdown and close the socket, if opened"""
> +        if not self.connected:
> +            return
> +
> +        self.qmp_monitor.close()
> +        self.connected = False
> +
> +    def _connect(self):
> +        """Connect to a QMP TCP/IP port, if not connected yet"""
> +
> +        if self.connected:
> +            return True
> +
> +        try:
> +            self.qmp_monitor.connect(negotiate=True)
> +        except ConnectionError:
> +            sys.exit(f"Can't connect to QMP host {self.host}:{self.port}")
> +
> +        self.connected = True
> +
> +        return True
> +
> +    BLOCK_STATUS_BITS = {
> +        "uncorrectable":            util.bit(0),
> +        "correctable":              util.bit(1),
> +        "multi-uncorrectable":      util.bit(2),
> +        "multi-correctable":        util.bit(3),
> +    }
> +
> +    ERROR_SEVERITY = {
> +        "recoverable":  0,
> +        "fatal":        1,
> +        "corrected":    2,
> +        "none":         3,
> +    }
> +
> +    VALIDATION_BITS = {
> +        "fru-id":       util.bit(0),
> +        "fru-text":     util.bit(1),
> +        "timestamp":    util.bit(2),
> +    }
> +
> +    GEDB_FLAGS_BITS = {
> +        "recovered":    util.bit(0),
> +        "prev-error":   util.bit(1),
> +        "simulated":    util.bit(2),
> +    }
> +
> +    GENERIC_DATA_SIZE = 72
> +
> +    def argparse(parser):
> +        """Prepare a parser group to query generic error data"""
> +
> +        block_status_bits = ",".join(qmp.BLOCK_STATUS_BITS.keys())
> +        error_severity_enum = ",".join(qmp.ERROR_SEVERITY.keys())
> +        validation_bits = ",".join(qmp.VALIDATION_BITS.keys())
> +        gedb_flags_bits = ",".join(qmp.GEDB_FLAGS_BITS.keys())
> +
> +        g_gen = parser.add_argument_group("Generic Error Data")  # pylint: disable=E1101
> +        g_gen.add_argument("--block-status",
> +                           help=f"block status bits: {block_status_bits}")
> +        g_gen.add_argument("--raw-data", nargs="+",
> +                        help="Raw data inside the Error Status Block")
> +        g_gen.add_argument("--error-severity", "--severity",
> +                           help=f"error severity: {error_severity_enum}")
> +        g_gen.add_argument("--gen-err-valid-bits",
> +                           "--generic-error-validation-bits",
> +                           help=f"validation bits: {validation_bits}")
> +        g_gen.add_argument("--fru-id", type=guid.UUID,
> +                           help="GUID representing a physical device")
> +        g_gen.add_argument("--fru-text",
> +                           help="ASCII string identifying the FRU hardware")
> +        g_gen.add_argument("--timestamp", type=util.time,
> +                           help="Time when the error info was collected")
> +        g_gen.add_argument("--precise", "--precise-timestamp",
> +                           action='store_true',
> +                           help="Marks the timestamp as precise if --timestamp is used")
> +        g_gen.add_argument("--gedb-flags",
> +                           help=f"General Error Data Block flags: {gedb_flags_bits}")
> +
> +    def set_args(self, args):
> +        """Set the arguments optionally defined via self.argparse()"""
> +
> +        if args.block_status:
> +            self.block_status = util.get_choice(name="block-status",
> +                                                value=args.block_status,
> +                                                choices=self.BLOCK_STATUS_BITS,
> +                                                bitmask=False)
> +        if args.raw_data:
> +            self.raw_data = util.get_array("raw-data", args.raw_data,
> +                                           max_val=255)
> +            print(self.raw_data)
> +
> +        if args.error_severity:
> +            self.error_severity = util.get_choice(name="error-severity",
> +                                                  value=args.error_severity,
> +                                                  choices=self.ERROR_SEVERITY,
> +                                                  bitmask=False)
> +
> +        if args.fru_id:
> +            self.fru_id = args.fru_id.to_bytes()
> +            if not args.gen_err_valid_bits:
> +                self.validation_bits |= self.VALIDATION_BITS["fru-id"]
> +
> +        if args.fru_text:
> +            text = bytearray(args.fru_text.encode('ascii'))
> +            if len(text) > 20:
> +                sys.exit("FRU text is too big to fit")
> +
> +            self.fru_text = text
> +            if not args.gen_err_valid_bits:
> +                self.validation_bits |= self.VALIDATION_BITS["fru-text"]
> +
> +        if args.timestamp:
> +            time = args.timestamp
> +            century = int(time.year / 100)
> +
> +            bcd = bytearray()
> +            util.data_add(bcd, (time.second // 10) << 4 | (time.second % 10), 1)
> +            util.data_add(bcd, (time.minute // 10) << 4 | (time.minute % 10), 1)
> +            util.data_add(bcd, (time.hour // 10) << 4 | (time.hour % 10), 1)
> +
> +            if args.precise:
> +                util.data_add(bcd, 1, 1)
> +            else:
> +                util.data_add(bcd, 0, 1)
> +
> +            util.data_add(bcd, (time.day // 10) << 4 | (time.day % 10), 1)
> +            util.data_add(bcd, (time.month // 10) << 4 | (time.month % 10), 1)
> +            util.data_add(bcd,
> +                          ((time.year % 100) // 10) << 4 | (time.year % 10), 1)
> +            util.data_add(bcd, ((century % 100) // 10) << 4 | (century % 10), 1)
> +
> +            self.timestamp = bcd
> +            if not args.gen_err_valid_bits:
> +                self.validation_bits |= self.VALIDATION_BITS["timestamp"]
> +
> +        if args.gen_err_valid_bits:
> +            self.validation_bits = util.get_choice(name="validation",
> +                                                   value=args.gen_err_valid_bits,
> +                                                   choices=self.VALIDATION_BITS)
> +
> +    def __init__(self, host, port, debug=False):
> +        """Initialize variables used by the QMP send logic"""
> +
> +        self.connected = False
> +        self.host = host
> +        self.port = port
> +        self.debug = debug
> +
> +        # ACPI 6.1: 18.3.2.7.1 Generic Error Data: Generic Error Status Block
> +        self.block_status = self.BLOCK_STATUS_BITS["uncorrectable"]
> +        self.raw_data = []
> +        self.error_severity = self.ERROR_SEVERITY["recoverable"]
> +
> +        # ACPI 6.1: 18.3.2.7.1 Generic Error Data: Generic Error Data Entry
> +        self.validation_bits = 0
> +        self.flags = 0
> +        self.fru_id = bytearray(16)
> +        self.fru_text = bytearray(20)
> +        self.timestamp = bytearray(8)
> +
> +        self.qmp_monitor = QEMUMonitorProtocol(address=(self.host, self.port))
> +
> +    #
> +    # Socket QMP send command
> +    #
> +    def send_cper_raw(self, cper_data):
> +        """Send a raw CPER data to QEMU though QMP TCP socket"""
> +
> +        data = b64encode(bytes(cper_data)).decode('ascii')
> +
> +        cmd_arg = {
> +            'cper': data
> +        }
> +
> +        self._connect()
> +
> +        if self.send_cmd("inject-ghes-v2-error", cmd_arg):
> +            print("Error injected.")
> +
> +    def send_cper(self, notif_type, payload):
> +        """Send commands to QEMU though QMP TCP socket"""
> +
> +        # Fill CPER record header
> +
> +        # NOTE: bits 4 to 13 of block status contain the number of
> +        # data entries in the data section. This is currently unsupported.
> +
> +        cper_length = len(payload)
> +        data_length = cper_length + len(self.raw_data) + self.GENERIC_DATA_SIZE
> +
> +        #  Generic Error Data Entry
> +        gede = bytearray()
> +
> +        gede.extend(notif_type.to_bytes())
> +        util.data_add(gede, self.error_severity, 4)
> +        util.data_add(gede, 0x300, 2)
> +        util.data_add(gede, self.validation_bits, 1)
> +        util.data_add(gede, self.flags, 1)
> +        util.data_add(gede, cper_length, 4)
> +        gede.extend(self.fru_id)
> +        gede.extend(self.fru_text)
> +        gede.extend(self.timestamp)
> +
> +        # Generic Error Status Block
> +        gebs = bytearray()
> +
> +        if self.raw_data:
> +            raw_data_offset = len(gebs)
> +        else:
> +            raw_data_offset = 0
> +
> +        util.data_add(gebs, self.block_status, 4)
> +        util.data_add(gebs, raw_data_offset, 4)
> +        util.data_add(gebs, len(self.raw_data), 4)
> +        util.data_add(gebs, data_length, 4)
> +        util.data_add(gebs, self.error_severity, 4)
> +
> +        cper_data = bytearray()
> +        cper_data.extend(gebs)
> +        cper_data.extend(gede)
> +        cper_data.extend(bytearray(self.raw_data))
> +        cper_data.extend(bytearray(payload))
> +
> +        if self.debug:
> +            print(f"GUID: {notif_type}")
> +
> +            util.dump_bytearray("Generic Error Status Block", gebs)
> +            util.dump_bytearray("Generic Error Data Entry", gede)
> +
> +            if self.raw_data:
> +                util.dump_bytearray("Raw data", bytearray(self.raw_data))
> +
> +            util.dump_bytearray("Payload", payload)
> +
> +        self.send_cper_raw(cper_data)
> +
> +
> +    def search_qom(self, path, prop, regex):
> +        """
> +        Return a list of devices that match path array like:
> +
> +            /machine/unattached/device
> +            /machine/peripheral-anon/device
> +            ...
> +        """
> +
> +        found = []
> +
> +        i = 0
> +        while 1:
> +            dev = f"{path}[{i}]"
> +            args = {
> +                'path': dev,
> +                'property': prop
> +            }
> +            ret = self.send_cmd("qom-get", args, may_open=True,
> +                                return_error=False)
> +            if not ret:
> +                break
> +
> +            if isinstance(ret, str):
> +                if regex.search(ret):
> +                    found.append(dev)
> +
> +            i += 1
> +            if i > 10000:
> +                print("Too many objects returned by qom-get!")
> +                break
> +
> +        return found
> +
> +class cper_guid:
> +    """
> +    Contains CPER GUID, as per:
> +    https://uefi.org/specs/UEFI/2.10/Apx_N_Common_Platform_Error_Record.html
> +    """
> +
> +    CPER_PROC_GENERIC =  guid(0x9876CCAD, 0x47B4, 0x4bdb,
> +                              [0xB6, 0x5E, 0x16, 0xF1,
> +                               0x93, 0xC4, 0xF3, 0xDB])
> +
> +    CPER_PROC_X86 = guid(0xDC3EA0B0, 0xA144, 0x4797,
> +                         [0xB9, 0x5B, 0x53, 0xFA,
> +                          0x24, 0x2B, 0x6E, 0x1D])
> +
> +    CPER_PROC_ITANIUM = guid(0xe429faf1, 0x3cb7, 0x11d4,
> +                             [0xbc, 0xa7, 0x00, 0x80,
> +                              0xc7, 0x3c, 0x88, 0x81])
> +
> +    CPER_PROC_ARM = guid(0xE19E3D16, 0xBC11, 0x11E4,
> +                         [0x9C, 0xAA, 0xC2, 0x05,
> +                          0x1D, 0x5D, 0x46, 0xB0])
> +
> +    CPER_PLATFORM_MEM = guid(0xA5BC1114, 0x6F64, 0x4EDE,
> +                             [0xB8, 0x63, 0x3E, 0x83,
> +                              0xED, 0x7C, 0x83, 0xB1])
> +
> +    CPER_PLATFORM_MEM2 = guid(0x61EC04FC, 0x48E6, 0xD813,
> +                              [0x25, 0xC9, 0x8D, 0xAA,
> +                               0x44, 0x75, 0x0B, 0x12])
> +
> +    CPER_PCIE = guid(0xD995E954, 0xBBC1, 0x430F,
> +                     [0xAD, 0x91, 0xB4, 0x4D,
> +                      0xCB, 0x3C, 0x6F, 0x35])
> +
> +    CPER_PCI_BUS = guid(0xC5753963, 0x3B84, 0x4095,
> +                        [0xBF, 0x78, 0xED, 0xDA,
> +                         0xD3, 0xF9, 0xC9, 0xDD])
> +
> +    CPER_PCI_DEV = guid(0xEB5E4685, 0xCA66, 0x4769,
> +                        [0xB6, 0xA2, 0x26, 0x06,
> +                         0x8B, 0x00, 0x13, 0x26])
> +
> +    CPER_FW_ERROR = guid(0x81212A96, 0x09ED, 0x4996,
> +                         [0x94, 0x71, 0x8D, 0x72,
> +                          0x9C, 0x8E, 0x69, 0xED])
> +
> +    CPER_DMA_GENERIC = guid(0x5B51FEF7, 0xC79D, 0x4434,
> +                            [0x8F, 0x1B, 0xAA, 0x62,
> +                             0xDE, 0x3E, 0x2C, 0x64])
> +
> +    CPER_DMA_VT = guid(0x71761D37, 0x32B2, 0x45cd,
> +                       [0xA7, 0xD0, 0xB0, 0xFE,
> +                        0xDD, 0x93, 0xE8, 0xCF])
> +
> +    CPER_DMA_IOMMU = guid(0x036F84E1, 0x7F37, 0x428c,
> +                         [0xA7, 0x9E, 0x57, 0x5F,
> +                          0xDF, 0xAA, 0x84, 0xEC])
> +
> +    CPER_CCIX_PER = guid(0x91335EF6, 0xEBFB, 0x4478,
> +                         [0xA6, 0xA6, 0x88, 0xB7,
> +                          0x28, 0xCF, 0x75, 0xD7])
> +
> +    CPER_CXL_PROT_ERR = guid(0x80B9EFB4, 0x52B5, 0x4DE3,
> +                             [0xA7, 0x77, 0x68, 0x78,
> +                              0x4B, 0x77, 0x10, 0x48])
> -- 
> 2.51.0



  parent reply	other threads:[~2025-09-22 12:49 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-05 15:09 [PATCH v11 00/17] Change ghes to use HEST-based offsets and add support for error inject Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 01/17] Revert "hw/acpi/ghes: Make ghes_record_cper_errors() static" Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 02/17] acpi/ghes: Cleanup the code which gets ghes ged state Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 03/17] acpi/ghes: prepare to change the way HEST offsets are calculated Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 04/17] acpi/ghes: add a firmware file with HEST address Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 05/17] acpi/ghes: Use HEST table offsets when preparing GHES records Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 06/17] acpi/ghes: don't hard-code the number of sources for HEST table Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 07/17] acpi/ghes: add a notifier to notify when error data is ready Mauro Carvalho Chehab
2025-09-22 19:53   ` Michael S. Tsirkin
2025-09-05 15:09 ` [PATCH v11 08/17] acpi/generic_event_device: Update GHES migration to cover hest addr Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 09/17] acpi/generic_event_device: add logic to detect if HEST addr is available Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 10/17] acpi/generic_event_device: add an APEI error device Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 11/17] tests/acpi: virt: allow acpi table changes at DSDT and HEST tables Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 12/17] arm/virt: Wire up a GED error device for ACPI / GHES Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 13/17] qapi/acpi-hest: add an interface to do generic CPER error injection Mauro Carvalho Chehab
2025-09-22 12:42   ` Michael S. Tsirkin
2025-09-05 15:09 ` [PATCH v11 14/17] acpi/generic_event_device.c: enable use_hest_addr for QEMU 10.x Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 15/17] tests/acpi: virt: update HEST and DSDT tables Mauro Carvalho Chehab
2025-09-22 10:52   ` Michael S. Tsirkin
2025-09-23  6:57     ` Mauro Carvalho Chehab
2025-09-05 15:09 ` [PATCH v11 16/17] docs: hest: add new "etc/acpi_table_hest_addr" and update workflow Mauro Carvalho Chehab
2025-09-22 12:51   ` Michael S. Tsirkin
2025-09-05 15:09 ` [PATCH v11 17/17] scripts/ghes_inject: add a script to generate GHES error inject Mauro Carvalho Chehab
2025-09-22 10:53   ` Michael S. Tsirkin
2025-09-22 12:48   ` Michael S. Tsirkin [this message]
2025-09-23  6:31     ` Mauro Carvalho Chehab

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250922084417-mutt-send-email-mst@kernel.org \
    --to=mst@redhat.com \
    --cc=Jonathan.Cameron@huawei.com \
    --cc=crosa@redhat.com \
    --cc=imammedo@redhat.com \
    --cc=jsnow@redhat.com \
    --cc=mchehab+huawei@kernel.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=shiju.jose@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.