diff --git a/xen/arch/riscv/Makefile b/xen/arch/riscv/Makefile index 9dde693db4..87c1148b00 100644 --- a/xen/arch/riscv/Makefile +++ b/xen/arch/riscv/Makefile @@ -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 $< $@ diff --git a/xen/arch/riscv/entry.S b/xen/arch/riscv/entry.S index 4db818ba8d..202a35fb03 100644 --- a/xen/arch/riscv/entry.S +++ b/xen/arch/riscv/entry.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) diff --git a/xen/arch/riscv/include/asm/processor.h b/xen/arch/riscv/include/asm/processor.h index 2502045642..6b89df4a2d 100644 --- a/xen/arch/riscv/include/asm/processor.h +++ b/xen/arch/riscv/include/asm/processor.h @@ -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; }; diff --git a/xen/arch/riscv/include/asm/vsbi.h b/xen/arch/riscv/include/asm/vsbi.h new file mode 100644 index 0000000000..fa0cad604e --- /dev/null +++ b/xen/arch/riscv/include/asm/vsbi.h @@ -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 diff --git a/xen/arch/riscv/riscv64/asm-offsets.c b/xen/arch/riscv/riscv64/asm-offsets.c index 3b5daf3b36..472cced4f8 100644 --- a/xen/arch/riscv/riscv64/asm-offsets.c +++ b/xen/arch/riscv/riscv64/asm-offsets.c @@ -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)); diff --git a/xen/arch/riscv/setup.c b/xen/arch/riscv/setup.c index 8f46f1a1de..9b4835960d 100644 --- a/xen/arch/riscv/setup.c +++ b/xen/arch/riscv/setup.c @@ -26,6 +26,7 @@ #include #include #include +#include /* 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. diff --git a/xen/arch/riscv/traps.c b/xen/arch/riscv/traps.c index f061004d83..e9c9677863 100644 --- a/xen/arch/riscv/traps.c +++ b/xen/arch/riscv/traps.c @@ -15,6 +15,7 @@ #include #include #include +#include /* * 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 ) { diff --git a/xen/arch/riscv/vsbi/Makefile b/xen/arch/riscv/vsbi/Makefile new file mode 100644 index 0000000000..820eb10ac2 --- /dev/null +++ b/xen/arch/riscv/vsbi/Makefile @@ -0,0 +1 @@ +obj-y += core.o diff --git a/xen/arch/riscv/vsbi/core.c b/xen/arch/riscv/vsbi/core.c new file mode 100644 index 0000000000..f5ded8f676 --- /dev/null +++ b/xen/arch/riscv/vsbi/core.c @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include + +#include +#include +#include + +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; +} diff --git a/xen/arch/riscv/xen.lds.S b/xen/arch/riscv/xen.lds.S index 45d2e053d0..331a7d63d3 100644 --- a/xen/arch/riscv/xen.lds.S +++ b/xen/arch/riscv/xen.lds.S @@ -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);