mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
xen/riscv: introduce vSBI extension framework
Introduce support for handling virtual SBI extensions in Xen. The changes include: - Added new vsbi/core.c and vsbi.h files to implement virtual SBI extension handling. - Modified traps.c to handle CAUSE_VIRTUAL_SUPERVISOR_ECALL by calling vsbi_handle_ecall() when the trap originates from VS-mode. - Updated xen.lds.S to include a new .vsbi.exts section for virtual SBI extension data. - Updated Makefile to include the new vsbi/ directory in the build. - Add hstatus register to struct cpu_user_regs as it is needed for a check that CAUSE_VIRTUAL_SUPERVISOR_ECALL happens from VS-mode. Also, add storing/restoring of hstatus register in handle_trap(). - Introduce vsbi_find_extension() to check if vsbi extension is supported by Xen. For now it is called only inside vsbi/core.c, but in future it is going to be called from other files. - Introduce check_vsbi_ext_ranges() to check if there EIDs ranges overlapping between extensions. The implementation allows for registration and handling of SBI extensions via a new vsbi_ext structure and ".vsbi.exts" section, enabling extensible virtual SBI support for RISC-V guests. Note: All EIDs are printed in the format #%#lx and all FIDs in #%lu, as the SBI spec uses these formats. Printing them this way makes it easier to search for them in the SBI spec. Signed-off-by: Oleksii Kurochko <oleksii.kurochko@gmail.com> Acked-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
62cb3c7510
commit
e717a5a4ae
10 files changed
+116
No files matched your search
@@ -20,6 +20,7 @@ obj-y += time.o
|
||||
obj-y += traps.o
|
||||
obj-y += vmid.o
|
||||
obj-y += vm_event.o
|
||||
obj-y += vsbi/
|
||||
|
||||
$(TARGET): $(TARGET)-syms
|
||||
$(OBJCOPY) -O binary -S $< $@
|
||||
|
||||
@@ -48,11 +48,17 @@ save_to_stack:
|
||||
csrr t0, CSR_SSTATUS
|
||||
REG_S t0, CPU_USER_REGS_SSTATUS(sp)
|
||||
|
||||
csrr t0, CSR_HSTATUS
|
||||
REG_S t0, CPU_USER_REGS_HSTATUS(sp)
|
||||
|
||||
mv a0, sp
|
||||
call do_trap
|
||||
|
||||
restore_registers:
|
||||
/* Restore stack_cpu_regs */
|
||||
REG_L t0, CPU_USER_REGS_HSTATUS(sp)
|
||||
csrw CSR_HSTATUS, t0
|
||||
|
||||
REG_L t0, CPU_USER_REGS_SEPC(sp)
|
||||
csrw CSR_SEPC, t0
|
||||
REG_L t0, CPU_USER_REGS_SSTATUS(sp)
|
||||
|
||||
@@ -49,6 +49,7 @@ struct cpu_user_regs
|
||||
unsigned long t6;
|
||||
unsigned long sepc;
|
||||
unsigned long sstatus;
|
||||
unsigned long hstatus;
|
||||
/* pointer to previous stack_cpu_regs */
|
||||
unsigned long pregs;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#ifndef ASM_RISCV_VSBI_H
|
||||
#define ASM_RISCV_VSBI_H
|
||||
|
||||
struct cpu_user_regs;
|
||||
|
||||
struct vsbi_ext {
|
||||
const char *name;
|
||||
unsigned long eid_start;
|
||||
unsigned long eid_end;
|
||||
int (*handler)(unsigned long eid, unsigned long fid,
|
||||
struct cpu_user_regs *regs);
|
||||
};
|
||||
|
||||
/* Ranges (start and end) are inclusive within an extension */
|
||||
#define VSBI_EXT(ext, start, end, handle) \
|
||||
static const struct vsbi_ext vsbi_ext_##ext __used \
|
||||
__section(".vsbi.exts") = { \
|
||||
.name = #ext, \
|
||||
.eid_start = start, \
|
||||
.eid_end = end, \
|
||||
.handler = handle, \
|
||||
};
|
||||
|
||||
void vsbi_handle_ecall(struct cpu_user_regs *regs);
|
||||
const struct vsbi_ext *vsbi_find_extension(unsigned long eid);
|
||||
|
||||
void check_vsbi_ext_ranges(void);
|
||||
|
||||
#endif
|
||||
@@ -49,6 +49,7 @@ void asm_offsets(void)
|
||||
OFFSET(CPU_USER_REGS_T6, struct cpu_user_regs, t6);
|
||||
OFFSET(CPU_USER_REGS_SEPC, struct cpu_user_regs, sepc);
|
||||
OFFSET(CPU_USER_REGS_SSTATUS, struct cpu_user_regs, sstatus);
|
||||
OFFSET(CPU_USER_REGS_HSTATUS, struct cpu_user_regs, hstatus);
|
||||
OFFSET(CPU_USER_REGS_PREGS, struct cpu_user_regs, pregs);
|
||||
BLANK();
|
||||
DEFINE(PCPU_INFO_SIZE, sizeof(struct pcpu_info));
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/vsbi.h>
|
||||
|
||||
/* Xen stack for bringing up the first CPU. */
|
||||
unsigned char __initdata cpu0_boot_stack[STACK_SIZE]
|
||||
@@ -110,6 +111,8 @@ void __init noreturn start_xen(unsigned long bootcpu_id,
|
||||
|
||||
end_boot_allocator();
|
||||
|
||||
check_vsbi_ext_ranges();
|
||||
|
||||
/*
|
||||
* The memory subsystem has been initialized, we can now switch from
|
||||
* early_boot -> boot.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/riscv_encoding.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/vsbi.h>
|
||||
|
||||
/*
|
||||
* Initialize the trap handling.
|
||||
@@ -114,6 +115,13 @@ void do_trap(struct cpu_user_regs *cpu_regs)
|
||||
|
||||
switch ( cause )
|
||||
{
|
||||
case CAUSE_VIRTUAL_SUPERVISOR_ECALL:
|
||||
/* CAUSE_VIRTUAL_SUPERVISOR_ECALL should come from VS-mode */
|
||||
BUG_ON(!(cpu_regs->hstatus & HSTATUS_SPV));
|
||||
|
||||
vsbi_handle_ecall(cpu_regs);
|
||||
break;
|
||||
|
||||
case CAUSE_ILLEGAL_INSTRUCTION:
|
||||
if ( do_bug_frame(cpu_regs, pc) >= 0 )
|
||||
{
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
obj-y += core.o
|
||||
@@ -0,0 +1,57 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
#include <xen/sched.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/vsbi.h>
|
||||
|
||||
extern const struct vsbi_ext _svsbi_exts[], _evsbi_exts[];
|
||||
|
||||
void __init check_vsbi_ext_ranges(void)
|
||||
{
|
||||
for ( const struct vsbi_ext *a = _svsbi_exts; a != _evsbi_exts; a++ )
|
||||
for ( const struct vsbi_ext *b = a + 1; b != _evsbi_exts; b++ )
|
||||
if ( !(a->eid_end < b->eid_start || b->eid_end < a->eid_start) )
|
||||
panic("EID range overlap detected: "
|
||||
"%s:[#%#lx..#%#lx] vs %s:[#%#lx..#%#lx]\n",
|
||||
a->name, a->eid_start, a->eid_end,
|
||||
b->name, b->eid_start, b->eid_end);
|
||||
}
|
||||
|
||||
const struct vsbi_ext *vsbi_find_extension(unsigned long eid)
|
||||
{
|
||||
for ( const struct vsbi_ext *ext = _svsbi_exts;
|
||||
ext != _evsbi_exts;
|
||||
ext++ )
|
||||
if ( (eid >= ext->eid_start) && (eid <= ext->eid_end) )
|
||||
return ext;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void vsbi_handle_ecall(struct cpu_user_regs *regs)
|
||||
{
|
||||
const unsigned long eid = regs->a7;
|
||||
const unsigned long fid = regs->a6;
|
||||
const struct vsbi_ext *ext = vsbi_find_extension(eid);
|
||||
int ret;
|
||||
|
||||
if ( ext )
|
||||
ret = ext->handler(eid, fid, regs);
|
||||
else
|
||||
{
|
||||
gprintk(XENLOG_ERR, "Unsupported Guest SBI EID #%#lx, FID #%lu\n",
|
||||
eid, regs->a1);
|
||||
ret = SBI_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
* The ecall instruction is not part of the RISC-V C extension (compressed
|
||||
* instructions), so it is always 4 bytes long. Therefore, it is safe to
|
||||
* use a fixed length of 4 bytes instead of reading guest memory to
|
||||
* determine the instruction length.
|
||||
*/
|
||||
regs->sepc += 4;
|
||||
regs->a0 = ret;
|
||||
}
|
||||
@@ -61,6 +61,13 @@ SECTIONS
|
||||
__note_gnu_build_id_end = .;
|
||||
} :note :text
|
||||
#endif
|
||||
|
||||
. = ALIGN(POINTER_ALIGN);
|
||||
DECL_SECTION(.vsbi.exts) {
|
||||
_svsbi_exts = .;
|
||||
*(.vsbi.exts)
|
||||
_evsbi_exts = .;
|
||||
} :text
|
||||
_erodata = .; /* End of read-only data */
|
||||
|
||||
. = ALIGN(PAGE_SIZE);
|
||||
|
||||