]> git.baikalelectronics.ru Git - kernel.git/commitdiff
RISC-V: KVM: Implement VCPU world-switch
authorAnup Patel <anup.patel@wdc.com>
Mon, 27 Sep 2021 11:40:05 +0000 (17:10 +0530)
committerAnup Patel <anup@brainfault.org>
Mon, 4 Oct 2021 10:19:57 +0000 (15:49 +0530)
This patch implements the VCPU world-switch for KVM RISC-V.

The KVM RISC-V world-switch (i.e. __kvm_riscv_switch_to()) mostly
switches general purpose registers, SSTATUS, STVEC, SSCRATCH and
HSTATUS CSRs. Other CSRs are switched via vcpu_load() and vcpu_put()
interface in kvm_arch_vcpu_load() and kvm_arch_vcpu_put() functions
respectively.

Signed-off-by: Anup Patel <anup.patel@wdc.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Alexander Graf <graf@amazon.com>
Acked-by: Palmer Dabbelt <palmerdabbelt@google.com>
arch/riscv/include/asm/kvm_host.h
arch/riscv/kernel/asm-offsets.c
arch/riscv/kvm/Makefile
arch/riscv/kvm/vcpu.c
arch/riscv/kvm/vcpu_switch.S [new file with mode: 0644]

index e307528bbdbf394571edfba6c2bf7bec354f64d0..8ef0ca100e7573a5eeaab975da4068b06c8c407b 100644 (file)
@@ -115,6 +115,14 @@ struct kvm_vcpu_arch {
        /* ISA feature bits (similar to MISA) */
        unsigned long isa;
 
+       /* SSCRATCH, STVEC, and SCOUNTEREN of Host */
+       unsigned long host_sscratch;
+       unsigned long host_stvec;
+       unsigned long host_scounteren;
+
+       /* CPU context of Host */
+       struct kvm_cpu_context host_context;
+
        /* CPU context of Guest VCPU */
        struct kvm_cpu_context guest_context;
 
@@ -163,7 +171,7 @@ int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run);
 int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run,
                        struct kvm_cpu_trap *trap);
 
-static inline void __kvm_riscv_switch_to(struct kvm_vcpu_arch *vcpu_arch) {}
+void __kvm_riscv_switch_to(struct kvm_vcpu_arch *vcpu_arch);
 
 int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq);
 int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq);
index 90f8ce64fa6f10ad2427c52505b7f315c1b33bd3..2fac70303341d94283fec4be3b9449c06d3a08a6 100644 (file)
@@ -7,7 +7,9 @@
 #define GENERATING_ASM_OFFSETS
 
 #include <linux/kbuild.h>
+#include <linux/mm.h>
 #include <linux/sched.h>
+#include <asm/kvm_host.h>
 #include <asm/thread_info.h>
 #include <asm/ptrace.h>
 
@@ -111,6 +113,82 @@ void asm_offsets(void)
        OFFSET(PT_BADADDR, pt_regs, badaddr);
        OFFSET(PT_CAUSE, pt_regs, cause);
 
+       OFFSET(KVM_ARCH_GUEST_ZERO, kvm_vcpu_arch, guest_context.zero);
+       OFFSET(KVM_ARCH_GUEST_RA, kvm_vcpu_arch, guest_context.ra);
+       OFFSET(KVM_ARCH_GUEST_SP, kvm_vcpu_arch, guest_context.sp);
+       OFFSET(KVM_ARCH_GUEST_GP, kvm_vcpu_arch, guest_context.gp);
+       OFFSET(KVM_ARCH_GUEST_TP, kvm_vcpu_arch, guest_context.tp);
+       OFFSET(KVM_ARCH_GUEST_T0, kvm_vcpu_arch, guest_context.t0);
+       OFFSET(KVM_ARCH_GUEST_T1, kvm_vcpu_arch, guest_context.t1);
+       OFFSET(KVM_ARCH_GUEST_T2, kvm_vcpu_arch, guest_context.t2);
+       OFFSET(KVM_ARCH_GUEST_S0, kvm_vcpu_arch, guest_context.s0);
+       OFFSET(KVM_ARCH_GUEST_S1, kvm_vcpu_arch, guest_context.s1);
+       OFFSET(KVM_ARCH_GUEST_A0, kvm_vcpu_arch, guest_context.a0);
+       OFFSET(KVM_ARCH_GUEST_A1, kvm_vcpu_arch, guest_context.a1);
+       OFFSET(KVM_ARCH_GUEST_A2, kvm_vcpu_arch, guest_context.a2);
+       OFFSET(KVM_ARCH_GUEST_A3, kvm_vcpu_arch, guest_context.a3);
+       OFFSET(KVM_ARCH_GUEST_A4, kvm_vcpu_arch, guest_context.a4);
+       OFFSET(KVM_ARCH_GUEST_A5, kvm_vcpu_arch, guest_context.a5);
+       OFFSET(KVM_ARCH_GUEST_A6, kvm_vcpu_arch, guest_context.a6);
+       OFFSET(KVM_ARCH_GUEST_A7, kvm_vcpu_arch, guest_context.a7);
+       OFFSET(KVM_ARCH_GUEST_S2, kvm_vcpu_arch, guest_context.s2);
+       OFFSET(KVM_ARCH_GUEST_S3, kvm_vcpu_arch, guest_context.s3);
+       OFFSET(KVM_ARCH_GUEST_S4, kvm_vcpu_arch, guest_context.s4);
+       OFFSET(KVM_ARCH_GUEST_S5, kvm_vcpu_arch, guest_context.s5);
+       OFFSET(KVM_ARCH_GUEST_S6, kvm_vcpu_arch, guest_context.s6);
+       OFFSET(KVM_ARCH_GUEST_S7, kvm_vcpu_arch, guest_context.s7);
+       OFFSET(KVM_ARCH_GUEST_S8, kvm_vcpu_arch, guest_context.s8);
+       OFFSET(KVM_ARCH_GUEST_S9, kvm_vcpu_arch, guest_context.s9);
+       OFFSET(KVM_ARCH_GUEST_S10, kvm_vcpu_arch, guest_context.s10);
+       OFFSET(KVM_ARCH_GUEST_S11, kvm_vcpu_arch, guest_context.s11);
+       OFFSET(KVM_ARCH_GUEST_T3, kvm_vcpu_arch, guest_context.t3);
+       OFFSET(KVM_ARCH_GUEST_T4, kvm_vcpu_arch, guest_context.t4);
+       OFFSET(KVM_ARCH_GUEST_T5, kvm_vcpu_arch, guest_context.t5);
+       OFFSET(KVM_ARCH_GUEST_T6, kvm_vcpu_arch, guest_context.t6);
+       OFFSET(KVM_ARCH_GUEST_SEPC, kvm_vcpu_arch, guest_context.sepc);
+       OFFSET(KVM_ARCH_GUEST_SSTATUS, kvm_vcpu_arch, guest_context.sstatus);
+       OFFSET(KVM_ARCH_GUEST_HSTATUS, kvm_vcpu_arch, guest_context.hstatus);
+       OFFSET(KVM_ARCH_GUEST_SCOUNTEREN, kvm_vcpu_arch, guest_csr.scounteren);
+
+       OFFSET(KVM_ARCH_HOST_ZERO, kvm_vcpu_arch, host_context.zero);
+       OFFSET(KVM_ARCH_HOST_RA, kvm_vcpu_arch, host_context.ra);
+       OFFSET(KVM_ARCH_HOST_SP, kvm_vcpu_arch, host_context.sp);
+       OFFSET(KVM_ARCH_HOST_GP, kvm_vcpu_arch, host_context.gp);
+       OFFSET(KVM_ARCH_HOST_TP, kvm_vcpu_arch, host_context.tp);
+       OFFSET(KVM_ARCH_HOST_T0, kvm_vcpu_arch, host_context.t0);
+       OFFSET(KVM_ARCH_HOST_T1, kvm_vcpu_arch, host_context.t1);
+       OFFSET(KVM_ARCH_HOST_T2, kvm_vcpu_arch, host_context.t2);
+       OFFSET(KVM_ARCH_HOST_S0, kvm_vcpu_arch, host_context.s0);
+       OFFSET(KVM_ARCH_HOST_S1, kvm_vcpu_arch, host_context.s1);
+       OFFSET(KVM_ARCH_HOST_A0, kvm_vcpu_arch, host_context.a0);
+       OFFSET(KVM_ARCH_HOST_A1, kvm_vcpu_arch, host_context.a1);
+       OFFSET(KVM_ARCH_HOST_A2, kvm_vcpu_arch, host_context.a2);
+       OFFSET(KVM_ARCH_HOST_A3, kvm_vcpu_arch, host_context.a3);
+       OFFSET(KVM_ARCH_HOST_A4, kvm_vcpu_arch, host_context.a4);
+       OFFSET(KVM_ARCH_HOST_A5, kvm_vcpu_arch, host_context.a5);
+       OFFSET(KVM_ARCH_HOST_A6, kvm_vcpu_arch, host_context.a6);
+       OFFSET(KVM_ARCH_HOST_A7, kvm_vcpu_arch, host_context.a7);
+       OFFSET(KVM_ARCH_HOST_S2, kvm_vcpu_arch, host_context.s2);
+       OFFSET(KVM_ARCH_HOST_S3, kvm_vcpu_arch, host_context.s3);
+       OFFSET(KVM_ARCH_HOST_S4, kvm_vcpu_arch, host_context.s4);
+       OFFSET(KVM_ARCH_HOST_S5, kvm_vcpu_arch, host_context.s5);
+       OFFSET(KVM_ARCH_HOST_S6, kvm_vcpu_arch, host_context.s6);
+       OFFSET(KVM_ARCH_HOST_S7, kvm_vcpu_arch, host_context.s7);
+       OFFSET(KVM_ARCH_HOST_S8, kvm_vcpu_arch, host_context.s8);
+       OFFSET(KVM_ARCH_HOST_S9, kvm_vcpu_arch, host_context.s9);
+       OFFSET(KVM_ARCH_HOST_S10, kvm_vcpu_arch, host_context.s10);
+       OFFSET(KVM_ARCH_HOST_S11, kvm_vcpu_arch, host_context.s11);
+       OFFSET(KVM_ARCH_HOST_T3, kvm_vcpu_arch, host_context.t3);
+       OFFSET(KVM_ARCH_HOST_T4, kvm_vcpu_arch, host_context.t4);
+       OFFSET(KVM_ARCH_HOST_T5, kvm_vcpu_arch, host_context.t5);
+       OFFSET(KVM_ARCH_HOST_T6, kvm_vcpu_arch, host_context.t6);
+       OFFSET(KVM_ARCH_HOST_SEPC, kvm_vcpu_arch, host_context.sepc);
+       OFFSET(KVM_ARCH_HOST_SSTATUS, kvm_vcpu_arch, host_context.sstatus);
+       OFFSET(KVM_ARCH_HOST_HSTATUS, kvm_vcpu_arch, host_context.hstatus);
+       OFFSET(KVM_ARCH_HOST_SSCRATCH, kvm_vcpu_arch, host_sscratch);
+       OFFSET(KVM_ARCH_HOST_STVEC, kvm_vcpu_arch, host_stvec);
+       OFFSET(KVM_ARCH_HOST_SCOUNTEREN, kvm_vcpu_arch, host_scounteren);
+
        /*
         * THREAD_{F,X}* might be larger than a S-type offset can handle, but
         * these are used in performance-sensitive assembly so we can't resort
index 4732094391bfb05c0a2e1920bc39e8271ab0cced..9e8133c898dcaefef3afb83f8bce4c6034321034 100644 (file)
@@ -10,4 +10,4 @@ KVM := ../../../virt/kvm
 obj-$(CONFIG_KVM) += kvm.o
 
 kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/binary_stats.o \
-        main.o vm.o mmu.o vcpu.o vcpu_exit.o
+        main.o vm.o mmu.o vcpu.o vcpu_exit.o vcpu_switch.o
index a15d97f3e5f631cb5840777895f0a6fd11059930..64f74290a90f1398b85a6b887c60982bca8ab8be 100644 (file)
@@ -565,14 +565,40 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
 
 void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
 {
-       /* TODO: */
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+
+       csr_write(CSR_VSSTATUS, csr->vsstatus);
+       csr_write(CSR_VSIE, csr->vsie);
+       csr_write(CSR_VSTVEC, csr->vstvec);
+       csr_write(CSR_VSSCRATCH, csr->vsscratch);
+       csr_write(CSR_VSEPC, csr->vsepc);
+       csr_write(CSR_VSCAUSE, csr->vscause);
+       csr_write(CSR_VSTVAL, csr->vstval);
+       csr_write(CSR_HVIP, csr->hvip);
+       csr_write(CSR_VSATP, csr->vsatp);
 
        kvm_riscv_stage2_update_hgatp(vcpu);
+
+       vcpu->cpu = cpu;
 }
 
 void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
 {
-       /* TODO: */
+       struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+
+       vcpu->cpu = -1;
+
+       csr_write(CSR_HGATP, 0);
+
+       csr->vsstatus = csr_read(CSR_VSSTATUS);
+       csr->vsie = csr_read(CSR_VSIE);
+       csr->vstvec = csr_read(CSR_VSTVEC);
+       csr->vsscratch = csr_read(CSR_VSSCRATCH);
+       csr->vsepc = csr_read(CSR_VSEPC);
+       csr->vscause = csr_read(CSR_VSCAUSE);
+       csr->vstval = csr_read(CSR_VSTVAL);
+       csr->hvip = csr_read(CSR_HVIP);
+       csr->vsatp = csr_read(CSR_VSATP);
 }
 
 static void kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu)
diff --git a/arch/riscv/kvm/vcpu_switch.S b/arch/riscv/kvm/vcpu_switch.S
new file mode 100644 (file)
index 0000000..5174b02
--- /dev/null
@@ -0,0 +1,203 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ *     Anup Patel <anup.patel@wdc.com>
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/csr.h>
+
+       .text
+       .altmacro
+       .option norelax
+
+ENTRY(__kvm_riscv_switch_to)
+       /* Save Host GPRs (except A0 and T0-T6) */
+       REG_S   ra, (KVM_ARCH_HOST_RA)(a0)
+       REG_S   sp, (KVM_ARCH_HOST_SP)(a0)
+       REG_S   gp, (KVM_ARCH_HOST_GP)(a0)
+       REG_S   tp, (KVM_ARCH_HOST_TP)(a0)
+       REG_S   s0, (KVM_ARCH_HOST_S0)(a0)
+       REG_S   s1, (KVM_ARCH_HOST_S1)(a0)
+       REG_S   a1, (KVM_ARCH_HOST_A1)(a0)
+       REG_S   a2, (KVM_ARCH_HOST_A2)(a0)
+       REG_S   a3, (KVM_ARCH_HOST_A3)(a0)
+       REG_S   a4, (KVM_ARCH_HOST_A4)(a0)
+       REG_S   a5, (KVM_ARCH_HOST_A5)(a0)
+       REG_S   a6, (KVM_ARCH_HOST_A6)(a0)
+       REG_S   a7, (KVM_ARCH_HOST_A7)(a0)
+       REG_S   s2, (KVM_ARCH_HOST_S2)(a0)
+       REG_S   s3, (KVM_ARCH_HOST_S3)(a0)
+       REG_S   s4, (KVM_ARCH_HOST_S4)(a0)
+       REG_S   s5, (KVM_ARCH_HOST_S5)(a0)
+       REG_S   s6, (KVM_ARCH_HOST_S6)(a0)
+       REG_S   s7, (KVM_ARCH_HOST_S7)(a0)
+       REG_S   s8, (KVM_ARCH_HOST_S8)(a0)
+       REG_S   s9, (KVM_ARCH_HOST_S9)(a0)
+       REG_S   s10, (KVM_ARCH_HOST_S10)(a0)
+       REG_S   s11, (KVM_ARCH_HOST_S11)(a0)
+
+       /* Save Host and Restore Guest SSTATUS */
+       REG_L   t0, (KVM_ARCH_GUEST_SSTATUS)(a0)
+       csrrw   t0, CSR_SSTATUS, t0
+       REG_S   t0, (KVM_ARCH_HOST_SSTATUS)(a0)
+
+       /* Save Host and Restore Guest HSTATUS */
+       REG_L   t1, (KVM_ARCH_GUEST_HSTATUS)(a0)
+       csrrw   t1, CSR_HSTATUS, t1
+       REG_S   t1, (KVM_ARCH_HOST_HSTATUS)(a0)
+
+       /* Save Host and Restore Guest SCOUNTEREN */
+       REG_L   t2, (KVM_ARCH_GUEST_SCOUNTEREN)(a0)
+       csrrw   t2, CSR_SCOUNTEREN, t2
+       REG_S   t2, (KVM_ARCH_HOST_SCOUNTEREN)(a0)
+
+       /* Save Host SSCRATCH and change it to struct kvm_vcpu_arch pointer */
+       csrrw   t3, CSR_SSCRATCH, a0
+       REG_S   t3, (KVM_ARCH_HOST_SSCRATCH)(a0)
+
+       /* Save Host STVEC and change it to return path */
+       la      t4, __kvm_switch_return
+       csrrw   t4, CSR_STVEC, t4
+       REG_S   t4, (KVM_ARCH_HOST_STVEC)(a0)
+
+       /* Restore Guest SEPC */
+       REG_L   t0, (KVM_ARCH_GUEST_SEPC)(a0)
+       csrw    CSR_SEPC, t0
+
+       /* Restore Guest GPRs (except A0) */
+       REG_L   ra, (KVM_ARCH_GUEST_RA)(a0)
+       REG_L   sp, (KVM_ARCH_GUEST_SP)(a0)
+       REG_L   gp, (KVM_ARCH_GUEST_GP)(a0)
+       REG_L   tp, (KVM_ARCH_GUEST_TP)(a0)
+       REG_L   t0, (KVM_ARCH_GUEST_T0)(a0)
+       REG_L   t1, (KVM_ARCH_GUEST_T1)(a0)
+       REG_L   t2, (KVM_ARCH_GUEST_T2)(a0)
+       REG_L   s0, (KVM_ARCH_GUEST_S0)(a0)
+       REG_L   s1, (KVM_ARCH_GUEST_S1)(a0)
+       REG_L   a1, (KVM_ARCH_GUEST_A1)(a0)
+       REG_L   a2, (KVM_ARCH_GUEST_A2)(a0)
+       REG_L   a3, (KVM_ARCH_GUEST_A3)(a0)
+       REG_L   a4, (KVM_ARCH_GUEST_A4)(a0)
+       REG_L   a5, (KVM_ARCH_GUEST_A5)(a0)
+       REG_L   a6, (KVM_ARCH_GUEST_A6)(a0)
+       REG_L   a7, (KVM_ARCH_GUEST_A7)(a0)
+       REG_L   s2, (KVM_ARCH_GUEST_S2)(a0)
+       REG_L   s3, (KVM_ARCH_GUEST_S3)(a0)
+       REG_L   s4, (KVM_ARCH_GUEST_S4)(a0)
+       REG_L   s5, (KVM_ARCH_GUEST_S5)(a0)
+       REG_L   s6, (KVM_ARCH_GUEST_S6)(a0)
+       REG_L   s7, (KVM_ARCH_GUEST_S7)(a0)
+       REG_L   s8, (KVM_ARCH_GUEST_S8)(a0)
+       REG_L   s9, (KVM_ARCH_GUEST_S9)(a0)
+       REG_L   s10, (KVM_ARCH_GUEST_S10)(a0)
+       REG_L   s11, (KVM_ARCH_GUEST_S11)(a0)
+       REG_L   t3, (KVM_ARCH_GUEST_T3)(a0)
+       REG_L   t4, (KVM_ARCH_GUEST_T4)(a0)
+       REG_L   t5, (KVM_ARCH_GUEST_T5)(a0)
+       REG_L   t6, (KVM_ARCH_GUEST_T6)(a0)
+
+       /* Restore Guest A0 */
+       REG_L   a0, (KVM_ARCH_GUEST_A0)(a0)
+
+       /* Resume Guest */
+       sret
+
+       /* Back to Host */
+       .align 2
+__kvm_switch_return:
+       /* Swap Guest A0 with SSCRATCH */
+       csrrw   a0, CSR_SSCRATCH, a0
+
+       /* Save Guest GPRs (except A0) */
+       REG_S   ra, (KVM_ARCH_GUEST_RA)(a0)
+       REG_S   sp, (KVM_ARCH_GUEST_SP)(a0)
+       REG_S   gp, (KVM_ARCH_GUEST_GP)(a0)
+       REG_S   tp, (KVM_ARCH_GUEST_TP)(a0)
+       REG_S   t0, (KVM_ARCH_GUEST_T0)(a0)
+       REG_S   t1, (KVM_ARCH_GUEST_T1)(a0)
+       REG_S   t2, (KVM_ARCH_GUEST_T2)(a0)
+       REG_S   s0, (KVM_ARCH_GUEST_S0)(a0)
+       REG_S   s1, (KVM_ARCH_GUEST_S1)(a0)
+       REG_S   a1, (KVM_ARCH_GUEST_A1)(a0)
+       REG_S   a2, (KVM_ARCH_GUEST_A2)(a0)
+       REG_S   a3, (KVM_ARCH_GUEST_A3)(a0)
+       REG_S   a4, (KVM_ARCH_GUEST_A4)(a0)
+       REG_S   a5, (KVM_ARCH_GUEST_A5)(a0)
+       REG_S   a6, (KVM_ARCH_GUEST_A6)(a0)
+       REG_S   a7, (KVM_ARCH_GUEST_A7)(a0)
+       REG_S   s2, (KVM_ARCH_GUEST_S2)(a0)
+       REG_S   s3, (KVM_ARCH_GUEST_S3)(a0)
+       REG_S   s4, (KVM_ARCH_GUEST_S4)(a0)
+       REG_S   s5, (KVM_ARCH_GUEST_S5)(a0)
+       REG_S   s6, (KVM_ARCH_GUEST_S6)(a0)
+       REG_S   s7, (KVM_ARCH_GUEST_S7)(a0)
+       REG_S   s8, (KVM_ARCH_GUEST_S8)(a0)
+       REG_S   s9, (KVM_ARCH_GUEST_S9)(a0)
+       REG_S   s10, (KVM_ARCH_GUEST_S10)(a0)
+       REG_S   s11, (KVM_ARCH_GUEST_S11)(a0)
+       REG_S   t3, (KVM_ARCH_GUEST_T3)(a0)
+       REG_S   t4, (KVM_ARCH_GUEST_T4)(a0)
+       REG_S   t5, (KVM_ARCH_GUEST_T5)(a0)
+       REG_S   t6, (KVM_ARCH_GUEST_T6)(a0)
+
+       /* Save Guest SEPC */
+       csrr    t0, CSR_SEPC
+       REG_S   t0, (KVM_ARCH_GUEST_SEPC)(a0)
+
+       /* Restore Host STVEC */
+       REG_L   t1, (KVM_ARCH_HOST_STVEC)(a0)
+       csrw    CSR_STVEC, t1
+
+       /* Save Guest A0 and Restore Host SSCRATCH */
+       REG_L   t2, (KVM_ARCH_HOST_SSCRATCH)(a0)
+       csrrw   t2, CSR_SSCRATCH, t2
+       REG_S   t2, (KVM_ARCH_GUEST_A0)(a0)
+
+       /* Save Guest and Restore Host SCOUNTEREN */
+       REG_L   t3, (KVM_ARCH_HOST_SCOUNTEREN)(a0)
+       csrrw   t3, CSR_SCOUNTEREN, t3
+       REG_S   t3, (KVM_ARCH_GUEST_SCOUNTEREN)(a0)
+
+       /* Save Guest and Restore Host HSTATUS */
+       REG_L   t4, (KVM_ARCH_HOST_HSTATUS)(a0)
+       csrrw   t4, CSR_HSTATUS, t4
+       REG_S   t4, (KVM_ARCH_GUEST_HSTATUS)(a0)
+
+       /* Save Guest and Restore Host SSTATUS */
+       REG_L   t5, (KVM_ARCH_HOST_SSTATUS)(a0)
+       csrrw   t5, CSR_SSTATUS, t5
+       REG_S   t5, (KVM_ARCH_GUEST_SSTATUS)(a0)
+
+       /* Restore Host GPRs (except A0 and T0-T6) */
+       REG_L   ra, (KVM_ARCH_HOST_RA)(a0)
+       REG_L   sp, (KVM_ARCH_HOST_SP)(a0)
+       REG_L   gp, (KVM_ARCH_HOST_GP)(a0)
+       REG_L   tp, (KVM_ARCH_HOST_TP)(a0)
+       REG_L   s0, (KVM_ARCH_HOST_S0)(a0)
+       REG_L   s1, (KVM_ARCH_HOST_S1)(a0)
+       REG_L   a1, (KVM_ARCH_HOST_A1)(a0)
+       REG_L   a2, (KVM_ARCH_HOST_A2)(a0)
+       REG_L   a3, (KVM_ARCH_HOST_A3)(a0)
+       REG_L   a4, (KVM_ARCH_HOST_A4)(a0)
+       REG_L   a5, (KVM_ARCH_HOST_A5)(a0)
+       REG_L   a6, (KVM_ARCH_HOST_A6)(a0)
+       REG_L   a7, (KVM_ARCH_HOST_A7)(a0)
+       REG_L   s2, (KVM_ARCH_HOST_S2)(a0)
+       REG_L   s3, (KVM_ARCH_HOST_S3)(a0)
+       REG_L   s4, (KVM_ARCH_HOST_S4)(a0)
+       REG_L   s5, (KVM_ARCH_HOST_S5)(a0)
+       REG_L   s6, (KVM_ARCH_HOST_S6)(a0)
+       REG_L   s7, (KVM_ARCH_HOST_S7)(a0)
+       REG_L   s8, (KVM_ARCH_HOST_S8)(a0)
+       REG_L   s9, (KVM_ARCH_HOST_S9)(a0)
+       REG_L   s10, (KVM_ARCH_HOST_S10)(a0)
+       REG_L   s11, (KVM_ARCH_HOST_S11)(a0)
+
+       /* Return to C code */
+       ret
+ENDPROC(__kvm_riscv_switch_to)