From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 03BD0C52D71 for ; Thu, 8 Aug 2024 20:59:36 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1scADt-0000se-TK; Thu, 08 Aug 2024 16:59:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1scADs-0000rw-EC for qemu-devel@nongnu.org; Thu, 08 Aug 2024 16:59:00 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1scADo-0005pk-66 for qemu-devel@nongnu.org; Thu, 08 Aug 2024 16:59:00 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1723150735; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=NmikgxZ0r/jpfCIgtMXecttpHXFuIGiJf7KQUHBaISo=; b=AZkCtgSStY1RQKwtLyCgmA73Lz3Tcjaxh7Acmzo5f6XZXQP8nXgkWMh01wAdR77vGrEMmT 0vYk+wVHiiMhbb3J3/YDZkPH6aoo5z7VrJFPpfQRdg8zeCmKkf5THr7eH4FJQEtNYOzvwq 1Z6nOy9QFI72t3ctNTLMEZWU/deYRIY= Received: from mail-wr1-f70.google.com (mail-wr1-f70.google.com [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-590-HCgfcErPNEeSGa6Ks6s17A-1; Thu, 08 Aug 2024 16:58:53 -0400 X-MC-Unique: HCgfcErPNEeSGa6Ks6s17A-1 Received: by mail-wr1-f70.google.com with SMTP id ffacd0b85a97d-3687e6d7a3aso686103f8f.2 for ; Thu, 08 Aug 2024 13:58:52 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1723150732; x=1723755532; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=NmikgxZ0r/jpfCIgtMXecttpHXFuIGiJf7KQUHBaISo=; b=ZgeKVjltA63ecGUMo76HTJF/BhaaGzjoqK+/LDrVTFYRnDDQ9UVxltMfYa2WBq646P sCsshV1cb9Pjh3aKXSkfw1JK/tp6LW05Z+KsH7p5NrEZLwUexCGAY1JxjWO8vw9LVk03 d4wNMP8quO3uAoMffoU9FZ1ZKNQ04XMdmlqXt27IUgLzu6srwWzn/JuhBwnJB43Mpjl4 i/RGeVe3Ou2tOGZOJX6RIWmVVwAM40xDnCCjPuMgiSLjcyyWUgudshQojCxydgn/H0A8 BABxKmHre33534/du8+U1k/tuJZL404sLVj8xMFjp/h1TGZT4GCwDR0H8MrYbiOONu5c GjBg== X-Forwarded-Encrypted: i=1; AJvYcCVtFNRLY7azKLLs9HzyloS7SaDO8f4gFNT7JE1H9Mg3QWYz4tttPaP2lEWbp6SP4Na5zADkzsuEgTvRCE1p5t0oiJwGGHE= X-Gm-Message-State: AOJu0Yxj6m4UyuugdOQItCPdLEngIwM707KbQoS1pN5ri/54XD9UWowO kk1FucUOMhe9UH4+qWtKSNttCJmtZrsKm2nOQnJUUkmnLROq+R7sIeYdNu0gggb/5q+GCkMBH70 VnZfDSRtT/xsqMVyf8gyOBXoBPgODwQl0tuxAQl4eCx7HJQyDoUIJwv4EBOJawxqyi6fX6zlykh 2CPFkajOWDvXEmBNF0sYH3/g88mYM= X-Received: by 2002:adf:cd0f:0:b0:368:7e8a:a8b4 with SMTP id ffacd0b85a97d-36d274dd030mr2283405f8f.23.1723150731477; Thu, 08 Aug 2024 13:58:51 -0700 (PDT) X-Google-Smtp-Source: AGHT+IGGf4wSpZe5UAEHm3WwgBPL9JtRr6jpdwswXGh9OtoWNSRiav80N9mws/pkoqMFnuUS9WCp5cEkL+dUwyKSarQ= X-Received: by 2002:adf:cd0f:0:b0:368:7e8a:a8b4 with SMTP id ffacd0b85a97d-36d274dd030mr2283399f8f.23.1723150730848; Thu, 08 Aug 2024 13:58:50 -0700 (PDT) MIME-Version: 1.0 References: <0654a89fe24f4343016b9cecc0752594ad1cd49f.1722634602.git.mchehab+huawei@kernel.org> In-Reply-To: <0654a89fe24f4343016b9cecc0752594ad1cd49f.1722634602.git.mchehab+huawei@kernel.org> From: John Snow Date: Thu, 8 Aug 2024 16:58:38 -0400 Message-ID: Subject: Re: [PATCH v5 7/7] scripts/ghes_inject: add a script to generate GHES error inject To: Mauro Carvalho Chehab Cc: Jonathan Cameron , Shiju Jose , Cleber Rosa , linux-kernel@vger.kernel.org, qemu-devel@nongnu.org Content-Type: multipart/alternative; boundary="000000000000351093061f324b8f" Received-SPF: pass client-ip=170.10.133.124; envelope-from=jsnow@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -21 X-Spam_score: -2.2 X-Spam_bar: -- X-Spam_report: (-2.2 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.141, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org --000000000000351093061f324b8f Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable On Fri, Aug 2, 2024 at 5:44=E2=80=AFPM Mauro Carvalho Chehab < mchehab+huawei@kernel.org> 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. > > Signed-off-by: Mauro Carvalho Chehab > --- > MAINTAINERS | 3 + > scripts/arm_processor_error.py | 352 +++++++++++++++++++++++++++++++++ > scripts/ghes_inject.py | 59 ++++++ > scripts/qmp_helper.py | 249 +++++++++++++++++++++++ > 4 files changed, 663 insertions(+) > create mode 100644 scripts/arm_processor_error.py > create mode 100755 scripts/ghes_inject.py > create mode 100644 scripts/qmp_helper.py > > diff --git a/MAINTAINERS b/MAINTAINERS > index 655edcb6688c..e490f69da1de 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -2081,6 +2081,9 @@ S: Maintained > F: hw/arm/ghes_cper.c > F: hw/acpi/ghes_cper_stub.c > F: qapi/ghes-cper.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..df4efa508790 > --- /dev/null > +++ b/scripts/arm_processor_error.py > @@ -0,0 +1,352 @@ > +#!/usr/bin/env python3 > +# > +# pylint: disable=3DC0301, C0114, R0912, R0913, R0914, R0915, W0511 > Out of curiosity, what tools are you using to delint your files and how are you invoking them? I don't really maintain any strict regime for python files under qemu.git/scripts (yet), so I am mostly curious as to what regimes others are using currently. I don't see most QEMU contributors checking in pylint ignores etc directly into the files, so it caught my eye. ~js > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Copyright (C) 2024 Mauro Carvalho Chehab > + > +# 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: > +# > +# - machine emulation architecture, as ARM current default is > +# for AArch64; > +# - ARM registers: power_state, midr, mpidr. > + > +import argparse > +import json > + > +from qmp_helper import (qmp_command, get_choice, get_mult_array, > + get_mult_choices, get_mult_int, bit, > + data_add, to_guid) > + > +# Arm processor EINJ logic > +# > +ACPI_GHES_ARM_CPER_LENGTH =3D 40 > +ACPI_GHES_ARM_CPER_PEI_LENGTH =3D 32 > + > +# TODO: query it from emulation. Current default valid only for Aarch64 > +CONTEXT_AARCH64_EL1 =3D 5 > + > +class ArmProcessorEinj: > + """ > + Implements ARM Processor Error injection via GHES > + """ > + > + def __init__(self): > + """Initialize the error injection class""" > + > + # Valid choice values > + self.arm_valid_bits =3D { > + "mpidr": bit(0), > + "affinity": bit(1), > + "running": bit(2), > + "vendor": bit(3), > + } > + > + self.pei_flags =3D { > + "first": bit(0), > + "last": bit(1), > + "propagated": bit(2), > + "overflow": bit(3), > + } > + > + self.pei_error_types =3D { > + "cache": bit(1), > + "tlb": bit(2), > + "bus": bit(3), > + "micro-arch": bit(4), > + } > + > + self.pei_valid_bits =3D { > + "multiple-error": bit(0), > + "flags": bit(1), > + "error-info": bit(2), > + "virt-addr": bit(3), > + "phy-addr": bit(4), > + } > + > + self.data =3D bytearray() > + > + def create_subparser(self, subparsers): > + """Add a subparser to handle for the error fields""" > + > + parser =3D subparsers.add_parser("arm", > + help=3D"Generate an ARM processor > CPER") > + > + arm_valid_bits =3D ",".join(self.arm_valid_bits.keys()) > + flags =3D ",".join(self.pei_flags.keys()) > + error_types =3D ",".join(self.pei_error_types.keys()) > + pei_valid_bits =3D ",".join(self.arm_valid_bits.keys()) > + > + # UEFI N.16 ARM Validation bits > + g_arm =3D parser.add_argument_group("ARM processor") > + g_arm.add_argument("--arm", "--arm-valid", > + help=3Df"ARM valid bits: {arm_valid_bits}") > + g_arm.add_argument("-a", "--affinity", "--level", > "--affinity-level", > + type=3Dlambda x: int(x, 0), > + help=3D"Affinity level (when multiple levels > apply)") > + g_arm.add_argument("-l", "--mpidr", type=3Dlambda x: int(x, 0), > + help=3D"Multiprocessor Affinity Register") > + g_arm.add_argument("-i", "--midr", type=3Dlambda x: int(x, 0), > + help=3D"Main ID Register") > + g_arm.add_argument("-r", "--running", > + action=3Dargparse.BooleanOptionalAction, > + default=3DNone, > + help=3D"Indicates if the processor is running= or > not") > + g_arm.add_argument("--psci", "--psci-state", > + type=3Dlambda x: int(x, 0), > + help=3D"Power State Coordination Interface - > PSCI state") > + > + # TODO: Add vendor-specific support > + > + # UEFI N.17 bitmaps (type and flags) > + g_pei =3D parser.add_argument_group("ARM Processor Error Info > (PEI)") > + g_pei.add_argument("-t", "--type", nargs=3D"+", > + help=3Df"one or more error types: {error_types}"= ) > + g_pei.add_argument("-f", "--flags", nargs=3D"*", > + help=3Df"zero or more error flags: {flags}") > + g_pei.add_argument("-V", "--pei-valid", "--error-valid", > nargs=3D"*", > + help=3Df"zero or more PEI valid bits: > {pei_valid_bits}") > + > + # UEFI N.17 Integer values > + g_pei.add_argument("-m", "--multiple-error", nargs=3D"+", > + help=3D"Number of errors: 0: Single error, 1: > Multiple errors, 2-65535: Error count if known") > + g_pei.add_argument("-e", "--error-info", nargs=3D"+", > + help=3D"Error information (UEFI 2.10 tables N.18= to > N.20)") > + g_pei.add_argument("-p", "--physical-address", nargs=3D"+", > + help=3D"Physical address") > + g_pei.add_argument("-v", "--virtual-address", nargs=3D"+", > + help=3D"Virtual address") > + > + # UEFI N.21 Context > + g_ctx =3D parser.add_argument_group("Processor Context") > + g_ctx.add_argument("--ctx-type", "--context-type", nargs=3D"*", > + help=3D"Type of the context (0=3DARM32 GPR, 5=3D= ARM64 > EL1, other values supported)") > + g_ctx.add_argument("--ctx-size", "--context-size", nargs=3D"*", > + help=3D"Minimal size of the context") > + g_ctx.add_argument("--ctx-array", "--context-array", nargs=3D"*"= , > + help=3D"Comma-separated arrays for each context"= ) > + > + # Vendor-specific data > + g_vendor =3D parser.add_argument_group("Vendor-specific data") > + g_vendor.add_argument("--vendor", "--vendor-specific", nargs=3D"= +", > + help=3D"Vendor-specific byte arrays of data") > + > + def parse_args(self, args): > + """Parse subcommand arguments""" > + > + cper =3D {} > + pei =3D {} > + ctx =3D {} > + vendor =3D {} > + > + arg =3D vars(args) > + > + # Handle global parameters > + if args.arm: > + arm_valid_init =3D False > + cper["valid"] =3D get_choice(name=3D"valid", > + value=3Dargs.arm, > + choices=3Dself.arm_valid_bits, > + suffixes=3D["-error", "-err"]) > + else: > + cper["valid"] =3D 0 > + arm_valid_init =3D True > + > + if "running" in arg: > + if args.running: > + cper["running-state"] =3D bit(0) > + else: > + cper["running-state"] =3D 0 > + else: > + cper["running-state"] =3D 0 > + > + if arm_valid_init: > + if args.affinity: > + cper["valid"] |=3D self.arm_valid_bits["affinity"] > + > + if args.mpidr: > + cper["valid"] |=3D self.arm_valid_bits["mpidr"] > + > + if "running-state" in cper: > + cper["valid"] |=3D self.arm_valid_bits["running"] > + > + if args.psci: > + cper["valid"] |=3D self.arm_valid_bits["running"] > + > + # Handle PEI > + if not args.type: > + args.type =3D ["cache-error"] > + > + get_mult_choices( > + pei, > + name=3D"valid", > + values=3Dargs.pei_valid, > + choices=3Dself.pei_valid_bits, > + suffixes=3D["-valid", "-info", "--information", "--addr"], > + ) > + get_mult_choices( > + pei, > + name=3D"type", > + values=3Dargs.type, > + choices=3Dself.pei_error_types, > + suffixes=3D["-error", "-err"], > + ) > + get_mult_choices( > + pei, > + name=3D"flags", > + values=3Dargs.flags, > + choices=3Dself.pei_flags, > + suffixes=3D["-error", "-cap"], > + ) > + get_mult_int(pei, "error-info", args.error_info) > + get_mult_int(pei, "multiple-error", args.multiple_error) > + get_mult_int(pei, "phy-addr", args.physical_address) > + get_mult_int(pei, "virt-addr", args.virtual_address) > + > + # Handle context > + get_mult_int(ctx, "type", args.ctx_type, allow_zero=3DTrue) > + get_mult_int(ctx, "minimal-size", args.ctx_size, allow_zero=3DTr= ue) > + get_mult_array(ctx, "register", args.ctx_array, allow_zero=3DTru= e) > + > + get_mult_array(vendor, "bytes", args.vendor, max_val=3D255) > + > + # Store PEI > + pei_data =3D bytearray() > + default_flags =3D self.pei_flags["first"] > + default_flags |=3D self.pei_flags["last"] > + > + error_info_num =3D 0 > + > + for i, p in pei.items(): # pylint: disable=3DW0612 > + error_info_num +=3D 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"] =3D=3D bit(1): > + p["error-info"] =3D 0x0091000F > + if p["type"] =3D=3D bit(2): > + p["error-info"] =3D 0x0054007F > + if p["type"] =3D=3D bit(3): > + p["error-info"] =3D 0x80D6460FFF > + if p["type"] =3D=3D bit(4): > + p["error-info"] =3D 0x78DA03FF > + > + if "valid" not in p: > + p["valid"] =3D 0 > + if "multiple-error" in p: > + p["valid"] |=3D self.pei_valid_bits["multiple-error"= ] > + > + if "flags" in p: > + p["valid"] |=3D self.pei_valid_bits["flags"] > + > + if "error-info" in p: > + p["valid"] |=3D self.pei_valid_bits["error-info"] > + > + if "phy-addr" in p: > + p["valid"] |=3D self.pei_valid_bits["phy-addr"] > + > + if "virt-addr" in p: > + p["valid"] |=3D self.pei_valid_bits["virt-addr"] > + > + # Version > + data_add(pei_data, 0, 1) > + > + data_add(pei_data, ACPI_GHES_ARM_CPER_PEI_LENGTH, 1) > + > + data_add(pei_data, p["valid"], 2) > + data_add(pei_data, p["type"], 1) > + data_add(pei_data, p.get("multiple-error", 1), 2) > + data_add(pei_data, p.get("flags", default_flags), 1) > + data_add(pei_data, p.get("error-info", 0), 8) > + data_add(pei_data, p.get("virt-addr", 0xDEADBEEF), 8) > + data_add(pei_data, p.get("phy-addr", 0xABBA0BAD), 8) > + > + # Store Context > + ctx_data =3D bytearray() > + context_info_num =3D 0 > + > + if ctx: > + for k in sorted(ctx.keys()): > + context_info_num +=3D 1 > + > + if "type" not in ctx: > + ctx[k]["type"] =3D CONTEXT_AARCH64_EL1 > + > + if "register" not in ctx: > + ctx[k]["register"] =3D [] > + > + reg_size =3D len(ctx[k]["register"]) > + size =3D 0 > + > + if "minimal-size" in ctx: > + size =3D ctx[k]["minimal-size"] > + > + size =3D max(size, reg_size) > + > + size =3D (size + 1) % 0xFFFE > + > + # Version > + data_add(ctx_data, 0, 2) > + > + data_add(ctx_data, ctx[k]["type"], 2) > + > + data_add(ctx_data, 8 * size, 4) > + > + for r in ctx[k]["register"]: > + data_add(ctx_data, r, 8) > + > + for i in range(reg_size, size): # pylint: disable=3DW0= 612 > + data_add(ctx_data, 0, 8) > + > + # Vendor-specific bytes are not grouped > + vendor_data =3D bytearray() > + if vendor: > + for k in sorted(vendor.keys()): > + for b in vendor[k]["bytes"]: > + data_add(vendor_data, b, 1) > + > + # Encode ARM Processor Error > + data =3D bytearray() > + > + data_add(data, cper["valid"], 4) > + > + data_add(data, error_info_num, 2) > + data_add(data, context_info_num, 2) > + > + # Calculate the length of the CPER data > + cper_length =3D ACPI_GHES_ARM_CPER_LENGTH > + cper_length +=3D len(pei_data) > + cper_length +=3D len(vendor_data) > + cper_length +=3D len(ctx_data) > + data_add(data, cper_length, 4) > + > + data_add(data, arg.get("affinity-level", 0), 1) > + > + # Reserved > + data_add(data, 0, 3) > + > + data_add(data, arg.get("mpidr-el1", 0), 8) > + data_add(data, arg.get("midr-el1", 0), 8) > + data_add(data, cper["running-state"], 4) > + 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 =3D data > + > + def run(self, host, port): > + """Execute QMP commands""" > + > + guid =3D to_guid(0xE19E3D16, 0xBC11, 0x11E4, > + [0x9C, 0xAA, 0xC2, 0x05, > + 0x1D, 0x5D, 0x46, 0xB0]) > + > + qmp_command(host, port, guid, self.data) > diff --git a/scripts/ghes_inject.py b/scripts/ghes_inject.py > new file mode 100755 > index 000000000000..8415ccbbc53d > --- /dev/null > +++ b/scripts/ghes_inject.py > @@ -0,0 +1,59 @@ > +#!/usr/bin/env python3 > +# > +# pylint: disable=3DC0301, C0114 > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Copyright (C) 2024 Mauro Carvalho Chehab > + > +import argparse > + > +from arm_processor_error import ArmProcessorEinj > + > +EINJ_DESCRIPTION =3D """ > +Handle ACPI GHESv2 error injection logic QEMU QMP interface.\n > + > +It allows using UEFI BIOS EINJ features to generate GHES records. > + > +It helps testing Linux CPER and GHES drivers and to test rasdaemon > +error handling logic. > + > +Currently, it support ARM processor error injection for ARM processor > +events, being compatible with UEFI 2.9A Errata. > + > +This small utility works together with those QEMU additions: > +- https://gitlab.com/mchehab_kernel/qemu/-/tree/arm-error-inject-v2 > +""" > + > +def main(): > + """Main program""" > + > + # Main parser - handle generic args like QEMU QMP TCP socket options > + parser =3D argparse.ArgumentParser(prog=3D"einj.py", > + > formatter_class=3Dargparse.RawDescriptionHelpFormatter, > + usage=3D"%(prog)s [options]", > + description=3DEINJ_DESCRIPTION, > + epilog=3D"If a field is not defined= , a > default value will be applied by QEMU.") > + > + g_options =3D parser.add_argument_group("QEMU QMP socket options") > + g_options.add_argument("-H", "--host", default=3D"localhost", type= =3Dstr, > + help=3D"host name") > + g_options.add_argument("-P", "--port", default=3D4445, type=3Dint, > + help=3D"TCP port number") > + > + arm_einj =3D ArmProcessorEinj() > + > + # Call subparsers > + subparsers =3D parser.add_subparsers(dest=3D'command') > + > + arm_einj.create_subparser(subparsers) > + > + args =3D parser.parse_args() > + > + # Handle subparser commands > + if args.command =3D=3D "arm": > + arm_einj.parse_args(args) > + arm_einj.run(args.host, args.port) > + > + > +if __name__ =3D=3D "__main__": > + main() > diff --git a/scripts/qmp_helper.py b/scripts/qmp_helper.py > new file mode 100644 > index 000000000000..13fae7a7af0e > --- /dev/null > +++ b/scripts/qmp_helper.py > @@ -0,0 +1,249 @@ > +#!/usr/bin/env python3 > +# > +# pylint: disable=3DC0301, C0114, R0912, R0913, R0915, W0511 > +# SPDX-License-Identifier: GPL-2.0 > +# > +# Copyright (C) 2024 Mauro Carvalho Chehab > + > +import json > +import socket > +import sys > + > +from base64 import b64encode > + > +# > +# Socket QMP send command > +# > +def qmp_command(host, port, guid, data): > + """Send commands to QEMU though QMP TCP socket""" > + > + # Fill the commands to be sent > + commands =3D [] > + > + # Needed to negotiate QMP and for QEMU to accept the command > + commands.append('{ "execute": "qmp_capabilities" } ') > + > + base64_data =3D b64encode(bytes(data)).decode('ascii') > + > + cmd_arg =3D { > + 'cper': { > + 'notification-type': guid, > + "raw-data": base64_data > + } > + } > + > + command =3D '{ "execute": "ghes-cper", ' > + command +=3D '"arguments": ' + json.dumps(cmd_arg) + " }" > + > + commands.append(command) > + > + s =3D socket.socket(socket.AF_INET, socket.SOCK_STREAM) > + try: > + s.connect((host, port)) > + except ConnectionRefusedError: > + sys.exit(f"Can't connect to QMP host {host}:{port}") > + > + data =3D s.recv(1024) > + try: > + obj =3D json.loads(data.decode("utf-8")) > + except json.JSONDecodeError as e: > + print(f"Invalid QMP answer: {e}") > + s.close() > + return > + > + if "QMP" not in obj: > + print(f"Invalid QMP answer: {data.decode("utf-8")}") > + s.close() > + return > + > + for i, command in enumerate(commands): > + s.sendall(command.encode("utf-8")) > + data =3D s.recv(1024) > + try: > + obj =3D json.loads(data.decode("utf-8")) > + except json.JSONDecodeError as e: > + print(f"Invalid QMP answer: {e}") > + s.close() > + return > + > + if isinstance(obj.get("return"), dict): > + if obj["return"]: > + print(json.dumps(obj["return"])) > + elif i > 0: > + print("Error injected.") > + elif isinstance(obj.get("error"), dict): > + error =3D obj["error"] > + print(f'{error["class"]}: {error["desc"]}') > + else: > + print(json.dumps(obj)) > + > + s.shutdown(socket.SHUT_WR) > + while 1: > + data =3D s.recv(1024) > + if data =3D=3D b"": > + break > + try: > + obj =3D json.loads(data.decode("utf-8")) > + except json.JSONDecodeError as e: > + print(f"Invalid QMP answer: {e}") > + s.close() > + return > + > + if isinstance(obj.get("return"), dict): > + print(json.dumps(obj["return"])) > + if isinstance(obj.get("error"), dict): > + error =3D obj["error"] > + print(f'{error["class"]}: {error["desc"]}') > + else: > + print(json.dumps(obj)) > + > + s.close() > + > + > +# > +# Helper routines to handle multiple choice arguments > +# > +def get_choice(name, value, choices, suffixes=3DNone): > + """Produce a list from multiple choice argument""" > + > + new_values =3D 0 > + > + if not value: > + return new_values > + > + for val in value.split(","): > + val =3D val.lower() > + > + if suffixes: > + for suffix in suffixes: > + val =3D val.removesuffix(suffix) > + > + if val not in choices.keys(): > + sys.exit(f"Error on '{name}': choice {val} is invalid.") > + > + val =3D choices[val] > + > + new_values |=3D val > + > + return new_values > + > + > +def get_mult_array(mult, name, values, allow_zero=3DFalse, max_val=3DNon= e): > + """Add numbered hashes from integer lists""" > + > + if not allow_zero: > + if not values: > + return > + else: > + if values is None: > + return > + > + if not values: > + i =3D 0 > + if i not in mult: > + mult[i] =3D {} > + > + mult[i][name] =3D [] > + return > + > + i =3D 0 > + for value in values: > + for val in value.split(","): > + try: > + val =3D 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] =3D {} > + > + if name not in mult[i]: > + mult[i][name] =3D [] > + > + mult[i][name].append(val) > + > + i +=3D 1 > + > + > +def get_mult_choices(mult, name, values, choices, > + suffixes=3DNone, allow_zero=3DFalse): > + """Add numbered hashes from multiple choice arguments""" > + > + if not allow_zero: > + if not values: > + return > + else: > + if values is None: > + return > + > + i =3D 0 > + for val in values: > + new_values =3D get_choice(name, val, choices, suffixes) > + > + if i not in mult: > + mult[i] =3D {} > + > + mult[i][name] =3D new_values > + i +=3D 1 > + > + > +def get_mult_int(mult, name, values, allow_zero=3DFalse): > + """Add numbered hashes from integer arguments""" > + if not allow_zero: > + if not values: > + return > + else: > + if values is None: > + return > + > + i =3D 0 > + for val in values: > + try: > + val =3D 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] =3D {} > + > + mult[i][name] =3D val > + i +=3D 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=3D"little")) > + > +def to_guid(time_low, time_mid, time_high, nodes): > + """Create an GUID string""" > + > + assert(len(nodes) =3D=3D 8) > + > + clock =3D nodes[0] << 8 | nodes[1] > + > + node =3D 0 > + for i in range(2, len(nodes)): > + node =3D node << 8 | nodes[i] > + > + s =3D f"{time_low:08x}-{time_mid:04x}-" > + s +=3D f"{time_high:04x}-{clock:04x}-{node:012x}" > + > + return s > -- > 2.45.2 > > --000000000000351093061f324b8f Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


=
On Fri, Aug 2, 2024 at 5:44=E2=80=AFP= M Mauro Carvalho Chehab <= mchehab+huawei@kernel.org> 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.

Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
=C2=A0MAINTAINERS=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 |=C2=A0 =C2=A03 +
=C2=A0scripts/arm_processor_error.py | 352 ++++++++++++++++++++++++++++++++= +
=C2=A0scripts/ghes_inject.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 59 ++= ++++
=C2=A0scripts/qmp_helper.py=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 249 +++++++= ++++++++++++++++
=C2=A04 files changed, 663 insertions(+)
=C2=A0create mode 100644 scripts/arm_processor_error.py
=C2=A0create mode 100755 scripts/ghes_inject.py
=C2=A0create mode 100644 scripts/qmp_helper.py

diff --git a/MAINTAINERS b/MAINTAINERS
index 655edcb6688c..e490f69da1de 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2081,6 +2081,9 @@ S: Maintained
=C2=A0F: hw/arm/ghes_cper.c
=C2=A0F: hw/acpi/ghes_cper_stub.c
=C2=A0F: qapi/ghes-cper.json
+F: scripts/ghes_inject.py
+F: scripts/arm_processor_error.py
+F: scripts/qmp_helper.py

=C2=A0ppc4xx
=C2=A0L: qemu-ppc@= nongnu.org
diff --git a/scripts/arm_processor_error.py b/scripts/arm_processor_error.p= y
new file mode 100644
index 000000000000..df4efa508790
--- /dev/null
+++ b/scripts/arm_processor_error.py
@@ -0,0 +1,352 @@
+#!/usr/bin/env python3
+#
+# pylint: disable=3DC0301, C0114, R0912, R0913, R0914, R0915, W0511

Out of curiosity, what tools are you using to= delint your files and how are you invoking them?

= I don't really maintain any strict regime for python files under qemu.g= it/scripts (yet), so I am mostly curious as to what regimes others are usin= g currently. I don't see most QEMU contributors checking in pylint igno= res etc directly into the files, so it caught my eye.

~js
=C2=A0
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2024 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:
+#
+#=C2=A0 =C2=A0- machine emulation architecture, as ARM current default is<= br> +#=C2=A0 =C2=A0 =C2=A0for AArch64;
+#=C2=A0 =C2=A0- ARM registers: power_state, midr, mpidr.
+
+import argparse
+import json
+
+from qmp_helper import (qmp_command, get_choice, get_mult_array,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 get_mult_choices, get_mult_int, bit,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 data_add, to_guid)
+
+# Arm processor EINJ logic
+#
+ACPI_GHES_ARM_CPER_LENGTH =3D 40
+ACPI_GHES_ARM_CPER_PEI_LENGTH =3D 32
+
+# TODO: query it from emulation. Current default valid only for Aarch64 +CONTEXT_AARCH64_EL1 =3D 5
+
+class ArmProcessorEinj:
+=C2=A0 =C2=A0 """
+=C2=A0 =C2=A0 Implements ARM Processor Error injection via GHES
+=C2=A0 =C2=A0 """
+
+=C2=A0 =C2=A0 def __init__(self):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Initialize the error injecti= on class"""
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Valid choice values
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.arm_valid_bits =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "mpidr":=C2=A0 =C2=A0 = bit(0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "affinity": bit(1), +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "running":=C2=A0 bit(2= ),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "vendor":=C2=A0 =C2=A0= bit(3),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.pei_flags =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "first":=C2=A0 =C2=A0 = =C2=A0 =C2=A0 bit(0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "last":=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0bit(1),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "propagated":=C2=A0 = =C2=A0bit(2),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "overflow":=C2=A0 =C2= =A0 =C2=A0bit(3),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.pei_error_types =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "cache":=C2=A0 =C2=A0 = =C2=A0 =C2=A0 bit(1),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "tlb":=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 bit(2),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "bus":=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 bit(3),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "micro-arch":=C2=A0 = =C2=A0bit(4),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.pei_valid_bits =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "multiple-error":=C2= =A0 =C2=A0bit(0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "flags":=C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 bit(1),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "error-info":=C2=A0 = =C2=A0 =C2=A0 =C2=A0bit(2),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "virt-addr":=C2=A0 =C2= =A0 =C2=A0 =C2=A0 bit(3),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "phy-addr":=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0bit(4),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.data =3D bytearray()
+
+=C2=A0 =C2=A0 def create_subparser(self, subparsers):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Add a subparser to handle fo= r the error fields"""
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 parser =3D subparsers.add_parser("arm&quo= t;,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0help=3D&q= uot;Generate an ARM processor CPER")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 arm_valid_bits =3D ",".join(self.arm= _valid_bits.keys())
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 flags =3D ",".join(self.pei_flags.ke= ys())
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_types =3D ",".join(self.pei_er= ror_types.keys())
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 pei_valid_bits =3D ",".join(self.arm= _valid_bits.keys())
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # UEFI N.16 ARM Validation bits
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_arm =3D parser.add_argument_group("ARM = processor")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_arm.add_argument("--arm", "--= arm-valid",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3Df"ARM valid bits: {arm_valid_bits}"= ;)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_arm.add_argument("-a", "--aff= inity",=C2=A0 "--level", "--affinity-level",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0type=3Dlambda x: int(x, 0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3D"Affinity level (when multiple levels a= pply)")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_arm.add_argument("-l", "--mpi= dr", type=3Dlambda x: int(x, 0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3D"Multiprocessor Affinity Register"= )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_arm.add_argument("-i", "--mid= r", type=3Dlambda x: int(x, 0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3D"Main ID Register")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_arm.add_argument("-r", "--run= ning",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0action=3Dargparse.BooleanOptionalAction,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0default=3DNone,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3D"Indicates if the processor is running = or not")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_arm.add_argument("--psci", "-= -psci-state",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0type=3Dlambda x: int(x, 0),
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3D"Power State Coordination Interface - P= SCI state")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # TODO: Add vendor-specific support
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # UEFI N.17 bitmaps (type and flags)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei =3D parser.add_argument_group("ARM = Processor Error Info (PEI)")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei.add_argument("-t", "--typ= e", nargs=3D"+",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3Df"one or more error types: {error_types}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei.add_argument("-f", "--fla= gs", nargs=3D"*",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3Df"zero or more error flags: {flags}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei.add_argument("-V", "--pei= -valid", "--error-valid", nargs=3D"*",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3Df"zero or more PEI valid bits: {pei_valid_bits}"= ;)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # UEFI N.17 Integer values
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei.add_argument("-m", "--mul= tiple-error", nargs=3D"+",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Number of errors: 0: Single error, 1: Multiple erro= rs, 2-65535: Error count if known")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei.add_argument("-e", "--err= or-info", nargs=3D"+",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Error information (UEFI 2.10 tables N.18 to N.20)&q= uot;)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei.add_argument("-p", "--phy= sical-address",=C2=A0 nargs=3D"+",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Physical address")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_pei.add_argument("-v", "--vir= tual-address",=C2=A0 nargs=3D"+",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Virtual address")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # UEFI N.21 Context
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_ctx =3D parser.add_argument_group("Proc= essor Context")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_ctx.add_argument("--ctx-type", &qu= ot;--context-type", nargs=3D"*",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Type of the context (0=3DARM32 GPR, 5=3DARM64 EL1, = other values supported)")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_ctx.add_argument("--ctx-size", &qu= ot;--context-size", nargs=3D"*",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Minimal size of the context")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_ctx.add_argument("--ctx-array", &q= uot;--context-array", nargs=3D"*",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Comma-separated arrays for each context")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Vendor-specific data
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_vendor =3D parser.add_argument_group("V= endor-specific data")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 g_vendor.add_argument("--vendor", &q= uot;--vendor-specific", nargs=3D"+",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 help=3D"Vendor-specific byte arrays of data")
+
+=C2=A0 =C2=A0 def parse_args(self, args):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Parse subcommand arguments&q= uot;""
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cper =3D {}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 pei =3D {}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx =3D {}
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vendor =3D {}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 arg =3D vars(args)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Handle global parameters
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if args.arm:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 arm_valid_init =3D False
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["valid"] =3D get_= choice(name=3D"valid",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0value=3Da= rgs.arm,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0choices= =3Dself.arm_valid_bits,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0suffixes= =3D["-error", "-err"])
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["valid"] =3D 0 +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 arm_valid_init =3D True
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if "running" in arg:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if args.running:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["running= -state"] =3D bit(0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["running= -state"] =3D 0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["running-state"] = =3D 0
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if arm_valid_init:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if args.affinity:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["valid&q= uot;] |=3D self.arm_valid_bits["affinity"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if args.mpidr:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["valid&q= uot;] |=3D self.arm_valid_bits["mpidr"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "running-state" in = cper:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["valid&q= uot;] |=3D self.arm_valid_bits["running"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if args.psci:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 cper["valid&q= uot;] |=3D self.arm_valid_bits["running"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Handle PEI
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not args.type:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 args.type =3D ["cache-error= "]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_choices(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pei,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name=3D"valid",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 values=3Dargs.pei_valid,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 choices=3Dself.pei_valid_bits, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 suffixes=3D["-valid", = "-info", "--information", "--addr"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_choices(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pei,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name=3D"type",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 values=3Dargs.type,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 choices=3Dself.pei_error_types,<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 suffixes=3D["-error", = "-err"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_choices(
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 pei,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 name=3D"flags",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 values=3Dargs.flags,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 choices=3Dself.pei_flags,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 suffixes=3D["-error", = "-cap"],
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_int(pei, "error-info", args= .error_info)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_int(pei, "multiple-error", = args.multiple_error)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_int(pei, "phy-addr", args.p= hysical_address)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_int(pei, "virt-addr", args.= virtual_address)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Handle context
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_int(ctx, "type", args.ctx_t= ype, allow_zero=3DTrue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_int(ctx, "minimal-size", ar= gs.ctx_size, allow_zero=3DTrue)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_array(ctx, "register", args= .ctx_array, allow_zero=3DTrue)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 get_mult_array(vendor, "bytes", args= .vendor, max_val=3D255)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Store PEI
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 pei_data =3D bytearray()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default_flags=C2=A0 =3D self.pei_flags["f= irst"]
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 default_flags |=3D self.pei_flags["last&q= uot;]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_info_num =3D 0
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for i, p in pei.items():=C2=A0 =C2=A0 =C2=A0 = =C2=A0 # pylint: disable=3DW0612
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_info_num +=3D 1
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # UEFI 2.10 doesn't define h= ow to encode error information
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # when multiple types are raised= . So, provide a default only
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # if a single type is there
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "error-info" not in= p:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if p["type&qu= ot;] =3D=3D bit(1):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;error-info"] =3D 0x0091000F
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if p["type&qu= ot;] =3D=3D bit(2):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;error-info"] =3D 0x0054007F
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if p["type&qu= ot;] =3D=3D bit(3):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;error-info"] =3D 0x80D6460FFF
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if p["type&qu= ot;] =3D=3D bit(4):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;error-info"] =3D 0x78DA03FF
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "valid" not in p: +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p["valid"= ;] =3D 0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "multiple-= error" in p:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;valid"] |=3D self.pei_valid_bits["multiple-error"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "flags&quo= t; in p:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;valid"] |=3D self.pei_valid_bits["flags"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "error-inf= o" in p:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;valid"] |=3D self.pei_valid_bits["error-info"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "phy-addr&= quot; in p:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;valid"] |=3D self.pei_valid_bits["phy-addr"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "virt-addr= " in p:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 p[&q= uot;valid"] |=3D self.pei_valid_bits["virt-addr"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # Version
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, 0, 1)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, ACPI_GHES_ARM= _CPER_PEI_LENGTH, 1)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, p["valid= "], 2)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, p["type&= quot;], 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, p.get("m= ultiple-error", 1), 2)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, p.get("f= lags", default_flags), 1)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, p.get("e= rror-info", 0), 8)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, p.get("v= irt-addr", 0xDEADBEEF), 8)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(pei_data, p.get("p= hy-addr", 0xABBA0BAD), 8)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Store Context
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx_data =3D bytearray()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 context_info_num =3D 0
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if ctx:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for k in sorted(ctx.keys()):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 context_info_num += =3D 1
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "type"= ; not in ctx:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx[= k]["type"] =3D CONTEXT_AARCH64_EL1
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "register&= quot; not in ctx:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ctx[= k]["register"] =3D []
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 reg_size =3D len(c= tx[k]["register"])
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size =3D 0
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if "minimal-s= ize" in ctx:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size= =3D ctx[k]["minimal-size"]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size =3D max(size,= reg_size)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 size =3D (size + 1= ) % 0xFFFE
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 # Version
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(ctx_data,= 0, 2)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(ctx_data,= ctx[k]["type"], 2)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(ctx_data,= 8 * size, 4)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for r in ctx[k][&q= uot;register"]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data= _add(ctx_data, r, 8)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for i in range(reg= _size, size):=C2=A0 =C2=A0# pylint: disable=3DW0612
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data= _add(ctx_data, 0, 8)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Vendor-specific bytes are not grouped
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vendor_data =3D bytearray()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if vendor:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for k in sorted(vendor.keys()):<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for b in vendor[k]= ["bytes"]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 data= _add(vendor_data, b, 1)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Encode ARM Processor Error
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data =3D bytearray()
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, cper["valid"], 4)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, error_info_num, 2)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, context_info_num, 2)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Calculate the length of the CPER data
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cper_length =3D ACPI_GHES_ARM_CPER_LENGTH
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cper_length +=3D len(pei_data)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cper_length +=3D len(vendor_data)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 cper_length +=3D len(ctx_data)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, cper_length, 4)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, arg.get("affinity-level&qu= ot;, 0), 1)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Reserved
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, 0, 3)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, arg.get("mpidr-el1", = 0), 8)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, arg.get("midr-el1", 0= ), 8)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, cper["running-state"]= , 4)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data_add(data, arg.get("psci-state",= 0), 4)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 # Add PEI
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data.extend(pei_data)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data.extend(ctx_data)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data.extend(vendor_data)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 self.data =3D data
+
+=C2=A0 =C2=A0 def run(self, host, port):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 """Execute QMP commands"&q= uot;"
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 guid =3D to_guid(0xE19E3D16, 0xBC11, 0x11E4, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0[0x9C, 0xAA, 0xC2, 0x05,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 0x1D, 0x5D, 0x46, 0xB0])
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qmp_command(host, port, guid, self.data)
diff --git a/scripts/ghes_inject.py b/scripts/ghes_inject.py
new file mode 100755
index 000000000000..8415ccbbc53d
--- /dev/null
+++ b/scripts/ghes_inject.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+#
+# pylint: disable=3DC0301, C0114
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2024 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+
+import argparse
+
+from arm_processor_error import ArmProcessorEinj
+
+EINJ_DESCRIPTION =3D """
+Handle ACPI GHESv2 error injection logic QEMU QMP interface.\n
+
+It allows using UEFI BIOS EINJ features to generate GHES records.
+
+It helps testing Linux CPER and GHES drivers and to test rasdaemon
+error handling logic.
+
+Currently, it support ARM processor error injection for ARM processor
+events, being compatible with UEFI 2.9A Errata.
+
+This small utility works together with those QEMU additions:
+- https://gitlab.com/mchehab_kerne= l/qemu/-/tree/arm-error-inject-v2
+"""
+
+def main():
+=C2=A0 =C2=A0 """Main program"""
+
+=C2=A0 =C2=A0 # Main parser - handle generic args like QEMU QMP TCP socket= options
+=C2=A0 =C2=A0 parser =3D argparse.ArgumentParser(prog=3D"einj.py"= ;,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0formatter_class= =3Dargparse.RawDescriptionHelpFormatter,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0usage=3D"%(= prog)s [options]",
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0description=3DEI= NJ_DESCRIPTION,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0epilog=3D"I= f a field is not defined, a default value will be applied by QEMU.") +
+=C2=A0 =C2=A0 g_options =3D parser.add_argument_group("QEMU QMP socke= t options")
+=C2=A0 =C2=A0 g_options.add_argument("-H", "--host", d= efault=3D"localhost", type=3Dstr,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3D"host name")
+=C2=A0 =C2=A0 g_options.add_argument("-P", "--port", d= efault=3D4445, type=3Dint,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0help=3D"TCP port number")
+
+=C2=A0 =C2=A0 arm_einj =3D ArmProcessorEinj()
+
+=C2=A0 =C2=A0 # Call subparsers
+=C2=A0 =C2=A0 subparsers =3D parser.add_subparsers(dest=3D'command'= ;)
+
+=C2=A0 =C2=A0 arm_einj.create_subparser(subparsers)
+
+=C2=A0 =C2=A0 args =3D parser.parse_args()
+
+=C2=A0 =C2=A0 # Handle subparser commands
+=C2=A0 =C2=A0 if args.command =3D=3D "arm":
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 arm_einj.parse_args(args)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 arm_einj.run(args.host, args.port)
+
+
+if __name__ =3D=3D "__main__":
+=C2=A0 =C2=A0 main()
diff --git a/scripts/qmp_helper.py b/scripts/qmp_helper.py
new file mode 100644
index 000000000000..13fae7a7af0e
--- /dev/null
+++ b/scripts/qmp_helper.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python3
+#
+# pylint: disable=3DC0301, C0114, R0912, R0913, R0915, W0511
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) 2024 Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+
+import json
+import socket
+import sys
+
+from base64 import b64encode
+
+#
+# Socket QMP send command
+#
+def qmp_command(host, port, guid, data):
+=C2=A0 =C2=A0 """Send commands to QEMU though QMP TCP socke= t"""
+
+=C2=A0 =C2=A0 # Fill the commands to be sent
+=C2=A0 =C2=A0 commands =3D []
+
+=C2=A0 =C2=A0 # Needed to negotiate QMP and for QEMU to accept the command=
+=C2=A0 =C2=A0 commands.append('{ "execute": "qmp_capabi= lities" } ')
+
+=C2=A0 =C2=A0 base64_data =3D b64encode(bytes(data)).decode('ascii'= ;)
+
+=C2=A0 =C2=A0 cmd_arg =3D {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 'cper': {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 'notification-type': gui= d,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "raw-data": base64_dat= a
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 command =3D '{ "execute": "ghes-cper"= ;, '
+=C2=A0 =C2=A0 command +=3D '"arguments": ' + json.dumps(= cmd_arg) + " }"
+
+=C2=A0 =C2=A0 commands.append(command)
+
+=C2=A0 =C2=A0 s =3D socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+=C2=A0 =C2=A0 try:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 s.connect((host, port))
+=C2=A0 =C2=A0 except ConnectionRefusedError:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 sys.exit(f"Can't connect to QMP host = {host}:{port}")
+
+=C2=A0 =C2=A0 data =3D s.recv(1024)
+=C2=A0 =C2=A0 try:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 obj =3D json.loads(data.decode("utf-8&quo= t;))
+=C2=A0 =C2=A0 except json.JSONDecodeError as e:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 print(f"Invalid QMP answer: {e}") +=C2=A0 =C2=A0 =C2=A0 =C2=A0 s.close()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 if "QMP" not in obj:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 print(f"Invalid QMP answer: {data.decode(= "utf-8")}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 s.close()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 for i, command in enumerate(commands):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 s.sendall(command.encode("utf-8")) +=C2=A0 =C2=A0 =C2=A0 =C2=A0 data =3D s.recv(1024)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 try:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 obj =3D json.loads(data.decode(&= quot;utf-8"))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 except json.JSONDecodeError as e:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(f"Invalid QMP answer:= {e}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s.close()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if isinstance(obj.get("return"), dic= t):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if obj["return"]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(json.dumps(o= bj["return"]))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 elif i > 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print("Error = injected.")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 elif isinstance(obj.get("error"), di= ct):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error =3D obj["error"]=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(f'{error["class&q= uot;]}: {error["desc"]}')
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(json.dumps(obj))
+
+=C2=A0 =C2=A0 s.shutdown(socket.SHUT_WR)
+=C2=A0 =C2=A0 while 1:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 data =3D s.recv(1024)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if data =3D=3D b"":
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 try:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 obj =3D json.loads(data.decode(&= quot;utf-8"))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 except json.JSONDecodeError as e:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(f"Invalid QMP answer:= {e}")
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 s.close()
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if isinstance(obj.get("return"), dic= t):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(json.dumps(obj["retur= n"]))
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if isinstance(obj.get("error"), dict= ):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error =3D obj["error"]=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(f'{error["class&q= uot;]}: {error["desc"]}')
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 print(json.dumps(obj))
+
+=C2=A0 =C2=A0 s.close()
+
+
+#
+# Helper routines to handle multiple choice arguments
+#
+def get_choice(name, value, choices, suffixes=3DNone):
+=C2=A0 =C2=A0 """Produce a list from multiple choice argume= nt"""
+
+=C2=A0 =C2=A0 new_values =3D 0
+
+=C2=A0 =C2=A0 if not value:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return new_values
+
+=C2=A0 =C2=A0 for val in value.split(","):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 val =3D val.lower()
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if suffixes:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 for suffix in suffixes:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 val =3D val.remove= suffix(suffix)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if val not in choices.keys():
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sys.exit(f"Error on '{n= ame}': choice {val} is invalid.")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 val =3D choices[val]
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 new_values |=3D val
+
+=C2=A0 =C2=A0 return new_values
+
+
+def get_mult_array(mult, name, values, allow_zero=3DFalse, max_val=3DNone)= :
+=C2=A0 =C2=A0 """Add numbered hashes from integer lists&quo= t;""
+
+=C2=A0 =C2=A0 if not allow_zero:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not values:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+=C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if values is None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not values:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 i =3D 0
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if i not in mult:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i] =3D {}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i][name] =3D []
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 i =3D 0
+=C2=A0 =C2=A0 for value in values:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 for val in value.split(","):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 try:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 val =3D int(val, 0= )
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 except ValueError:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sys.exit(f"Er= ror on '{name}': {val} is not an integer")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if val < 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sys.exit(f"Er= ror on '{name}': {val} is not unsigned")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if max_val and val > max_val:=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sys.exit(f"Er= ror on '{name}': {val} is too little")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if i not in mult:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i] =3D {}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if name not in mult[i]:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i][name] =3D = []
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i][name].append(val)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 i +=3D 1
+
+
+def get_mult_choices(mult, name, values, choices,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0suffixes=3DNone, allow_zero=3DFalse):
+=C2=A0 =C2=A0 """Add numbered hashes from multiple choice a= rguments"""
+
+=C2=A0 =C2=A0 if not allow_zero:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not values:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+=C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if values is None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 i =3D 0
+=C2=A0 =C2=A0 for val in values:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 new_values =3D get_choice(name, val, choices, = suffixes)
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if i not in mult:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i] =3D {}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i][name] =3D new_values
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 i +=3D 1
+
+
+def get_mult_int(mult, name, values, allow_zero=3DFalse):
+=C2=A0 =C2=A0 """Add numbered hashes from integer arguments= """
+=C2=A0 =C2=A0 if not allow_zero:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if not values:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+=C2=A0 =C2=A0 else:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if values is None:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return
+
+=C2=A0 =C2=A0 i =3D 0
+=C2=A0 =C2=A0 for val in values:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 try:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 val =3D int(val, 0)
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 except ValueError:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sys.exit(f"Error on '{n= ame}': {val} is not an integer")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if val < 0:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sys.exit(f"Error on '{n= ame}': {val} is not unsigned")
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if i not in mult:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i] =3D {}
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 mult[i][name] =3D val
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 i +=3D 1
+
+
+#
+# Data encode helper functions
+#
+def bit(b):
+=C2=A0 =C2=A0 """Simple macro to define a bit on a bitmask&= quot;""
+=C2=A0 =C2=A0 return 1 << b
+
+
+def data_add(data, value, num_bytes):
+=C2=A0 =C2=A0 """Adds bytes from value inside a bitarray&qu= ot;""
+
+=C2=A0 =C2=A0 data.extend(value.to_bytes(num_bytes, byteorder=3D"litt= le"))
+
+def to_guid(time_low, time_mid, time_high, nodes):
+=C2=A0 =C2=A0 """Create an GUID string""" +
+=C2=A0 =C2=A0 assert(len(nodes) =3D=3D 8)
+
+=C2=A0 =C2=A0 clock =3D nodes[0] << 8 | nodes[1]
+
+=C2=A0 =C2=A0 node =3D 0
+=C2=A0 =C2=A0 for i in range(2, len(nodes)):
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 node =3D node << 8 | nodes[i]
+
+=C2=A0 =C2=A0 s =3D f"{time_low:08x}-{time_mid:04x}-"
+=C2=A0 =C2=A0 s +=3D f"{time_high:04x}-{clock:04x}-{node:012x}"<= br> +
+=C2=A0 =C2=A0 return s
--
2.45.2

--000000000000351093061f324b8f--