[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH v7 14/14] xen/riscv: Disable SSTC extension and add trap-based CSR probing


  • To: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
  • From: Jan Beulich <jbeulich@xxxxxxxx>
  • Date: Tue, 10 Mar 2026 10:15:12 +0100
  • Autocrypt: addr=jbeulich@xxxxxxxx; keydata= xsDiBFk3nEQRBADAEaSw6zC/EJkiwGPXbWtPxl2xCdSoeepS07jW8UgcHNurfHvUzogEq5xk hu507c3BarVjyWCJOylMNR98Yd8VqD9UfmX0Hb8/BrA+Hl6/DB/eqGptrf4BSRwcZQM32aZK 7Pj2XbGWIUrZrd70x1eAP9QE3P79Y2oLrsCgbZJfEwCgvz9JjGmQqQkRiTVzlZVCJYcyGGsD /0tbFCzD2h20ahe8rC1gbb3K3qk+LpBtvjBu1RY9drYk0NymiGbJWZgab6t1jM7sk2vuf0Py O9Hf9XBmK0uE9IgMaiCpc32XV9oASz6UJebwkX+zF2jG5I1BfnO9g7KlotcA/v5ClMjgo6Gl MDY4HxoSRu3i1cqqSDtVlt+AOVBJBACrZcnHAUSuCXBPy0jOlBhxPqRWv6ND4c9PH1xjQ3NP nxJuMBS8rnNg22uyfAgmBKNLpLgAGVRMZGaGoJObGf72s6TeIqKJo/LtggAS9qAUiuKVnygo 3wjfkS9A3DRO+SpU7JqWdsveeIQyeyEJ/8PTowmSQLakF+3fote9ybzd880fSmFuIEJldWxp Y2ggPGpiZXVsaWNoQHN1c2UuY29tPsJgBBMRAgAgBQJZN5xEAhsDBgsJCAcDAgQVAggDBBYC AwECHgECF4AACgkQoDSui/t3IH4J+wCfQ5jHdEjCRHj23O/5ttg9r9OIruwAn3103WUITZee e7Sbg12UgcQ5lv7SzsFNBFk3nEQQCACCuTjCjFOUdi5Nm244F+78kLghRcin/awv+IrTcIWF hUpSs1Y91iQQ7KItirz5uwCPlwejSJDQJLIS+QtJHaXDXeV6NI0Uef1hP20+y8qydDiVkv6l IreXjTb7DvksRgJNvCkWtYnlS3mYvQ9NzS9PhyALWbXnH6sIJd2O9lKS1Mrfq+y0IXCP10eS FFGg+Av3IQeFatkJAyju0PPthyTqxSI4lZYuJVPknzgaeuJv/2NccrPvmeDg6Coe7ZIeQ8Yj t0ARxu2xytAkkLCel1Lz1WLmwLstV30g80nkgZf/wr+/BXJW/oIvRlonUkxv+IbBM3dX2OV8 AmRv1ySWPTP7AAMFB/9PQK/VtlNUJvg8GXj9ootzrteGfVZVVT4XBJkfwBcpC/XcPzldjv+3 HYudvpdNK3lLujXeA5fLOH+Z/G9WBc5pFVSMocI71I8bT8lIAzreg0WvkWg5V2WZsUMlnDL9 mpwIGFhlbM3gfDMs7MPMu8YQRFVdUvtSpaAs8OFfGQ0ia3LGZcjA6Ik2+xcqscEJzNH+qh8V m5jjp28yZgaqTaRbg3M/+MTbMpicpZuqF4rnB0AQD12/3BNWDR6bmh+EkYSMcEIpQmBM51qM EKYTQGybRCjpnKHGOxG0rfFY1085mBDZCH5Kx0cl0HVJuQKC+dV2ZY5AqjcKwAxpE75MLFkr wkkEGBECAAkFAlk3nEQCGwwACgkQoDSui/t3IH7nnwCfcJWUDUFKdCsBH/E5d+0ZnMQi+G0A nAuWpQkjM1ASeQwSHEeAWPgskBQL
  • Cc: Romain Caritey <Romain.Caritey@xxxxxxxxxxxxx>, Doug Goldstein <cardoe@xxxxxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Alistair Francis <alistair.francis@xxxxxxx>, Connor Davis <connojdavis@xxxxxxxxx>, xen-devel@xxxxxxxxxxxxxxxxxxxx
  • Delivery-date: Tue, 10 Mar 2026 09:15:23 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>

On 06.03.2026 17:33, Oleksii Kurochko wrote:
> Some RISC-V platforms expose the SSTC extension, but its CSRs are not
> properly saved and restored by Xen. Using SSTC in Xen could therefore
> lead to unexpected behaviour.

And what's wrong with (or what gets in the way of) adding proper
saving/restoring? Also, wouldn't a guest use vstimecmp anyway? I.e. what
saving/restoring are you talking about here?

> To avoid this in QEMU, disable SSTC by passing "sstc=off". On real
> hardware, OpenSBI does not provide a mechanism to disable SSTC via the
> DTS (riscv,isa or similar property), as it does not rely on that
> property to determine extension availability. Instead, it directly
> probes the CSR_STIMECMP register.
> 
> Introduce struct trap_info together with the do_expected_trap() handler
> to safely probe CSRs. The helper csr_read_allowed() attempts to read a
> CSR while catching traps, allowing Xen to detect whether the register
> is accessible. This mechanism is used at boot to verify SSTC support and
> panic if the CSR is not available.
> 
> The trap handling infrastructure may also be reused for other cases
> where controlled trap handling is required (e.g. probing instructions
> such as HLV*).

Hmm, won't you need a more generic way of dealing with traps anyway? See
Linux'es _ASM_EXTABLE(). See also comments further down.

> --- a/automation/scripts/qemu-smoke-riscv64.sh
> +++ b/automation/scripts/qemu-smoke-riscv64.sh
> @@ -7,7 +7,7 @@ rm -f smoke.serial
>  
>  export TEST_CMD="qemu-system-riscv64 \
>      -M virt,aia=aplic-imsic \
> -    -cpu rv64,svpbmt=on \
> +    -cpu rv64,svpbmt=on,sstc=off \
>      -smp 1 \
>      -nographic \
>      -m 2g \

How does this fit with you panic()ing when SSTC isn't available (i.e. the
register cannot be read)? I must be missing something, likely a result of
me not being able to really understand the description.

> --- a/xen/arch/riscv/cpufeature.c
> +++ b/xen/arch/riscv/cpufeature.c
> @@ -17,6 +17,8 @@
>  #include <xen/sections.h>
>  
>  #include <asm/cpufeature.h>
> +#include <asm/csr.h>
> +#include <asm/traps.h>
>  
>  #ifdef CONFIG_ACPI
>  # error "cpufeature.c functions should be updated to support ACPI"
> @@ -483,6 +485,7 @@ void __init riscv_fill_hwcap(void)
>      unsigned int i;
>      const size_t req_extns_amount = ARRAY_SIZE(required_extensions);
>      bool all_extns_available = true;
> +    struct trap_info trap;
>  
>      riscv_fill_hwcap_from_isa_string();
>  
> @@ -509,4 +512,9 @@ void __init riscv_fill_hwcap(void)
>      if ( !all_extns_available )
>          panic("Look why the extensions above are needed in "
>                
> "https://xenbits.xenproject.org/docs/unstable/misc/riscv/booting.txt\n";);
> +
> +    csr_read_allowed(CSR_STIMECMP, (unsigned long)&trap);

Please avoid such casts; see also below.

> --- a/xen/arch/riscv/entry.S
> +++ b/xen/arch/riscv/entry.S
> @@ -99,3 +99,27 @@ restore_registers:
>  
>          sret
>  END(handle_trap)
> +
> +        /*
> +         * We assume that the faulting instruction is 4 bytes long and 
> blindly
> +         * increment SEPC by 4.
> +         *
> +         * This should be safe because all places that may trigger this 
> handler
> +         * use ".option norvc" around the instruction that could cause the 
> trap,
> +         * or the instruction is not available in the RVC instruction set.
> +         *
> +         * do_expected_trap(a3, a4):
> +         *   a3 <- pointer to struct trap_info
> +         *   a4 <- temporary register
> +         */
> +FUNC(do_expected_trap)
> +        csrr    a4, CSR_SEPC
> +        REG_S   a4, RISCV_TRAP_SEPC(a3)
> +        csrr    a4, CSR_SCAUSE
> +        REG_S   a4, RISCV_TRAP_SCAUSE(a3)
> +
> +        csrr    a4, CSR_SEPC

Why read sepc a 2nd time? Yet further, what's the point of storing the value
in the first place? The sole present user doesn't care.

> --- a/xen/arch/riscv/include/asm/csr.h
> +++ b/xen/arch/riscv/include/asm/csr.h
> @@ -9,6 +9,7 @@
>  #include <asm/asm.h>
>  #include <xen/const.h>
>  #include <asm/riscv_encoding.h>
> +#include <asm/traps.h>
>  
>  #ifndef __ASSEMBLER__
>  
> @@ -78,6 +79,37 @@
>                             : "memory" );                        \
>  })
>  
> +/*
> + * Some functions inside asm/system.h requires some of the macros above,
> + * so this header should be included after the macros above are introduced.
> + */
> +#include <asm/system.h>
> +
> +#define csr_read_allowed(csr_num, trap) \
> +({ \
> +    register unsigned long tinfo asm("a3") = (unsigned long)trap; \

Why can't this variable be of the correct (pointer) type? This would then
at the same time serve as a compile-time check for the caller to have
passed an argument of the correct type.

> +    register unsigned long ttmp asm("a4"); \
> +    register unsigned long stvec = (unsigned long)&do_expected_trap; \

Fiddling with stvec may be okay-ish very early during boot. NMIs, for
example, do exist in principle on RISC-V, aiui. There must be a way for them
to be dealt with by other than just M-mode. 

> +    register unsigned long ret = 0; \
> +    unsigned long flags; \
> +    ((struct trap_info *)(trap))->scause = 0; \

"trap" would better be of the correct type. Don't use casts like this, please.

Further, wouldn't you better set the field to a guaranteed invalid value? 0 is
CAUSE_MISALIGNED_FETCH, after all.

> +    local_irq_save(flags); \
> +    asm volatile ( \
> +        ".option push\n" \
> +        ".option norvc\n" \

Shouldn't this come later?

> +        "add %[ttmp], %[tinfo], zero\n" \

Why "add", when you really mean "mv"? And why set ttmp in the first place, when
that's what do_expected_trap() writes to? Don't you really mean to specify "a4"
as a clobber?

> +        "csrrw %[stvec], " STR(CSR_STVEC) ", %[stvec]\n" \

The assembler does understand "stvec" as an operand, doesn't it?

> +        "csrr %[ret], %[csr]\n" \
> +        "csrw " STR(CSR_STVEC) ", %[stvec]\n" \
> +        ".option pop" \
> +        : [stvec] "+&r" (stvec), [tinfo] "+&r" (tinfo), \

tinfo isn't modified, is it?

> +          [ttmp] "+&r" (ttmp), [ret] "=&r" (ret) \

ttmp isn't initialized (in C), so the compiler could legitimately complain
about the use of an uninitialized variable here (due to the use of + where
= is meant).

Whereas for ret the situation is the other way around - you initialize the
variable, just to then tell the compiler that it can drop this
initialization, as - supposedly - the asm() always sets it (which it doesn't
when the csrr faults).

> +        : [csr] "i" (csr_num) \
> +        : "memory" ); \
> +    local_irq_restore(flags); \
> +    ret; \
> +})

A macro of this name would better return an indicator of what it is checking,
rather than the CSR value (which the sole user of this macro doesn't even
care about). Ideally such would also be an inline function.

Jan



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.