xen/riscv: introduce guest riscv,isa string

Introduce build_guest_isa_str() to generate the riscv,isa string to be
passed to the guest via the Device Tree riscv,isa property.

Introduce the per-domain guest ISA bitmap, populated during domain
creation by calling init_guest_isa().

Introduce struct riscv_isa_ext_entry with a new guest_flags field
to filter out ISA extensions that should not be exposed to guests:

- f/d/q/v: FPU and vector context save/restore are not yet implemented
  for guests.
- Z*inx are not exposed either: they aren't in riscv_isa_ext[], so they
  can never be set in riscv_isa and thus never reach a guest, and no
  current hardware/guest-OS advertises or expects them. Supporting them
  would be cheaper than F/D/Q (FP values stay in integer registers Xen
  already context-switches), but is left as future work.
- h: Nested virtualisation is not supported.
- sstc: Xen owns the supervisor timer; guests must use SBI.
- svade: Xen manages hardware A/D bit updates in stage-2 page tables.
- svpbmt: Page-based memory types are not yet wired up in stage-2 code.

Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com>
Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Oleksii Kurochko authored and Jan Beulich committed 2026-09-21 10:06:37 +02:00
1 parent a83e5b41c4
commit 282bbc3cab
4 files changed
+186 -24

No files matched your search

+176 -24
View File
@@ -14,7 +14,9 @@
#include <xen/errno.h>
#include <xen/init.h>
#include <xen/lib.h>
#include <xen/sched.h>
#include <xen/sections.h>
#include <xen/xvmalloc.h>
#include <asm/cpufeature.h>
#include <asm/csr.h>
@@ -34,9 +36,64 @@ struct riscv_isa_ext_data {
.name = #ext_name, \
}
/*
* Which guests an extension may be handed out to, by guest XLEN.
*
* These flags express Xen's policy, not the ISA's rules: extensions which
* are architecturally tied to one XLEN (Zilsd on RV32, say) need no special
* treatment here, as they can only ever appear in the "riscv,isa" of a host
* of that XLEN, and guest_isa is masked against the host ISA bitmap anyway.
* They are only of use for extensions Xen chooses not to expose to guests of
* a given width despite the hardware implementing them.
*/
#define RISCV_ISA_EXT_GUEST_NONE 0
#define RISCV_ISA_EXT_GUEST_RV32 (1U << 0)
#define RISCV_ISA_EXT_GUEST_RV64 (1U << 1)
#define RISCV_ISA_EXT_GUEST_ANY (RISCV_ISA_EXT_GUEST_RV32 | \
RISCV_ISA_EXT_GUEST_RV64)
/*
* Guests are of the same width as Xen itself for the time being; once guest
* XLEN can differ from host XLEN (hstatus.VSXL), this becomes a per-domain
* property, just as guest_isa below does.
*/
#if defined(CONFIG_RISCV_32)
#define RISCV_ISA_EXT_GUEST_XLEN RISCV_ISA_EXT_GUEST_RV32
#elif defined(CONFIG_RISCV_64)
#define RISCV_ISA_EXT_GUEST_XLEN RISCV_ISA_EXT_GUEST_RV64
#else
# error "Unsupported RISC-V bitness"
#endif
struct riscv_isa_ext_entry {
unsigned int id;
const char *name;
unsigned int guest_flags;
};
#define RISCV_ISA_EXT_ENTRY(ext_name, guest) \
{ \
.id = RISCV_ISA_EXT_ ## ext_name, \
.name = #ext_name, \
.guest_flags = RISCV_ISA_EXT_GUEST_ ## guest, \
}
/* Host ISA bitmap */
static __ro_after_init DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX);
/*
* ISA bitmap handed out to every guest.
*
* All guests are given the same extensions for the time being, so this is
* computed once out of riscv_isa_ext[] and riscv_isa, rather than redoing
* the walk for every domain created. Should per-domain ISA policy ever be
* introduced, this will need to become per-domain again.
*/
static __ro_after_init DECLARE_BITMAP(guest_isa, RISCV_ISA_EXT_MAX);
/* "riscv,isa" string corresponding to guest_isa, shared by all domains. */
static char *__ro_after_init guest_isa_str;
static int __init dt_get_cpuid_from_node(const struct dt_device_node *cpu,
unsigned long *dt_cpuid)
{
@@ -120,29 +177,30 @@ static int __init dt_get_cpuid_from_node(const struct dt_device_node *cpu,
* and strncmp() is used in match_isa_ext() to compare extension names instead
* of strncasecmp().
*/
const struct riscv_isa_ext_data __initconst riscv_isa_ext[] = {
RISCV_ISA_EXT_DATA(i),
RISCV_ISA_EXT_DATA(m),
RISCV_ISA_EXT_DATA(a),
RISCV_ISA_EXT_DATA(f),
RISCV_ISA_EXT_DATA(d),
RISCV_ISA_EXT_DATA(q),
RISCV_ISA_EXT_DATA(c),
RISCV_ISA_EXT_DATA(h),
RISCV_ISA_EXT_DATA(zicntr),
RISCV_ISA_EXT_DATA(zicsr),
RISCV_ISA_EXT_DATA(zifencei),
RISCV_ISA_EXT_DATA(zihintpause),
RISCV_ISA_EXT_DATA(zihpm),
RISCV_ISA_EXT_DATA(zba),
RISCV_ISA_EXT_DATA(zbb),
RISCV_ISA_EXT_DATA(zbs),
RISCV_ISA_EXT_DATA(smaia),
RISCV_ISA_EXT_DATA(smstateen),
RISCV_ISA_EXT_DATA(ssaia),
RISCV_ISA_EXT_DATA(sstc),
RISCV_ISA_EXT_DATA(svade),
RISCV_ISA_EXT_DATA(svpbmt),
static const struct riscv_isa_ext_entry __initconstrel riscv_isa_ext[] = {
RISCV_ISA_EXT_ENTRY(i, ANY),
RISCV_ISA_EXT_ENTRY(m, ANY),
RISCV_ISA_EXT_ENTRY(a, ANY),
RISCV_ISA_EXT_ENTRY(f, NONE),
RISCV_ISA_EXT_ENTRY(d, NONE),
RISCV_ISA_EXT_ENTRY(q, NONE),
RISCV_ISA_EXT_ENTRY(c, ANY),
RISCV_ISA_EXT_ENTRY(v, NONE),
RISCV_ISA_EXT_ENTRY(h, NONE),
RISCV_ISA_EXT_ENTRY(zicntr, ANY),
RISCV_ISA_EXT_ENTRY(zicsr, ANY),
RISCV_ISA_EXT_ENTRY(zifencei, ANY),
RISCV_ISA_EXT_ENTRY(zihintpause, ANY),
RISCV_ISA_EXT_ENTRY(zihpm, ANY),
RISCV_ISA_EXT_ENTRY(zba, ANY),
RISCV_ISA_EXT_ENTRY(zbb, ANY),
RISCV_ISA_EXT_ENTRY(zbs, ANY),
RISCV_ISA_EXT_ENTRY(smaia, ANY),
RISCV_ISA_EXT_ENTRY(smstateen, ANY),
RISCV_ISA_EXT_ENTRY(ssaia, ANY),
RISCV_ISA_EXT_ENTRY(sstc, NONE),
RISCV_ISA_EXT_ENTRY(svade, NONE),
RISCV_ISA_EXT_ENTRY(svpbmt, NONE),
};
static const struct riscv_isa_ext_data __initconst required_extensions[] = {
@@ -181,7 +239,7 @@ static void __init match_isa_ext(const char *name, const char *name_end,
for ( unsigned int i = 0; i < riscv_isa_ext_count; i++ )
{
const struct riscv_isa_ext_data *ext = &riscv_isa_ext[i];
const struct riscv_isa_ext_entry *ext = &riscv_isa_ext[i];
/*
* `ext->name` (according to initialization of riscv_isa_ext[]
@@ -480,6 +538,98 @@ bool riscv_isa_extension_available(const unsigned long *isa_bitmap,
return test_bit(id, isa_bitmap);
}
static int __init build_guest_isa_str(char *buf, size_t size)
{
char *p = buf;
size_t left = size;
int total;
#if defined(CONFIG_RISCV_32)
total = snprintf(p, left, "rv32");
#elif defined(CONFIG_RISCV_64)
total = snprintf(p, left, "rv64");
#else
# error "Unsupported RISC-V bitness"
#endif
if ( total < 0 )
return total;
if ( buf )
{
if ( (size_t)total >= left )
return -ENOSPC;
p += total;
left -= total;
}
for ( unsigned int i = 0; i < ARRAY_SIZE(riscv_isa_ext); i++ )
{
const struct riscv_isa_ext_entry *ext = &riscv_isa_ext[i];
int ret;
if ( !riscv_isa_extension_available(guest_isa, ext->id) )
continue;
ret = snprintf(p, left, "%s%s",
ext->id >= RISCV_ISA_EXT_BASE ? "_" : "",
ext->name);
if ( ret < 0 )
return ret;
total += ret;
if ( buf )
{
if ( (size_t)ret >= left )
return -ENOSPC;
p += ret;
left -= ret;
}
}
return total;
}
static void __init compute_guest_isa(void)
{
int len;
for ( unsigned int i = 0; i < ARRAY_SIZE(riscv_isa_ext); i++ )
{
const struct riscv_isa_ext_entry *ext = &riscv_isa_ext[i];
if ( (ext->guest_flags & RISCV_ISA_EXT_GUEST_XLEN) &&
riscv_isa_extension_available(NULL, ext->id) )
__set_bit(ext->id, guest_isa);
}
/*
* All domains are given the same guest ISA, so the "riscv,isa" string
* is built only once here and then shared by all of them.
*/
if ( (len = build_guest_isa_str(NULL, 0)) < 0 )
panic("Failed to calculate guest \"riscv,isa\" length: %d\n", len);
if ( !(guest_isa_str = xvmalloc_array(char, len + 1)) )
panic("Failed to allocate guest \"riscv,isa\" string\n");
if ( build_guest_isa_str(guest_isa_str, len + 1) != len )
panic("Failed to build guest \"riscv,isa\" string\n");
}
void init_guest_isa(struct domain *d)
{
d->arch.isa = guest_isa;
}
const char *get_guest_isa_str(void)
{
return guest_isa_str;
}
void __init riscv_fill_hwcap(void)
{
unsigned int i;
@@ -527,4 +677,6 @@ 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");
compute_guest_isa();
}
+2
View File
@@ -308,6 +308,8 @@ int arch_domain_create(struct domain *d,
if ( is_idle_domain(d) )
return 0;
init_guest_isa(d);
if ( (rc = p2m_init(d, config)) != 0)
goto fail;
+5
View File
@@ -5,6 +5,7 @@
#ifndef __ASSEMBLER__
#include <xen/stdbool.h>
#include <xen/types.h>
/*
* These macros represent the logical IDs of each multi-letter RISC-V ISA
@@ -44,7 +45,11 @@ enum riscv_isa_ext_id {
RISCV_ISA_EXT_MAX
};
struct domain;
void riscv_fill_hwcap(void);
void init_guest_isa(struct domain *d);
const char *get_guest_isa_str(void);
bool riscv_isa_extension_available(const unsigned long *isa_bitmap,
enum riscv_isa_ext_id id);
+3
View File
@@ -7,6 +7,7 @@
#include <xen/xmalloc.h>
#include <public/hvm/params.h>
#include <asm/cpufeature.h>
#include <asm/guest-layout.h>
#include <asm/p2m.h>
#include <asm/vtimer.h>
@@ -94,6 +95,8 @@ struct arch_domain {
struct p2m_domain p2m;
struct paging_domain paging;
const unsigned long *isa;
};
#include <xen/sched.h>
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.