[kvm-unit-tests PATCH 7/7] riscv: Add BRS SBI requirement tests

Andrew Jones andrew.jones at linux.dev
Wed Sep 30 04:19:56 PDT 2026


On Thu, Sep 24, 2026 at 11:39:45AM +0800, Xiang W wrote:
> Add a riscv-brs unit test for the SBI rules in the RISC-V Boot and
> Runtime Services specification:
> 
>   SBI_010  spec version must be v2.0 or later
>   SBI_020  HSM must be implemented
>   SBI_030  TIME is required unless Sstc is present
>   SBI_040  IPI is required unless Ssaia is present
>   SBI_050  RFENCE is required unless Ssaia is present
>   SBI_060  PMU is required unless Smcsrind, Sscsrind, Smcdeleg
>            and Ssccfg are all present
>   SBI_070  DBCN is required when ACPI SPCR Interface Type is 0x15
> 
> Parse the extra ISA names used as skip conditions, and add the RFNC
> and PMU extension IDs needed to probe those extensions.
> 
> Signed-off-by: Xiang W <wangxiang at iscas.ac.cn>
> ---
>  lib/riscv/asm/isa.h |   5 ++
>  lib/riscv/asm/sbi.h |   2 +
>  lib/riscv/isa.c     |  14 +++-
>  riscv/Makefile      |   1 +
>  riscv/riscv-brs.c   | 171 ++++++++++++++++++++++++++++++++++++++++++++
>  riscv/unittests.cfg |   4 ++
>  6 files changed, 195 insertions(+), 2 deletions(-)
>  create mode 100644 riscv/riscv-brs.c
> 
> diff --git a/lib/riscv/asm/isa.h b/lib/riscv/asm/isa.h
> index fb3af67d..8a124881 100644
> --- a/lib/riscv/asm/isa.h
> +++ b/lib/riscv/asm/isa.h
> @@ -13,7 +13,12 @@
>   * by name not tracked here with cpu_has_extension_name()
>   */
>  enum {
> +	ISA_SMCDELEG,
> +	ISA_SMCSRIND,

I'm not sure we should expect machine level ISA extensions to be in the
S-mode ISA string.

Indeed the spec says

"""
The Performance Monitoring Extension (PMU) MUST be implemented, if the
counter delegation-related S-Mode ISA extensions (Sscsrind [13] and Ssccfg [14])
are not present.
"""

So I'm not sure why you're looking for the M-mode extensions at all.

> +	ISA_SSAIA,
> +	ISA_SSCCFG,
>  	ISA_SSTC,
> +	ISA_SSCSRIND,
>  	ISA_MAX,
>  };
>  __static_assert(ISA_MAX <= __riscv_xlen, "Need to increase thread_info.isa");
> diff --git a/lib/riscv/asm/sbi.h b/lib/riscv/asm/sbi.h
> index 289a6a24..c92a66dc 100644
> --- a/lib/riscv/asm/sbi.h
> +++ b/lib/riscv/asm/sbi.h
> @@ -45,8 +45,10 @@ enum sbi_ext_id {
>  	SBI_EXT_BASE = 0x10,
>  	SBI_EXT_TIME = 0x54494d45,
>  	SBI_EXT_IPI = 0x735049,
> +	SBI_EXT_RFNC = 0x52464E43,
>  	SBI_EXT_HSM = 0x48534d,
>  	SBI_EXT_SRST = 0x53525354,
> +	SBI_EXT_PMU = 0x504D55,
>  	SBI_EXT_DBCN = 0x4442434E,
>  	SBI_EXT_SUSP = 0x53555350,
>  	SBI_EXT_FWFT = 0x46574654,
> diff --git a/lib/riscv/isa.c b/lib/riscv/isa.c
> index b0257979..f5e2a61b 100644
> --- a/lib/riscv/isa.c
> +++ b/lib/riscv/isa.c
> @@ -39,8 +39,18 @@ static void isa_bit(const char *name, int len, void *data)
>  {
>  	struct thread_info *info = (struct thread_info *)data;
>  
> -	if (isa_match("sstc", name, len))
> -		set_bit(ISA_SSTC, info->isa);
> +#define ISA_MATCH_AND_SET(ext) \
> +	if (isa_match(#ext, name, len)) \
> +		set_bit(ISA_##ext, info->isa)
> +
> +	ISA_MATCH_AND_SET(SMCDELEG);
> +	ISA_MATCH_AND_SET(SMCSRIND);
> +	ISA_MATCH_AND_SET(SSAIA);
> +	ISA_MATCH_AND_SET(SSCCFG);
> +	ISA_MATCH_AND_SET(SSCSRIND);
> +	ISA_MATCH_AND_SET(SSTC);
> +
> +#undef ISA_MATCH_AND_SET
>  }
>  
>  static void isa_parse(const char *isa_string, int len, struct isa_info *info)
> diff --git a/riscv/Makefile b/riscv/Makefile
> index 348a30a7..7da7526f 100644
> --- a/riscv/Makefile
> +++ b/riscv/Makefile
> @@ -15,6 +15,7 @@ tests += $(TEST_DIR)/sbi.$(exe)
>  tests += $(TEST_DIR)/selftest.$(exe)
>  tests += $(TEST_DIR)/sieve.$(exe)
>  tests += $(TEST_DIR)/isa-dbltrp.$(exe)
> +tests += $(TEST_DIR)/riscv-brs.$(exe)
>  
>  all: $(tests)
>  
> diff --git a/riscv/riscv-brs.c b/riscv/riscv-brs.c
> new file mode 100644
> index 00000000..0d4a8f47
> --- /dev/null
> +++ b/riscv/riscv-brs.c
> @@ -0,0 +1,171 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + *  RISC-V BRS SBI requirement tests
> + *
> + * Copyright (C) 2026, Institute of Software, CAS., Xiang W <wangxiang at iscas.ac.cn>
> + */
> +
> +#include <libcflat.h>
> +#include <devicetree.h>
> +#include <asm/sbi.h>
> +#include <asm/isa.h>
> +#include <asm/smp.h>
> +
> +#ifdef CONFIG_EFI
> +#include <acpi.h>
> +#endif
> +
> +static void check_sbi_010(void)
> +{
> +	struct sbiret ret;
> +
> +	report_prefix_push("SBI_010");
> +
> +	ret = sbi_get_spec_version();
> +	if (ret.error) {
> +		report_fail("sbi_get_spec_version failed with %ld", ret.error);
> +		report_prefix_pop();
> +		return;
> +	}
> +
> +	report_info("SBI spec version %lu.%lu",
> +		    (ret.value >> SBI_SPEC_VERSION_MAJOR_SHIFT) &
> +		    SBI_SPEC_VERSION_MAJOR_MASK,
> +		    ret.value & SBI_SPEC_VERSION_MINOR_MASK);
> +
> +	report(ret.value >= sbi_mk_version(2, 0),
> +	       "SBI spec version is v2.0 or later");
> +
> +	report_prefix_pop();
> +}
> +
> +static void check_sbi_020(void)
> +{
> +	report_prefix_push("SBI_020");
> +	report(sbi_probe(SBI_EXT_HSM), "HSM extension is implemented");
> +	report_prefix_pop();
> +}
> +
> +static void check_sbi_030(void)
> +{
> +	report_prefix_push("SBI_030");
> +
> +	if (cpu_has_extension(smp_processor_id(), ISA_SSTC)) {
> +		report_skip("Sstc is present, TIME is not mandated");
> +		report_prefix_pop();
> +		return;
> +	}
> +
> +	report(sbi_probe(SBI_EXT_TIME), "TIME extension is implemented");
> +	report_prefix_pop();
> +}
> +
> +static void check_sbi_040(void)
> +{
> +	report_prefix_push("SBI_040");
> +
> +	if (cpu_has_extension(smp_processor_id(), ISA_SSAIA)) {

I'm not sure this is a sufficient check. IIRC, the IMSIC is an optional
feature of the AIA and the BRS rule states:

"""
The S-Mode IPI Extension (sPI) MUST be implemented, if the Incoming MSI
Controller (IMSIC, [12]) is not available.
"""

I think we should check both ISA_SSAIA and ensure each hart has a nonzero
acpi_madt_rintc.imsic_size. (And, unless, I'm mistaken, the BRS SBI rules
apply to both BRS-I and BRS-B, so we should implement a DT equivalent
IMSIC check too.)


> +		report_skip("Ssaia is present, IPI is not mandated");
> +		report_prefix_pop();
> +		return;
> +	}
> +
> +	report(sbi_probe(SBI_EXT_IPI), "IPI extension is implemented");
> +	report_prefix_pop();
> +}
> +
> +static void check_sbi_050(void)
> +{
> +	report_prefix_push("SBI_050");
> +
> +	if (cpu_has_extension(smp_processor_id(), ISA_SSAIA)) {
> +		report_skip("Ssaia is present, RFENCE is not mandated");
> +		report_prefix_pop();
> +		return;
> +	}
> +
> +	report(sbi_probe(SBI_EXT_RFNC), "RFENCE extension is implemented");
> +	report_prefix_pop();
> +}
> +
> +static void check_sbi_060(void)
> +{
> +	int cpu = smp_processor_id();
> +	bool skip = true;
> +	bool has_ext;
> +
> +	report_prefix_push("SBI_060");
> +
> +	has_ext = cpu_has_extension(cpu, ISA_SMCSRIND);
> +	if (!has_ext)
> +		report_info("Smcsrind is not present");
> +	skip &= has_ext;
> +
> +	has_ext = cpu_has_extension(cpu, ISA_SSCSRIND);
> +	if (!has_ext)
> +		report_info("Sscsrind is not present");
> +	skip &= has_ext;
> +
> +	has_ext = cpu_has_extension(cpu, ISA_SMCDELEG);
> +	if (!has_ext)
> +		report_info("Smcdeleg is not present");
> +	skip &= has_ext;
> +
> +	has_ext = cpu_has_extension(cpu, ISA_SSCCFG);
> +	if (!has_ext)
> +		report_info("Ssccfg is not present");
> +	skip &= has_ext;
> +
> +	if (skip) {
> +		report_skip("Smcsrind, Sscsrind, Smcdeleg and Ssccfg are present, PMU is not mandated");
> +		report_prefix_pop();
> +		return;
> +	}
> +
> +	report(sbi_probe(SBI_EXT_PMU), "PMU extension is implemented");
> +	report_prefix_pop();
> +}
> +
> +static void check_sbi_070(void)
> +{
> +	report_prefix_push("SBI_070");
> +
> +#ifdef CONFIG_EFI
> +	if (!dt_available()) {
> +		struct spcr_descriptor *spcr;
> +
> +		spcr = find_acpi_table_addr(SPCR_SIGNATURE);
> +		if (spcr) {
> +			report_info("SPCR interface type is %#x", spcr->interface_type);
> +			if (spcr->interface_type == 0x15) {
> +				report(sbi_probe(SBI_EXT_DBCN), "DBCN extension is implemented");
> +				report_prefix_pop();
> +				return;
> +			}
> +			report_skip("SPCR interface type is not 0x15, DBCN is not mandated");
> +			report_prefix_pop();
> +			return;
> +		}
> +		report_skip("SPCR is not present, DBCN is not mandated");
> +		report_prefix_pop();
> +		return;

nit: could use a goto to get rid of the repeated

  report_prefix_pop();
  return;

lines.

> +	}
> +#endif
> +	report_skip("ACPI not available, DBCN is not mandated");
> +	report_prefix_pop();
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	report_prefix_push("riscv-brs");
> +
> +	check_sbi_010();
> +	check_sbi_020();
> +	check_sbi_030();
> +	check_sbi_040();
> +	check_sbi_050();
> +	check_sbi_060();
> +	check_sbi_070();
> +
> +	return report_summary();
> +}
> diff --git a/riscv/unittests.cfg b/riscv/unittests.cfg
> index 9e9f58d9..990f03db 100644
> --- a/riscv/unittests.cfg
> +++ b/riscv/unittests.cfg
> @@ -22,3 +22,7 @@ groups = sbi
>  [dbltrp]
>  file = isa-dbltrp.flat
>  groups = isa sbi
> +
> +[riscv-brs]
> +file = riscv-brs.flat
> +groups = riscv-brs nodefault
> -- 
> 2.47.3
>

Thanks,
drew



More information about the kvm-riscv mailing list