mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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:
1 parent
a83e5b41c4
commit
282bbc3cab
4 files changed
+186
-24
No files matched your search
+176
-24
@@ -14,7 +14,9 @@
|
|||||||
#include <xen/errno.h>
|
#include <xen/errno.h>
|
||||||
#include <xen/init.h>
|
#include <xen/init.h>
|
||||||
#include <xen/lib.h>
|
#include <xen/lib.h>
|
||||||
|
#include <xen/sched.h>
|
||||||
#include <xen/sections.h>
|
#include <xen/sections.h>
|
||||||
|
#include <xen/xvmalloc.h>
|
||||||
|
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
#include <asm/csr.h>
|
#include <asm/csr.h>
|
||||||
@@ -34,9 +36,64 @@ struct riscv_isa_ext_data {
|
|||||||
.name = #ext_name, \
|
.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 */
|
/* Host ISA bitmap */
|
||||||
static __ro_after_init DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX);
|
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,
|
static int __init dt_get_cpuid_from_node(const struct dt_device_node *cpu,
|
||||||
unsigned long *dt_cpuid)
|
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
|
* and strncmp() is used in match_isa_ext() to compare extension names instead
|
||||||
* of strncasecmp().
|
* of strncasecmp().
|
||||||
*/
|
*/
|
||||||
const struct riscv_isa_ext_data __initconst riscv_isa_ext[] = {
|
static const struct riscv_isa_ext_entry __initconstrel riscv_isa_ext[] = {
|
||||||
RISCV_ISA_EXT_DATA(i),
|
RISCV_ISA_EXT_ENTRY(i, ANY),
|
||||||
RISCV_ISA_EXT_DATA(m),
|
RISCV_ISA_EXT_ENTRY(m, ANY),
|
||||||
RISCV_ISA_EXT_DATA(a),
|
RISCV_ISA_EXT_ENTRY(a, ANY),
|
||||||
RISCV_ISA_EXT_DATA(f),
|
RISCV_ISA_EXT_ENTRY(f, NONE),
|
||||||
RISCV_ISA_EXT_DATA(d),
|
RISCV_ISA_EXT_ENTRY(d, NONE),
|
||||||
RISCV_ISA_EXT_DATA(q),
|
RISCV_ISA_EXT_ENTRY(q, NONE),
|
||||||
RISCV_ISA_EXT_DATA(c),
|
RISCV_ISA_EXT_ENTRY(c, ANY),
|
||||||
RISCV_ISA_EXT_DATA(h),
|
RISCV_ISA_EXT_ENTRY(v, NONE),
|
||||||
RISCV_ISA_EXT_DATA(zicntr),
|
RISCV_ISA_EXT_ENTRY(h, NONE),
|
||||||
RISCV_ISA_EXT_DATA(zicsr),
|
RISCV_ISA_EXT_ENTRY(zicntr, ANY),
|
||||||
RISCV_ISA_EXT_DATA(zifencei),
|
RISCV_ISA_EXT_ENTRY(zicsr, ANY),
|
||||||
RISCV_ISA_EXT_DATA(zihintpause),
|
RISCV_ISA_EXT_ENTRY(zifencei, ANY),
|
||||||
RISCV_ISA_EXT_DATA(zihpm),
|
RISCV_ISA_EXT_ENTRY(zihintpause, ANY),
|
||||||
RISCV_ISA_EXT_DATA(zba),
|
RISCV_ISA_EXT_ENTRY(zihpm, ANY),
|
||||||
RISCV_ISA_EXT_DATA(zbb),
|
RISCV_ISA_EXT_ENTRY(zba, ANY),
|
||||||
RISCV_ISA_EXT_DATA(zbs),
|
RISCV_ISA_EXT_ENTRY(zbb, ANY),
|
||||||
RISCV_ISA_EXT_DATA(smaia),
|
RISCV_ISA_EXT_ENTRY(zbs, ANY),
|
||||||
RISCV_ISA_EXT_DATA(smstateen),
|
RISCV_ISA_EXT_ENTRY(smaia, ANY),
|
||||||
RISCV_ISA_EXT_DATA(ssaia),
|
RISCV_ISA_EXT_ENTRY(smstateen, ANY),
|
||||||
RISCV_ISA_EXT_DATA(sstc),
|
RISCV_ISA_EXT_ENTRY(ssaia, ANY),
|
||||||
RISCV_ISA_EXT_DATA(svade),
|
RISCV_ISA_EXT_ENTRY(sstc, NONE),
|
||||||
RISCV_ISA_EXT_DATA(svpbmt),
|
RISCV_ISA_EXT_ENTRY(svade, NONE),
|
||||||
|
RISCV_ISA_EXT_ENTRY(svpbmt, NONE),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct riscv_isa_ext_data __initconst required_extensions[] = {
|
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++ )
|
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[]
|
* `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);
|
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)
|
void __init riscv_fill_hwcap(void)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
@@ -527,4 +677,6 @@ void __init riscv_fill_hwcap(void)
|
|||||||
if ( !all_extns_available )
|
if ( !all_extns_available )
|
||||||
panic("Look why the extensions above are needed in "
|
panic("Look why the extensions above are needed in "
|
||||||
"https://xenbits.xenproject.org/docs/unstable/misc/riscv/booting.txt\n");
|
"https://xenbits.xenproject.org/docs/unstable/misc/riscv/booting.txt\n");
|
||||||
|
|
||||||
|
compute_guest_isa();
|
||||||
}
|
}
|
||||||
@@ -308,6 +308,8 @@ int arch_domain_create(struct domain *d,
|
|||||||
if ( is_idle_domain(d) )
|
if ( is_idle_domain(d) )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
init_guest_isa(d);
|
||||||
|
|
||||||
if ( (rc = p2m_init(d, config)) != 0)
|
if ( (rc = p2m_init(d, config)) != 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#ifndef __ASSEMBLER__
|
#ifndef __ASSEMBLER__
|
||||||
|
|
||||||
#include <xen/stdbool.h>
|
#include <xen/stdbool.h>
|
||||||
|
#include <xen/types.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These macros represent the logical IDs of each multi-letter RISC-V ISA
|
* 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
|
RISCV_ISA_EXT_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct domain;
|
||||||
|
|
||||||
void riscv_fill_hwcap(void);
|
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,
|
bool riscv_isa_extension_available(const unsigned long *isa_bitmap,
|
||||||
enum riscv_isa_ext_id id);
|
enum riscv_isa_ext_id id);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <xen/xmalloc.h>
|
#include <xen/xmalloc.h>
|
||||||
#include <public/hvm/params.h>
|
#include <public/hvm/params.h>
|
||||||
|
|
||||||
|
#include <asm/cpufeature.h>
|
||||||
#include <asm/guest-layout.h>
|
#include <asm/guest-layout.h>
|
||||||
#include <asm/p2m.h>
|
#include <asm/p2m.h>
|
||||||
#include <asm/vtimer.h>
|
#include <asm/vtimer.h>
|
||||||
@@ -94,6 +95,8 @@ struct arch_domain {
|
|||||||
struct p2m_domain p2m;
|
struct p2m_domain p2m;
|
||||||
|
|
||||||
struct paging_domain paging;
|
struct paging_domain paging;
|
||||||
|
|
||||||
|
const unsigned long *isa;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <xen/sched.h>
|
#include <xen/sched.h>
|
||||||
|
|||||||