* [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen
@ 2026-05-29 17:09 Ayan Kumar Halder
2026-06-19 21:12 ` Julien Grall
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Ayan Kumar Halder @ 2026-05-29 17:09 UTC (permalink / raw)
To: xen-devel
Cc: Ayan Kumar Halder, Doug Goldstein, Stefano Stabellini,
Andrew Cooper, Anthony PERARD, Michal Orzel, Jan Beulich,
Julien Grall, Roger Pau Monné, Bertrand Marquis,
Volodymyr Babchuk
Boot self-tests (also referred to as boot-time tests or power-on
self-tests) are intended to validate internal features of Xen during
bring-up. They are meant to be run in a debug / validation environment;
Xen is not expected to remain functional for production use after the
self-tests have executed. The purpose of these tests is to catch
hardware configuration issues early and to confirm that the platform
on which Xen has been brought up is sane. The expected flow is:
build Xen with the self-tests enabled, boot it, inspect the results,
and then reboot into the usual production configuration.
Introduce the tests to confirm that:
1. A cpu can send SGI 0 to itself
2. A cpu can send SGI 0 to another specific CPU
3. A cpu can send SGI 0 to all the other CPUs
4. A cpu can send SGI 1 to another CPU
These tests aim to test Xen has configured the GIC correctly to use SGIs.
Thus, the tests invoke specific APIs of GIC driver.
Also, introduce a config CONFIG_BOOT_SELFTEST which enables these tests.
The option defaults to N; it should be disabled for production builds and
is intended for the validation pipeline and coverage measurement. The
tests run during Xen boot and validate internal interfaces such as Xen's
interface with hardware, firmware and the bootloader.
Also, introduce an integer command line parameter "gic-test". By default, it
is set to 0 which means no tests are enabled.
For running SGI tests, "gic-test" should be set to 1. In future if we add
tests for distributer, ITS, LPI, etc, then we can use different numbers.
Thus, each number denotes a functionality of GICv3 which can be tested
independently and within a single boot of Xen.
In this way, we ensure that the tests to validate SGIs do not impact any other
tests.
In order to keep all the boot-time self-tests together in the binary, we
have introduced a separate section "initcallboottest". All the tests are
registered using __initcallboottest. During the bootup of each core, Xen
invokes do_init_boottests() to run the these tests. All these tests are
invoked before Xen creates the domains (in case of primary core) or runs
the idle loop (in case of secondary core).
Note: it was suggested that, once the boot self-tests have run, Xen
should call machine_halt() rather than continue booting (since this
build is only intended for validation). This is not wired in here
because the SGIs are sent from the primary and secondary CPUs and
received asynchronously on the target CPUs. There is no definite point
in the boot flow at which Xen can know that every send has been
observed by its receiver, so "after the tests have completed" has no
well-defined moment at which to insert machine_halt().
Signed-off-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com>
Signed-off-by: Michal Orzel <michal.orzel@amd.com>
---
Link to v1 (RFC):
https://lists.xenproject.org/archives/html/xen-devel/2025-09/msg00956.html
Upstream CI run (xen-project/people/ayankuma/xen fork, one commit on
top of xen-project/xen staging — all Linux builds + tests including
qemu-smoke-boot-selftest-arm64-gcc-debug passed; only the macos jobs
sit pending because the personal fork has no macos runner):
https://gitlab.com/xen-project/people/ayankuma/xen/-/pipelines/2561806695
Changes in v2:
- Renamed the patch from "xen/arm: Introduce GICV3 Self Tests" to
"Add GICv3 SGI boot/self tests in Xen", and rewrote the commit
message to explain the intent of boot self-tests (debug /
validation builds only, Xen not expected to remain functional
afterwards).
- Moved the selftest code out of gic-v3.c into a dedicated file
xen/arch/arm/gic-test.c, gated by CONFIG_BOOT_SELFTEST
(Stefano, Grygorii).
- Introduced a generic boot-self-test framework: new section
"initcallboottest", registration macro __initcallboottest, and
do_init_boottests() invoked once per CPU after
local_irq_enable(), so the test runs on every CPU (boot +
secondaries) and no longer collides with the IRQ-enable timing
in gicv3_init() (Julien #1, Julien #3).
- Added Kconfig option CONFIG_BOOT_SELFTEST in
xen/arch/arm/Kconfig (arm-only for now; arch-specific because
the only registered test is GICv3-specific).
- Reserved a dedicated SGI value GIC_SGI_TEST in enum gic_sgi
(xen/arch/arm/include/asm/gic.h), so the selftest never
reuses a functional SGI (Grygorii #3).
- Added a runtime integer command-line parameter "gic-test" so
the selftest binary can be shipped but its execution selected
at boot (gic-test=0 -> no-op; gic-test=1 -> SGI tests). Future
GICv3 features (distributor, ITS, LPI, ...) can claim further
values (Grygorii #2, partial).
- Documented why machine_halt() is not invoked after the tests:
SGI delivery is asynchronous, so there is no well-defined
point after which every send has been observed by its
receiver (Julien #2).
- Wired the tests into upstream GitLab CI: new build job
alpine-3.18-gcc-debug-arm64-boot-selftest, new test job
qemu-smoke-boot-selftest-arm64-gcc-debug, and the runner
script automation/scripts/qemu-boot-selftest-arm64.sh that
dumps the QEMU virt DTB, injects
"gic-test=1 console=dtuart sync_console" into
/chosen/xen,xen-bootargs via fdtput, boots Xen, and checks
for each "Sending GIC_SGI_TEST ..." followed by the matching
"CPU%u: GIC_SGI_TEST received".
automation/gitlab-ci/build.yaml | 8 ++
automation/gitlab-ci/test.yaml | 8 ++
.../scripts/qemu-boot-selftest-arm64.sh | 81 +++++++++++++++++++
xen/arch/arm/Kconfig | 15 ++++
xen/arch/arm/Makefile | 1 +
xen/arch/arm/gic-test.c | 52 ++++++++++++
xen/arch/arm/gic.c | 5 ++
xen/arch/arm/include/asm/gic.h | 3 +
xen/arch/arm/setup.c | 2 +
xen/arch/arm/smpboot.c | 2 +
xen/arch/arm/xen.lds.S | 4 +
xen/common/kernel.c | 11 +++
xen/include/xen/init.h | 3 +
13 files changed, 195 insertions(+)
create mode 100755 automation/scripts/qemu-boot-selftest-arm64.sh
create mode 100644 xen/arch/arm/gic-test.c
diff --git a/automation/gitlab-ci/build.yaml b/automation/gitlab-ci/build.yaml
index 7f5b5938e8..8df45caa86 100644
--- a/automation/gitlab-ci/build.yaml
+++ b/automation/gitlab-ci/build.yaml
@@ -439,6 +439,14 @@ alpine-3.18-gcc-debug-arm64:
CONFIG_UBSAN=y
CONFIG_UBSAN_FATAL=y
+alpine-3.18-gcc-debug-arm64-boot-selftest:
+ extends: .gcc-arm64-build-debug
+ <<: *build-test
+ variables:
+ CONTAINER: alpine:3.18-arm64v8
+ EXTRA_XEN_CONFIG: |
+ CONFIG_BOOT_SELFTEST=y
+
alpine-3.18-gcc-arm64-randconfig:
extends: .gcc-arm64-build
variables:
diff --git a/automation/gitlab-ci/test.yaml b/automation/gitlab-ci/test.yaml
index 8770c523e2..2398c6299a 100644
--- a/automation/gitlab-ci/test.yaml
+++ b/automation/gitlab-ci/test.yaml
@@ -524,6 +524,14 @@ qemu-smoke-dom0less-arm64-gcc-debug-gicv3:
- *arm64-test-needs
- alpine-3.18-gcc-debug-arm64
+qemu-smoke-boot-selftest-arm64-gcc-debug:
+ extends: .qemu-arm64
+ script:
+ - ./automation/scripts/qemu-boot-selftest-arm64.sh 2>&1 | tee ${LOGFILE}
+ needs:
+ - *arm64-test-needs
+ - alpine-3.18-gcc-debug-arm64-boot-selftest
+
qemu-smoke-dom0less-arm64-gcc-debug-staticmem:
extends: .qemu-arm64
script:
diff --git a/automation/scripts/qemu-boot-selftest-arm64.sh b/automation/scripts/qemu-boot-selftest-arm64.sh
new file mode 100755
index 0000000000..a37dba3e07
--- /dev/null
+++ b/automation/scripts/qemu-boot-selftest-arm64.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+
+set -ex -o pipefail
+
+# Boot the prebuilt Xen binary under QEMU with CONFIG_BOOT_SELFTEST=y enabled
+# and gic-test=1 in xen,xen-bootargs, then verify the four GICv3 SGI self-tests
+# pass by inspecting the serial log.
+
+XEN=binaries/xen
+QEMU=./binaries/qemu-system-aarch64
+DTB_RAW=binaries/virt.dtb
+DTB=binaries/virt-bootselftest.dtb
+LOG=smoke.serial
+
+test -x ${QEMU}
+test -f ${XEN}
+
+# Dump the auto-generated DT from the QEMU virt machine, then inject
+# /chosen/xen,xen-bootargs. The selftest infrastructure invokes
+# do_init_boottests() during early boot; gic-test=1 selects the GICv3 SGI
+# tests.
+# -net none avoids QEMU's default virtio-net-pci, whose efi-virtio.rom
+# is not shipped with the qemu-system-aarch64 artifact used in CI.
+${QEMU} \
+ -machine virt,virtualization=true,gic-version=3,dumpdtb=${DTB_RAW} \
+ -cpu cortex-a57 -m 1024 -smp 2 -display none -net none
+
+cp ${DTB_RAW} ${DTB}
+fdtput -t s ${DTB} /chosen xen,xen-bootargs \
+ "gic-test=1 console=dtuart sync_console"
+
+rm -f ${LOG}
+timeout 60 ${QEMU} \
+ -machine virt,virtualization=true,gic-version=3 \
+ -cpu cortex-a57 -m 1024 -smp 2 \
+ -serial file:${LOG} \
+ -monitor none -display none -no-reboot -net none \
+ -dtb ${DTB} \
+ -kernel ${XEN} || true
+
+# Each "Sending GIC_SGI_TEST ..." line must be followed by the matching
+# "CPU%u: GIC_SGI_TEST received".
+fail=0
+check_pair() {
+ local send_pat=$1
+ local recv_pat=$2
+ local send_line recv_line
+
+ send_line=$(grep -n -- "${send_pat}" ${LOG} | head -n1 | cut -d: -f1)
+ if [ -z "${send_line}" ]; then
+ echo "MISSING: ${send_pat}"
+ fail=1
+ return
+ fi
+
+ recv_line=$(grep -n -- "${recv_pat}" ${LOG} \
+ | awk -v bl="${send_line}" -F: '$1 > bl {print $1; exit}')
+ if [ -z "${recv_line}" ]; then
+ echo "MISSING (after line ${send_line}): ${recv_pat}"
+ fail=1
+ return
+ fi
+
+ echo "OK: '${send_pat}' -> '${recv_pat}' (lines ${send_line} -> ${recv_line})"
+}
+
+# Boot CPU sends SGI to itself
+check_pair "Sending GIC_SGI_TEST to self CPU0" "CPU0: GIC_SGI_TEST received"
+# Secondary CPU sends SGI to itself
+check_pair "Sending GIC_SGI_TEST to self CPU1" "CPU1: GIC_SGI_TEST received"
+# Secondary CPU sends SGI to primary
+check_pair "Sending GIC_SGI_TEST to CPU0 from CPU1" "CPU0: GIC_SGI_TEST received"
+# Send to all-but-self
+check_pair "Sending GIC_SGI_TEST to all except CPU1" "CPU0: GIC_SGI_TEST received"
+
+if [ ${fail} -ne 0 ]; then
+ echo "FAILED"
+ exit 1
+fi
+
+echo "PASSED"
diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index 79622b46a1..0e23bbf20b 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -476,6 +476,21 @@ config ARM64_HARDEN_BRANCH_PREDICTOR
config ARM32_HARDEN_BRANCH_PREDICTOR
def_bool y if ARM_32 && HARDEN_BRANCH_PREDICTOR
+config BOOT_SELFTEST
+ bool "Enable boot-time self-tests"
+ default n
+ help
+ This option enables boot-time self-tests that validate Xen's internal
+ interfaces with hardware, firmware and the bootloader. The tests are
+ registered with __initcallboottest and executed by do_init_boottests()
+ during early boot, before domains are created.
+
+ These tests are intended for validation and coverage measurement, not
+ for production builds. With this option enabled, Xen may not be
+ functional after the tests have run.
+
+ If unsure, say N.
+
source "arch/arm/platforms/Kconfig"
source "common/Kconfig"
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index 84c4062b30..0090761682 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -24,6 +24,7 @@ obj-y += domctl.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-y += efi/
obj-y += gic.o
+obj-$(CONFIG_BOOT_SELFTEST) += gic-test.init.o
obj-$(CONFIG_GICV2) += gic-v2.o
obj-$(CONFIG_GICV3) += gic-v3.o
obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
diff --git a/xen/arch/arm/gic-test.c b/xen/arch/arm/gic-test.c
new file mode 100644
index 0000000000..ca922e5d2a
--- /dev/null
+++ b/xen/arch/arm/gic-test.c
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <xen/delay.h>
+#include <xen/init.h>
+#include <xen/param.h>
+#include <xen/shutdown.h>
+#include <asm/gic.h>
+
+/*
+ * gic_test: Specifies the gic test to be executed.
+ * 0 = no tests are executed
+ * 1 = SGI tests are executed
+ */
+static unsigned int __initdata gic_test = 0;
+integer_param("gic-test", gic_test);
+
+/*
+ * CPU0: GIC_SGI_DUMP_STATE to self
+ * CPU{0-N}: GIC_SGI_TEST to self
+ * CPU{1-N}: GIC_SGI_TEST to CPU0
+ * CPU{N}: GIC_SGI_TEST to all but self
+ */
+static int __init gic_self_sgi_test(void)
+{
+ if ( !gic_test )
+ return 0;
+
+ printk("Sending GIC_SGI_TEST to self CPU%u\n", smp_processor_id());
+ send_SGI_self(GIC_SGI_TEST);
+
+ if ( smp_processor_id() == 0 )
+ {
+ printk("Sending GIC_SGI_DUMP_STATE to CPU0\n");
+ smp_send_state_dump(0);
+
+ return 0;
+ }
+
+ printk("Sending GIC_SGI_TEST to CPU0 from CPU%u\n", smp_processor_id());
+ send_SGI_one(0, GIC_SGI_TEST);
+
+ /* Execute this test only from the last core */
+ if ( smp_processor_id() == (smp_get_max_cpus() - 1) )
+ {
+ printk("Sending GIC_SGI_TEST to all except CPU%u\n", smp_processor_id());
+ send_SGI_allbutself(GIC_SGI_TEST);
+ }
+
+ return 0;
+
+}
+__initcallboottest(gic_self_sgi_test);
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index ee75258fc3..9736b0c7df 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -324,6 +324,11 @@ static void do_static_sgi(struct cpu_user_regs *regs, enum gic_sgi sgi)
case GIC_SGI_CALL_FUNCTION:
smp_call_function_interrupt();
break;
+#ifdef CONFIG_BOOT_SELFTEST
+ case GIC_SGI_TEST:
+ printk("CPU%u: GIC_SGI_TEST received\n", smp_processor_id());
+ break;
+#endif
default:
panic("Unhandled SGI %d on CPU%d\n", sgi, smp_processor_id());
break;
diff --git a/xen/arch/arm/include/asm/gic.h b/xen/arch/arm/include/asm/gic.h
index ff22dea40d..74bdd4ff63 100644
--- a/xen/arch/arm/include/asm/gic.h
+++ b/xen/arch/arm/include/asm/gic.h
@@ -306,6 +306,9 @@ enum gic_sgi {
GIC_SGI_EVENT_CHECK,
GIC_SGI_DUMP_STATE,
GIC_SGI_CALL_FUNCTION,
+#ifdef CONFIG_BOOT_SELFTEST
+ GIC_SGI_TEST,
+#endif
GIC_SGI_STATIC_MAX,
};
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
index 6310a47d68..4e5db93027 100644
--- a/xen/arch/arm/setup.c
+++ b/xen/arch/arm/setup.c
@@ -470,6 +470,8 @@ void asmlinkage __init noreturn start_xen(unsigned long fdt_paddr)
enable_errata_workarounds();
enable_cpu_features();
+ do_init_boottests();
+
/* Create initial domain 0. */
if ( !is_dom0less_mode() )
create_dom0();
diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
index 7f3cfa812e..a016ff00f5 100644
--- a/xen/arch/arm/smpboot.c
+++ b/xen/arch/arm/smpboot.c
@@ -405,6 +405,8 @@ void asmlinkage noreturn start_secondary(void)
printk(XENLOG_DEBUG "CPU %u booted.\n", smp_processor_id());
+ do_init_boottests();
+
startup_cpu_idle_loop();
}
diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
index 2d5f1c516d..14f64a856c 100644
--- a/xen/arch/arm/xen.lds.S
+++ b/xen/arch/arm/xen.lds.S
@@ -146,6 +146,10 @@ SECTIONS
*(.initcall1.init)
__initcall_end = .;
+ __initcall_boot_test_start = .;
+ *(.initcallboottest.init)
+ __initcall_boot_test_end = .;
+
. = ALIGN(4);
__alt_instructions = .;
*(.altinstructions)
diff --git a/xen/common/kernel.c b/xen/common/kernel.c
index fb45f81399..2047fe2a3f 100644
--- a/xen/common/kernel.c
+++ b/xen/common/kernel.c
@@ -412,6 +412,7 @@ void add_taint(unsigned int taint)
extern const initcall_t __initcall_start[], __presmp_initcall_end[],
__initcall_end[];
+extern const initcall_t __initcall_boot_test_start[], __initcall_boot_test_end[];
void __init do_presmp_initcalls(void)
{
@@ -427,6 +428,16 @@ void __init do_initcalls(void)
(*call)();
}
+void __init do_init_boottests(void)
+{
+#ifdef CONFIG_BOOT_SELFTEST
+ const initcall_t *call;
+ for ( call = __initcall_boot_test_start; call < __initcall_boot_test_end;
+ call++ )
+ (*call)();
+#endif
+}
+
#ifdef CONFIG_HYPFS
static unsigned int __read_mostly major_version;
static unsigned int __read_mostly minor_version;
diff --git a/xen/include/xen/init.h b/xen/include/xen/init.h
index 0c921672c1..bd518bcea9 100644
--- a/xen/include/xen/init.h
+++ b/xen/include/xen/init.h
@@ -66,11 +66,14 @@ typedef void (*exitcall_t)(void);
static const initcall_t __initcall_##fn __init_call("presmp") = (fn)
#define __initcall(fn) \
static const initcall_t __initcall_##fn __init_call("1") = (fn)
+#define __initcallboottest(fn) \
+ static const initcall_t __initcall_##fn __init_call("boottest") = (fn)
#define __exitcall(fn) \
static exitcall_t __exitcall_##fn __exit_call = fn
void do_presmp_initcalls(void);
void do_initcalls(void);
+void do_init_boottests(void);
#endif /* __ASSEMBLER__ */
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen
2026-05-29 17:09 [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen Ayan Kumar Halder
@ 2026-06-19 21:12 ` Julien Grall
2026-08-27 19:57 ` Halder, Ayan Kumar
2026-06-19 21:21 ` Julien Grall
2026-08-28 10:29 ` [PATCH v3 for 4.23] Add GIC " Ayan Kumar Halder
2 siblings, 1 reply; 6+ messages in thread
From: Julien Grall @ 2026-06-19 21:12 UTC (permalink / raw)
To: Ayan Kumar Halder, xen-devel
Cc: Doug Goldstein, Stefano Stabellini, Andrew Cooper, Anthony PERARD,
Michal Orzel, Jan Beulich, Roger Pau Monné, Bertrand Marquis,
Volodymyr Babchuk
Hi Ayan,
On 29/05/2026 18:09, Ayan Kumar Halder wrote:
> Boot self-tests (also referred to as boot-time tests or power-on
> self-tests) are intended to validate internal features of Xen during
> bring-up. They are meant to be run in a debug / validation environment;
> Xen is not expected to remain functional for production use after the
> self-tests have executed.
Looking at the code below, isn't Xen functional even after the self-test?
> The purpose of these tests is to catch
> hardware configuration issues early and to confirm that the platform
> on which Xen has been brought up is sane. The expected flow is:
> build Xen with the self-tests enabled, boot it, inspect the results,
> and then reboot into the usual production configuration.
>
> Introduce the tests to confirm that:
> 1. A cpu can send SGI 0 to itself
> 2. A cpu can send SGI 0 to another specific CPU
> 3. A cpu can send SGI 0 to all the other CPUs
> 4. A cpu can send SGI 1 to another CPU
I am not sure what you meant by SGI 0 and SGI 1? Below you seem to be
use only a SGI (which is neither 0 or 1) except for one specific test:
cpu 0 injecting an SGI to a specific CPU (0).
>
> These tests aim to test Xen has configured the GIC correctly to use SGIs.
> Thus, the tests invoke specific APIs of GIC driver.
>
> Also, introduce a config CONFIG_BOOT_SELFTEST which enables these tests.
> The option defaults to N; it should be disabled for production builds and
> is intended for the validation pipeline and coverage measurement. The
> tests run during Xen boot and validate internal interfaces such as Xen's
> interface with hardware, firmware and the bootloader.
>
> Also, introduce an integer command line parameter "gic-test". By default, it
> is set to 0 which means no tests are enabled.
> For running SGI tests, "gic-test" should be set to 1. In future if we add
> tests for distributer, ITS, LPI, etc, then we can use different numbers.
> Thus, each number denotes a functionality of GICv3 which can be tested
> independently and within a single boot of Xen.
>
> In this way, we ensure that the tests to validate SGIs do not impact any other
> tests.
>
> In order to keep all the boot-time self-tests together in the binary, we
> have introduced a separate section "initcallboottest". All the tests are
> registered using __initcallboottest. During the bootup of each core, Xen
> invokes do_init_boottests() to run the these tests. All these tests are
> invoked before Xen creates the domains (in case of primary core) or runs
> the idle loop (in case of secondary core).
>
> Note: it was suggested that, once the boot self-tests have run, Xen
> should call machine_halt() rather than continue booting (since this
> build is only intended for validation). This is not wired in here
> because the SGIs are sent from the primary and secondary CPUs and
> received asynchronously on the target CPUs. There is no definite point
> in the boot flow at which Xen can know that every send has been
> observed by its receiver, so "after the tests have completed" has no
> well-defined moment at which to insert machine_halt().
>
> Signed-off-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com>
> Signed-off-by: Michal Orzel <michal.orzel@amd.com>
> ---
> Link to v1 (RFC):
> https://lists.xenproject.org/archives/html/xen-devel/2025-09/msg00956.html
>
> Upstream CI run (xen-project/people/ayankuma/xen fork, one commit on
> top of xen-project/xen staging — all Linux builds + tests including
> qemu-smoke-boot-selftest-arm64-gcc-debug passed; only the macos jobs
> sit pending because the personal fork has no macos runner):
> https://gitlab.com/xen-project/people/ayankuma/xen/-/pipelines/2561806695
>
> Changes in v2:
> - Renamed the patch from "xen/arm: Introduce GICV3 Self Tests" to
> "Add GICv3 SGI boot/self tests in Xen", and rewrote the commit
> message to explain the intent of boot self-tests (debug /
> validation builds only, Xen not expected to remain functional
> afterwards).
> - Moved the selftest code out of gic-v3.c into a dedicated file
> xen/arch/arm/gic-test.c, gated by CONFIG_BOOT_SELFTEST
> (Stefano, Grygorii).
> - Introduced a generic boot-self-test framework: new section
> "initcallboottest", registration macro __initcallboottest, and
> do_init_boottests() invoked once per CPU after
> local_irq_enable(), so the test runs on every CPU (boot +
> secondaries) and no longer collides with the IRQ-enable timing
> in gicv3_init() (Julien #1, Julien #3).
> - Added Kconfig option CONFIG_BOOT_SELFTEST in
> xen/arch/arm/Kconfig (arm-only for now; arch-specific because
> the only registered test is GICv3-specific).
> - Reserved a dedicated SGI value GIC_SGI_TEST in enum gic_sgi
> (xen/arch/arm/include/asm/gic.h), so the selftest never
> reuses a functional SGI (Grygorii #3).
> - Added a runtime integer command-line parameter "gic-test" so
> the selftest binary can be shipped but its execution selected
> at boot (gic-test=0 -> no-op; gic-test=1 -> SGI tests). Future
> GICv3 features (distributor, ITS, LPI, ...) can claim further
> values (Grygorii #2, partial).
> - Documented why machine_halt() is not invoked after the tests:
> SGI delivery is asynchronous, so there is no well-defined
> point after which every send has been observed by its
> receiver (Julien #2).
> - Wired the tests into upstream GitLab CI: new build job
> alpine-3.18-gcc-debug-arm64-boot-selftest, new test job
> qemu-smoke-boot-selftest-arm64-gcc-debug, and the runner
> script automation/scripts/qemu-boot-selftest-arm64.sh that
> dumps the QEMU virt DTB, injects
> "gic-test=1 console=dtuart sync_console" into
> /chosen/xen,xen-bootargs via fdtput, boots Xen, and checks
> for each "Sending GIC_SGI_TEST ..." followed by the matching
> "CPU%u: GIC_SGI_TEST received".
>
> automation/gitlab-ci/build.yaml | 8 ++
> automation/gitlab-ci/test.yaml | 8 ++
> .../scripts/qemu-boot-selftest-arm64.sh | 81 +++++++++++++++++++
> xen/arch/arm/Kconfig | 15 ++++
> xen/arch/arm/Makefile | 1 +
> xen/arch/arm/gic-test.c | 52 ++++++++++++
> xen/arch/arm/gic.c | 5 ++
> xen/arch/arm/include/asm/gic.h | 3 +
> xen/arch/arm/setup.c | 2 +
> xen/arch/arm/smpboot.c | 2 +
> xen/arch/arm/xen.lds.S | 4 +
> xen/common/kernel.c | 11 +++
> xen/include/xen/init.h | 3 +
> 13 files changed, 195 insertions(+)
> create mode 100755 automation/scripts/qemu-boot-selftest-arm64.sh
> create mode 100644 xen/arch/arm/gic-test.c
>
> diff --git a/automation/gitlab-ci/build.yaml b/automation/gitlab-ci/build.yaml
> index 7f5b5938e8..8df45caa86 100644
> --- a/automation/gitlab-ci/build.yaml
> +++ b/automation/gitlab-ci/build.yaml
> @@ -439,6 +439,14 @@ alpine-3.18-gcc-debug-arm64:
> CONFIG_UBSAN=y
> CONFIG_UBSAN_FATAL=y
>
> +alpine-3.18-gcc-debug-arm64-boot-selftest:
> + extends: .gcc-arm64-build-debug
> + <<: *build-test
> + variables:
> + CONTAINER: alpine:3.18-arm64v8
> + EXTRA_XEN_CONFIG: |
> + CONFIG_BOOT_SELFTEST=y
> +
> alpine-3.18-gcc-arm64-randconfig:
> extends: .gcc-arm64-build
> variables:
> diff --git a/automation/gitlab-ci/test.yaml b/automation/gitlab-ci/test.yaml
> index 8770c523e2..2398c6299a 100644
> --- a/automation/gitlab-ci/test.yaml
> +++ b/automation/gitlab-ci/test.yaml
> @@ -524,6 +524,14 @@ qemu-smoke-dom0less-arm64-gcc-debug-gicv3:
> - *arm64-test-needs
> - alpine-3.18-gcc-debug-arm64
>
> +qemu-smoke-boot-selftest-arm64-gcc-debug:
> + extends: .qemu-arm64
> + script:
> + - ./automation/scripts/qemu-boot-selftest-arm64.sh 2>&1 | tee ${LOGFILE}
> + needs:
> + - *arm64-test-needs
> + - alpine-3.18-gcc-debug-arm64-boot-selftest
> +
> qemu-smoke-dom0less-arm64-gcc-debug-staticmem:
> extends: .qemu-arm64
> script:
> diff --git a/automation/scripts/qemu-boot-selftest-arm64.sh b/automation/scripts/qemu-boot-selftest-arm64.sh
> new file mode 100755
> index 0000000000..a37dba3e07
> --- /dev/null
> +++ b/automation/scripts/qemu-boot-selftest-arm64.sh
> @@ -0,0 +1,81 @@
> +#!/bin/bash
> +
> +set -ex -o pipefail
> +
> +# Boot the prebuilt Xen binary under QEMU with CONFIG_BOOT_SELFTEST=y enabled
> +# and gic-test=1 in xen,xen-bootargs, then verify the four GICv3 SGI self-tests
> +# pass by inspecting the serial log.
> +
> +XEN=binaries/xen
> +QEMU=./binaries/qemu-system-aarch64
> +DTB_RAW=binaries/virt.dtb
> +DTB=binaries/virt-bootselftest.dtb
> +LOG=smoke.serial
> +
> +test -x ${QEMU}
> +test -f ${XEN}
> +
> +# Dump the auto-generated DT from the QEMU virt machine, then inject
> +# /chosen/xen,xen-bootargs. The selftest infrastructure invokes
> +# do_init_boottests() during early boot; gic-test=1 selects the GICv3 SGI
> +# tests.
> +# -net none avoids QEMU's default virtio-net-pci, whose efi-virtio.rom
> +# is not shipped with the qemu-system-aarch64 artifact used in CI.
> +${QEMU} \
> + -machine virt,virtualization=true,gic-version=3,dumpdtb=${DTB_RAW} \
> + -cpu cortex-a57 -m 1024 -smp 2 -display none -net none
> +
> +cp ${DTB_RAW} ${DTB}
> +fdtput -t s ${DTB} /chosen xen,xen-bootargs \
> + "gic-test=1 console=dtuart sync_console"
> +
> +rm -f ${LOG}
> +timeout 60 ${QEMU} \
> + -machine virt,virtualization=true,gic-version=3 \
> + -cpu cortex-a57 -m 1024 -smp 2 \
This means that there is no much difference between "send an SGI to a
specific CPU" and "send an SGI to others CPU". I think it would be more
meaningful to use 3 or more pCPUs.
> + -serial file:${LOG} \
> + -monitor none -display none -no-reboot -net none \
> + -dtb ${DTB} \
> + -kernel ${XEN} || true
> +
> +# Each "Sending GIC_SGI_TEST ..." line must be followed by the matching
> +# "CPU%u: GIC_SGI_TEST received".
> +fail=0
> +check_pair() {
> + local send_pat=$1
> + local recv_pat=$2
> + local send_line recv_line
> +
> + send_line=$(grep -n -- "${send_pat}" ${LOG} | head -n1 | cut -d: -f1)
> + if [ -z "${send_line}" ]; then
> + echo "MISSING: ${send_pat}"
> + fail=1
> + return
> + fi
> +
> + recv_line=$(grep -n -- "${recv_pat}" ${LOG} \
> + | awk -v bl="${send_line}" -F: '$1 > bl {print $1; exit}')
> + if [ -z "${recv_line}" ]; then
> + echo "MISSING (after line ${send_line}): ${recv_pat}"
> + fail=1
> + return
> + fi
> +
> + echo "OK: '${send_pat}' -> '${recv_pat}' (lines ${send_line} -> ${recv_line})"
> +}
> +
> +# Boot CPU sends SGI to itself
> +check_pair "Sending GIC_SGI_TEST to self CPU0" "CPU0: GIC_SGI_TEST received"
> +# Secondary CPU sends SGI to itself
> +check_pair "Sending GIC_SGI_TEST to self CPU1" "CPU1: GIC_SGI_TEST received"
> +# Secondary CPU sends SGI to primary
> +check_pair "Sending GIC_SGI_TEST to CPU0 from CPU1" "CPU0: GIC_SGI_TEST received"
> +# Send to all-but-self
> +check_pair "Sending GIC_SGI_TEST to all except CPU1" "CPU0: GIC_SGI_TEST received"
> +
> +if [ ${fail} -ne 0 ]; then
> + echo "FAILED"
> + exit 1
> +fi
> +
> +echo "PASSED"
> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
> index 79622b46a1..0e23bbf20b 100644
> --- a/xen/arch/arm/Kconfig
> +++ b/xen/arch/arm/Kconfig
> @@ -476,6 +476,21 @@ config ARM64_HARDEN_BRANCH_PREDICTOR
> config ARM32_HARDEN_BRANCH_PREDICTOR
> def_bool y if ARM_32 && HARDEN_BRANCH_PREDICTOR
>
> +config BOOT_SELFTEST
> + bool "Enable boot-time self-tests"
> + default n
Above you said, this is not meant for production. So I was expecting to
see a dependency on CONFIG_DEBUG.
If the intention is to use it in release build, given the current
behavior (e.g. breaking test), I think this should depend on UNSUPPORTED
so we don't get security report because Xen is broken after the boot tests.
> + help> + This option enables boot-time self-tests that
validate Xen's internal
> + interfaces with hardware, firmware and the bootloader. The tests are
> + registered with __initcallboottest and executed by do_init_boottests()
> + during early boot, before domains are created.
> +
> + These tests are intended for validation and coverage measurement, not
> + for production builds. With this option enabled, Xen may not be
> + functional after the tests have run.
If we know a test break, I think it is best that Xen doesn't continue.
Otherwise, it is quite confusing for the user to know what's going on.
My preference is the bootest would act like other self tests and Xen can
continue booting normally. So the tests could also be meaningful in
non-test setups.
> +
> + If unsure, say N.
> +
> source "arch/arm/platforms/Kconfig"
>
> source "common/Kconfig"
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index 84c4062b30..0090761682 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -24,6 +24,7 @@ obj-y += domctl.o
> obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
> obj-y += efi/
> obj-y += gic.o
> +obj-$(CONFIG_BOOT_SELFTEST) += gic-test.init.o
> obj-$(CONFIG_GICV2) += gic-v2.o
> obj-$(CONFIG_GICV3) += gic-v3.o
> obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
> diff --git a/xen/arch/arm/gic-test.c b/xen/arch/arm/gic-test.c
> new file mode 100644
> index 0000000000..ca922e5d2a
> --- /dev/null
> +++ b/xen/arch/arm/gic-test.c
> @@ -0,0 +1,52 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#include <xen/delay.h>
> +#include <xen/init.h>
> +#include <xen/param.h>
> +#include <xen/shutdown.h>
Can you clarify why you need this header?
> +#include <asm/gic.h>
> +
> +/*
> + * gic_test: Specifies the gic test to be executed.
> + * 0 = no tests are executed
> + * 1 = SGI tests are executed
> + */
> +static unsigned int __initdata gic_test = 0;
> +integer_param("gic-test", gic_test);
Given this is mean to be 0 or 1, why not using "boolean_param"?
Also, new command line option should be documented in the docs. That
said, I am not really sure about
> +
> +/*
> + * CPU0: GIC_SGI_DUMP_STATE to self
> + * CPU{0-N}: GIC_SGI_TEST to self
> + * CPU{1-N}: GIC_SGI_TEST to CPU0
> + * CPU{N}: GIC_SGI_TEST to all but self
> + */
> +static int __init gic_self_sgi_test(void)
> +{
> + if ( !gic_test )
> + return 0;
> +
> + printk("Sending GIC_SGI_TEST to self CPU%u\n", smp_processor_id());
> + send_SGI_self(GIC_SGI_TEST);
> +
> + if ( smp_processor_id() == 0 )
> + {
> + printk("Sending GIC_SGI_DUMP_STATE to CPU0\n");
> + smp_send_state_dump(0);
OOI, why is this only called for CPU0? You also don't seem to check that
smp_send_state_dump() in the CI test.
> +
> + return 0;
> + }
> +
> + printk("Sending GIC_SGI_TEST to CPU0 from CPU%u\n", smp_processor_id());
> + send_SGI_one(0, GIC_SGI_TEST);
> +
> + /* Execute this test only from the last core */
> + if ( smp_processor_id() == (smp_get_max_cpus() - 1) )
This is relying on how Xen is boot CPUs. Would it be better to check the
number of online CPUs at the time of the check? (You might need to
re-order some code for that)
> + {
> + printk("Sending GIC_SGI_TEST to all except CPU%u\n", smp_processor_id());
> + send_SGI_allbutself(GIC_SGI_TEST);
> + }
> +
> + return 0;
> +
> +}
> +__initcallboottest(gic_self_sgi_test);
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index ee75258fc3..9736b0c7df 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -324,6 +324,11 @@ static void do_static_sgi(struct cpu_user_regs *regs, enum gic_sgi sgi)
> case GIC_SGI_CALL_FUNCTION:
> smp_call_function_interrupt();
> break;
> +#ifdef CONFIG_BOOT_SELFTEST
> + case GIC_SGI_TEST:
> + printk("CPU%u: GIC_SGI_TEST received\n", smp_processor_id());
To confirm, we will solely rely on logging? IOW, there is no plan to
have Xen self-sufficient (e.g. using a global variable).
> + break;
> +#endif
> default:
> panic("Unhandled SGI %d on CPU%d\n", sgi, smp_processor_id());
> break;
> diff --git a/xen/arch/arm/include/asm/gic.h b/xen/arch/arm/include/asm/gic.h
> index ff22dea40d..74bdd4ff63 100644
> --- a/xen/arch/arm/include/asm/gic.h
> +++ b/xen/arch/arm/include/asm/gic.h
> @@ -306,6 +306,9 @@ enum gic_sgi {
> GIC_SGI_EVENT_CHECK,
> GIC_SGI_DUMP_STATE,
> GIC_SGI_CALL_FUNCTION,
> +#ifdef CONFIG_BOOT_SELFTEST
> + GIC_SGI_TEST,
> +#endif
> GIC_SGI_STATIC_MAX,
> };
>
> diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
> index 6310a47d68..4e5db93027 100644
> --- a/xen/arch/arm/setup.c
> +++ b/xen/arch/arm/setup.c
> @@ -470,6 +470,8 @@ void asmlinkage __init noreturn start_xen(unsigned long fdt_paddr)
> enable_errata_workarounds();
> enable_cpu_features();
>
> + do_init_boottests();
> +
> /* Create initial domain 0. */
> if ( !is_dom0less_mode() )
> create_dom0();
> diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
> index 7f3cfa812e..a016ff00f5 100644
> --- a/xen/arch/arm/smpboot.c
> +++ b/xen/arch/arm/smpboot.c
> @@ -405,6 +405,8 @@ void asmlinkage noreturn start_secondary(void)
>
> printk(XENLOG_DEBUG "CPU %u booted.\n", smp_processor_id());
>
> + do_init_boottests();
> +
> startup_cpu_idle_loop();
> }
>
> diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
> index 2d5f1c516d..14f64a856c 100644
> --- a/xen/arch/arm/xen.lds.S
> +++ b/xen/arch/arm/xen.lds.S
> @@ -146,6 +146,10 @@ SECTIONS
> *(.initcall1.init)
> __initcall_end = .;
>
> + __initcall_boot_test_start = .;
> + *(.initcallboottest.init)
> + __initcall_boot_test_end = .;
> +
> . = ALIGN(4);
> __alt_instructions = .;
> *(.altinstructions)
> diff --git a/xen/common/kernel.c b/xen/common/kernel.c
> index fb45f81399..2047fe2a3f 100644
> --- a/xen/common/kernel.c
> +++ b/xen/common/kernel.c
> @@ -412,6 +412,7 @@ void add_taint(unsigned int taint)
>
> extern const initcall_t __initcall_start[], __presmp_initcall_end[],
> __initcall_end[];
> +extern const initcall_t __initcall_boot_test_start[], __initcall_boot_test_end[];
>
> void __init do_presmp_initcalls(void)
> {
> @@ -427,6 +428,16 @@ void __init do_initcalls(void)
> (*call)();
> }
>
> +void __init do_init_boottests(void)
> +{
> +#ifdef CONFIG_BOOT_SELFTEST
I think it would be worth printing before and after to indicate the
begin/end of the selftest.
> + const initcall_t *call;
> + for ( call = __initcall_boot_test_start; call < __initcall_boot_test_end;
> + call++ )
> + (*call)();
> +#endif
> +}
> +
> #ifdef CONFIG_HYPFS
> static unsigned int __read_mostly major_version;
> static unsigned int __read_mostly minor_version;
> diff --git a/xen/include/xen/init.h b/xen/include/xen/init.h
> index 0c921672c1..bd518bcea9 100644
> --- a/xen/include/xen/init.h
> +++ b/xen/include/xen/init.h
> @@ -66,11 +66,14 @@ typedef void (*exitcall_t)(void);
> static const initcall_t __initcall_##fn __init_call("presmp") = (fn)
> #define __initcall(fn) \
> static const initcall_t __initcall_##fn __init_call("1") = (fn)
> +#define __initcallboottest(fn) \
> + static const initcall_t __initcall_##fn __init_call("boottest") = (fn)
> #define __exitcall(fn) \
> static exitcall_t __exitcall_##fn __exit_call = fn
>
> void do_presmp_initcalls(void);
> void do_initcalls(void);
> +void do_init_boottests(void);
>
> #endif /* __ASSEMBLER__ */
Cheers,
--
Julien Grall
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen
2026-05-29 17:09 [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen Ayan Kumar Halder
2026-06-19 21:12 ` Julien Grall
@ 2026-06-19 21:21 ` Julien Grall
2026-08-28 10:29 ` [PATCH v3 for 4.23] Add GIC " Ayan Kumar Halder
2 siblings, 0 replies; 6+ messages in thread
From: Julien Grall @ 2026-06-19 21:21 UTC (permalink / raw)
To: Ayan Kumar Halder, xen-devel
Cc: Doug Goldstein, Stefano Stabellini, Andrew Cooper, Anthony PERARD,
Michal Orzel, Jan Beulich, Roger Pau Monné, Bertrand Marquis,
Volodymyr Babchuk
Hi again,
On 29/05/2026 18:09, Ayan Kumar Halder wrote:
> diff --git a/xen/arch/arm/gic-test.c b/xen/arch/arm/gic-test.c
> new file mode 100644
> index 0000000000..ca922e5d2a
> --- /dev/null
> +++ b/xen/arch/arm/gic-test.c
> @@ -0,0 +1,52 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +
> +#include <xen/delay.h>
I think this header is also unecessary.
> +#include <xen/init.h>
> +#include <xen/param.h>
> +#include <xen/shutdown.h>
> +#include <asm/gic.h>
[...]
> +static int __init gic_self_sgi_test(void)
> +{
> + if ( !gic_test )
> + return 0;
> +
> + printk("Sending GIC_SGI_TEST to self CPU%u\n", smp_processor_id());
> + send_SGI_self(GIC_SGI_TEST);
> +
> + if ( smp_processor_id() == 0 )
> + {
> + printk("Sending GIC_SGI_DUMP_STATE to CPU0\n");
> + smp_send_state_dump(0);
> +
> + return 0;
> + }
> +
> + printk("Sending GIC_SGI_TEST to CPU0 from CPU%u\n", smp_processor_id());
> + send_SGI_one(0, GIC_SGI_TEST);
> +
> + /* Execute this test only from the last core */
> + if ( smp_processor_id() == (smp_get_max_cpus() - 1) )
> + {
> + printk("Sending GIC_SGI_TEST to all except CPU%u\n", smp_processor_id());
> + send_SGI_allbutself(GIC_SGI_TEST);
> + }
> +
> + return 0;
> +
Style: The newline seems spurious.
> +}
> +__initcallboottest(gic_self_sgi_test);
Cheers,
--
Julien Grall
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen
2026-06-19 21:12 ` Julien Grall
@ 2026-08-27 19:57 ` Halder, Ayan Kumar
0 siblings, 0 replies; 6+ messages in thread
From: Halder, Ayan Kumar @ 2026-08-27 19:57 UTC (permalink / raw)
To: Julien Grall, Ayan Kumar Halder, xen-devel
Cc: Doug Goldstein, Stefano Stabellini, Andrew Cooper, Anthony PERARD,
Michal Orzel, Jan Beulich, Roger Pau Monné, Bertrand Marquis,
Volodymyr Babchuk
On 19/06/2026 22:12, Julien Grall wrote:
> Hi Ayan,
Hi Julien,
>
> On 29/05/2026 18:09, Ayan Kumar Halder wrote:
>> Boot self-tests (also referred to as boot-time tests or power-on
>> self-tests) are intended to validate internal features of Xen during
>> bring-up. They are meant to be run in a debug / validation environment;
>> Xen is not expected to remain functional for production use after the
>> self-tests have executed.
>
> Looking at the code below, isn't Xen functional even after the self-test?
Yes as discussed on Matrix, I have modified the test so that Xen boots
normally after running the self-test.
Xen will panic if the self test fails.
>
>> The purpose of these tests is to catch
>> hardware configuration issues early and to confirm that the platform
>> on which Xen has been brought up is sane. The expected flow is:
>> build Xen with the self-tests enabled, boot it, inspect the results,
>> and then reboot into the usual production configuration.
>>
>> Introduce the tests to confirm that:
>> 1. A cpu can send SGI 0 to itself
>> 2. A cpu can send SGI 0 to another specific CPU
>> 3. A cpu can send SGI 0 to all the other CPUs
>> 4. A cpu can send SGI 1 to another CPU
>
> I am not sure what you meant by SGI 0 and SGI 1? Below you seem to be
> use only a SGI (which is neither 0 or 1) except for one specific test:
> cpu 0 injecting an SGI to a specific CPU (0).
Yes, I fixed this in v3.
>
>>
>> These tests aim to test Xen has configured the GIC correctly to use
>> SGIs.
>> Thus, the tests invoke specific APIs of GIC driver.
>>
>> Also, introduce a config CONFIG_BOOT_SELFTEST which enables these tests.
>> The option defaults to N; it should be disabled for production builds
>> and
>> is intended for the validation pipeline and coverage measurement. The
>> tests run during Xen boot and validate internal interfaces such as Xen's
>> interface with hardware, firmware and the bootloader.
>>
>> Also, introduce an integer command line parameter "gic-test". By
>> default, it
>> is set to 0 which means no tests are enabled.
>> For running SGI tests, "gic-test" should be set to 1. In future if we
>> add
>> tests for distributer, ITS, LPI, etc, then we can use different numbers.
>> Thus, each number denotes a functionality of GICv3 which can be tested
>> independently and within a single boot of Xen.
>>
>> In this way, we ensure that the tests to validate SGIs do not impact
>> any other
>> tests.
>>
>> In order to keep all the boot-time self-tests together in the binary, we
>> have introduced a separate section "initcallboottest". All the tests are
>> registered using __initcallboottest. During the bootup of each core, Xen
>> invokes do_init_boottests() to run the these tests. All these tests are
>> invoked before Xen creates the domains (in case of primary core) or runs
>> the idle loop (in case of secondary core).
>>
>> Note: it was suggested that, once the boot self-tests have run, Xen
>> should call machine_halt() rather than continue booting (since this
>> build is only intended for validation). This is not wired in here
>> because the SGIs are sent from the primary and secondary CPUs and
>> received asynchronously on the target CPUs. There is no definite point
>> in the boot flow at which Xen can know that every send has been
>> observed by its receiver, so "after the tests have completed" has no
>> well-defined moment at which to insert machine_halt().
>>
>> Signed-off-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com>
>> Signed-off-by: Michal Orzel <michal.orzel@amd.com>
>> ---
>> Link to v1 (RFC):
>> https://lists.xenproject.org/archives/html/xen-devel/2025-09/msg00956.html
>>
>>
>> Upstream CI run (xen-project/people/ayankuma/xen fork, one commit on
>> top of xen-project/xen staging — all Linux builds + tests including
>> qemu-smoke-boot-selftest-arm64-gcc-debug passed; only the macos jobs
>> sit pending because the personal fork has no macos runner):
>> https://gitlab.com/xen-project/people/ayankuma/xen/-/pipelines/2561806695
>>
>>
>> Changes in v2:
>> - Renamed the patch from "xen/arm: Introduce GICV3 Self Tests" to
>> "Add GICv3 SGI boot/self tests in Xen", and rewrote the commit
>> message to explain the intent of boot self-tests (debug /
>> validation builds only, Xen not expected to remain functional
>> afterwards).
>> - Moved the selftest code out of gic-v3.c into a dedicated file
>> xen/arch/arm/gic-test.c, gated by CONFIG_BOOT_SELFTEST
>> (Stefano, Grygorii).
>> - Introduced a generic boot-self-test framework: new section
>> "initcallboottest", registration macro __initcallboottest, and
>> do_init_boottests() invoked once per CPU after
>> local_irq_enable(), so the test runs on every CPU (boot +
>> secondaries) and no longer collides with the IRQ-enable timing
>> in gicv3_init() (Julien #1, Julien #3).
>> - Added Kconfig option CONFIG_BOOT_SELFTEST in
>> xen/arch/arm/Kconfig (arm-only for now; arch-specific because
>> the only registered test is GICv3-specific).
>> - Reserved a dedicated SGI value GIC_SGI_TEST in enum gic_sgi
>> (xen/arch/arm/include/asm/gic.h), so the selftest never
>> reuses a functional SGI (Grygorii #3).
>> - Added a runtime integer command-line parameter "gic-test" so
>> the selftest binary can be shipped but its execution selected
>> at boot (gic-test=0 -> no-op; gic-test=1 -> SGI tests). Future
>> GICv3 features (distributor, ITS, LPI, ...) can claim further
>> values (Grygorii #2, partial).
>> - Documented why machine_halt() is not invoked after the tests:
>> SGI delivery is asynchronous, so there is no well-defined
>> point after which every send has been observed by its
>> receiver (Julien #2).
>> - Wired the tests into upstream GitLab CI: new build job
>> alpine-3.18-gcc-debug-arm64-boot-selftest, new test job
>> qemu-smoke-boot-selftest-arm64-gcc-debug, and the runner
>> script automation/scripts/qemu-boot-selftest-arm64.sh that
>> dumps the QEMU virt DTB, injects
>> "gic-test=1 console=dtuart sync_console" into
>> /chosen/xen,xen-bootargs via fdtput, boots Xen, and checks
>> for each "Sending GIC_SGI_TEST ..." followed by the matching
>> "CPU%u: GIC_SGI_TEST received".
>>
>> automation/gitlab-ci/build.yaml | 8 ++
>> automation/gitlab-ci/test.yaml | 8 ++
>> .../scripts/qemu-boot-selftest-arm64.sh | 81 +++++++++++++++++++
>> xen/arch/arm/Kconfig | 15 ++++
>> xen/arch/arm/Makefile | 1 +
>> xen/arch/arm/gic-test.c | 52 ++++++++++++
>> xen/arch/arm/gic.c | 5 ++
>> xen/arch/arm/include/asm/gic.h | 3 +
>> xen/arch/arm/setup.c | 2 +
>> xen/arch/arm/smpboot.c | 2 +
>> xen/arch/arm/xen.lds.S | 4 +
>> xen/common/kernel.c | 11 +++
>> xen/include/xen/init.h | 3 +
>> 13 files changed, 195 insertions(+)
>> create mode 100755 automation/scripts/qemu-boot-selftest-arm64.sh
>> create mode 100644 xen/arch/arm/gic-test.c
>>
>> diff --git a/automation/gitlab-ci/build.yaml
>> b/automation/gitlab-ci/build.yaml
>> index 7f5b5938e8..8df45caa86 100644
>> --- a/automation/gitlab-ci/build.yaml
>> +++ b/automation/gitlab-ci/build.yaml
>> @@ -439,6 +439,14 @@ alpine-3.18-gcc-debug-arm64:
>> CONFIG_UBSAN=y
>> CONFIG_UBSAN_FATAL=y
>> +alpine-3.18-gcc-debug-arm64-boot-selftest:
>> + extends: .gcc-arm64-build-debug
>> + <<: *build-test
>> + variables:
>> + CONTAINER: alpine:3.18-arm64v8
>> + EXTRA_XEN_CONFIG: |
>> + CONFIG_BOOT_SELFTEST=y
>> +
>> alpine-3.18-gcc-arm64-randconfig:
>> extends: .gcc-arm64-build
>> variables:
>> diff --git a/automation/gitlab-ci/test.yaml
>> b/automation/gitlab-ci/test.yaml
>> index 8770c523e2..2398c6299a 100644
>> --- a/automation/gitlab-ci/test.yaml
>> +++ b/automation/gitlab-ci/test.yaml
>> @@ -524,6 +524,14 @@ qemu-smoke-dom0less-arm64-gcc-debug-gicv3:
>> - *arm64-test-needs
>> - alpine-3.18-gcc-debug-arm64
>> +qemu-smoke-boot-selftest-arm64-gcc-debug:
>> + extends: .qemu-arm64
>> + script:
>> + - ./automation/scripts/qemu-boot-selftest-arm64.sh 2>&1 | tee
>> ${LOGFILE}
>> + needs:
>> + - *arm64-test-needs
>> + - alpine-3.18-gcc-debug-arm64-boot-selftest
>> +
>> qemu-smoke-dom0less-arm64-gcc-debug-staticmem:
>> extends: .qemu-arm64
>> script:
>> diff --git a/automation/scripts/qemu-boot-selftest-arm64.sh
>> b/automation/scripts/qemu-boot-selftest-arm64.sh
>> new file mode 100755
>> index 0000000000..a37dba3e07
>> --- /dev/null
>> +++ b/automation/scripts/qemu-boot-selftest-arm64.sh
>> @@ -0,0 +1,81 @@
>> +#!/bin/bash
>> +
>> +set -ex -o pipefail
>> +
>> +# Boot the prebuilt Xen binary under QEMU with
>> CONFIG_BOOT_SELFTEST=y enabled
>> +# and gic-test=1 in xen,xen-bootargs, then verify the four GICv3 SGI
>> self-tests
>> +# pass by inspecting the serial log.
>> +
>> +XEN=binaries/xen
>> +QEMU=./binaries/qemu-system-aarch64
>> +DTB_RAW=binaries/virt.dtb
>> +DTB=binaries/virt-bootselftest.dtb
>> +LOG=smoke.serial
>> +
>> +test -x ${QEMU}
>> +test -f ${XEN}
>> +
>> +# Dump the auto-generated DT from the QEMU virt machine, then inject
>> +# /chosen/xen,xen-bootargs. The selftest infrastructure invokes
>> +# do_init_boottests() during early boot; gic-test=1 selects the
>> GICv3 SGI
>> +# tests.
>> +# -net none avoids QEMU's default virtio-net-pci, whose efi-virtio.rom
>> +# is not shipped with the qemu-system-aarch64 artifact used in CI.
>> +${QEMU} \
>> + -machine
>> virt,virtualization=true,gic-version=3,dumpdtb=${DTB_RAW} \
>> + -cpu cortex-a57 -m 1024 -smp 2 -display none -net none
>> +
>> +cp ${DTB_RAW} ${DTB}
>> +fdtput -t s ${DTB} /chosen xen,xen-bootargs \
>> + "gic-test=1 console=dtuart sync_console"
>> +
>> +rm -f ${LOG}
>> +timeout 60 ${QEMU} \
>> + -machine virt,virtualization=true,gic-version=3 \
>> + -cpu cortex-a57 -m 1024 -smp 2 \
>
> This means that there is no much difference between "send an SGI to a
> specific CPU" and "send an SGI to others CPU". I think it would be
> more meaningful to use 3 or more pCPUs.
Yes, now I use 4 pCPUs.
>
>> + -serial file:${LOG} \
>> + -monitor none -display none -no-reboot -net none \
>> + -dtb ${DTB} \
>> + -kernel ${XEN} || true
>> +
>> +# Each "Sending GIC_SGI_TEST ..." line must be followed by the matching
>> +# "CPU%u: GIC_SGI_TEST received".
>> +fail=0
>> +check_pair() {
>> + local send_pat=$1
>> + local recv_pat=$2
>> + local send_line recv_line
>> +
>> + send_line=$(grep -n -- "${send_pat}" ${LOG} | head -n1 | cut -d:
>> -f1)
>> + if [ -z "${send_line}" ]; then
>> + echo "MISSING: ${send_pat}"
>> + fail=1
>> + return
>> + fi
>> +
>> + recv_line=$(grep -n -- "${recv_pat}" ${LOG} \
>> + | awk -v bl="${send_line}" -F: '$1 > bl {print $1; exit}')
>> + if [ -z "${recv_line}" ]; then
>> + echo "MISSING (after line ${send_line}): ${recv_pat}"
>> + fail=1
>> + return
>> + fi
>> +
>> + echo "OK: '${send_pat}' -> '${recv_pat}' (lines ${send_line} ->
>> ${recv_line})"
>> +}
>> +
>> +# Boot CPU sends SGI to itself
>> +check_pair "Sending GIC_SGI_TEST to self CPU0" "CPU0: GIC_SGI_TEST
>> received"
>> +# Secondary CPU sends SGI to itself
>> +check_pair "Sending GIC_SGI_TEST to self CPU1" "CPU1: GIC_SGI_TEST
>> received"
>> +# Secondary CPU sends SGI to primary
>> +check_pair "Sending GIC_SGI_TEST to CPU0 from CPU1" "CPU0:
>> GIC_SGI_TEST received"
>> +# Send to all-but-self
>> +check_pair "Sending GIC_SGI_TEST to all except CPU1" "CPU0:
>> GIC_SGI_TEST received"
>> +
>> +if [ ${fail} -ne 0 ]; then
>> + echo "FAILED"
>> + exit 1
>> +fi
>> +
>> +echo "PASSED"
>> diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
>> index 79622b46a1..0e23bbf20b 100644
>> --- a/xen/arch/arm/Kconfig
>> +++ b/xen/arch/arm/Kconfig
>> @@ -476,6 +476,21 @@ config ARM64_HARDEN_BRANCH_PREDICTOR
>> config ARM32_HARDEN_BRANCH_PREDICTOR
>> def_bool y if ARM_32 && HARDEN_BRANCH_PREDICTOR
>> +config BOOT_SELFTEST
>> + bool "Enable boot-time self-tests"
>> + default n
>
> Above you said, this is not meant for production. So I was expecting
> to see a dependency on CONFIG_DEBUG.
yes, I have added this
>
> If the intention is to use it in release build, given the current
> behavior (e.g. breaking test), I think this should depend on
> UNSUPPORTED so we don't get security report because Xen is broken
> after the boot tests.
No, it is disabled in release build.
>
> > + help> + This option enables boot-time self-tests that
> validate Xen's internal
>> + interfaces with hardware, firmware and the bootloader. The
>> tests are
>> + registered with __initcallboottest and executed by
>> do_init_boottests()
>> + during early boot, before domains are created.
>> +
>> + These tests are intended for validation and coverage
>> measurement, not
>> + for production builds. With this option enabled, Xen may not be
>> + functional after the tests have run.
>
> If we know a test break, I think it is best that Xen doesn't continue.
> Otherwise, it is quite confusing for the user to know what's going on.
>
> My preference is the bootest would act like other self tests and Xen
> can continue booting normally. So the tests could also be meaningful
> in non-test setups.
Yes, now Xen is functional even after running the self tests.
>
>> +
>> + If unsure, say N.
>> +
>> source "arch/arm/platforms/Kconfig"
>> source "common/Kconfig"
>> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
>> index 84c4062b30..0090761682 100644
>> --- a/xen/arch/arm/Makefile
>> +++ b/xen/arch/arm/Makefile
>> @@ -24,6 +24,7 @@ obj-y += domctl.o
>> obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
>> obj-y += efi/
>> obj-y += gic.o
>> +obj-$(CONFIG_BOOT_SELFTEST) += gic-test.init.o
>> obj-$(CONFIG_GICV2) += gic-v2.o
>> obj-$(CONFIG_GICV3) += gic-v3.o
>> obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
>> diff --git a/xen/arch/arm/gic-test.c b/xen/arch/arm/gic-test.c
>> new file mode 100644
>> index 0000000000..ca922e5d2a
>> --- /dev/null
>> +++ b/xen/arch/arm/gic-test.c
>> @@ -0,0 +1,52 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +
>> +#include <xen/delay.h>
>> +#include <xen/init.h>
>> +#include <xen/param.h>
>> +#include <xen/shutdown.h>
>
> Can you clarify why you need this header?
I have dropped this.
>
>> +#include <asm/gic.h>
>> +
>> +/*
>> + * gic_test: Specifies the gic test to be executed.
>> + * 0 = no tests are executed
>> + * 1 = SGI tests are executed
>> + */
>> +static unsigned int __initdata gic_test = 0;
>> +integer_param("gic-test", gic_test);
>
> Given this is mean to be 0 or 1, why not using "boolean_param"?
Yes, I have this as a boolean param
>
> Also, new command line option should be documented in the docs. That
> said, I am not really sure about
I have documented it.
>
>> +
>> +/*
>> + * CPU0: GIC_SGI_DUMP_STATE to self
>> + * CPU{0-N}: GIC_SGI_TEST to self
>> + * CPU{1-N}: GIC_SGI_TEST to CPU0
>> + * CPU{N}: GIC_SGI_TEST to all but self
>> + */
>> +static int __init gic_self_sgi_test(void)
>> +{
>> + if ( !gic_test )
>> + return 0;
>> +
>> + printk("Sending GIC_SGI_TEST to self CPU%u\n", smp_processor_id());
>> + send_SGI_self(GIC_SGI_TEST);
>> +
>> + if ( smp_processor_id() == 0 )
>> + {
>> + printk("Sending GIC_SGI_DUMP_STATE to CPU0\n");
>> + smp_send_state_dump(0);
>
> OOI, why is this only called for CPU0? You also don't seem to check
> that smp_send_state_dump() in the CI test.
Ah, I have dropped it.
>> +
>> + return 0;
>> + }
>> +
>> + printk("Sending GIC_SGI_TEST to CPU0 from CPU%u\n",
>> smp_processor_id());
>> + send_SGI_one(0, GIC_SGI_TEST);
>> +
>> + /* Execute this test only from the last core */
>> + if ( smp_processor_id() == (smp_get_max_cpus() - 1) )
>
> This is relying on how Xen is boot CPUs. Would it be better to check
> the number of online CPUs at the time of the check? (You might need to
> re-order some code for that)
yes, I have changed the code. I now check the number of online CPUs.
>
>> + {
>> + printk("Sending GIC_SGI_TEST to all except CPU%u\n",
>> smp_processor_id());
>> + send_SGI_allbutself(GIC_SGI_TEST);
>> + }
>> +
>> + return 0;
>> +
>> +}
>> +__initcallboottest(gic_self_sgi_test);
>> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
>> index ee75258fc3..9736b0c7df 100644
>> --- a/xen/arch/arm/gic.c
>> +++ b/xen/arch/arm/gic.c
>> @@ -324,6 +324,11 @@ static void do_static_sgi(struct cpu_user_regs
>> *regs, enum gic_sgi sgi)
>> case GIC_SGI_CALL_FUNCTION:
>> smp_call_function_interrupt();
>> break;
>> +#ifdef CONFIG_BOOT_SELFTEST
>> + case GIC_SGI_TEST:
>> + printk("CPU%u: GIC_SGI_TEST received\n", smp_processor_id());
>
> To confirm, we will solely rely on logging? IOW, there is no plan to
> have Xen self-sufficient (e.g. using a global variable).
yes, I know use a per cpu variable.
>
>
>> + break;
>> +#endif
>> default:
>> panic("Unhandled SGI %d on CPU%d\n", sgi, smp_processor_id());
>> break;
>> diff --git a/xen/arch/arm/include/asm/gic.h
>> b/xen/arch/arm/include/asm/gic.h
>> index ff22dea40d..74bdd4ff63 100644
>> --- a/xen/arch/arm/include/asm/gic.h
>> +++ b/xen/arch/arm/include/asm/gic.h
>> @@ -306,6 +306,9 @@ enum gic_sgi {
>> GIC_SGI_EVENT_CHECK,
>> GIC_SGI_DUMP_STATE,
>> GIC_SGI_CALL_FUNCTION,
>> +#ifdef CONFIG_BOOT_SELFTEST
>> + GIC_SGI_TEST,
>> +#endif
>> GIC_SGI_STATIC_MAX,
>> };
>> diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
>> index 6310a47d68..4e5db93027 100644
>> --- a/xen/arch/arm/setup.c
>> +++ b/xen/arch/arm/setup.c
>> @@ -470,6 +470,8 @@ void asmlinkage __init noreturn
>> start_xen(unsigned long fdt_paddr)
>> enable_errata_workarounds();
>> enable_cpu_features();
>> + do_init_boottests();
>> +
>> /* Create initial domain 0. */
>> if ( !is_dom0less_mode() )
>> create_dom0();
>> diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
>> index 7f3cfa812e..a016ff00f5 100644
>> --- a/xen/arch/arm/smpboot.c
>> +++ b/xen/arch/arm/smpboot.c
>> @@ -405,6 +405,8 @@ void asmlinkage noreturn start_secondary(void)
>> printk(XENLOG_DEBUG "CPU %u booted.\n", smp_processor_id());
>> + do_init_boottests();
>> +
>> startup_cpu_idle_loop();
>> }
>> diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
>> index 2d5f1c516d..14f64a856c 100644
>> --- a/xen/arch/arm/xen.lds.S
>> +++ b/xen/arch/arm/xen.lds.S
>> @@ -146,6 +146,10 @@ SECTIONS
>> *(.initcall1.init)
>> __initcall_end = .;
>> + __initcall_boot_test_start = .;
>> + *(.initcallboottest.init)
>> + __initcall_boot_test_end = .;
>> +
>> . = ALIGN(4);
>> __alt_instructions = .;
>> *(.altinstructions)
>> diff --git a/xen/common/kernel.c b/xen/common/kernel.c
>> index fb45f81399..2047fe2a3f 100644
>> --- a/xen/common/kernel.c
>> +++ b/xen/common/kernel.c
>> @@ -412,6 +412,7 @@ void add_taint(unsigned int taint)
>> extern const initcall_t __initcall_start[], __presmp_initcall_end[],
>> __initcall_end[];
>> +extern const initcall_t __initcall_boot_test_start[],
>> __initcall_boot_test_end[];
>> void __init do_presmp_initcalls(void)
>> {
>> @@ -427,6 +428,16 @@ void __init do_initcalls(void)
>> (*call)();
>> }
>> +void __init do_init_boottests(void)
>> +{
>> +#ifdef CONFIG_BOOT_SELFTEST
>
> I think it would be worth printing before and after to indicate the
> begin/end of the selftest.
yes, I have added the prints in v3.
>
>> + const initcall_t *call;
>> + for ( call = __initcall_boot_test_start; call <
>> __initcall_boot_test_end;
>> + call++ )
>> + (*call)();
>> +#endif
>> +}
>> +
>> #ifdef CONFIG_HYPFS
>> static unsigned int __read_mostly major_version;
>> static unsigned int __read_mostly minor_version;
>> diff --git a/xen/include/xen/init.h b/xen/include/xen/init.h
>> index 0c921672c1..bd518bcea9 100644
>> --- a/xen/include/xen/init.h
>> +++ b/xen/include/xen/init.h
>> @@ -66,11 +66,14 @@ typedef void (*exitcall_t)(void);
>> static const initcall_t __initcall_##fn __init_call("presmp") =
>> (fn)
>> #define __initcall(fn) \
>> static const initcall_t __initcall_##fn __init_call("1") = (fn)
>> +#define __initcallboottest(fn) \
>> + static const initcall_t __initcall_##fn __init_call("boottest")
>> = (fn)
>> #define __exitcall(fn) \
>> static exitcall_t __exitcall_##fn __exit_call = fn
>> void do_presmp_initcalls(void);
>> void do_initcalls(void);
>> +void do_init_boottests(void);
>> #endif /* __ASSEMBLER__ */
>
> Cheers,
Thanks
- Ayan
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v3 for 4.23] Add GIC SGI boot/self tests in Xen
2026-05-29 17:09 [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen Ayan Kumar Halder
2026-06-19 21:12 ` Julien Grall
2026-06-19 21:21 ` Julien Grall
@ 2026-08-28 10:29 ` Ayan Kumar Halder
2026-09-04 19:21 ` Julien Grall
2 siblings, 1 reply; 6+ messages in thread
From: Ayan Kumar Halder @ 2026-08-28 10:29 UTC (permalink / raw)
To: xen-devel
Cc: Stefano Stabellini, Julien Grall, Bertrand Marquis, Michal Orzel,
Volodymyr Babchuk, Andrew Cooper, Anthony PERARD, Jan Beulich,
Roger Pau Monne, Doug Goldstein
Boot self-tests (also referred to as boot-time tests or power-on
self-tests) are intended to check that Xen has configured the hardware
correctly before bringing up any domains.
Introduce tests to confirm that, using a dedicated SGI (GIC_SGI_TEST):
1. A cpu can send the SGI to itself
2. A cpu can send the SGI to another specific CPU (CPU0)
3. A cpu can send the SGI to all the other CPUs
Each CPU counts the test SGIs it takes. A sender samples those counters
before sending and then waits for the count of every CPU it targeted to
change, which is how it tells that the SGI was really delivered. The
counters are never reset, so comparing against a sample rather than an
absolute value keeps concurrent senders from disturbing each other.
A test reports a failure by panic(), so Xen never continues on a
platform where SGI delivery is broken. When the tests pass, Xen carries
on booting normally.
Also introduce a config CONFIG_BOOT_SELFTEST which enables these
tests. It depends on DEBUG and is off unless explicitly enabled.
Also introduce a boolean command line parameter "gic-test", so that a
build with CONFIG_BOOT_SELFTEST enabled can be shipped but the tests
selected at boot. It is documented in
docs/misc/xen-command-line.pandoc.
In order to keep all the boot self-tests together in the binary, a
separate section "initcallboottest" is introduced. Tests are registered
using __initcallboottest() and run once on every CPU by
do_init_boottests(), bracketed by begin/end messages. They run before
any domain is created on the primary core, and before the idle loop is
entered on a secondary core.
Signed-off-by: Ayan Kumar Halder <ayan.kumar.halder@amd.com>
Signed-off-by: Michal Orzel <michal.orzel@amd.com>
---
Upstream CI run:
https://gitlab.com/xen-project/people/ayankuma/xen/-/pipelines/2799278627
Changes in v3:
- Rewrote the commit message: dropped the claim that Xen is not
functional after the tests (it is), and the misleading "SGI 0 / SGI 1"
numbering - only one SGI, GIC_SGI_TEST, is used (Julien).
- Retitled from "GICv3 SGI" to "GIC SGI": the tests only use
send_SGI_{self,one,allbutself}(), which GICv2 implements too. Verified
by running them on arm32/GICv2.
- The tests are now self-checking instead of log-scraping: each CPU
counts the GIC_SGI_TEST interrupts it takes and the sender waits for
that count, panicking after 100ms. Xen no longer continues on a
platform where SGI delivery is broken, and is otherwise unaffected,
so the option is meaningful on an ordinary debug build (Julien).
- CONFIG_BOOT_SELFTEST now depends on DEBUG (Julien).
- "gic-test" is a boolean_param() and is documented in
docs/misc/xen-command-line.pandoc (Julien).
- Dropped the unnecessary <xen/delay.h> and <xen/shutdown.h> includes,
and the unchecked smp_send_state_dump() call (Julien).
- The "all but self" test is run by whichever CPU observes it is the
last to arrive, comparing against num_online_cpus(), and targets
cpu_online_map minus itself, rather than keying off
smp_get_max_cpus() - 1 (Julien).
- do_init_boottests() prints a begin/end marker (Julien).
- Removed the spurious blank line before the closing brace (Julien).
- The CI test now boots 4 CPUs, so that "send to CPU0" and "send to all
but self" cover different sets of CPUs (Julien).
- The registration macro and do_init_boottests() moved to arch/arm, so
that xen/include/xen/init.h and xen/common/kernel.c are untouched: the
.initcallboottest.init section only exists in arch/arm/xen.lds.S, and a
test registered elsewhere would land in an orphan section.
- do_init_boottests() is no longer __init: it is called from
start_secondary(), which lives in .text.
- The static counter used to spot the last CPU is __initdata, so it no
longer occupies .bss for the life of the hypervisor.
- Kconfig: use tabs to match the rest of arch/arm/Kconfig, and drop the
redundant "default n".
- gic-test.c is SPDX GPL-2.0-or-later, to match the neighbouring GIC
files.
- The CI test script takes qemu-system-aarch64 from the test container's
PATH, the same way the other qemu-smoke-*-arm64 scripts do, rather
than expecting it in binaries/.
- Rebased onto current staging: the CI jobs are named after alpine 3.24
rather than 3.18.
Changes in v2:
- Renamed the patch from "xen/arm: Introduce GICV3 Self Tests" to
"Add GICv3 SGI boot/self tests in Xen", and rewrote the commit
message to explain the intent of boot self-tests (debug /
validation builds only, Xen not expected to remain functional
afterwards).
- Moved the selftest code out of gic-v3.c into a dedicated file
xen/arch/arm/gic-test.c, gated by CONFIG_BOOT_SELFTEST
(Stefano, Grygorii).
- Introduced a generic boot-self-test framework: new section
"initcallboottest", registration macro __initcallboottest, and
do_init_boottests() invoked once per CPU after
local_irq_enable(), so the test runs on every CPU (boot +
secondaries) and no longer collides with the IRQ-enable timing
in gicv3_init() (Julien #1, Julien #3).
- Added Kconfig option CONFIG_BOOT_SELFTEST in
xen/arch/arm/Kconfig (arm-only for now; arch-specific because
the only registered test is GICv3-specific).
- Reserved a dedicated SGI value GIC_SGI_TEST in enum gic_sgi
(xen/arch/arm/include/asm/gic.h), so the selftest never
reuses a functional SGI (Grygorii #3).
- Added a runtime integer command-line parameter "gic-test" so
the selftest binary can be shipped but its execution selected
at boot (gic-test=0 -> no-op; gic-test=1 -> SGI tests). Future
GICv3 features (distributor, ITS, LPI, ...) can claim further
values (Grygorii #2, partial).
- Documented why machine_halt() is not invoked after the tests:
SGI delivery is asynchronous, so there is no well-defined
point after which every send has been observed by its
receiver (Julien #2).
- Wired the tests into upstream GitLab CI: new build job
alpine-3.18-gcc-debug-arm64-boot-selftest, new test job
qemu-smoke-boot-selftest-arm64-gcc-debug, and the runner
script automation/scripts/qemu-boot-selftest-arm64.sh that
dumps the QEMU virt DTB, injects
"gic-test=1 console=dtuart sync_console" into
/chosen/xen,xen-bootargs via fdtput, boots Xen, and checks
for each "Sending GIC_SGI_TEST ..." followed by the matching
"CPU%u: GIC_SGI_TEST received".
automation/gitlab-ci/build.yaml | 8 ++
automation/gitlab-ci/test.yaml | 8 ++
.../scripts/qemu-boot-selftest-arm64.sh | 72 +++++++++++++
docs/misc/xen-command-line.pandoc | 10 ++
xen/arch/arm/Kconfig | 13 +++
xen/arch/arm/Makefile | 1 +
xen/arch/arm/gic-test.c | 102 ++++++++++++++++++
xen/arch/arm/gic.c | 5 +
xen/arch/arm/include/asm/gic.h | 8 ++
xen/arch/arm/include/asm/setup.h | 9 ++
xen/arch/arm/setup.c | 20 ++++
xen/arch/arm/smpboot.c | 3 +
xen/arch/arm/xen.lds.S | 4 +
13 files changed, 263 insertions(+)
create mode 100755 automation/scripts/qemu-boot-selftest-arm64.sh
create mode 100644 xen/arch/arm/gic-test.c
diff --git a/automation/gitlab-ci/build.yaml b/automation/gitlab-ci/build.yaml
index 27eefec5f9..3d37e0b572 100644
--- a/automation/gitlab-ci/build.yaml
+++ b/automation/gitlab-ci/build.yaml
@@ -420,6 +420,14 @@ alpine-3.24-arm64-gcc-debug:
CONFIG_UBSAN=y
CONFIG_UBSAN_FATAL=y
+alpine-3.24-arm64-gcc-debug-boot-selftest:
+ extends: .gcc-arm64-build-debug
+ <<: *build-test
+ variables:
+ CONTAINER: alpine:3.24-arm64v8
+ EXTRA_XEN_CONFIG: |
+ CONFIG_BOOT_SELFTEST=y
+
alpine-3.24-arm64-gcc-randconfig:
extends: .gcc-arm64-build
variables:
diff --git a/automation/gitlab-ci/test.yaml b/automation/gitlab-ci/test.yaml
index 61adc1baff..96127995d7 100644
--- a/automation/gitlab-ci/test.yaml
+++ b/automation/gitlab-ci/test.yaml
@@ -605,6 +605,14 @@ qemu-smoke-dom0less-arm64-gcc-debug-gicv3:
- *arm64-test-needs
- alpine-3.24-arm64-gcc-debug
+qemu-smoke-boot-selftest-arm64-gcc-debug:
+ extends: .qemu-arm64
+ script:
+ - ./automation/scripts/qemu-boot-selftest-arm64.sh 2>&1 | tee ${LOGFILE}
+ needs:
+ - *arm64-test-needs
+ - alpine-3.24-arm64-gcc-debug-boot-selftest
+
qemu-smoke-dom0less-arm64-gcc-debug-staticmem:
extends: .qemu-arm64
script:
diff --git a/automation/scripts/qemu-boot-selftest-arm64.sh b/automation/scripts/qemu-boot-selftest-arm64.sh
new file mode 100755
index 0000000000..e36bd8d94a
--- /dev/null
+++ b/automation/scripts/qemu-boot-selftest-arm64.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+
+set -ex -o pipefail
+
+# Boot Xen under QEMU with gic-test in xen,xen-bootargs and check that every
+# self-test reported OK and that Xen carried on booting.
+
+XEN=binaries/xen
+# qemu-system-aarch64 comes from the debian:13-arm64v8 test container, the
+# same way the other qemu-smoke-*-arm64 scripts get it.
+QEMU=qemu-system-aarch64
+DTB_RAW=binaries/virt.dtb
+DTB=binaries/virt-bootselftest.dtb
+LOG=smoke.serial
+
+NR_CPUS=4
+
+test -f ${XEN}
+
+${QEMU} \
+ -machine virt,virtualization=true,gic-version=3,dumpdtb=${DTB_RAW} \
+ -cpu cortex-a57 -m 1024 -smp ${NR_CPUS} -display none -net none
+
+cp ${DTB_RAW} ${DTB}
+fdtput -t s ${DTB} /chosen xen,xen-bootargs \
+ "gic-test console=dtuart sync_console"
+
+rm -f ${LOG}
+timeout 60 ${QEMU} \
+ -machine virt,virtualization=true,gic-version=3 \
+ -cpu cortex-a57 -m 1024 -smp ${NR_CPUS} \
+ -serial file:${LOG} \
+ -monitor none -display none -no-reboot -net none \
+ -dtb ${DTB} \
+ -kernel ${XEN} || true
+
+fail=0
+
+check() {
+ local what=$1
+ local expected=$2
+ local got
+
+ got=$(grep -c -- "${what}" ${LOG} || true)
+ if [ "${got}" -ne "${expected}" ]; then
+ echo "FAIL: '${what}': expected ${expected}, got ${got}"
+ fail=1
+ return
+ fi
+
+ echo "OK: '${what}' x${expected}"
+}
+
+# Every CPU sends an SGI to itself...
+check "GIC selftest: CPU[0-9]*: SGI to self: OK" ${NR_CPUS}
+# ...every secondary CPU sends one to CPU0...
+check "GIC selftest: CPU[0-9]*: SGI to CPU0: OK" $((NR_CPUS - 1))
+# ...and whichever CPU runs last sends one to all the others.
+check "GIC selftest: CPU[0-9]*: SGI to all but self: OK" 1
+
+check "boot self-tests done" ${NR_CPUS}
+check "GIC selftest: .*did not receive" 0
+
+# A passing self-test must leave Xen booting normally.
+check "LOADING DOMAIN 0\|Xen dom0less mode detected" 1
+
+if [ ${fail} -ne 0 ]; then
+ echo "FAILED"
+ exit 1
+fi
+
+echo "PASSED"
diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc
index 1c711fa980..6d7277ae1b 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -1267,6 +1267,16 @@ available on Intel Panther Lake and Diamond Rapids CPUs, and AMD Zen6 CPUs.
FRED is fully supported on AMD hardware. On Intel hardware it is still tech
preview, and in particular not security supported.
+### gic-test (arm)
+> `= <boolean>`
+
+> Default: `false`
+
+Only available when `CONFIG_BOOT_SELFTEST` is enabled.
+
+Run the GIC SGI boot self-tests while each CPU is brought up. Xen panics if
+an SGI is not delivered; otherwise it carries on booting normally.
+
### gnttab
> `= List of [ max-ver:<integer>, transitive=<bool>, transfer=<bool> ]`
diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index 843a43897e..92a1788854 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -498,6 +498,19 @@ config ARM64_HARDEN_BRANCH_PREDICTOR
config ARM32_HARDEN_BRANCH_PREDICTOR
def_bool y if ARM_32 && HARDEN_BRANCH_PREDICTOR
+config BOOT_SELFTEST
+ bool "Enable boot self-tests"
+ depends on DEBUG
+ help
+ This option enables boot self-tests. They are intended to check that
+ Xen has configured the hardware correctly before bringing up any
+ domains. A failure is reported by panic(); when the tests pass, Xen
+ boots normally.
+
+ Selected at boot with the "gic-test" command line option.
+
+ If unsure, say N.
+
source "arch/arm/platforms/Kconfig"
source "common/Kconfig"
diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
index b7afd3e58c..71f177824b 100644
--- a/xen/arch/arm/Makefile
+++ b/xen/arch/arm/Makefile
@@ -24,6 +24,7 @@ obj-y += domctl.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-y += efi/
obj-y += gic.o
+obj-$(CONFIG_BOOT_SELFTEST) += gic-test.o
obj-$(CONFIG_GICV2) += gic-v2.o
obj-$(CONFIG_GICV3) += gic-v3.o
obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
diff --git a/xen/arch/arm/gic-test.c b/xen/arch/arm/gic-test.c
new file mode 100644
index 0000000000..9ddd47cad2
--- /dev/null
+++ b/xen/arch/arm/gic-test.c
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <xen/atomic.h>
+#include <xen/cpumask.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/param.h>
+#include <xen/percpu.h>
+#include <xen/smp.h>
+#include <xen/time.h>
+#include <asm/gic.h>
+#include <asm/processor.h>
+#include <asm/setup.h>
+
+static bool __initdata opt_gic_test;
+boolean_param("gic-test", opt_gic_test);
+
+static DEFINE_PER_CPU(unsigned int, sgi_test_count);
+
+void gic_sgi_test_interrupt(void)
+{
+ this_cpu(sgi_test_count)++;
+}
+
+static unsigned int __init sgi_count(unsigned int cpu)
+{
+ return ACCESS_ONCE(per_cpu(sgi_test_count, cpu));
+}
+
+static void __init snapshot_sgi(unsigned int *before)
+{
+ unsigned int cpu;
+
+ for_each_online_cpu ( cpu )
+ before[cpu] = sgi_count(cpu);
+}
+
+/*
+ * Wait for every CPU in @mask to take one more GIC_SGI_TEST than the count
+ * recorded in @before.
+ */
+static void __init expect_sgi(const cpumask_t *mask,
+ const unsigned int *before, const char *what)
+{
+ s_time_t deadline = NOW() + MILLISECS(100);
+ unsigned int cpu;
+
+ for_each_cpu ( cpu, mask )
+ {
+ while ( sgi_count(cpu) == before[cpu] )
+ {
+ if ( NOW() > deadline )
+ panic("GIC selftest: %s: CPU%u did not receive GIC_SGI_TEST\n",
+ what, cpu);
+ cpu_relax();
+ }
+ }
+
+ printk("GIC selftest: CPU%u: %s: OK\n", smp_processor_id(), what);
+}
+
+/*
+ * "All but self" is only meaningful once every CPU can take an SGI, so it is
+ * run by whichever CPU observes that it is the last one to get here.
+ */
+static int __init gic_sgi_selftest(void)
+{
+ static atomic_t __initdata seen = ATOMIC_INIT(0);
+ unsigned int before[NR_CPUS] = { };
+ unsigned int cpu = smp_processor_id();
+
+ if ( !opt_gic_test )
+ return 0;
+
+ snapshot_sgi(before);
+ send_SGI_self(GIC_SGI_TEST);
+ expect_sgi(cpumask_of(cpu), before, "SGI to self");
+
+ if ( cpu != 0 )
+ {
+ snapshot_sgi(before);
+ send_SGI_one(0, GIC_SGI_TEST);
+ expect_sgi(cpumask_of(0), before, "SGI to CPU0");
+ }
+
+ if ( atomic_add_return(1, &seen) == num_online_cpus() )
+ {
+ cpumask_t target;
+
+ cpumask_andnot(&target, &cpu_online_map, cpumask_of(cpu));
+
+ if ( !cpumask_empty(&target) )
+ {
+ snapshot_sgi(before);
+ send_SGI_allbutself(GIC_SGI_TEST);
+ expect_sgi(&target, before, "SGI to all but self");
+ }
+ }
+
+ return 0;
+}
+__initcallboottest(gic_sgi_selftest);
diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
index 078049e741..6a132c64e1 100644
--- a/xen/arch/arm/gic.c
+++ b/xen/arch/arm/gic.c
@@ -330,6 +330,11 @@ static void do_static_sgi(struct cpu_user_regs *regs, enum gic_sgi sgi)
case GIC_SGI_CALL_FUNCTION:
smp_call_function_interrupt();
break;
+#ifdef CONFIG_BOOT_SELFTEST
+ case GIC_SGI_TEST:
+ gic_sgi_test_interrupt();
+ break;
+#endif
default:
panic("Unhandled SGI %d on CPU%d\n", sgi, smp_processor_id());
break;
diff --git a/xen/arch/arm/include/asm/gic.h b/xen/arch/arm/include/asm/gic.h
index ee2c26adb4..40635a9d32 100644
--- a/xen/arch/arm/include/asm/gic.h
+++ b/xen/arch/arm/include/asm/gic.h
@@ -306,6 +306,9 @@ enum gic_sgi {
GIC_SGI_EVENT_CHECK,
GIC_SGI_DUMP_STATE,
GIC_SGI_CALL_FUNCTION,
+#ifdef CONFIG_BOOT_SELFTEST
+ GIC_SGI_TEST,
+#endif
GIC_SGI_STATIC_MAX,
};
@@ -321,6 +324,11 @@ extern void send_SGI_one(unsigned int cpu, enum gic_sgi sgi);
extern void send_SGI_self(enum gic_sgi sgi);
extern void send_SGI_allbutself(enum gic_sgi sgi);
+#ifdef CONFIG_BOOT_SELFTEST
+/* Record a GIC_SGI_TEST delivered to this CPU (see arch/arm/gic-test.c). */
+void gic_sgi_test_interrupt(void);
+#endif
+
/* print useful debug info */
extern void gic_dump_info(struct vcpu *v);
extern void gic_dump_vgic_info(struct vcpu *v);
diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h
index 0adfa4993a..2fdf5da526 100644
--- a/xen/arch/arm/include/asm/setup.h
+++ b/xen/arch/arm/include/asm/setup.h
@@ -50,6 +50,15 @@ void setup_mm(void);
extern uint32_t hyp_traps_vector[];
void init_traps(void);
+#ifdef CONFIG_BOOT_SELFTEST
+#define __initcallboottest(fn) \
+ static const initcall_t __initcall_##fn __init_call("boottest") = (fn)
+
+void do_init_boottests(void);
+#else
+static inline void do_init_boottests(void) {}
+#endif
+
int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt,
struct rangeset *iomem_ranges, struct rangeset *irq_ranges);
diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
index 6310a47d68..c7abbdb04e 100644
--- a/xen/arch/arm/setup.c
+++ b/xen/arch/arm/setup.c
@@ -83,6 +83,24 @@ static void __init init_idle_domain(void)
/* TODO: setup_idle_pagetable(); */
}
+#ifdef CONFIG_BOOT_SELFTEST
+extern const initcall_t __initcall_boot_test_start[],
+ __initcall_boot_test_end[];
+
+void do_init_boottests(void)
+{
+ const initcall_t *call;
+
+ printk("CPU%u: boot self-tests start\n", smp_processor_id());
+
+ for ( call = __initcall_boot_test_start; call < __initcall_boot_test_end;
+ call++ )
+ (*call)();
+
+ printk("CPU%u: boot self-tests done\n", smp_processor_id());
+}
+#endif /* CONFIG_BOOT_SELFTEST */
+
static const char * __initdata processor_implementers[] = {
['A'] = "ARM Limited",
['B'] = "Broadcom Corporation",
@@ -470,6 +488,8 @@ void asmlinkage __init noreturn start_xen(unsigned long fdt_paddr)
enable_errata_workarounds();
enable_cpu_features();
+ do_init_boottests();
+
/* Create initial domain 0. */
if ( !is_dom0less_mode() )
create_dom0();
diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
index 1806c47a08..97d8b19cf4 100644
--- a/xen/arch/arm/smpboot.c
+++ b/xen/arch/arm/smpboot.c
@@ -28,6 +28,7 @@
#include <asm/gic.h>
#include <asm/procinfo.h>
#include <asm/psci.h>
+#include <asm/setup.h>
#include <asm/acpi.h>
#include <asm/tee/tee.h>
@@ -413,6 +414,8 @@ void asmlinkage noreturn start_secondary(void)
printk(XENLOG_DEBUG "CPU %u booted.\n", smp_processor_id());
+ do_init_boottests();
+
startup_cpu_idle_loop();
}
diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
index 2d5f1c516d..14f64a856c 100644
--- a/xen/arch/arm/xen.lds.S
+++ b/xen/arch/arm/xen.lds.S
@@ -146,6 +146,10 @@ SECTIONS
*(.initcall1.init)
__initcall_end = .;
+ __initcall_boot_test_start = .;
+ *(.initcallboottest.init)
+ __initcall_boot_test_end = .;
+
. = ALIGN(4);
__alt_instructions = .;
*(.altinstructions)
--
2.25.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3 for 4.23] Add GIC SGI boot/self tests in Xen
2026-08-28 10:29 ` [PATCH v3 for 4.23] Add GIC " Ayan Kumar Halder
@ 2026-09-04 19:21 ` Julien Grall
0 siblings, 0 replies; 6+ messages in thread
From: Julien Grall @ 2026-09-04 19:21 UTC (permalink / raw)
To: Ayan Kumar Halder, xen-devel
Cc: Stefano Stabellini, Bertrand Marquis, Michal Orzel,
Volodymyr Babchuk, Andrew Cooper, Anthony PERARD, Jan Beulich,
Roger Pau Monne, Doug Goldstein
Hi Ayan,
It is usually preferred to send a new version in its own thread rather
than in-reply-to an existing version.
On 28/08/2026 12:29, Ayan Kumar Halder wrote:
> diff --git a/xen/arch/arm/Makefile b/xen/arch/arm/Makefile
> index b7afd3e58c..71f177824b 100644
> --- a/xen/arch/arm/Makefile
> +++ b/xen/arch/arm/Makefile
> @@ -24,6 +24,7 @@ obj-y += domctl.o
> obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
> obj-y += efi/
> obj-y += gic.o
> +obj-$(CONFIG_BOOT_SELFTEST) += gic-test.o
> obj-$(CONFIG_GICV2) += gic-v2.o
> obj-$(CONFIG_GICV3) += gic-v3.o
> obj-$(CONFIG_HAS_ITS) += gic-v3-its.o
> diff --git a/xen/arch/arm/gic-test.c b/xen/arch/arm/gic-test.c
> new file mode 100644
> index 0000000000..9ddd47cad2
> --- /dev/null
> +++ b/xen/arch/arm/gic-test.c
> @@ -0,0 +1,102 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
The preferred license for Xen is GPLv2-only (see COPYING). Can you
confirm the use of GPL2+ is intended?
> +
> +#include <xen/atomic.h>
> +#include <xen/cpumask.h>
> +#include <xen/init.h>
> +#include <xen/lib.h>
> +#include <xen/param.h>
> +#include <xen/percpu.h>
> +#include <xen/smp.h>
> +#include <xen/time.h>
> +#include <asm/gic.h>
> +#include <asm/processor.h>
> +#include <asm/setup.h>
> +
> +static bool __initdata opt_gic_test;
> +boolean_param("gic-test", opt_gic_test);
> +
> +static DEFINE_PER_CPU(unsigned int, sgi_test_count);
> +
> +void gic_sgi_test_interrupt(void)
> +{
> + this_cpu(sgi_test_count)++;
In sgi_count(), you are using ACCESS_ONCE() to read the content of the
variable, but I am not entirely sure this_cpu(...)++ is guarantee to be
a single write.
As this happen on different CPU, don't we also need to use ACCESS_ONCE()
here too or atomically increment?
[...]
> diff --git a/xen/arch/arm/gic.c b/xen/arch/arm/gic.c
> index 078049e741..6a132c64e1 100644
> --- a/xen/arch/arm/gic.c
> +++ b/xen/arch/arm/gic.c
> @@ -330,6 +330,11 @@ static void do_static_sgi(struct cpu_user_regs *regs, enum gic_sgi sgi)
> case GIC_SGI_CALL_FUNCTION:
> smp_call_function_interrupt();
> break;
> +#ifdef CONFIG_BOOT_SELFTEST
> + case GIC_SGI_TEST:
> + gic_sgi_test_interrupt();
> + break;
> +#endif
> default:
> panic("Unhandled SGI %d on CPU%d\n", sgi, smp_processor_id());
> break;
> diff --git a/xen/arch/arm/include/asm/gic.h b/xen/arch/arm/include/asm/gic.h
> index ee2c26adb4..40635a9d32 100644
> --- a/xen/arch/arm/include/asm/gic.h
> +++ b/xen/arch/arm/include/asm/gic.h
> @@ -306,6 +306,9 @@ enum gic_sgi {
> GIC_SGI_EVENT_CHECK,
> GIC_SGI_DUMP_STATE,
> GIC_SGI_CALL_FUNCTION,
> +#ifdef CONFIG_BOOT_SELFTEST
> + GIC_SGI_TEST,
> +#endif
> GIC_SGI_STATIC_MAX,
> };
>
> @@ -321,6 +324,11 @@ extern void send_SGI_one(unsigned int cpu, enum gic_sgi sgi);
> extern void send_SGI_self(enum gic_sgi sgi);
> extern void send_SGI_allbutself(enum gic_sgi sgi);
>
> +#ifdef CONFIG_BOOT_SELFTEST
> +/* Record a GIC_SGI_TEST delivered to this CPU (see arch/arm/gic-test.c). */
I would suggest to remove (see ...). One can easily find
gic_sgi_test_interrupt() and this reduces the risk of stale file name.
> +void gic_sgi_test_interrupt(void);
> +#endif
> +
> /* print useful debug info */
> extern void gic_dump_info(struct vcpu *v);
> extern void gic_dump_vgic_info(struct vcpu *v);
> diff --git a/xen/arch/arm/include/asm/setup.h b/xen/arch/arm/include/asm/setup.h
> index 0adfa4993a..2fdf5da526 100644
> --- a/xen/arch/arm/include/asm/setup.h
> +++ b/xen/arch/arm/include/asm/setup.h
> @@ -50,6 +50,15 @@ void setup_mm(void);
> extern uint32_t hyp_traps_vector[];
> void init_traps(void);
>
> +#ifdef CONFIG_BOOT_SELFTEST
> +#define __initcallboottest(fn) \
> + static const initcall_t __initcall_##fn __init_call("boottest") = (fn)
> +
> +void do_init_boottests(void);
> +#else
> +static inline void do_init_boottests(void) {}
> +#endif
> +
> int handle_device(struct domain *d, struct dt_device_node *dev, p2m_type_t p2mt,
> struct rangeset *iomem_ranges, struct rangeset *irq_ranges);
>
> diff --git a/xen/arch/arm/setup.c b/xen/arch/arm/setup.c
> index 6310a47d68..c7abbdb04e 100644
> --- a/xen/arch/arm/setup.c
> +++ b/xen/arch/arm/setup.c
> @@ -83,6 +83,24 @@ static void __init init_idle_domain(void)
> /* TODO: setup_idle_pagetable(); */
> }
>
> +#ifdef CONFIG_BOOT_SELFTEST
> +extern const initcall_t __initcall_boot_test_start[],
> + __initcall_boot_test_end[];
> +
> +void do_init_boottests(void)
> +{
> + const initcall_t *call;
> +
> + printk("CPU%u: boot self-tests start\n", smp_processor_id());
> +
> + for ( call = __initcall_boot_test_start; call < __initcall_boot_test_end;
> + call++ )
> + (*call)();
> +
> + printk("CPU%u: boot self-tests done\n", smp_processor_id());
> +}
> +#endif /* CONFIG_BOOT_SELFTEST */
> +
> static const char * __initdata processor_implementers[] = {
> ['A'] = "ARM Limited",
> ['B'] = "Broadcom Corporation",
> @@ -470,6 +488,8 @@ void asmlinkage __init noreturn start_xen(unsigned long fdt_paddr)
> enable_errata_workarounds();
> enable_cpu_features();
>
> + do_init_boottests();
> +
> /* Create initial domain 0. */
> if ( !is_dom0less_mode() )
> create_dom0();
> diff --git a/xen/arch/arm/smpboot.c b/xen/arch/arm/smpboot.c
> index 1806c47a08..97d8b19cf4 100644
> --- a/xen/arch/arm/smpboot.c
> +++ b/xen/arch/arm/smpboot.c
> @@ -28,6 +28,7 @@
> #include <asm/gic.h>
> #include <asm/procinfo.h>
> #include <asm/psci.h>
> +#include <asm/setup.h>
Style: I think this wants to go after asm/tee/tee.h (acpi.h seems to be
misplaced).
> #include <asm/acpi.h>
> #include <asm/tee/tee.h>
>
> @@ -413,6 +414,8 @@ void asmlinkage noreturn start_secondary(void)
>
> printk(XENLOG_DEBUG "CPU %u booted.\n", smp_processor_id());
>
> + do_init_boottests();
> +
> startup_cpu_idle_loop();
> }
>
> diff --git a/xen/arch/arm/xen.lds.S b/xen/arch/arm/xen.lds.S
> index 2d5f1c516d..14f64a856c 100644
> --- a/xen/arch/arm/xen.lds.S
> +++ b/xen/arch/arm/xen.lds.S
> @@ -146,6 +146,10 @@ SECTIONS
> *(.initcall1.init)
> __initcall_end = .;
>
> + __initcall_boot_test_start = .;
> + *(.initcallboottest.init)
> + __initcall_boot_test_end = .;
> +
> . = ALIGN(4);
> __alt_instructions = .;
> *(.altinstructions)
Cheers,
--
Julien Grall
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-04 19:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-29 17:09 [PATCH v2 for 4.23] Add GICv3 SGI boot/self tests in Xen Ayan Kumar Halder
2026-06-19 21:12 ` Julien Grall
2026-08-27 19:57 ` Halder, Ayan Kumar
2026-06-19 21:21 ` Julien Grall
2026-08-28 10:29 ` [PATCH v3 for 4.23] Add GIC " Ayan Kumar Halder
2026-09-04 19:21 ` Julien Grall
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.