This series adds KVM LoongArch support. Loongson 3A5000 supports hardware
assisted virtualization. With cpu virtualization, there are separate
hw-supported user mode and kernel mode in guest mode. With memory
virtualization, there are two-level hw mmu table for guest mode and host
mode. Also there is separate hw cpu timer with consant frequency in
guest mode, so that vm can migrate between hosts with different freq.
Currently, we are able to boot LoongArch Linux Guests.
Few key aspects of KVM LoongArch added by this series are:
1. Enable kvm hardware function when kvm module is loaded.
2. Implement VM and vcpu related ioctl interface such as vcpu create,
vcpu run etc. GET_ONE_REG/SET_ONE_REG ioctl commands are use to
get general registers one by one.
3. Hardware access about MMU, timer and csr are emulated in kernel.
4. Hardwares such as mmio and iocsr device are emulated in user space
such as APIC, IPI, pci devices etc.
The running environment of LoongArch virt machine:
1. Cross tools to build kernel and uefi:
$ wget https://github.com/loongson/build-tools/releases/download/2022.09.06/loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz
tar -vxf loongarch64-clfs-6.3-cross-tools-gcc-glibc.tar.xz -C /opt
export PATH=/opt/cross-tools/bin:$PATH
export LD_LIBRARY_PATH=/opt/cross-tools/lib:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/opt/cross-tools/loongarch64-unknown-linux-gnu/lib/:$LD_LIBRARY_PATH
2. This series is based on the linux source code:
https://github.com/loongson/linux-loongarch-kvm
Build command:
git checkout kvm-loongarch
make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu- loongson3_defconfig
make ARCH=loongarch CROSS_COMPILE=loongarch64-unknown-linux-gnu-
3. QEMU hypervisor with LoongArch supported:
https://github.com/loongson/qemu
Build command:
git checkout kvm-loongarch
./configure --target-list="loongarch64-softmmu" --enable-kvm
make
4. Uefi bios of LoongArch virt machine:
Link: https://github.com/tianocore/edk2-platforms/tree/master/Platform/Loongson/LoongArchQemuPkg#readme
5. you can also access the binary files we have already build:
https://github.com/yangxiaojuan-loongson/qemu-binary
The command to boot loongarch virt machine:
$ qemu-system-loongarch64 -machine virt -m 4G -cpu la464 \
-smp 1 -bios QEMU_EFI.fd -kernel vmlinuz.efi -initrd ramdisk \
-serial stdio -monitor telnet:localhost:4495,server,nowait \
-append "root=/dev/ram rdinit=/sbin/init console=ttyS0,115200" \
--nographic
Changes for v6:
1. Fix the Documentation/virt/kvm/api.rst compile warning about
loongarch parts.
Changes for v5:
1. Implement get/set mp_state ioctl interface, and only the
KVM_MP_STATE_RUNNABLE state is supported now, and other states
will be completed in the future. The state is also used when vcpu
run idle instruction, if vcpu state is changed to RUNNABLE, the
vcpu will have the possibility to be woken up.
2. Supplement kvm document about loongarch-specific part, such as add
api introduction for GET/SET_ONE_REG, GET/SET_FPU, GET/SET_MP_STATE,
etc.
3. Improve the kvm_switch_to_guest function in switch.S, remove the
previous tmp,tmp1 arguments and replace it with t0,t1 reg.
Changes for v4:
1. Add a csr_need_update flag in _vcpu_put, as most csr registers keep
unchanged during process context switch, so we need not to update it
every time. We can do this only if the soft csr is different form hardware.
That is to say all of csrs should update after vcpu enter guest, as for
set_csr_ioctl, we have written soft csr to keep consistent with hardware.
2. Improve get/set_csr_ioctl interface, we set SW or HW or INVALID flag
for all csrs according to it's features when kvm init. In get/set_csr_ioctl,
if csr is HW, we use gcsrrd/ gcsrwr instruction to access it, else if csr is
SW, we use software to emulate it, and others return false.
3. Add set_hw_gcsr function in csr_ops.S, and it is used in set_csr_ioctl.
We have splited hw gcsr into three parts, so we can calculate the code offset
by gcsrid and jump here to run the gcsrwr instruction. We use this function to
make the code easier and avoid to use the previous SET_HW_GCSR(XXX) interface.
4. Improve kvm mmu functions, such as flush page table and make clean page table
interface.
Changes for v3:
1. Remove the vpid array list in kvm_vcpu_arch and use a vpid variable here,
because a vpid will never be recycled if a vCPU migrates from physical CPU A
to B and back to A.
2. Make some constant variables in kvm_context to global such as vpid_mask,
guest_eentry, enter_guest, etc.
3. Add some new tracepoints, such as kvm_trace_idle, kvm_trace_cache,
kvm_trace_gspr, etc.
4. There are some duplicate codes in kvm_handle_exit and kvm_vcpu_run,
so we move it to a new function kvm_pre_enter_guest.
5. Change the RESUME_HOST, RESUME_GUEST value, return 1 for resume guest
and "<= 0" for resume host.
6. Fcsr and fpu registers are saved/restored together.
Changes for v2:
1. Seprate the original patch-01 and patch-03 into small patches, and the
patches mainly contain kvm module init, module exit, vcpu create, vcpu run,
etc.
2. Remove the original KVM_{GET,SET}_CSRS ioctl in the kvm uapi header,
and we use the common KVM_{GET,SET}_ONE_REG to access register.
3. Use BIT(x) to replace the "1 << n_bits" statement.
Tianrui Zhao (30):
LoongArch: KVM: Add kvm related header files
LoongArch: KVM: Implement kvm module related interface
LoongArch: KVM: Implement kvm hardware enable, disable interface
LoongArch: KVM: Implement VM related functions
LoongArch: KVM: Add vcpu related header files
LoongArch: KVM: Implement vcpu create and destroy interface
LoongArch: KVM: Implement vcpu run interface
LoongArch: KVM: Implement vcpu handle exit interface
LoongArch: KVM: Implement vcpu get, vcpu set registers
LoongArch: KVM: Implement vcpu ENABLE_CAP ioctl interface
LoongArch: KVM: Implement fpu related operations for vcpu
LoongArch: KVM: Implement vcpu interrupt operations
LoongArch: KVM: Implement misc vcpu related interfaces
LoongArch: KVM: Implement vcpu load and vcpu put operations
LoongArch: KVM: Implement vcpu status description
LoongArch: KVM: Implement update VM id function
LoongArch: KVM: Implement virtual machine tlb operations
LoongArch: KVM: Implement vcpu timer operations
LoongArch: KVM: Implement kvm mmu operations
LoongArch: KVM: Implement handle csr excption
LoongArch: KVM: Implement handle iocsr exception
LoongArch: KVM: Implement handle idle exception
LoongArch: KVM: Implement handle gspr exception
LoongArch: KVM: Implement handle mmio exception
LoongArch: KVM: Implement handle fpu exception
LoongArch: KVM: Implement kvm exception vector
LoongArch: KVM: Implement vcpu world switch
LoongArch: KVM: Implement probe virtualization when loongarch cpu init
LoongArch: KVM: Enable kvm config and add the makefile
LoongArch: KVM: Supplement kvm document about loongarch-specific part
Documentation/virt/kvm/api.rst | 71 +-
arch/loongarch/Kbuild | 1 +
arch/loongarch/Kconfig | 2 +
arch/loongarch/configs/loongson3_defconfig | 2 +
arch/loongarch/include/asm/cpu-features.h | 22 +
arch/loongarch/include/asm/cpu-info.h | 13 +
arch/loongarch/include/asm/inst.h | 16 +
arch/loongarch/include/asm/kvm_csr.h | 55 ++
arch/loongarch/include/asm/kvm_host.h | 268 +++++++
arch/loongarch/include/asm/kvm_types.h | 11 +
arch/loongarch/include/asm/kvm_vcpu.h | 114 +++
arch/loongarch/include/asm/loongarch.h | 209 ++++-
arch/loongarch/include/uapi/asm/kvm.h | 107 +++
arch/loongarch/kernel/asm-offsets.c | 32 +
arch/loongarch/kernel/cpu-probe.c | 53 ++
arch/loongarch/kvm/Kconfig | 38 +
arch/loongarch/kvm/Makefile | 22 +
arch/loongarch/kvm/csr_ops.S | 76 ++
arch/loongarch/kvm/exit.c | 709 +++++++++++++++++
arch/loongarch/kvm/interrupt.c | 126 +++
arch/loongarch/kvm/main.c | 340 ++++++++
arch/loongarch/kvm/mmu.c | 730 +++++++++++++++++
arch/loongarch/kvm/switch.S | 301 +++++++
arch/loongarch/kvm/timer.c | 266 +++++++
arch/loongarch/kvm/tlb.c | 31 +
arch/loongarch/kvm/trace.h | 160 ++++
arch/loongarch/kvm/vcpu.c | 882 +++++++++++++++++++++
arch/loongarch/kvm/vm.c | 79 ++
arch/loongarch/kvm/vmid.c | 65 ++
include/uapi/linux/kvm.h | 9 +
30 files changed, 4795 insertions(+), 15 deletions(-)
create mode 100644 arch/loongarch/include/asm/kvm_csr.h
create mode 100644 arch/loongarch/include/asm/kvm_host.h
create mode 100644 arch/loongarch/include/asm/kvm_types.h
create mode 100644 arch/loongarch/include/asm/kvm_vcpu.h
create mode 100644 arch/loongarch/include/uapi/asm/kvm.h
create mode 100644 arch/loongarch/kvm/Kconfig
create mode 100644 arch/loongarch/kvm/Makefile
create mode 100644 arch/loongarch/kvm/csr_ops.S
create mode 100644 arch/loongarch/kvm/exit.c
create mode 100644 arch/loongarch/kvm/interrupt.c
create mode 100644 arch/loongarch/kvm/main.c
create mode 100644 arch/loongarch/kvm/mmu.c
create mode 100644 arch/loongarch/kvm/switch.S
create mode 100644 arch/loongarch/kvm/timer.c
create mode 100644 arch/loongarch/kvm/tlb.c
create mode 100644 arch/loongarch/kvm/trace.h
create mode 100644 arch/loongarch/kvm/vcpu.c
create mode 100644 arch/loongarch/kvm/vm.c
create mode 100644 arch/loongarch/kvm/vmid.c
--
2.31.1
Implement vcpu run interface, handling mmio, iocsr reading fault
and deliver interrupt, lose fpu before vcpu enter guest.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vcpu.c | 86 +++++++++++++++++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 7af2ff64b8c7..bc9e3889fee1 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -18,6 +18,44 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
return 0;
}
+/* Returns 1 if the guest TLB may be clobbered */
+static int _kvm_check_requests(struct kvm_vcpu *vcpu, int cpu)
+{
+ int ret = 0;
+
+ if (!kvm_request_pending(vcpu))
+ return 0;
+
+ if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu)) {
+ /* Drop vpid for this VCPU */
+ vcpu->arch.vpid = 0;
+ /* This will clobber guest TLB contents too */
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static void kvm_pre_enter_guest(struct kvm_vcpu *vcpu)
+{
+ int cpu;
+
+ /*
+ * handle vcpu timer, interrupts, check requests and
+ * check vmid before vcpu enter guest
+ */
+ kvm_acquire_timer(vcpu);
+ _kvm_deliver_intr(vcpu);
+ /* make sure the vcpu mode has been written */
+ smp_store_mb(vcpu->mode, IN_GUEST_MODE);
+ cpu = smp_processor_id();
+ _kvm_check_requests(vcpu, cpu);
+ _kvm_check_vmid(vcpu, cpu);
+ vcpu->arch.host_eentry = csr_read64(LOONGARCH_CSR_EENTRY);
+ /* clear KVM_LARCH_CSR as csr will change when enter guest */
+ vcpu->arch.aux_inuse &= ~KVM_LARCH_CSR;
+}
+
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
{
unsigned long timer_hz;
@@ -87,3 +125,51 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
context->last_vcpu = NULL;
}
}
+
+int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
+{
+ int r = -EINTR;
+ struct kvm_run *run = vcpu->run;
+
+ vcpu_load(vcpu);
+
+ kvm_sigset_activate(vcpu);
+
+ if (vcpu->mmio_needed) {
+ if (!vcpu->mmio_is_write)
+ _kvm_complete_mmio_read(vcpu, run);
+ vcpu->mmio_needed = 0;
+ }
+
+ if (run->exit_reason == KVM_EXIT_LOONGARCH_IOCSR) {
+ if (!run->iocsr_io.is_write)
+ _kvm_complete_iocsr_read(vcpu, run);
+ }
+
+ /* clear exit_reason */
+ run->exit_reason = KVM_EXIT_UNKNOWN;
+ if (run->immediate_exit)
+ goto out;
+
+ lose_fpu(1);
+
+ local_irq_disable();
+ guest_timing_enter_irqoff();
+
+ kvm_pre_enter_guest(vcpu);
+ trace_kvm_enter(vcpu);
+
+ guest_state_enter_irqoff();
+ r = kvm_loongarch_ops->enter_guest(run, vcpu);
+
+ /* guest_state_exit_irqoff() already done. */
+ trace_kvm_out(vcpu);
+ guest_timing_exit_irqoff();
+ local_irq_enable();
+
+out:
+ kvm_sigset_deactivate(vcpu);
+
+ vcpu_put(vcpu);
+ return r;
+}
--
2.31.1
Implement vcpu handle exit interface, getting the exit code by ESTAT
register and using kvm exception vector to handle it.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vcpu.c | 46 +++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index bc9e3889fee1..651ae5ca0691 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -56,6 +56,52 @@ static void kvm_pre_enter_guest(struct kvm_vcpu *vcpu)
vcpu->arch.aux_inuse &= ~KVM_LARCH_CSR;
}
+/*
+ * Return 1 for resume guest and "<= 0" for resume host.
+ */
+static int _kvm_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu)
+{
+ unsigned long exst = vcpu->arch.host_estat;
+ u32 intr = exst & 0x1fff; /* ignore NMI */
+ u32 exccode = (exst & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT;
+ int ret = RESUME_GUEST;
+
+ vcpu->mode = OUTSIDE_GUEST_MODE;
+
+ /* Set a default exit reason */
+ run->exit_reason = KVM_EXIT_UNKNOWN;
+ run->ready_for_interrupt_injection = 1;
+
+ local_irq_enable();
+ guest_state_exit_irqoff();
+
+ trace_kvm_exit(vcpu, exccode);
+ if (exccode) {
+ ret = _kvm_handle_fault(vcpu, exccode);
+ } else {
+ WARN(!intr, "suspicious vm exiting");
+ ++vcpu->stat.int_exits;
+ }
+
+ cond_resched();
+ local_irq_disable();
+
+ if (ret == RESUME_HOST)
+ return ret;
+
+ /* Only check for signals if not already exiting to userspace */
+ if (signal_pending(current)) {
+ vcpu->run->exit_reason = KVM_EXIT_INTR;
+ ++vcpu->stat.signal_exits;
+ return -EINTR;
+ }
+
+ kvm_pre_enter_guest(vcpu);
+ trace_kvm_reenter(vcpu);
+ guest_state_enter_irqoff();
+ return RESUME_GUEST;
+}
+
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
{
unsigned long timer_hz;
--
2.31.1
Add LoongArch vcpu related header files, including vcpu csr
information, irq number defines, and some vcpu interfaces.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/include/asm/cpu-info.h | 13 ++
arch/loongarch/include/asm/kvm_vcpu.h | 114 ++++++++++++++
arch/loongarch/include/asm/loongarch.h | 209 ++++++++++++++++++++++++-
arch/loongarch/kvm/trace.h | 160 +++++++++++++++++++
4 files changed, 490 insertions(+), 6 deletions(-)
create mode 100644 arch/loongarch/include/asm/kvm_vcpu.h
create mode 100644 arch/loongarch/kvm/trace.h
diff --git a/arch/loongarch/include/asm/cpu-info.h b/arch/loongarch/include/asm/cpu-info.h
index cd73a6f57fe3..1b426a2ca5dc 100644
--- a/arch/loongarch/include/asm/cpu-info.h
+++ b/arch/loongarch/include/asm/cpu-info.h
@@ -32,6 +32,15 @@ struct cache_desc {
#define CACHE_LEVEL_MAX 3
#define CACHE_LEAVES_MAX 6
+struct guest_info {
+ unsigned long ases;
+ unsigned long ases_dyn;
+ unsigned long options;
+ unsigned long options_dyn;
+ unsigned char conf;
+ unsigned int kscratch_mask;
+};
+
struct cpuinfo_loongarch {
u64 asid_cache;
unsigned long asid_mask;
@@ -60,6 +69,10 @@ struct cpuinfo_loongarch {
unsigned int watch_dreg_count; /* Number data breakpoints */
unsigned int watch_ireg_count; /* Number instruction breakpoints */
unsigned int watch_reg_use_cnt; /* min(NUM_WATCH_REGS, watch_dreg_count + watch_ireg_count), Usable by ptrace */
+
+ /* VZ & Guest features */
+ struct guest_info guest;
+ unsigned long guest_cfg;
} __aligned(SMP_CACHE_BYTES);
extern struct cpuinfo_loongarch cpu_data[];
diff --git a/arch/loongarch/include/asm/kvm_vcpu.h b/arch/loongarch/include/asm/kvm_vcpu.h
new file mode 100644
index 000000000000..5a53cfcb3f4c
--- /dev/null
+++ b/arch/loongarch/include/asm/kvm_vcpu.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_LOONGARCH_KVM_VCPU_H__
+#define __ASM_LOONGARCH_KVM_VCPU_H__
+
+#include <linux/kvm_host.h>
+#include <asm/loongarch.h>
+#include <asm/kvm_host.h>
+
+#define LARCH_INT_SIP0 (EXCCODE_SIP0 - EXCCODE_INT_START)
+#define LARCH_INT_SIP1 (EXCCODE_SIP1 - EXCCODE_INT_START)
+#define LARCH_INT_IP0 (EXCCODE_IP0 - EXCCODE_INT_START)
+#define LARCH_INT_IP1 (EXCCODE_IP1 - EXCCODE_INT_START)
+#define LARCH_INT_IP2 (EXCCODE_IP2 - EXCCODE_INT_START)
+#define LARCH_INT_IP3 (EXCCODE_IP3 - EXCCODE_INT_START)
+#define LARCH_INT_IP4 (EXCCODE_IP4 - EXCCODE_INT_START)
+#define LARCH_INT_IP5 (EXCCODE_IP5 - EXCCODE_INT_START)
+#define LARCH_INT_IP6 (EXCCODE_IP6 - EXCCODE_INT_START)
+#define LARCH_INT_IP7 (EXCCODE_IP7 - EXCCODE_INT_START)
+#define LARCH_INT_PMU (EXCCODE_PMC - EXCCODE_INT_START)
+#define LARCH_INT_TIMER (EXCCODE_TIMER - EXCCODE_INT_START)
+#define LARCH_INT_IPI (EXCCODE_IPI - EXCCODE_INT_START)
+#define LOONGARCH_EXC_MAX (LARCH_INT_IPI + 1)
+#define LOONGARCH_EXC_IPNUM (LOONGARCH_EXC_MAX)
+
+/* Controlled by 0x5 guest exst */
+#define CPU_SIP0 (_ULCAST_(1))
+#define CPU_SIP1 (_ULCAST_(1) << 1)
+#define CPU_PMU (_ULCAST_(1) << 10)
+#define CPU_TIMER (_ULCAST_(1) << 11)
+#define CPU_IPI (_ULCAST_(1) << 12)
+
+/* Controlled by 0x52 guest exception VIP
+ * aligned to exst bit 5~12
+ */
+#define CPU_IP0 (_ULCAST_(1))
+#define CPU_IP1 (_ULCAST_(1) << 1)
+#define CPU_IP2 (_ULCAST_(1) << 2)
+#define CPU_IP3 (_ULCAST_(1) << 3)
+#define CPU_IP4 (_ULCAST_(1) << 4)
+#define CPU_IP5 (_ULCAST_(1) << 5)
+#define CPU_IP6 (_ULCAST_(1) << 6)
+#define CPU_IP7 (_ULCAST_(1) << 7)
+
+#define MNSEC_PER_SEC (NSEC_PER_SEC >> 20)
+
+/* KVM_IRQ_LINE irq field index values */
+#define KVM_LOONGSON_IRQ_TYPE_SHIFT 24
+#define KVM_LOONGSON_IRQ_TYPE_MASK 0xff
+#define KVM_LOONGSON_IRQ_VCPU_SHIFT 16
+#define KVM_LOONGSON_IRQ_VCPU_MASK 0xff
+#define KVM_LOONGSON_IRQ_NUM_SHIFT 0
+#define KVM_LOONGSON_IRQ_NUM_MASK 0xffff
+
+/* irq_type field */
+#define KVM_LOONGSON_IRQ_TYPE_CPU_IP 0
+#define KVM_LOONGSON_IRQ_TYPE_CPU_IO 1
+#define KVM_LOONGSON_IRQ_TYPE_HT 2
+#define KVM_LOONGSON_IRQ_TYPE_MSI 3
+#define KVM_LOONGSON_IRQ_TYPE_IOAPIC 4
+#define KVM_LOONGSON_IRQ_TYPE_ROUTE 5
+
+/* out-of-kernel GIC cpu interrupt injection irq_number field */
+#define KVM_LOONGSON_IRQ_CPU_IRQ 0
+#define KVM_LOONGSON_IRQ_CPU_FIQ 1
+#define KVM_LOONGSON_CPU_IP_NUM 8
+
+typedef union loongarch_instruction larch_inst;
+typedef int (*exit_handle_fn)(struct kvm_vcpu *);
+
+int _kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst);
+int _kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst);
+int _kvm_complete_mmio_read(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int _kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run);
+int _kvm_emu_idle(struct kvm_vcpu *vcpu);
+int _kvm_handle_pv_hcall(struct kvm_vcpu *vcpu);
+int _kvm_pending_timer(struct kvm_vcpu *vcpu);
+int _kvm_handle_fault(struct kvm_vcpu *vcpu, int fault);
+void _kvm_deliver_intr(struct kvm_vcpu *vcpu);
+
+void kvm_own_fpu(struct kvm_vcpu *vcpu);
+void kvm_lose_fpu(struct kvm_vcpu *vcpu);
+void kvm_save_fpu(struct loongarch_fpu *fpu);
+void kvm_restore_fpu(struct loongarch_fpu *fpu);
+void kvm_restore_fcsr(struct loongarch_fpu *fpu);
+
+void kvm_acquire_timer(struct kvm_vcpu *vcpu);
+void kvm_reset_timer(struct kvm_vcpu *vcpu);
+enum hrtimer_restart kvm_count_timeout(struct kvm_vcpu *vcpu);
+void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long hz);
+void kvm_restore_timer(struct kvm_vcpu *vcpu);
+void kvm_save_timer(struct kvm_vcpu *vcpu);
+
+int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
+ struct kvm_loongarch_interrupt *irq);
+/*
+ * Loongarch KVM guest interrupt handling.
+ */
+static inline void _kvm_queue_irq(struct kvm_vcpu *vcpu, unsigned int irq)
+{
+ set_bit(irq, &vcpu->arch.irq_pending);
+ clear_bit(irq, &vcpu->arch.irq_clear);
+}
+
+static inline void _kvm_dequeue_irq(struct kvm_vcpu *vcpu, unsigned int irq)
+{
+ clear_bit(irq, &vcpu->arch.irq_pending);
+ set_bit(irq, &vcpu->arch.irq_clear);
+}
+
+#endif /* __ASM_LOONGARCH_KVM_VCPU_H__ */
diff --git a/arch/loongarch/include/asm/loongarch.h b/arch/loongarch/include/asm/loongarch.h
index 65b7dcdea16d..aa8e14acd8bc 100644
--- a/arch/loongarch/include/asm/loongarch.h
+++ b/arch/loongarch/include/asm/loongarch.h
@@ -236,6 +236,58 @@ static __always_inline u64 csr_xchg64(u64 val, u64 mask, u32 reg)
return __csrxchg_d(val, mask, reg);
}
+/* GCSR */
+static inline u64 gcsr_read(u32 reg)
+{
+ u64 val = 0;
+ /* Instructions will be available in binutils later */
+ asm volatile (
+ "parse_r __reg, %[val]\n\t"
+ /*
+ * read val from guest csr register %[reg]
+ * gcsrrd %[val], %[reg]
+ */
+ ".word 0x5 << 24 | %[reg] << 10 | 0 << 5 | __reg\n\t"
+ : [val] "+r" (val)
+ : [reg] "i" (reg)
+ : "memory");
+
+ return val;
+}
+
+static inline void gcsr_write(u64 val, u32 reg)
+{
+ /* Instructions will be available in binutils later */
+ asm volatile (
+ "parse_r __reg, %[val]\n\t"
+ /*
+ * write val to guest csr register %[reg]
+ * gcsrwr %[val], %[reg]
+ */
+ ".word 0x5 << 24 | %[reg] << 10 | 1 << 5 | __reg\n\t"
+ : [val] "+r" (val)
+ : [reg] "i" (reg)
+ : "memory");
+}
+
+static inline u64 gcsr_xchg(u64 val, u64 mask, u32 reg)
+{
+ /* Instructions will be available in binutils later */
+ asm volatile (
+ "parse_r __rd, %[val]\n\t"
+ "parse_r __rj, %[mask]\n\t"
+ /*
+ * replace masked bits of guest csr register %[reg] with val
+ * gcsrxchg %[val], %[mask], %[reg]
+ */
+ ".word 0x5 << 24 | %[reg] << 10 | __rj << 5 | __rd\n\t"
+ : [val] "+r" (val)
+ : [mask] "r" (mask), [reg] "i" (reg)
+ : "memory");
+
+ return val;
+}
+
/* IOCSR */
static __always_inline u32 iocsr_read32(u32 reg)
{
@@ -309,6 +361,7 @@ static __always_inline void iocsr_write64(u64 val, u32 reg)
#define LOONGARCH_CSR_ECFG 0x4 /* Exception config */
#define CSR_ECFG_VS_SHIFT 16
#define CSR_ECFG_VS_WIDTH 3
+#define CSR_ECFG_VS_SHIFT_END (CSR_ECFG_VS_SHIFT + CSR_ECFG_VS_WIDTH - 1)
#define CSR_ECFG_VS (_ULCAST_(0x7) << CSR_ECFG_VS_SHIFT)
#define CSR_ECFG_IM_SHIFT 0
#define CSR_ECFG_IM_WIDTH 13
@@ -397,13 +450,14 @@ static __always_inline void iocsr_write64(u64 val, u32 reg)
#define CSR_TLBLO1_V (_ULCAST_(0x1) << CSR_TLBLO1_V_SHIFT)
#define LOONGARCH_CSR_GTLBC 0x15 /* Guest TLB control */
-#define CSR_GTLBC_RID_SHIFT 16
-#define CSR_GTLBC_RID_WIDTH 8
-#define CSR_GTLBC_RID (_ULCAST_(0xff) << CSR_GTLBC_RID_SHIFT)
+#define CSR_GTLBC_TGID_SHIFT 16
+#define CSR_GTLBC_TGID_WIDTH 8
+#define CSR_GTLBC_TGID_SHIFT_END (CSR_GTLBC_TGID_SHIFT + CSR_GTLBC_TGID_WIDTH - 1)
+#define CSR_GTLBC_TGID (_ULCAST_(0xff) << CSR_GTLBC_TGID_SHIFT)
#define CSR_GTLBC_TOTI_SHIFT 13
#define CSR_GTLBC_TOTI (_ULCAST_(0x1) << CSR_GTLBC_TOTI_SHIFT)
-#define CSR_GTLBC_USERID_SHIFT 12
-#define CSR_GTLBC_USERID (_ULCAST_(0x1) << CSR_GTLBC_USERID_SHIFT)
+#define CSR_GTLBC_USETGID_SHIFT 12
+#define CSR_GTLBC_USETGID (_ULCAST_(0x1) << CSR_GTLBC_USETGID_SHIFT)
#define CSR_GTLBC_GMTLBSZ_SHIFT 0
#define CSR_GTLBC_GMTLBSZ_WIDTH 6
#define CSR_GTLBC_GMTLBSZ (_ULCAST_(0x3f) << CSR_GTLBC_GMTLBSZ_SHIFT)
@@ -555,6 +609,7 @@ static __always_inline void iocsr_write64(u64 val, u32 reg)
#define LOONGARCH_CSR_GSTAT 0x50 /* Guest status */
#define CSR_GSTAT_GID_SHIFT 16
#define CSR_GSTAT_GID_WIDTH 8
+#define CSR_GSTAT_GID_SHIFT_END (CSR_GSTAT_GID_SHIFT + CSR_GSTAT_GID_WIDTH - 1)
#define CSR_GSTAT_GID (_ULCAST_(0xff) << CSR_GSTAT_GID_SHIFT)
#define CSR_GSTAT_GIDBIT_SHIFT 4
#define CSR_GSTAT_GIDBIT_WIDTH 6
@@ -605,6 +660,12 @@ static __always_inline void iocsr_write64(u64 val, u32 reg)
#define CSR_GCFG_MATC_GUEST (_ULCAST_(0x0) << CSR_GCFG_MATC_SHITF)
#define CSR_GCFG_MATC_ROOT (_ULCAST_(0x1) << CSR_GCFG_MATC_SHITF)
#define CSR_GCFG_MATC_NEST (_ULCAST_(0x2) << CSR_GCFG_MATC_SHITF)
+#define CSR_GCFG_MATP_SHITF 0
+#define CSR_GCFG_MATP_WIDTH 4
+#define CSR_GCFG_MATP_MASK (_ULCAST_(0x3) << CSR_GCFG_MATP_SHITF)
+#define CSR_GCFG_MATP_GUEST (_ULCAST_(0x0) << CSR_GCFG_MATP_SHITF)
+#define CSR_GCFG_MATP_ROOT (_ULCAST_(0x1) << CSR_GCFG_MATP_SHITF)
+#define CSR_GCFG_MATP_NEST (_ULCAST_(0x2) << CSR_GCFG_MATP_SHITF)
#define LOONGARCH_CSR_GINTC 0x52 /* Guest interrupt control */
#define CSR_GINTC_HC_SHIFT 16
@@ -1276,6 +1337,131 @@ static inline void write_csr_tlbrefill_pagesize(unsigned int size)
#define write_csr_perfctrl3(val) csr_write64(val, LOONGARCH_CSR_PERFCTRL3)
#define write_csr_perfcntr3(val) csr_write64(val, LOONGARCH_CSR_PERFCNTR3)
+/* Guest related CSRS */
+#define read_csr_gtlbc() csr_read64(LOONGARCH_CSR_GTLBC)
+#define write_csr_gtlbc(val) csr_write64(val, LOONGARCH_CSR_GTLBC)
+#define read_csr_trgp() csr_read64(LOONGARCH_CSR_TRGP)
+#define read_csr_gcfg() csr_read64(LOONGARCH_CSR_GCFG)
+#define write_csr_gcfg(val) csr_write64(val, LOONGARCH_CSR_GCFG)
+#define read_csr_gstat() csr_read64(LOONGARCH_CSR_GSTAT)
+#define write_csr_gstat(val) csr_write64(val, LOONGARCH_CSR_GSTAT)
+#define read_csr_gintc() csr_read64(LOONGARCH_CSR_GINTC)
+#define write_csr_gintc(val) csr_write64(val, LOONGARCH_CSR_GINTC)
+#define read_csr_gcntc() csr_read64(LOONGARCH_CSR_GCNTC)
+#define write_csr_gcntc(val) csr_write64(val, LOONGARCH_CSR_GCNTC)
+
+/* Guest CSRS read and write */
+#define read_gcsr_crmd() gcsr_read(LOONGARCH_CSR_CRMD)
+#define write_gcsr_crmd(val) gcsr_write(val, LOONGARCH_CSR_CRMD)
+#define read_gcsr_prmd() gcsr_read(LOONGARCH_CSR_PRMD)
+#define write_gcsr_prmd(val) gcsr_write(val, LOONGARCH_CSR_PRMD)
+#define read_gcsr_euen() gcsr_read(LOONGARCH_CSR_EUEN)
+#define write_gcsr_euen(val) gcsr_write(val, LOONGARCH_CSR_EUEN)
+#define read_gcsr_misc() gcsr_read(LOONGARCH_CSR_MISC)
+#define write_gcsr_misc(val) gcsr_write(val, LOONGARCH_CSR_MISC)
+#define read_gcsr_ecfg() gcsr_read(LOONGARCH_CSR_ECFG)
+#define write_gcsr_ecfg(val) gcsr_write(val, LOONGARCH_CSR_ECFG)
+#define read_gcsr_estat() gcsr_read(LOONGARCH_CSR_ESTAT)
+#define write_gcsr_estat(val) gcsr_write(val, LOONGARCH_CSR_ESTAT)
+#define read_gcsr_era() gcsr_read(LOONGARCH_CSR_ERA)
+#define write_gcsr_era(val) gcsr_write(val, LOONGARCH_CSR_ERA)
+#define read_gcsr_badv() gcsr_read(LOONGARCH_CSR_BADV)
+#define write_gcsr_badv(val) gcsr_write(val, LOONGARCH_CSR_BADV)
+#define read_gcsr_badi() gcsr_read(LOONGARCH_CSR_BADI)
+#define write_gcsr_badi(val) gcsr_write(val, LOONGARCH_CSR_BADI)
+#define read_gcsr_eentry() gcsr_read(LOONGARCH_CSR_EENTRY)
+#define write_gcsr_eentry(val) gcsr_write(val, LOONGARCH_CSR_EENTRY)
+
+#define read_gcsr_tlbidx() gcsr_read(LOONGARCH_CSR_TLBIDX)
+#define write_gcsr_tlbidx(val) gcsr_write(val, LOONGARCH_CSR_TLBIDX)
+#define read_gcsr_tlbhi() gcsr_read(LOONGARCH_CSR_TLBEHI)
+#define write_gcsr_tlbhi(val) gcsr_write(val, LOONGARCH_CSR_TLBEHI)
+#define read_gcsr_tlblo0() gcsr_read(LOONGARCH_CSR_TLBELO0)
+#define write_gcsr_tlblo0(val) gcsr_write(val, LOONGARCH_CSR_TLBELO0)
+#define read_gcsr_tlblo1() gcsr_read(LOONGARCH_CSR_TLBELO1)
+#define write_gcsr_tlblo1(val) gcsr_write(val, LOONGARCH_CSR_TLBELO1)
+
+#define read_gcsr_asid() gcsr_read(LOONGARCH_CSR_ASID)
+#define write_gcsr_asid(val) gcsr_write(val, LOONGARCH_CSR_ASID)
+#define read_gcsr_pgdl() gcsr_read(LOONGARCH_CSR_PGDL)
+#define write_gcsr_pgdl(val) gcsr_write(val, LOONGARCH_CSR_PGDL)
+#define read_gcsr_pgdh() gcsr_read(LOONGARCH_CSR_PGDH)
+#define write_gcsr_pgdh(val) gcsr_write(val, LOONGARCH_CSR_PGDH)
+#define write_gcsr_pgd(val) gcsr_write(val, LOONGARCH_CSR_PGD)
+#define read_gcsr_pgd() gcsr_read(LOONGARCH_CSR_PGD)
+#define read_gcsr_pwctl0() gcsr_read(LOONGARCH_CSR_PWCTL0)
+#define write_gcsr_pwctl0(val) gcsr_write(val, LOONGARCH_CSR_PWCTL0)
+#define read_gcsr_pwctl1() gcsr_read(LOONGARCH_CSR_PWCTL1)
+#define write_gcsr_pwctl1(val) gcsr_write(val, LOONGARCH_CSR_PWCTL1)
+#define read_gcsr_stlbpgsize() gcsr_read(LOONGARCH_CSR_STLBPGSIZE)
+#define write_gcsr_stlbpgsize(val) gcsr_write(val, LOONGARCH_CSR_STLBPGSIZE)
+#define read_gcsr_rvacfg() gcsr_read(LOONGARCH_CSR_RVACFG)
+#define write_gcsr_rvacfg(val) gcsr_write(val, LOONGARCH_CSR_RVACFG)
+
+#define read_gcsr_cpuid() gcsr_read(LOONGARCH_CSR_CPUID)
+#define write_gcsr_cpuid(val) gcsr_write(val, LOONGARCH_CSR_CPUID)
+#define read_gcsr_prcfg1() gcsr_read(LOONGARCH_CSR_PRCFG1)
+#define write_gcsr_prcfg1(val) gcsr_write(val, LOONGARCH_CSR_PRCFG1)
+#define read_gcsr_prcfg2() gcsr_read(LOONGARCH_CSR_PRCFG2)
+#define write_gcsr_prcfg2(val) gcsr_write(val, LOONGARCH_CSR_PRCFG2)
+#define read_gcsr_prcfg3() gcsr_read(LOONGARCH_CSR_PRCFG3)
+#define write_gcsr_prcfg3(val) gcsr_write(val, LOONGARCH_CSR_PRCFG3)
+
+#define read_gcsr_kscratch0() gcsr_read(LOONGARCH_CSR_KS0)
+#define write_gcsr_kscratch0(val) gcsr_write(val, LOONGARCH_CSR_KS0)
+#define read_gcsr_kscratch1() gcsr_read(LOONGARCH_CSR_KS1)
+#define write_gcsr_kscratch1(val) gcsr_write(val, LOONGARCH_CSR_KS1)
+#define read_gcsr_kscratch2() gcsr_read(LOONGARCH_CSR_KS2)
+#define write_gcsr_kscratch2(val) gcsr_write(val, LOONGARCH_CSR_KS2)
+#define read_gcsr_kscratch3() gcsr_read(LOONGARCH_CSR_KS3)
+#define write_gcsr_kscratch3(val) gcsr_write(val, LOONGARCH_CSR_KS3)
+#define read_gcsr_kscratch4() gcsr_read(LOONGARCH_CSR_KS4)
+#define write_gcsr_kscratch4(val) gcsr_write(val, LOONGARCH_CSR_KS4)
+#define read_gcsr_kscratch5() gcsr_read(LOONGARCH_CSR_KS5)
+#define write_gcsr_kscratch5(val) gcsr_write(val, LOONGARCH_CSR_KS5)
+#define read_gcsr_kscratch6() gcsr_read(LOONGARCH_CSR_KS6)
+#define write_gcsr_kscratch6(val) gcsr_write(val, LOONGARCH_CSR_KS6)
+#define read_gcsr_kscratch7() gcsr_read(LOONGARCH_CSR_KS7)
+#define write_gcsr_kscratch7(val) gcsr_write(val, LOONGARCH_CSR_KS7)
+
+#define read_gcsr_timerid() gcsr_read(LOONGARCH_CSR_TMID)
+#define write_gcsr_timerid(val) gcsr_write(val, LOONGARCH_CSR_TMID)
+#define read_gcsr_timercfg() gcsr_read(LOONGARCH_CSR_TCFG)
+#define write_gcsr_timercfg(val) gcsr_write(val, LOONGARCH_CSR_TCFG)
+#define read_gcsr_timertick() gcsr_read(LOONGARCH_CSR_TVAL)
+#define write_gcsr_timertick(val) gcsr_write(val, LOONGARCH_CSR_TVAL)
+#define read_gcsr_timeroffset() gcsr_read(LOONGARCH_CSR_CNTC)
+#define write_gcsr_timeroffset(val) gcsr_write(val, LOONGARCH_CSR_CNTC)
+
+#define read_gcsr_llbctl() gcsr_read(LOONGARCH_CSR_LLBCTL)
+#define write_gcsr_llbctl(val) gcsr_write(val, LOONGARCH_CSR_LLBCTL)
+
+#define read_gcsr_tlbrentry() gcsr_read(LOONGARCH_CSR_TLBRENTRY)
+#define write_gcsr_tlbrentry(val) gcsr_write(val, LOONGARCH_CSR_TLBRENTRY)
+#define read_gcsr_tlbrbadv() gcsr_read(LOONGARCH_CSR_TLBRBADV)
+#define write_gcsr_tlbrbadv(val) gcsr_write(val, LOONGARCH_CSR_TLBRBADV)
+#define read_gcsr_tlbrera() gcsr_read(LOONGARCH_CSR_TLBRERA)
+#define write_gcsr_tlbrera(val) gcsr_write(val, LOONGARCH_CSR_TLBRERA)
+#define read_gcsr_tlbrsave() gcsr_read(LOONGARCH_CSR_TLBRSAVE)
+#define write_gcsr_tlbrsave(val) gcsr_write(val, LOONGARCH_CSR_TLBRSAVE)
+#define read_gcsr_tlbrelo0() gcsr_read(LOONGARCH_CSR_TLBRELO0)
+#define write_gcsr_tlbrelo0(val) gcsr_write(val, LOONGARCH_CSR_TLBRELO0)
+#define read_gcsr_tlbrelo1() gcsr_read(LOONGARCH_CSR_TLBRELO1)
+#define write_gcsr_tlbrelo1(val) gcsr_write(val, LOONGARCH_CSR_TLBRELO1)
+#define read_gcsr_tlbrehi() gcsr_read(LOONGARCH_CSR_TLBREHI)
+#define write_gcsr_tlbrehi(val) gcsr_write(val, LOONGARCH_CSR_TLBREHI)
+#define read_gcsr_tlbrprmd() gcsr_read(LOONGARCH_CSR_TLBRPRMD)
+#define write_gcsr_tlbrprmd(val) gcsr_write(val, LOONGARCH_CSR_TLBRPRMD)
+
+#define read_gcsr_directwin0() gcsr_read(LOONGARCH_CSR_DMWIN0)
+#define write_gcsr_directwin0(val) gcsr_write(val, LOONGARCH_CSR_DMWIN0)
+#define read_gcsr_directwin1() gcsr_read(LOONGARCH_CSR_DMWIN1)
+#define write_gcsr_directwin1(val) gcsr_write(val, LOONGARCH_CSR_DMWIN1)
+#define read_gcsr_directwin2() gcsr_read(LOONGARCH_CSR_DMWIN2)
+#define write_gcsr_directwin2(val) gcsr_write(val, LOONGARCH_CSR_DMWIN2)
+#define read_gcsr_directwin3() gcsr_read(LOONGARCH_CSR_DMWIN3)
+#define write_gcsr_directwin3(val) gcsr_write(val, LOONGARCH_CSR_DMWIN3)
+
/*
* Manipulate bits in a register.
*/
@@ -1318,15 +1504,26 @@ change_##name(unsigned long change, unsigned long val) \
}
#define __BUILD_CSR_OP(name) __BUILD_CSR_COMMON(csr_##name)
+#define __BUILD_GCSR_OP(name) __BUILD_CSR_COMMON(gcsr_##name)
__BUILD_CSR_OP(euen)
__BUILD_CSR_OP(ecfg)
__BUILD_CSR_OP(tlbidx)
+__BUILD_CSR_OP(gcfg)
+__BUILD_CSR_OP(gstat)
+__BUILD_CSR_OP(gtlbc)
+__BUILD_CSR_OP(gintc)
+__BUILD_GCSR_OP(llbctl)
+__BUILD_GCSR_OP(tlbidx)
#define set_csr_estat(val) \
csr_xchg32(val, val, LOONGARCH_CSR_ESTAT)
#define clear_csr_estat(val) \
csr_xchg32(~(val), val, LOONGARCH_CSR_ESTAT)
+#define set_gcsr_estat(val) \
+ gcsr_xchg(val, val, LOONGARCH_CSR_ESTAT)
+#define clear_gcsr_estat(val) \
+ gcsr_xchg(~(val), val, LOONGARCH_CSR_ESTAT)
#endif /* __ASSEMBLY__ */
@@ -1411,7 +1608,7 @@ __BUILD_CSR_OP(tlbidx)
#define EXCCODE_WATCH 19 /* Watch address reference */
#define EXCCODE_BTDIS 20 /* Binary Trans. Disabled */
#define EXCCODE_BTE 21 /* Binary Trans. Exception */
-#define EXCCODE_PSI 22 /* Guest Privileged Error */
+#define EXCCODE_GSPR 22 /* Guest Privileged Error */
#define EXCCODE_HYP 23 /* Hypercall */
#define EXCCODE_GCM 24 /* Guest CSR modified */
#define EXCSUBCODE_GCSC 0 /* Software caused */
diff --git a/arch/loongarch/kvm/trace.h b/arch/loongarch/kvm/trace.h
new file mode 100644
index 000000000000..f96d06761279
--- /dev/null
+++ b/arch/loongarch/kvm/trace.h
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#if !defined(_TRACE_KVM_H)
+#define _TRACE_KVM_H
+
+#include <linux/tracepoint.h>
+#include <asm/kvm_csr.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kvm
+
+/*
+ * Tracepoints for VM enters
+ */
+DECLARE_EVENT_CLASS(kvm_transition,
+ TP_PROTO(struct kvm_vcpu *vcpu),
+ TP_ARGS(vcpu),
+ TP_STRUCT__entry(
+ __field(unsigned long, pc)
+ ),
+
+ TP_fast_assign(
+ __entry->pc = vcpu->arch.pc;
+ ),
+
+ TP_printk("PC: 0x%08lx",
+ __entry->pc)
+);
+
+DEFINE_EVENT(kvm_transition, kvm_enter,
+ TP_PROTO(struct kvm_vcpu *vcpu),
+ TP_ARGS(vcpu));
+
+DEFINE_EVENT(kvm_transition, kvm_reenter,
+ TP_PROTO(struct kvm_vcpu *vcpu),
+ TP_ARGS(vcpu));
+
+DEFINE_EVENT(kvm_transition, kvm_out,
+ TP_PROTO(struct kvm_vcpu *vcpu),
+ TP_ARGS(vcpu));
+
+/* Further exit reasons */
+#define KVM_TRACE_EXIT_IDLE 64
+#define KVM_TRACE_EXIT_CACHE 65
+#define KVM_TRACE_EXIT_SIGNAL 66
+
+/* Tracepoints for VM exits */
+#define kvm_trace_symbol_exit_types \
+ { KVM_TRACE_EXIT_IDLE, "IDLE" }, \
+ { KVM_TRACE_EXIT_CACHE, "CACHE" }, \
+ { KVM_TRACE_EXIT_SIGNAL, "Signal" }
+
+TRACE_EVENT(kvm_exit_gspr,
+ TP_PROTO(struct kvm_vcpu *vcpu, unsigned int inst_word),
+ TP_ARGS(vcpu, inst_word),
+ TP_STRUCT__entry(
+ __field(unsigned int, inst_word)
+ ),
+
+ TP_fast_assign(
+ __entry->inst_word = inst_word;
+ ),
+
+ TP_printk("inst word: 0x%08x",
+ __entry->inst_word)
+);
+
+
+DECLARE_EVENT_CLASS(kvm_exit,
+ TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason),
+ TP_ARGS(vcpu, reason),
+ TP_STRUCT__entry(
+ __field(unsigned long, pc)
+ __field(unsigned int, reason)
+ ),
+
+ TP_fast_assign(
+ __entry->pc = vcpu->arch.pc;
+ __entry->reason = reason;
+ ),
+
+ TP_printk("[%s]PC: 0x%08lx",
+ __print_symbolic(__entry->reason,
+ kvm_trace_symbol_exit_types),
+ __entry->pc)
+);
+
+DEFINE_EVENT(kvm_exit, kvm_exit_idle,
+ TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason),
+ TP_ARGS(vcpu, reason));
+
+DEFINE_EVENT(kvm_exit, kvm_exit_cache,
+ TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason),
+ TP_ARGS(vcpu, reason));
+
+DEFINE_EVENT(kvm_exit, kvm_exit,
+ TP_PROTO(struct kvm_vcpu *vcpu, unsigned int reason),
+ TP_ARGS(vcpu, reason));
+
+#define KVM_TRACE_AUX_RESTORE 0
+#define KVM_TRACE_AUX_SAVE 1
+#define KVM_TRACE_AUX_ENABLE 2
+#define KVM_TRACE_AUX_DISABLE 3
+#define KVM_TRACE_AUX_DISCARD 4
+
+#define KVM_TRACE_AUX_FPU 1
+
+#define kvm_trace_symbol_aux_op \
+ { KVM_TRACE_AUX_RESTORE, "restore" }, \
+ { KVM_TRACE_AUX_SAVE, "save" }, \
+ { KVM_TRACE_AUX_ENABLE, "enable" }, \
+ { KVM_TRACE_AUX_DISABLE, "disable" }, \
+ { KVM_TRACE_AUX_DISCARD, "discard" }
+
+#define kvm_trace_symbol_aux_state \
+ { KVM_TRACE_AUX_FPU, "FPU" }
+
+TRACE_EVENT(kvm_aux,
+ TP_PROTO(struct kvm_vcpu *vcpu, unsigned int op,
+ unsigned int state),
+ TP_ARGS(vcpu, op, state),
+ TP_STRUCT__entry(
+ __field(unsigned long, pc)
+ __field(u8, op)
+ __field(u8, state)
+ ),
+
+ TP_fast_assign(
+ __entry->pc = vcpu->arch.pc;
+ __entry->op = op;
+ __entry->state = state;
+ ),
+
+ TP_printk("%s %s PC: 0x%08lx",
+ __print_symbolic(__entry->op,
+ kvm_trace_symbol_aux_op),
+ __print_symbolic(__entry->state,
+ kvm_trace_symbol_aux_state),
+ __entry->pc)
+);
+
+TRACE_EVENT(kvm_vpid_change,
+ TP_PROTO(struct kvm_vcpu *vcpu, unsigned long vpid),
+ TP_ARGS(vcpu, vpid),
+ TP_STRUCT__entry(
+ __field(unsigned long, vpid)
+ ),
+
+ TP_fast_assign(
+ __entry->vpid = vpid;
+ ),
+
+ TP_printk("vpid: 0x%08lx",
+ __entry->vpid)
+);
+
+#endif /* _TRACE_KVM_H */
--
2.31.1
Implement loongarch vcpu KVM_ENABLE_CAP ioctl interface.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vcpu.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 9ad6a285cbb4..e1e6167699de 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -187,6 +187,23 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
return 0;
}
+static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
+ struct kvm_enable_cap *cap)
+{
+ int r = 0;
+
+ if (!kvm_vm_ioctl_check_extension(vcpu->kvm, cap->cap))
+ return -EINVAL;
+ if (cap->flags)
+ return -EINVAL;
+ if (cap->args[0])
+ return -EINVAL;
+ if (cap->cap)
+ return -EINVAL;
+
+ return r;
+}
+
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -210,6 +227,15 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
r = _kvm_get_reg(vcpu, ®);
break;
}
+ case KVM_ENABLE_CAP: {
+ struct kvm_enable_cap cap;
+
+ r = -EFAULT;
+ if (copy_from_user(&cap, argp, sizeof(cap)))
+ break;
+ r = kvm_vcpu_ioctl_enable_cap(vcpu, &cap);
+ break;
+ }
default:
r = -ENOIOCTLCMD;
break;
--
2.31.1
Implement loongarch fpu related interface for vcpu, such as get fpu, set
fpu, own fpu and lose fpu, etc.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vcpu.c | 60 +++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index e1e6167699de..d1e8cd402900 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -245,6 +245,66 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
return r;
}
+int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
+{
+ int i = 0;
+
+ /* no need vcpu_load and vcpu_put */
+ fpu->fcsr = vcpu->arch.fpu.fcsr;
+ fpu->fcc = vcpu->arch.fpu.fcc;
+ for (i = 0; i < NUM_FPU_REGS; i++)
+ memcpy(&fpu->fpr[i], &vcpu->arch.fpu.fpr[i], FPU_REG_WIDTH / 64);
+
+ return 0;
+}
+
+int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
+{
+ int i = 0;
+
+ /* no need vcpu_load and vcpu_put */
+ vcpu->arch.fpu.fcsr = fpu->fcsr;
+ vcpu->arch.fpu.fcc = fpu->fcc;
+ for (i = 0; i < NUM_FPU_REGS; i++)
+ memcpy(&vcpu->arch.fpu.fpr[i], &fpu->fpr[i], FPU_REG_WIDTH / 64);
+
+ return 0;
+}
+
+/* Enable FPU for guest and restore context */
+void kvm_own_fpu(struct kvm_vcpu *vcpu)
+{
+ preempt_disable();
+
+ /*
+ * Enable FPU for guest
+ */
+ set_csr_euen(CSR_EUEN_FPEN);
+
+ kvm_restore_fpu(&vcpu->arch.fpu);
+ vcpu->arch.aux_inuse |= KVM_LARCH_FPU;
+ trace_kvm_aux(vcpu, KVM_TRACE_AUX_RESTORE, KVM_TRACE_AUX_FPU);
+
+ preempt_enable();
+}
+
+/* Save and disable FPU */
+void kvm_lose_fpu(struct kvm_vcpu *vcpu)
+{
+ preempt_disable();
+
+ if (vcpu->arch.aux_inuse & KVM_LARCH_FPU) {
+ kvm_save_fpu(&vcpu->arch.fpu);
+ vcpu->arch.aux_inuse &= ~KVM_LARCH_FPU;
+ trace_kvm_aux(vcpu, KVM_TRACE_AUX_SAVE, KVM_TRACE_AUX_FPU);
+
+ /* Disable FPU */
+ clear_csr_euen(CSR_EUEN_FPEN);
+ }
+
+ preempt_enable();
+}
+
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
{
return 0;
--
2.31.1
Implement loongarch vcpu get registers and set registers operations, it
is called when user space use the ioctl interface to get or set regs.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/csr_ops.S | 76 +++++++++++++
arch/loongarch/kvm/vcpu.c | 206 +++++++++++++++++++++++++++++++++++
2 files changed, 282 insertions(+)
create mode 100644 arch/loongarch/kvm/csr_ops.S
diff --git a/arch/loongarch/kvm/csr_ops.S b/arch/loongarch/kvm/csr_ops.S
new file mode 100644
index 000000000000..962b96d8291a
--- /dev/null
+++ b/arch/loongarch/kvm/csr_ops.S
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <asm/regdef.h>
+#include <linux/linkage.h>
+ .text
+ .section .text
+ .cfi_sections .debug_frame
+/*
+ * we have splited hw gcsr into three parts, so we can
+ * calculate the code offset by gcsrid and jump here to
+ * run the gcsrwr instruction.
+ */
+SYM_FUNC_START(set_hw_gcsr)
+ addi.d t0, a0, 0
+ addi.w t1, zero, 96
+ bltu t1, t0, 1f
+ la.pcrel t0, 10f
+ alsl.d t0, a0, t0, 3
+ jirl zero, t0, 0
+1:
+ addi.w t1, a0, -128
+ addi.w t2, zero, 15
+ bltu t2, t1, 2f
+ la.pcrel t0, 11f
+ alsl.d t0, t1, t0, 3
+ jirl zero, t0, 0
+2:
+ addi.w t1, a0, -384
+ addi.w t2, zero, 3
+ bltu t2, t1, 3f
+ la.pcrel t0, 12f
+ alsl.d t0, t1, t0, 3
+ jirl zero, t0, 0
+3:
+ addi.w a0, zero, -1
+ jirl zero, ra, 0
+/*
+ * write guest csr
+ * 0x05000000 | (LOONGARCH_CSR_XXX << 10) | 1 << 5 | a1
+ * range from 0x0(KVM_CSR_CRMD) to 0x60 (KVM_CSR_LLBCTL)
+ */
+10:
+ csrnum = 0
+ .rept 0x61
+ .word 0x05000020 | csrnum << 10 | 5
+ jirl zero, ra, 0
+ csrnum = csrnum + 1
+ .endr
+/*
+ * write guest csr
+ * 0x05000000 | (LOONGARCH_CSR_XXX << 10) | 1<<5 | a1
+ * range from 0x80 (KVM_CSR_IMPCTL1) to 0x8f (KVM_CSR_TLBRPRMD)
+ */
+11:
+ csrnum = 0x80
+ .rept 0x10
+ .word 0x05000020 | csrnum << 10 | 5
+ jirl zero, ra, 0
+ csrnum = csrnum + 1
+ .endr
+/*
+ * write guest csr
+ * 0x05000000 | (LOONGARCH_CSR_XXX << 10) | 1<<5 | a1
+ * range from 0x180(KVM_CSR_DMWIN0) to 0x183(KVM_CSR_DMWIN3)
+ */
+12:
+ csrnum = 0x180
+ .rept 0x4
+ .word 0x05000020 | csrnum << 10 | 5
+ jirl zero, ra, 0
+ csrnum = csrnum + 1
+ .endr
+SYM_FUNC_END(set_hw_gcsr)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index 651ae5ca0691..9ad6a285cbb4 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -13,6 +13,212 @@
#define CREATE_TRACE_POINTS
#include "trace.h"
+int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *v)
+{
+ unsigned long val;
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+
+ if (id >= CSR_ALL_SIZE || !(get_gcsr_flag(id) & (HW_GCSR | SW_GCSR)))
+ return -EINVAL;
+
+ if (id == LOONGARCH_CSR_ESTAT) {
+ /* interrupt status IP0 -- IP7 from GINTC */
+ val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_GINTC) & 0xff;
+ *v = kvm_read_sw_gcsr(csr, id) | (val << 2);
+ return 0;
+ }
+
+ /*
+ * get software csr state if csrid is valid, since software
+ * csr state is consistent with hardware
+ */
+ *v = kvm_read_sw_gcsr(csr, id);
+
+ return 0;
+}
+
+int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 val)
+{
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+ int ret = 0, gintc;
+
+ if (id >= CSR_ALL_SIZE || !(get_gcsr_flag(id) & (HW_GCSR | SW_GCSR)))
+ return -EINVAL;
+
+ if (id == LOONGARCH_CSR_ESTAT) {
+ /* estat IP0~IP7 inject through guestexcept */
+ gintc = (val >> 2) & 0xff;
+ write_csr_gintc(gintc);
+ kvm_set_sw_gcsr(csr, LOONGARCH_CSR_GINTC, gintc);
+
+ gintc = val & ~(0xffUL << 2);
+ write_gcsr_estat(gintc);
+ kvm_set_sw_gcsr(csr, LOONGARCH_CSR_ESTAT, gintc);
+
+ return ret;
+ }
+
+ if (get_gcsr_flag(id) & HW_GCSR) {
+ set_hw_gcsr(id, val);
+ /* write sw gcsr to keep consistent with hardware */
+ kvm_write_sw_gcsr(csr, id, val);
+ } else
+ kvm_write_sw_gcsr(csr, id, val);
+
+ return ret;
+}
+
+static int _kvm_get_one_reg(struct kvm_vcpu *vcpu,
+ const struct kvm_one_reg *reg, s64 *v)
+{
+ int reg_idx, ret = 0;
+
+ if ((reg->id & KVM_REG_LOONGARCH_MASK) == KVM_REG_LOONGARCH_CSR) {
+ reg_idx = KVM_GET_IOC_CSRIDX(reg->id);
+ ret = _kvm_getcsr(vcpu, reg_idx, v);
+ } else if (reg->id == KVM_REG_LOONGARCH_COUNTER)
+ *v = drdtime() + vcpu->kvm->arch.time_offset;
+ else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int _kvm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+ int ret = -EINVAL;
+ s64 v;
+
+ if ((reg->id & KVM_REG_SIZE_MASK) != KVM_REG_SIZE_U64)
+ return ret;
+
+ if (_kvm_get_one_reg(vcpu, reg, &v))
+ return ret;
+
+ return put_user(v, (u64 __user *)(long)reg->addr);
+}
+
+static int _kvm_set_one_reg(struct kvm_vcpu *vcpu,
+ const struct kvm_one_reg *reg,
+ s64 v)
+{
+ int ret = 0;
+ unsigned long flags;
+ u64 val;
+ int reg_idx;
+
+ val = v;
+ if ((reg->id & KVM_REG_LOONGARCH_MASK) == KVM_REG_LOONGARCH_CSR) {
+ reg_idx = KVM_GET_IOC_CSRIDX(reg->id);
+ ret = _kvm_setcsr(vcpu, reg_idx, val);
+ } else if (reg->id == KVM_REG_LOONGARCH_COUNTER) {
+ local_irq_save(flags);
+ /*
+ * gftoffset is relative with board, not vcpu
+ * only set for the first time for smp system
+ */
+ if (vcpu->vcpu_id == 0)
+ vcpu->kvm->arch.time_offset = (signed long)(v - drdtime());
+ write_csr_gcntc((ulong)vcpu->kvm->arch.time_offset);
+ local_irq_restore(flags);
+ } else if (reg->id == KVM_REG_LOONGARCH_VCPU_RESET) {
+ kvm_reset_timer(vcpu);
+ memset(&vcpu->arch.irq_pending, 0, sizeof(vcpu->arch.irq_pending));
+ memset(&vcpu->arch.irq_clear, 0, sizeof(vcpu->arch.irq_clear));
+ } else
+ ret = -EINVAL;
+
+ return ret;
+}
+
+static int _kvm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+{
+ s64 v;
+ int ret = -EINVAL;
+
+ if ((reg->id & KVM_REG_SIZE_MASK) != KVM_REG_SIZE_U64)
+ return ret;
+
+ if (get_user(v, (u64 __user *)(long)reg->addr))
+ return ret;
+
+ return _kvm_set_one_reg(vcpu, reg, v);
+}
+
+int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
+ struct kvm_sregs *sregs)
+{
+ return -ENOIOCTLCMD;
+}
+
+int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
+ struct kvm_sregs *sregs)
+{
+ return -ENOIOCTLCMD;
+}
+
+int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+ int i;
+
+ vcpu_load(vcpu);
+
+ for (i = 0; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
+ regs->gpr[i] = vcpu->arch.gprs[i];
+
+ regs->pc = vcpu->arch.pc;
+
+ vcpu_put(vcpu);
+ return 0;
+}
+
+int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
+{
+ int i;
+
+ vcpu_load(vcpu);
+
+ for (i = 1; i < ARRAY_SIZE(vcpu->arch.gprs); i++)
+ vcpu->arch.gprs[i] = regs->gpr[i];
+ vcpu->arch.gprs[0] = 0; /* zero is special, and cannot be set. */
+ vcpu->arch.pc = regs->pc;
+
+ vcpu_put(vcpu);
+ return 0;
+}
+
+long kvm_arch_vcpu_ioctl(struct file *filp,
+ unsigned int ioctl, unsigned long arg)
+{
+ struct kvm_vcpu *vcpu = filp->private_data;
+ void __user *argp = (void __user *)arg;
+ long r;
+
+ vcpu_load(vcpu);
+
+ switch (ioctl) {
+ case KVM_SET_ONE_REG:
+ case KVM_GET_ONE_REG: {
+ struct kvm_one_reg reg;
+
+ r = -EFAULT;
+ if (copy_from_user(®, argp, sizeof(reg)))
+ break;
+ if (ioctl == KVM_SET_ONE_REG)
+ r = _kvm_set_reg(vcpu, ®);
+ else
+ r = _kvm_get_reg(vcpu, ®);
+ break;
+ }
+ default:
+ r = -ENOIOCTLCMD;
+ break;
+ }
+
+ vcpu_put(vcpu);
+ return r;
+}
+
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
{
return 0;
--
2.31.1
Implement some misc vcpu relaterd interfaces, such as vcpu runnable,
vcpu should kick, vcpu dump regs, etc.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vcpu.c | 108 ++++++++++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index ee648bdb657c..af9aa3fbb932 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -13,6 +13,114 @@
#define CREATE_TRACE_POINTS
#include "trace.h"
+int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
+{
+ return !!(vcpu->arch.irq_pending) &&
+ vcpu->arch.mp_state.mp_state == KVM_MP_STATE_RUNNABLE;
+}
+
+int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu)
+{
+ return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE;
+}
+
+bool kvm_arch_vcpu_in_kernel(struct kvm_vcpu *vcpu)
+{
+ return false;
+}
+
+vm_fault_t kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf)
+{
+ return VM_FAULT_SIGBUS;
+}
+
+int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
+ struct kvm_translation *tr)
+{
+ return -EINVAL;
+}
+
+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+ return _kvm_pending_timer(vcpu) ||
+ kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT) &
+ (1 << (EXCCODE_TIMER - EXCCODE_INT_START));
+}
+
+int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu)
+{
+ int i;
+
+ if (!vcpu)
+ return -1;
+
+ kvm_debug("VCPU Register Dump:\n");
+ kvm_debug("\tpc = 0x%08lx\n", vcpu->arch.pc);
+ kvm_debug("\texceptions: %08lx\n", vcpu->arch.irq_pending);
+
+ for (i = 0; i < 32; i += 4) {
+ kvm_debug("\tgpr%02d: %08lx %08lx %08lx %08lx\n", i,
+ vcpu->arch.gprs[i],
+ vcpu->arch.gprs[i + 1],
+ vcpu->arch.gprs[i + 2], vcpu->arch.gprs[i + 3]);
+ }
+
+ kvm_debug("\tCRMOD: 0x%08llx, exst: 0x%08llx\n",
+ kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD),
+ kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT));
+
+ kvm_debug("\tERA: 0x%08llx\n", kvm_read_hw_gcsr(LOONGARCH_CSR_ERA));
+
+ return 0;
+}
+
+int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
+ struct kvm_mp_state *mp_state)
+{
+ *mp_state = vcpu->arch.mp_state;
+
+ return 0;
+}
+
+int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
+ struct kvm_mp_state *mp_state)
+{
+ int ret = 0;
+
+ switch (mp_state->mp_state) {
+ case KVM_MP_STATE_RUNNABLE:
+ vcpu->arch.mp_state = *mp_state;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
+ struct kvm_guest_debug *dbg)
+{
+ return -EINVAL;
+}
+
+/**
+ * kvm_migrate_count() - Migrate timer.
+ * @vcpu: Virtual CPU.
+ *
+ * Migrate hrtimer to the current CPU by cancelling and restarting it
+ * if it was running prior to being cancelled.
+ *
+ * Must be called when the VCPU is migrated to a different CPU to ensure that
+ * timer expiry during guest execution interrupts the guest and causes the
+ * interrupt to be delivered in a timely manner.
+ */
+static void kvm_migrate_count(struct kvm_vcpu *vcpu)
+{
+ if (hrtimer_cancel(&vcpu->arch.swtimer))
+ hrtimer_restart(&vcpu->arch.swtimer);
+}
+
int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *v)
{
unsigned long val;
--
2.31.1
Implement loongarch vcpu status description such as idle exits counter,
signal exits counter, cpucfg exits counter, etc.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vcpu.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index bac4ce2ec1eb..735fa3b0ab43 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -13,6 +13,23 @@
#define CREATE_TRACE_POINTS
#include "trace.h"
+const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = {
+ KVM_GENERIC_VCPU_STATS(),
+ STATS_DESC_COUNTER(VCPU, idle_exits),
+ STATS_DESC_COUNTER(VCPU, signal_exits),
+ STATS_DESC_COUNTER(VCPU, int_exits),
+ STATS_DESC_COUNTER(VCPU, cpucfg_exits),
+};
+
+const struct kvm_stats_header kvm_vcpu_stats_header = {
+ .name_size = KVM_STATS_NAME_SIZE,
+ .num_desc = ARRAY_SIZE(kvm_vcpu_stats_desc),
+ .id_offset = sizeof(struct kvm_stats_header),
+ .desc_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE,
+ .data_offset = sizeof(struct kvm_stats_header) + KVM_STATS_NAME_SIZE +
+ sizeof(kvm_vcpu_stats_desc),
+};
+
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
return !!(vcpu->arch.irq_pending) &&
--
2.31.1
Implement kvm check vmid and update vmid, the vmid should be checked before
vcpu enter guest.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vmid.c | 65 +++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
create mode 100644 arch/loongarch/kvm/vmid.c
diff --git a/arch/loongarch/kvm/vmid.c b/arch/loongarch/kvm/vmid.c
new file mode 100644
index 000000000000..7d9688c24184
--- /dev/null
+++ b/arch/loongarch/kvm/vmid.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_host.h>
+#include "trace.h"
+
+static void _kvm_update_vpid(struct kvm_vcpu *vcpu, int cpu)
+{
+ struct kvm_context *context;
+ unsigned long vpid;
+
+ context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu);
+ vpid = context->vpid_cache + 1;
+ if (!(vpid & vpid_mask)) {
+ /* finish round of 64 bit loop */
+ if (unlikely(!vpid))
+ vpid = vpid_mask + 1;
+
+ /* vpid 0 reserved for root */
+ ++vpid;
+
+ /* start new vpid cycle */
+ kvm_flush_tlb_all();
+ }
+
+ context->vpid_cache = vpid;
+ vcpu->arch.vpid = vpid;
+}
+
+void _kvm_check_vmid(struct kvm_vcpu *vcpu, int cpu)
+{
+ struct kvm_context *context;
+ bool migrated;
+ unsigned long ver, old, vpid;
+
+ /*
+ * Are we entering guest context on a different CPU to last time?
+ * If so, the VCPU's guest TLB state on this CPU may be stale.
+ */
+ context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu);
+ migrated = (vcpu->arch.last_exec_cpu != cpu);
+ vcpu->arch.last_exec_cpu = cpu;
+
+ /*
+ * Check if our vpid is of an older version
+ *
+ * We also discard the stored vpid if we've executed on
+ * another CPU, as the guest mappings may have changed without
+ * hypervisor knowledge.
+ */
+ ver = vcpu->arch.vpid & ~vpid_mask;
+ old = context->vpid_cache & ~vpid_mask;
+ if (migrated || (ver != old)) {
+ _kvm_update_vpid(vcpu, cpu);
+ trace_kvm_vpid_change(vcpu, vcpu->arch.vpid);
+ }
+
+ /* Restore GSTAT(0x50).vpid */
+ vpid = (vcpu->arch.vpid & vpid_mask)
+ << CSR_GSTAT_GID_SHIFT;
+ change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid);
+}
--
2.31.1
Implement loongarch virtual machine tlb operations such as flush tlb by
specific gpa parameter and flush all of the virt machines tlb.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/tlb.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
create mode 100644 arch/loongarch/kvm/tlb.c
diff --git a/arch/loongarch/kvm/tlb.c b/arch/loongarch/kvm/tlb.c
new file mode 100644
index 000000000000..66e116cf2486
--- /dev/null
+++ b/arch/loongarch/kvm/tlb.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/tlb.h>
+
+int kvm_flush_tlb_gpa(struct kvm_vcpu *vcpu, unsigned long gpa)
+{
+ preempt_disable();
+ gpa &= (PAGE_MASK << 1);
+ invtlb(INVTLB_GID_ADDR, read_csr_gstat() & CSR_GSTAT_GID, gpa);
+ preempt_enable();
+ return 0;
+}
+
+/**
+ * kvm_flush_tlb_all() - Flush all root TLB entries for
+ * guests.
+ *
+ * Invalidate all entries including GVA-->GPA and GPA-->HPA mappings.
+ */
+void kvm_flush_tlb_all(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ invtlb_all(INVTLB_ALLGID, 0, 0);
+ local_irq_restore(flags);
+}
--
2.31.1
Implement loongarch vcpu load and vcpu put operations, including
load csr value into hardware and save csr value into vcpu structure.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/vcpu.c | 199 ++++++++++++++++++++++++++++++++++++++
1 file changed, 199 insertions(+)
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index af9aa3fbb932..bac4ce2ec1eb 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -617,6 +617,205 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
}
}
+static int _kvm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+ struct kvm_context *context;
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+ bool migrated, all;
+
+ /*
+ * Have we migrated to a different CPU?
+ * If so, any old guest TLB state may be stale.
+ */
+ migrated = (vcpu->arch.last_sched_cpu != cpu);
+
+ /*
+ * Was this the last VCPU to run on this CPU?
+ * If not, any old guest state from this VCPU will have been clobbered.
+ */
+ context = per_cpu_ptr(vcpu->kvm->arch.vmcs, cpu);
+ all = migrated || (context->last_vcpu != vcpu);
+ context->last_vcpu = vcpu;
+
+ /*
+ * Restore timer state regardless
+ */
+ kvm_restore_timer(vcpu);
+
+ /* Control guest page CCA attribute */
+ change_csr_gcfg(CSR_GCFG_MATC_MASK, CSR_GCFG_MATC_ROOT);
+ /* Don't bother restoring registers multiple times unless necessary */
+ if (!all)
+ return 0;
+
+ write_csr_gcntc((ulong)vcpu->kvm->arch.time_offset);
+ /*
+ * Restore guest CSR registers
+ */
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_CRMD);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_PRMD);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_EUEN);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_MISC);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ECFG);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ERA);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_BADV);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_BADI);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_EENTRY);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBIDX);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBEHI);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBELO0);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBELO1);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ASID);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_PGDL);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_PGDH);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_PWCTL0);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_PWCTL1);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_STLBPGSIZE);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_RVACFG);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_CPUID);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS0);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS1);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS2);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS3);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS4);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS5);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS6);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_KS7);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TMID);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_CNTC);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBRENTRY);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBRBADV);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBRERA);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBRSAVE);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBRELO0);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBRELO1);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBREHI);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TLBRPRMD);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_DMWIN0);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_DMWIN1);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_DMWIN2);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_DMWIN3);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_LLBCTL);
+
+ /* restore Root.Guestexcept from unused Guest guestexcept register */
+ write_csr_gintc(csr->csrs[LOONGARCH_CSR_GINTC]);
+
+ /*
+ * We should clear linked load bit to break interrupted atomics. This
+ * prevents a SC on the next VCPU from succeeding by matching a LL on
+ * the previous VCPU.
+ */
+ if (vcpu->kvm->created_vcpus > 1)
+ set_gcsr_llbctl(CSR_LLBCTL_WCLLB);
+
+ return 0;
+}
+
+void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ vcpu->cpu = cpu;
+ if (vcpu->arch.last_sched_cpu != cpu) {
+ kvm_debug("[%d->%d]KVM VCPU[%d] switch\n",
+ vcpu->arch.last_sched_cpu, cpu, vcpu->vcpu_id);
+ /*
+ * Migrate the timer interrupt to the current CPU so that it
+ * always interrupts the guest and synchronously triggers a
+ * guest timer interrupt.
+ */
+ kvm_migrate_count(vcpu);
+ }
+
+ /* restore guest state to registers */
+ _kvm_vcpu_load(vcpu, cpu);
+ local_irq_restore(flags);
+}
+
+static int _kvm_vcpu_put(struct kvm_vcpu *vcpu, int cpu)
+{
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+
+ kvm_lose_fpu(vcpu);
+ /*
+ * update csr state from hardware if software csr state is stale,
+ * most csr registers are kept unchanged during process context
+ * switch except csr registers like remaining timer tick value and
+ * injected interrupt state.
+ */
+ if (!(vcpu->arch.aux_inuse & KVM_LARCH_CSR)) {
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_CRMD);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PRMD);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_EUEN);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_MISC);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ECFG);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ERA);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_BADV);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_BADI);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_EENTRY);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBIDX);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBEHI);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBELO0);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBELO1);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ASID);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PGDL);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PGDH);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PGD);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PWCTL0);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PWCTL1);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_STLBPGSIZE);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_RVACFG);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_CPUID);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PRCFG1);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PRCFG2);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_PRCFG3);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS0);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS1);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS2);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS3);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS4);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS5);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS6);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_KS7);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TMID);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_CNTC);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_LLBCTL);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBRENTRY);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBRBADV);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBRERA);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBRSAVE);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBRELO0);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBRELO1);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBREHI);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TLBRPRMD);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_DMWIN0);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_DMWIN1);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_DMWIN2);
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_DMWIN3);
+ vcpu->arch.aux_inuse |= KVM_LARCH_CSR;
+ }
+ /* save Root.Guestexcept in unused Guest guestexcept register */
+ kvm_save_timer(vcpu);
+ csr->csrs[LOONGARCH_CSR_GINTC] = read_csr_gintc();
+ return 0;
+}
+
+void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
+{
+ unsigned long flags;
+ int cpu;
+
+ local_irq_save(flags);
+ cpu = smp_processor_id();
+ vcpu->arch.last_sched_cpu = cpu;
+ vcpu->cpu = -1;
+
+ /* save guest state in registers */
+ _kvm_vcpu_put(vcpu, cpu);
+ local_irq_restore(flags);
+}
+
int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
{
int r = -EINTR;
--
2.31.1
Implement vcpu interrupt operations such as vcpu set irq and
vcpu clear irq, using set_gcsr_estat to set irq which is
parsed by the irq bitmap.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/interrupt.c | 126 +++++++++++++++++++++++++++++++++
arch/loongarch/kvm/vcpu.c | 45 ++++++++++++
2 files changed, 171 insertions(+)
create mode 100644 arch/loongarch/kvm/interrupt.c
diff --git a/arch/loongarch/kvm/interrupt.c b/arch/loongarch/kvm/interrupt.c
new file mode 100644
index 000000000000..02267a71d1aa
--- /dev/null
+++ b/arch/loongarch/kvm/interrupt.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <asm/kvm_vcpu.h>
+
+static unsigned int int_to_coreint[LOONGARCH_EXC_MAX] = {
+ [LARCH_INT_TIMER] = CPU_TIMER,
+ [LARCH_INT_IPI] = CPU_IPI,
+ [LARCH_INT_SIP0] = CPU_SIP0,
+ [LARCH_INT_SIP1] = CPU_SIP1,
+ [LARCH_INT_IP0] = CPU_IP0,
+ [LARCH_INT_IP1] = CPU_IP1,
+ [LARCH_INT_IP2] = CPU_IP2,
+ [LARCH_INT_IP3] = CPU_IP3,
+ [LARCH_INT_IP4] = CPU_IP4,
+ [LARCH_INT_IP5] = CPU_IP5,
+ [LARCH_INT_IP6] = CPU_IP6,
+ [LARCH_INT_IP7] = CPU_IP7,
+};
+
+static int _kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
+{
+ unsigned int irq = 0;
+
+ clear_bit(priority, &vcpu->arch.irq_pending);
+ if (priority < LOONGARCH_EXC_MAX)
+ irq = int_to_coreint[priority];
+
+ switch (priority) {
+ case LARCH_INT_TIMER:
+ case LARCH_INT_IPI:
+ case LARCH_INT_SIP0:
+ case LARCH_INT_SIP1:
+ set_gcsr_estat(irq);
+ break;
+
+ case LARCH_INT_IP0:
+ case LARCH_INT_IP1:
+ case LARCH_INT_IP2:
+ case LARCH_INT_IP3:
+ case LARCH_INT_IP4:
+ case LARCH_INT_IP5:
+ case LARCH_INT_IP6:
+ case LARCH_INT_IP7:
+ set_csr_gintc(irq);
+ break;
+
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+static int _kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
+{
+ unsigned int irq = 0;
+
+ clear_bit(priority, &vcpu->arch.irq_clear);
+ if (priority < LOONGARCH_EXC_MAX)
+ irq = int_to_coreint[priority];
+
+ switch (priority) {
+ case LARCH_INT_TIMER:
+ case LARCH_INT_IPI:
+ case LARCH_INT_SIP0:
+ case LARCH_INT_SIP1:
+ clear_gcsr_estat(irq);
+ break;
+
+ case LARCH_INT_IP0:
+ case LARCH_INT_IP1:
+ case LARCH_INT_IP2:
+ case LARCH_INT_IP3:
+ case LARCH_INT_IP4:
+ case LARCH_INT_IP5:
+ case LARCH_INT_IP6:
+ case LARCH_INT_IP7:
+ clear_csr_gintc(irq);
+ break;
+
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+void _kvm_deliver_intr(struct kvm_vcpu *vcpu)
+{
+ unsigned long *pending = &vcpu->arch.irq_pending;
+ unsigned long *pending_clr = &vcpu->arch.irq_clear;
+ unsigned int priority;
+
+ if (!(*pending) && !(*pending_clr))
+ return;
+
+ if (*pending_clr) {
+ priority = __ffs(*pending_clr);
+ while (priority <= LOONGARCH_EXC_IPNUM) {
+ _kvm_irq_clear(vcpu, priority);
+ priority = find_next_bit(pending_clr,
+ BITS_PER_BYTE * sizeof(*pending_clr),
+ priority + 1);
+ }
+ }
+
+ if (*pending) {
+ priority = __ffs(*pending);
+ while (priority <= LOONGARCH_EXC_IPNUM) {
+ _kvm_irq_deliver(vcpu, priority);
+ priority = find_next_bit(pending,
+ BITS_PER_BYTE * sizeof(*pending),
+ priority + 1);
+ }
+ }
+}
+
+int _kvm_pending_timer(struct kvm_vcpu *vcpu)
+{
+ return test_bit(LARCH_INT_TIMER, &vcpu->arch.irq_pending);
+}
diff --git a/arch/loongarch/kvm/vcpu.c b/arch/loongarch/kvm/vcpu.c
index d1e8cd402900..ee648bdb657c 100644
--- a/arch/loongarch/kvm/vcpu.c
+++ b/arch/loongarch/kvm/vcpu.c
@@ -305,6 +305,51 @@ void kvm_lose_fpu(struct kvm_vcpu *vcpu)
preempt_enable();
}
+int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu,
+ struct kvm_loongarch_interrupt *irq)
+{
+ int intr = (int)irq->irq;
+ struct kvm_vcpu *dvcpu = NULL;
+
+ if (irq->cpu == -1)
+ dvcpu = vcpu;
+ else
+ dvcpu = kvm_get_vcpu(vcpu->kvm, irq->cpu);
+
+ if (intr > 0)
+ _kvm_queue_irq(dvcpu, intr);
+ else if (intr < 0)
+ _kvm_dequeue_irq(dvcpu, -intr);
+ else {
+ kvm_err("%s: invalid interrupt ioctl (%d:%d)\n", __func__,
+ irq->cpu, irq->irq);
+ return -EINVAL;
+ }
+
+ kvm_vcpu_kick(dvcpu);
+ return 0;
+}
+
+long kvm_arch_vcpu_async_ioctl(struct file *filp,
+ unsigned int ioctl, unsigned long arg)
+{
+ struct kvm_vcpu *vcpu = filp->private_data;
+ void __user *argp = (void __user *)arg;
+
+ if (ioctl == KVM_INTERRUPT) {
+ struct kvm_loongarch_interrupt irq;
+
+ if (copy_from_user(&irq, argp, sizeof(irq)))
+ return -EFAULT;
+ kvm_debug("[%d] %s: irq: %d\n", vcpu->vcpu_id, __func__,
+ irq.irq);
+
+ return kvm_vcpu_ioctl_interrupt(vcpu, &irq);
+ }
+
+ return -ENOIOCTLCMD;
+}
+
int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
{
return 0;
--
2.31.1
Implement kvm handle vcpu iocsr exception, setting the iocsr info into
vcpu_run and return to user space to handle it.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/include/asm/inst.h | 16 ++++++
arch/loongarch/kvm/exit.c | 92 +++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+)
diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index a04fe755d719..e95040e880fa 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -56,6 +56,14 @@ enum reg2_op {
revbd_op = 0x0f,
revh2w_op = 0x10,
revhd_op = 0x11,
+ iocsrrdb_op = 0x19200,
+ iocsrrdh_op = 0x19201,
+ iocsrrdw_op = 0x19202,
+ iocsrrdd_op = 0x19203,
+ iocsrwrb_op = 0x19204,
+ iocsrwrh_op = 0x19205,
+ iocsrwrw_op = 0x19206,
+ iocsrwrd_op = 0x19207,
};
enum reg2i5_op {
@@ -272,6 +280,13 @@ struct reg3sa2_format {
unsigned int opcode : 15;
};
+struct reg2csr_format {
+ unsigned int rd : 5;
+ unsigned int rj : 5;
+ unsigned int csr : 14;
+ unsigned int opcode : 8;
+};
+
union loongarch_instruction {
unsigned int word;
struct reg0i15_format reg0i15_format;
@@ -287,6 +302,7 @@ union loongarch_instruction {
struct reg2bstrd_format reg2bstrd_format;
struct reg3_format reg3_format;
struct reg3sa2_format reg3sa2_format;
+ struct reg2csr_format reg2csr_format;
};
#define LOONGARCH_INSN_SIZE sizeof(union loongarch_instruction)
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index dcb8eb815b74..f151cc2dd0b9 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -98,3 +98,95 @@ static int _kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst)
return EMULATE_DONE;
}
+
+int _kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
+{
+ u32 rd, rj, opcode;
+ u32 addr;
+ unsigned long val;
+ int ret;
+
+ /*
+ * Each IOCSR with different opcode
+ */
+ rd = inst.reg2_format.rd;
+ rj = inst.reg2_format.rj;
+ opcode = inst.reg2_format.opcode;
+ addr = vcpu->arch.gprs[rj];
+ ret = EMULATE_DO_IOCSR;
+ run->iocsr_io.phys_addr = addr;
+ run->iocsr_io.is_write = 0;
+
+ /* LoongArch is Little endian */
+ switch (opcode) {
+ case iocsrrdb_op:
+ run->iocsr_io.len = 1;
+ break;
+ case iocsrrdh_op:
+ run->iocsr_io.len = 2;
+ break;
+ case iocsrrdw_op:
+ run->iocsr_io.len = 4;
+ break;
+ case iocsrrdd_op:
+ run->iocsr_io.len = 8;
+ break;
+ case iocsrwrb_op:
+ run->iocsr_io.len = 1;
+ run->iocsr_io.is_write = 1;
+ break;
+ case iocsrwrh_op:
+ run->iocsr_io.len = 2;
+ run->iocsr_io.is_write = 1;
+ break;
+ case iocsrwrw_op:
+ run->iocsr_io.len = 4;
+ run->iocsr_io.is_write = 1;
+ break;
+ case iocsrwrd_op:
+ run->iocsr_io.len = 8;
+ run->iocsr_io.is_write = 1;
+ break;
+ default:
+ ret = EMULATE_FAIL;
+ break;
+ }
+
+ if (ret == EMULATE_DO_IOCSR) {
+ if (run->iocsr_io.is_write) {
+ val = vcpu->arch.gprs[rd];
+ memcpy(run->iocsr_io.data, &val, run->iocsr_io.len);
+ }
+ vcpu->arch.io_gpr = rd;
+ }
+
+ return ret;
+}
+
+int _kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+ unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr];
+ enum emulation_result er = EMULATE_DONE;
+
+ switch (run->iocsr_io.len) {
+ case 8:
+ *gpr = *(s64 *)run->iocsr_io.data;
+ break;
+ case 4:
+ *gpr = *(int *)run->iocsr_io.data;
+ break;
+ case 2:
+ *gpr = *(short *)run->iocsr_io.data;
+ break;
+ case 1:
+ *gpr = *(char *) run->iocsr_io.data;
+ break;
+ default:
+ kvm_err("Bad IOCSR length: %d,addr is 0x%lx",
+ run->iocsr_io.len, vcpu->arch.badv);
+ er = EMULATE_FAIL;
+ break;
+ }
+
+ return er;
+}
--
2.31.1
Implement kvm handle gspr exception interface, including emulate the
reading and writing of cpucfg, csr, iocsr resource.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/exit.c | 115 ++++++++++++++++++++++++++++++++++++++
1 file changed, 115 insertions(+)
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index 3de08a4798fc..717ef01338db 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -210,3 +210,118 @@ int _kvm_emu_idle(struct kvm_vcpu *vcpu)
return EMULATE_DONE;
}
+
+static int _kvm_trap_handle_gspr(struct kvm_vcpu *vcpu)
+{
+ enum emulation_result er = EMULATE_DONE;
+ struct kvm_run *run = vcpu->run;
+ larch_inst inst;
+ unsigned long curr_pc;
+ int rd, rj;
+ unsigned int index;
+
+ /*
+ * Fetch the instruction.
+ */
+ inst.word = vcpu->arch.badi;
+ curr_pc = vcpu->arch.pc;
+ update_pc(&vcpu->arch);
+
+ trace_kvm_exit_gspr(vcpu, inst.word);
+ er = EMULATE_FAIL;
+ switch (((inst.word >> 24) & 0xff)) {
+ case 0x0:
+ /* cpucfg GSPR */
+ if (inst.reg2_format.opcode == 0x1B) {
+ rd = inst.reg2_format.rd;
+ rj = inst.reg2_format.rj;
+ ++vcpu->stat.cpucfg_exits;
+ index = vcpu->arch.gprs[rj];
+
+ vcpu->arch.gprs[rd] = read_cpucfg(index);
+ /* Nested KVM is not supported */
+ if (index == 2)
+ vcpu->arch.gprs[rd] &= ~CPUCFG2_LVZP;
+ if (index == 6)
+ vcpu->arch.gprs[rd] &= ~CPUCFG6_PMP;
+ er = EMULATE_DONE;
+ }
+ break;
+ case 0x4:
+ /* csr GSPR */
+ er = _kvm_handle_csr(vcpu, inst);
+ break;
+ case 0x6:
+ /* iocsr,cache,idle GSPR */
+ switch (((inst.word >> 22) & 0x3ff)) {
+ case 0x18:
+ /* cache GSPR */
+ er = EMULATE_DONE;
+ trace_kvm_exit_cache(vcpu, KVM_TRACE_EXIT_CACHE);
+ break;
+ case 0x19:
+ /* iocsr/idle GSPR */
+ switch (((inst.word >> 15) & 0x1ffff)) {
+ case 0xc90:
+ /* iocsr GSPR */
+ er = _kvm_emu_iocsr(inst, run, vcpu);
+ break;
+ case 0xc91:
+ /* idle GSPR */
+ er = _kvm_emu_idle(vcpu);
+ break;
+ default:
+ er = EMULATE_FAIL;
+ break;
+ }
+ break;
+ default:
+ er = EMULATE_FAIL;
+ break;
+ }
+ break;
+ default:
+ er = EMULATE_FAIL;
+ break;
+ }
+
+ /* Rollback PC only if emulation was unsuccessful */
+ if (er == EMULATE_FAIL) {
+ kvm_err("[%#lx]%s: unsupported gspr instruction 0x%08x\n",
+ curr_pc, __func__, inst.word);
+
+ kvm_arch_vcpu_dump_regs(vcpu);
+ vcpu->arch.pc = curr_pc;
+ }
+ return er;
+}
+
+/*
+ * Execute cpucfg instruction will tirggerGSPR,
+ * Also the access to unimplemented csrs 0x15
+ * 0x16, 0x50~0x53, 0x80, 0x81, 0x90~0x95, 0x98
+ * 0xc0~0xff, 0x100~0x109, 0x500~0x502,
+ * cache_op, idle_op iocsr ops the same
+ */
+static int _kvm_handle_gspr(struct kvm_vcpu *vcpu)
+{
+ enum emulation_result er = EMULATE_DONE;
+ int ret = RESUME_GUEST;
+
+ er = _kvm_trap_handle_gspr(vcpu);
+
+ if (er == EMULATE_DONE) {
+ ret = RESUME_GUEST;
+ } else if (er == EMULATE_DO_MMIO) {
+ vcpu->run->exit_reason = KVM_EXIT_MMIO;
+ ret = RESUME_HOST;
+ } else if (er == EMULATE_DO_IOCSR) {
+ vcpu->run->exit_reason = KVM_EXIT_LOONGARCH_IOCSR;
+ ret = RESUME_HOST;
+ } else {
+ kvm_err("%s internal error\n", __func__);
+ vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ ret = RESUME_HOST;
+ }
+ return ret;
+}
--
2.31.1
Implement kvm handle loongarch vcpu exit caused by reading and
writing csr. Using loongarch_csr structure to emulate the registers.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/include/asm/kvm_csr.h | 55 +++++++++++++++
arch/loongarch/kvm/exit.c | 100 +++++++++++++++++++++++++++
2 files changed, 155 insertions(+)
create mode 100644 arch/loongarch/include/asm/kvm_csr.h
create mode 100644 arch/loongarch/kvm/exit.c
diff --git a/arch/loongarch/include/asm/kvm_csr.h b/arch/loongarch/include/asm/kvm_csr.h
new file mode 100644
index 000000000000..18052d0ada6b
--- /dev/null
+++ b/arch/loongarch/include/asm/kvm_csr.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_LOONGARCH_KVM_CSR_H__
+#define __ASM_LOONGARCH_KVM_CSR_H__
+#include <asm/loongarch.h>
+#include <asm/kvm_host.h>
+#include <asm/kvm_vcpu.h>
+#include <linux/uaccess.h>
+#include <linux/kvm_host.h>
+
+#define kvm_read_hw_gcsr(id) gcsr_read(id)
+#define kvm_write_hw_gcsr(csr, id, val) gcsr_write(val, id)
+
+int _kvm_getcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 *v);
+int _kvm_setcsr(struct kvm_vcpu *vcpu, unsigned int id, u64 v);
+
+int _kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu);
+
+static inline void kvm_save_hw_gcsr(struct loongarch_csrs *csr, int gid)
+{
+ csr->csrs[gid] = gcsr_read(gid);
+}
+
+static inline void kvm_restore_hw_gcsr(struct loongarch_csrs *csr, int gid)
+{
+ gcsr_write(csr->csrs[gid], gid);
+}
+
+static inline unsigned long kvm_read_sw_gcsr(struct loongarch_csrs *csr, int gid)
+{
+ return csr->csrs[gid];
+}
+
+static inline void kvm_write_sw_gcsr(struct loongarch_csrs *csr, int gid, unsigned long val)
+{
+ csr->csrs[gid] = val;
+}
+
+static inline void kvm_set_sw_gcsr(struct loongarch_csrs *csr, int gid, unsigned long val)
+{
+ csr->csrs[gid] |= val;
+}
+
+static inline void kvm_change_sw_gcsr(struct loongarch_csrs *csr, int gid, unsigned long mask,
+ unsigned long val)
+{
+ unsigned long _mask = mask;
+
+ csr->csrs[gid] &= ~_mask;
+ csr->csrs[gid] |= val & _mask;
+}
+#endif /* __ASM_LOONGARCH_KVM_CSR_H__ */
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
new file mode 100644
index 000000000000..dcb8eb815b74
--- /dev/null
+++ b/arch/loongarch/kvm/exit.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/preempt.h>
+#include <linux/vmalloc.h>
+#include <asm/fpu.h>
+#include <asm/inst.h>
+#include <asm/time.h>
+#include <asm/tlb.h>
+#include <asm/loongarch.h>
+#include <asm/numa.h>
+#include <asm/kvm_vcpu.h>
+#include <asm/kvm_csr.h>
+#include <linux/kvm_host.h>
+#include <asm/mmzone.h>
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
+static unsigned long _kvm_emu_read_csr(struct kvm_vcpu *vcpu, int csrid)
+{
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+ unsigned long val = 0;
+
+ if (csrid < 4096 && (get_gcsr_flag(csrid) & SW_GCSR))
+ val = kvm_read_sw_gcsr(csr, csrid);
+ else
+ pr_warn_once("Unsupport csrread 0x%x with pc %lx\n",
+ csrid, vcpu->arch.pc);
+ return val;
+}
+
+static void _kvm_emu_write_csr(struct kvm_vcpu *vcpu, int csrid,
+ unsigned long val)
+{
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+
+ if (csrid < 4096 && (get_gcsr_flag(csrid) & SW_GCSR))
+ kvm_write_sw_gcsr(csr, csrid, val);
+ else
+ pr_warn_once("Unsupport csrwrite 0x%x with pc %lx\n",
+ csrid, vcpu->arch.pc);
+}
+
+static void _kvm_emu_xchg_csr(struct kvm_vcpu *vcpu, int csrid,
+ unsigned long csr_mask, unsigned long val)
+{
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+
+ if (csrid < 4096 && (get_gcsr_flag(csrid) & SW_GCSR)) {
+ unsigned long orig;
+
+ orig = kvm_read_sw_gcsr(csr, csrid);
+ orig &= ~csr_mask;
+ orig |= val & csr_mask;
+ kvm_write_sw_gcsr(csr, csrid, orig);
+ } else
+ pr_warn_once("Unsupport csrxchg 0x%x with pc %lx\n",
+ csrid, vcpu->arch.pc);
+}
+
+static int _kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst)
+{
+ unsigned int rd, rj, csrid;
+ unsigned long csr_mask;
+ unsigned long val = 0;
+
+ /*
+ * CSR value mask imm
+ * rj = 0 means csrrd
+ * rj = 1 means csrwr
+ * rj != 0,1 means csrxchg
+ */
+ rd = inst.reg2csr_format.rd;
+ rj = inst.reg2csr_format.rj;
+ csrid = inst.reg2csr_format.csr;
+
+ /* Process CSR ops */
+ if (rj == 0) {
+ /* process csrrd */
+ val = _kvm_emu_read_csr(vcpu, csrid);
+ vcpu->arch.gprs[rd] = val;
+ } else if (rj == 1) {
+ /* process csrwr */
+ val = vcpu->arch.gprs[rd];
+ _kvm_emu_write_csr(vcpu, csrid, val);
+ } else {
+ /* process csrxchg */
+ val = vcpu->arch.gprs[rd];
+ csr_mask = vcpu->arch.gprs[rj];
+ _kvm_emu_xchg_csr(vcpu, csrid, csr_mask, val);
+ }
+
+ return EMULATE_DONE;
+}
--
2.31.1
Implement loongarch vcpu timer operations such as init kvm timer,
require kvm timer, save kvm timer and restore kvm timer. When
vcpu exit, we use kvm soft timer to emulate hardware timer. If
timeout happens, the vcpu timer interrupt will be set and it is
going to be handled at vcpu next entrance.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/timer.c | 266 +++++++++++++++++++++++++++++++++++++
1 file changed, 266 insertions(+)
create mode 100644 arch/loongarch/kvm/timer.c
diff --git a/arch/loongarch/kvm/timer.c b/arch/loongarch/kvm/timer.c
new file mode 100644
index 000000000000..2c7677248746
--- /dev/null
+++ b/arch/loongarch/kvm/timer.c
@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/kvm_host.h>
+#include <asm/kvm_csr.h>
+#include <asm/kvm_vcpu.h>
+
+/* low level hrtimer wake routine */
+enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer)
+{
+ struct kvm_vcpu *vcpu;
+
+ vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer);
+ _kvm_queue_irq(vcpu, LARCH_INT_TIMER);
+ rcuwait_wake_up(&vcpu->wait);
+ return kvm_count_timeout(vcpu);
+}
+
+/*
+ * ktime_to_tick() - Scale ktime_t to a 64-bit stable timer.
+ *
+ * Caches the dynamic nanosecond bias in vcpu->arch.timer_dyn_bias.
+ */
+static unsigned long ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now)
+{
+ s64 now_ns, periods;
+ unsigned long delta;
+
+ now_ns = ktime_to_ns(now);
+ delta = now_ns + vcpu->arch.timer_dyn_bias;
+
+ if (delta >= vcpu->arch.timer_period) {
+ /* If delta is out of safe range the bias needs adjusting */
+ periods = div64_s64(now_ns, vcpu->arch.timer_period);
+ vcpu->arch.timer_dyn_bias = -periods * vcpu->arch.timer_period;
+ /* Recalculate delta with new bias */
+ delta = now_ns + vcpu->arch.timer_dyn_bias;
+ }
+
+ /*
+ * We've ensured that:
+ * delta < timer_period
+ */
+ return div_u64(delta * vcpu->arch.timer_mhz, MNSEC_PER_SEC);
+}
+
+/**
+ * kvm_resume_hrtimer() - Resume hrtimer, updating expiry.
+ * @vcpu: Virtual CPU.
+ * @now: ktime at point of resume.
+ * @val: stable timer at point of resume.
+ *
+ * Resumes the timer and updates the timer expiry based on @now and @count.
+ */
+static void kvm_resume_hrtimer(struct kvm_vcpu *vcpu, ktime_t now,
+ unsigned long val)
+{
+ unsigned long delta;
+ ktime_t expire;
+
+ /* Stable timer decreased to zero or
+ * initialize to zero, set 4 second timer
+ */
+ delta = div_u64(val * MNSEC_PER_SEC, vcpu->arch.timer_mhz);
+ expire = ktime_add_ns(now, delta);
+
+ /* Update hrtimer to use new timeout */
+ hrtimer_cancel(&vcpu->arch.swtimer);
+ hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED);
+}
+
+/**
+ * kvm_init_timer() - Initialise stable timer.
+ * @vcpu: Virtual CPU.
+ * @timer_hz: Frequency of timer.
+ *
+ * Initialise the timer to the specified frequency, zero it, and set it going if
+ * it's enabled.
+ */
+void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz)
+{
+ ktime_t now;
+ unsigned long ticks;
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+
+ ticks = (unsigned long)MNSEC_PER_SEC * CSR_TCFG_VAL;
+ vcpu->arch.timer_mhz = timer_hz >> 20;
+ vcpu->arch.timer_period = div_u64(ticks, vcpu->arch.timer_mhz);
+ vcpu->arch.timer_dyn_bias = 0;
+
+ /* Starting at 0 */
+ ticks = 0;
+ now = ktime_get();
+ vcpu->arch.timer_bias = ticks - ktime_to_tick(vcpu, now);
+ vcpu->arch.timer_bias &= CSR_TCFG_VAL;
+ kvm_write_sw_gcsr(csr, LOONGARCH_CSR_TVAL, ticks);
+}
+
+/**
+ * kvm_count_timeout() - Push timer forward on timeout.
+ * @vcpu: Virtual CPU.
+ *
+ * Handle an hrtimer event by push the hrtimer forward a period.
+ *
+ * Returns: The hrtimer_restart value to return to the hrtimer subsystem.
+ */
+enum hrtimer_restart kvm_count_timeout(struct kvm_vcpu *vcpu)
+{
+ unsigned long cfg;
+
+ /* Add the Count period to the current expiry time */
+ cfg = kvm_read_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TCFG);
+ if (cfg & CSR_TCFG_PERIOD) {
+ hrtimer_add_expires_ns(&vcpu->arch.swtimer, cfg & CSR_TCFG_VAL);
+ return HRTIMER_RESTART;
+ } else
+ return HRTIMER_NORESTART;
+}
+
+/*
+ * kvm_restore_timer() - Restore timer state.
+ * @vcpu: Virtual CPU.
+ *
+ * Restore soft timer state from saved context.
+ */
+void kvm_restore_timer(struct kvm_vcpu *vcpu)
+{
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+ ktime_t saved_ktime, now;
+ unsigned long val, new, delta;
+ int expired = 0;
+ unsigned long cfg;
+
+ /*
+ * Set guest stable timer cfg csr
+ */
+ cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);
+ if (!(cfg & CSR_TCFG_EN)) {
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TCFG);
+ kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TVAL);
+ return;
+ }
+
+ now = ktime_get();
+ saved_ktime = vcpu->arch.stable_ktime_saved;
+ val = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);
+
+ /*hrtimer not expire */
+ delta = ktime_to_tick(vcpu, ktime_sub(now, saved_ktime));
+ if (delta >= val) {
+ expired = 1;
+ if (cfg & CSR_TCFG_PERIOD)
+ new = (delta - val) % (cfg & CSR_TCFG_VAL);
+ else
+ new = 1;
+ } else
+ new = val - delta;
+
+ new &= CSR_TCFG_VAL;
+ write_gcsr_timercfg(cfg);
+ write_gcsr_timertick(new);
+ if (expired)
+ _kvm_queue_irq(vcpu, LARCH_INT_TIMER);
+}
+
+/*
+ * kvm_acquire_timer() - Switch to hard timer state.
+ * @vcpu: Virtual CPU.
+ *
+ * Restore hard timer state on top of existing soft timer state if possible.
+ *
+ * Since hard timer won't remain active over preemption, preemption should be
+ * disabled by the caller.
+ */
+void kvm_acquire_timer(struct kvm_vcpu *vcpu)
+{
+ unsigned long flags, guestcfg;
+
+ guestcfg = read_csr_gcfg();
+ if (!(guestcfg & CSR_GCFG_TIT))
+ return;
+
+ /* enable guest access to hard timer */
+ write_csr_gcfg(guestcfg & ~CSR_GCFG_TIT);
+
+ /*
+ * Freeze the soft-timer and sync the guest stable timer with it. We do
+ * this with interrupts disabled to avoid latency.
+ */
+ local_irq_save(flags);
+ hrtimer_cancel(&vcpu->arch.swtimer);
+ local_irq_restore(flags);
+}
+
+
+/*
+ * _kvm_save_timer() - Switch to software emulation of guest timer.
+ * @vcpu: Virtual CPU.
+ *
+ * Save guest timer state and switch to software emulation of guest
+ * timer. The hard timer must already be in use, so preemption should be
+ * disabled.
+ */
+static ktime_t _kvm_save_timer(struct kvm_vcpu *vcpu, unsigned long *val)
+{
+ unsigned long end_time;
+ ktime_t before_time;
+
+ before_time = ktime_get();
+
+ /*
+ * Record a final stable timer which we will transfer to the soft-timer.
+ */
+ end_time = read_gcsr_timertick();
+ *val = end_time;
+
+ kvm_resume_hrtimer(vcpu, before_time, end_time);
+ return before_time;
+}
+
+/*
+ * kvm_save_timer() - Save guest timer state.
+ * @vcpu: Virtual CPU.
+ *
+ * Save guest timer state and switch to soft guest timer if hard timer was in
+ * use.
+ */
+void kvm_save_timer(struct kvm_vcpu *vcpu)
+{
+ struct loongarch_csrs *csr = vcpu->arch.csr;
+ unsigned long guestcfg, val;
+ ktime_t save_ktime;
+
+ preempt_disable();
+ guestcfg = read_csr_gcfg();
+ if (!(guestcfg & CSR_GCFG_TIT)) {
+ /* disable guest use of hard timer */
+ write_csr_gcfg(guestcfg | CSR_GCFG_TIT);
+
+ /* save hard timer state */
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG);
+ if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN) {
+ save_ktime = _kvm_save_timer(vcpu, &val);
+ kvm_write_sw_gcsr(csr, LOONGARCH_CSR_TVAL, val);
+ vcpu->arch.stable_ktime_saved = save_ktime;
+ if (val == CSR_TCFG_VAL)
+ _kvm_queue_irq(vcpu, LARCH_INT_TIMER);
+ } else {
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL);
+ }
+ }
+
+ /* save timer-related state to VCPU context */
+ kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);
+ preempt_enable();
+}
+
+void kvm_reset_timer(struct kvm_vcpu *vcpu)
+{
+ write_gcsr_timercfg(0);
+ kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TCFG, 0);
+ hrtimer_cancel(&vcpu->arch.swtimer);
+}
--
2.31.1
Implement loongarch kvm mmu, it is used to switch gpa to hpa when
guest exit because of address translation exception. This patch
implement allocate gpa page table, search gpa from it and flush guest
gpa in the table.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/mmu.c | 730 +++++++++++++++++++++++++++++++++++++++
1 file changed, 730 insertions(+)
create mode 100644 arch/loongarch/kvm/mmu.c
diff --git a/arch/loongarch/kvm/mmu.c b/arch/loongarch/kvm/mmu.c
new file mode 100644
index 000000000000..d950cb4e9f37
--- /dev/null
+++ b/arch/loongarch/kvm/mmu.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/highmem.h>
+#include <linux/page-flags.h>
+#include <linux/kvm_host.h>
+#include <linux/uaccess.h>
+#include <asm/kvm_host.h>
+#include <asm/mmu_context.h>
+#include <asm/pgalloc.h>
+#include <asm/tlb.h>
+
+/*
+ * KVM_MMU_CACHE_MIN_PAGES is the number of GPA page table translation levels
+ * for which pages need to be cached.
+ */
+#if defined(__PAGETABLE_PMD_FOLDED)
+#define KVM_MMU_CACHE_MIN_PAGES 1
+#else
+#define KVM_MMU_CACHE_MIN_PAGES 2
+#endif
+
+/**
+ * kvm_pgd_alloc() - Allocate and initialise a KVM GPA page directory.
+ *
+ * Allocate a blank KVM GPA page directory (PGD) for representing guest physical
+ * to host physical page mappings.
+ *
+ * Returns: Pointer to new KVM GPA page directory.
+ * NULL on allocation failure.
+ */
+pgd_t *kvm_pgd_alloc(void)
+{
+ pgd_t *pgd;
+
+ pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, 0);
+ if (pgd)
+ pgd_init((void *)pgd);
+
+ return pgd;
+}
+
+/**
+ * kvm_walk_pgd() - Walk page table with optional allocation.
+ * @pgd: Page directory pointer.
+ * @addr: Address to index page table using.
+ * @cache: MMU page cache to allocate new page tables from, or NULL.
+ *
+ * Walk the page tables pointed to by @pgd to find the PTE corresponding to the
+ * address @addr. If page tables don't exist for @addr, they will be created
+ * from the MMU cache if @cache is not NULL.
+ *
+ * Returns: Pointer to pte_t corresponding to @addr.
+ * NULL if a page table doesn't exist for @addr and !@cache.
+ * NULL if a page table allocation failed.
+ */
+static pte_t *kvm_walk_pgd(pgd_t *pgd, struct kvm_mmu_memory_cache *cache,
+ unsigned long addr)
+{
+ p4d_t *p4d;
+ pud_t *pud;
+ pmd_t *pmd;
+
+ pgd += pgd_index(addr);
+ if (pgd_none(*pgd)) {
+ /* Not used yet */
+ BUG();
+ return NULL;
+ }
+ p4d = p4d_offset(pgd, addr);
+ pud = pud_offset(p4d, addr);
+ if (pud_none(*pud)) {
+ pmd_t *new_pmd;
+
+ if (!cache)
+ return NULL;
+ new_pmd = kvm_mmu_memory_cache_alloc(cache);
+ pmd_init((void *)new_pmd);
+ pud_populate(NULL, pud, new_pmd);
+ }
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd)) {
+ pte_t *new_pte;
+
+ if (!cache)
+ return NULL;
+ new_pte = kvm_mmu_memory_cache_alloc(cache);
+ clear_page(new_pte);
+ pmd_populate_kernel(NULL, pmd, new_pte);
+ }
+ return pte_offset_kernel(pmd, addr);
+}
+
+/* Caller must hold kvm->mm_lock */
+static pte_t *kvm_pte_for_gpa(struct kvm *kvm,
+ struct kvm_mmu_memory_cache *cache,
+ unsigned long addr)
+{
+ return kvm_walk_pgd(kvm->arch.gpa_mm.pgd, cache, addr);
+}
+
+/*
+ * level2_flush_{pte,pmd,pud,pgd,pt}.
+ * Flush a range of guest physical address space from the VM's GPA page tables.
+ */
+static int level2_flush_pte(pmd_t *pmd, unsigned long addr, unsigned long end)
+{
+ pte_t *pte;
+ unsigned long next, start;
+ int ret;
+
+ ret = 0;
+ start = addr;
+ pte = pte_offset_kernel(pmd, addr);
+ do {
+ next = addr + PAGE_SIZE;
+ if (!pte_present(*pte))
+ continue;
+
+ set_pte(pte, __pte(0));
+ ret = 1;
+ } while (pte++, addr = next, addr != end);
+
+ if (start + PMD_SIZE == end) {
+ pte = pte_offset_kernel(pmd, 0);
+ pmd_clear(pmd);
+ pte_free_kernel(NULL, pte);
+ }
+ return ret;
+}
+
+static int level2_flush_pmd(pud_t *pud, unsigned long addr, unsigned long end)
+{
+ pmd_t *pmd;
+ unsigned long next, start;
+ int ret;
+
+ ret = 0;
+ start = addr;
+ pmd = pmd_offset(pud, addr);
+ do {
+ next = pmd_addr_end(addr, end);
+ if (!pmd_present(*pmd))
+ continue;
+
+ ret |= level2_flush_pte(pmd, addr, next);
+ } while (pmd++, addr = next, addr != end);
+
+ if (start + PUD_SIZE == end) {
+ pmd = pmd_offset(pud, 0);
+ pud_clear(pud);
+ pmd_free(NULL, pmd);
+ }
+ return ret;
+}
+
+static int level2_flush_pud(pgd_t *pgd, unsigned long addr, unsigned long end)
+{
+ p4d_t *p4d;
+ pud_t *pud;
+ unsigned long next, start;
+ int ret;
+
+ ret = 0;
+ start = addr;
+ p4d = p4d_offset(pgd, addr);
+ pud = pud_offset(p4d, addr);
+ do {
+ next = pud_addr_end(addr, end);
+ if (!pud_present(*pud))
+ continue;
+
+ ret |= level2_flush_pmd(pud, addr, next);
+ } while (pud++, addr = next, addr != end);
+
+ if (start + PGDIR_SIZE == end) {
+ pud = pud_offset(p4d, 0);
+ pgd_clear(pgd);
+ pud_free(NULL, pud);
+ }
+ return ret;
+}
+
+static int level2_flush_pgd(pgd_t *pgd, unsigned long addr, unsigned long end)
+{
+ unsigned long next;
+ int ret;
+
+ ret = 0;
+ if (addr > end - 1)
+ return ret;
+ pgd = pgd + pgd_index(addr);
+ do {
+ next = pgd_addr_end(addr, end);
+ if (!pgd_present(*pgd))
+ continue;
+
+ ret |= level2_flush_pud(pgd, addr, next);
+ } while (pgd++, addr = next, addr != end);
+
+ return ret;
+}
+
+/**
+ * level2_flush_range() - Flush a range of guest physical addresses.
+ * @kvm: KVM pointer.
+ * @start_gfn: Guest frame number of first page in GPA range to flush.
+ * @end_gfn: Guest frame number of last page in GPA range to flush.
+ *
+ * Flushes a range of GPA mappings from the GPA page tables.
+ *
+ * The caller must hold the @kvm->mmu_lock spinlock.
+ *
+ * Returns: Whether its safe to remove the top level page directory because
+ * all lower levels have been removed.
+ */
+static bool level2_flush_range(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn)
+{
+ return level2_flush_pgd(kvm->arch.gpa_mm.pgd, start_gfn << PAGE_SHIFT,
+ end_gfn << PAGE_SHIFT);
+}
+
+typedef int (*level2_pte_ops)(void *pte);
+/*
+ * level2_mkclean_pte
+ * Mark a range of guest physical address space clean (writes fault) in the VM's
+ * GPA page table to allow dirty page tracking.
+ */
+static int level2_mkclean_pte(void *pte)
+{
+ pte_t val;
+
+ val = *(pte_t *)pte;
+ if (pte_dirty(val)) {
+ *(pte_t *)pte = pte_mkclean(val);
+ return 1;
+ }
+ return 0;
+}
+
+static int level2_ptw_pte(pmd_t *pmd, unsigned long addr, unsigned long end,
+ level2_pte_ops func)
+{
+ pte_t *pte;
+ unsigned long next;
+ int ret;
+
+ ret = 0;
+ pte = pte_offset_kernel(pmd, addr);
+ do {
+ next = addr + PAGE_SIZE;
+ if (!pte_present(*pte))
+ continue;
+
+ ret |= func(pte);
+ } while (pte++, addr = next, addr != end);
+
+ return ret;
+}
+
+static int level2_ptw_pmd(pud_t *pud, unsigned long addr, unsigned long end,
+ level2_pte_ops func)
+{
+ pmd_t *pmd;
+ unsigned long next;
+ int ret;
+
+ ret = 0;
+ pmd = pmd_offset(pud, addr);
+ do {
+ next = pmd_addr_end(addr, end);
+ if (!pmd_present(*pmd))
+ continue;
+
+ ret |= level2_ptw_pte(pmd, addr, next, func);
+ } while (pmd++, addr = next, addr != end);
+
+ return ret;
+}
+
+static int level2_ptw_pud(pgd_t *pgd, unsigned long addr, unsigned long end,
+ level2_pte_ops func)
+{
+ p4d_t *p4d;
+ pud_t *pud;
+ unsigned long next;
+ int ret;
+
+ ret = 0;
+ p4d = p4d_offset(pgd, addr);
+ pud = pud_offset(p4d, addr);
+ do {
+ next = pud_addr_end(addr, end);
+ if (!pud_present(*pud))
+ continue;
+
+ ret |= level2_ptw_pmd(pud, addr, next, func);
+ } while (pud++, addr = next, addr != end);
+
+ return ret;
+}
+
+static int level2_ptw_pgd(pgd_t *pgd, unsigned long addr, unsigned long end,
+ level2_pte_ops func)
+{
+ unsigned long next;
+ int ret;
+
+ ret = 0;
+ if (addr > end - 1)
+ return ret;
+ pgd = pgd + pgd_index(addr);
+ do {
+ next = pgd_addr_end(addr, end);
+ if (!pgd_present(*pgd))
+ continue;
+
+ ret |= level2_ptw_pud(pgd, addr, next, func);
+ } while (pgd++, addr = next, addr != end);
+
+ return ret;
+}
+
+/*
+ * kvm_mkclean_gpa_pt() - Make a range of guest physical addresses clean.
+ * @kvm: KVM pointer.
+ * @start_gfn: Guest frame number of first page in GPA range to flush.
+ * @end_gfn: Guest frame number of last page in GPA range to flush.
+ *
+ * Make a range of GPA mappings clean so that guest writes will fault and
+ * trigger dirty page logging.
+ *
+ * The caller must hold the @kvm->mmu_lock spinlock.
+ *
+ * Returns: Whether any GPA mappings were modified, which would require
+ * derived mappings (GVA page tables & TLB enties) to be
+ * invalidated.
+ */
+static int kvm_mkclean_gpa_pt(struct kvm *kvm, gfn_t start_gfn, gfn_t end_gfn)
+{
+ return level2_ptw_pgd(kvm->arch.gpa_mm.pgd, start_gfn << PAGE_SHIFT,
+ end_gfn << PAGE_SHIFT, level2_mkclean_pte);
+}
+
+/*
+ * kvm_arch_mmu_enable_log_dirty_pt_masked() - write protect dirty pages
+ * @kvm: The KVM pointer
+ * @slot: The memory slot associated with mask
+ * @gfn_offset: The gfn offset in memory slot
+ * @mask: The mask of dirty pages at offset 'gfn_offset' in this memory
+ * slot to be write protected
+ *
+ * Walks bits set in mask write protects the associated pte's. Caller must
+ * acquire @kvm->mmu_lock.
+ */
+void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm,
+ struct kvm_memory_slot *slot,
+ gfn_t gfn_offset, unsigned long mask)
+{
+ gfn_t base_gfn = slot->base_gfn + gfn_offset;
+ gfn_t start = base_gfn + __ffs(mask);
+ gfn_t end = base_gfn + __fls(mask) + 1;
+
+ kvm_mkclean_gpa_pt(kvm, start, end);
+}
+
+void kvm_arch_commit_memory_region(struct kvm *kvm,
+ struct kvm_memory_slot *old,
+ const struct kvm_memory_slot *new,
+ enum kvm_mr_change change)
+{
+ int needs_flush;
+
+ /*
+ * If dirty page logging is enabled, write protect all pages in the slot
+ * ready for dirty logging.
+ *
+ * There is no need to do this in any of the following cases:
+ * CREATE: No dirty mappings will already exist.
+ * MOVE/DELETE: The old mappings will already have been cleaned up by
+ * kvm_arch_flush_shadow_memslot()
+ */
+ if (change == KVM_MR_FLAGS_ONLY &&
+ (!(old->flags & KVM_MEM_LOG_DIRTY_PAGES) &&
+ new->flags & KVM_MEM_LOG_DIRTY_PAGES)) {
+ spin_lock(&kvm->mmu_lock);
+ /* Write protect GPA page table entries */
+ needs_flush = kvm_mkclean_gpa_pt(kvm, new->base_gfn,
+ new->base_gfn + new->npages);
+ if (needs_flush)
+ kvm_flush_remote_tlbs(kvm);
+ spin_unlock(&kvm->mmu_lock);
+ }
+}
+
+void kvm_arch_flush_shadow_all(struct kvm *kvm)
+{
+ /* Flush whole GPA */
+ level2_flush_range(kvm, 0, kvm->arch.gpa_size >> PAGE_SHIFT);
+ /* Flush vpid for each VCPU individually */
+ kvm_flush_remote_tlbs(kvm);
+}
+
+void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
+ struct kvm_memory_slot *slot)
+{
+ int ret;
+
+ /*
+ * The slot has been made invalid (ready for moving or deletion), so we
+ * need to ensure that it can no longer be accessed by any guest VCPUs.
+ */
+ spin_lock(&kvm->mmu_lock);
+ /* Flush slot from GPA */
+ ret = level2_flush_range(kvm, slot->base_gfn,
+ slot->base_gfn + slot->npages);
+ /* Let implementation do the rest */
+ if (ret)
+ kvm_flush_remote_tlbs(kvm);
+ spin_unlock(&kvm->mmu_lock);
+}
+
+void _kvm_destroy_mm(struct kvm *kvm)
+{
+ /* It should always be safe to remove after flushing the whole range */
+ level2_flush_range(kvm, 0, kvm->arch.gpa_size >> PAGE_SHIFT);
+ pgd_free(NULL, kvm->arch.gpa_mm.pgd);
+ kvm->arch.gpa_mm.pgd = NULL;
+}
+
+/*
+ * Mark a range of guest physical address space old (all accesses fault) in the
+ * VM's GPA page table to allow detection of commonly used pages.
+ */
+static int level2_mkold_pte(void *pte)
+{
+ pte_t val;
+
+ val = *(pte_t *)pte;
+ if (pte_young(val)) {
+ *(pte_t *)pte = pte_mkold(val);
+ return 1;
+ }
+ return 0;
+}
+
+bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ return level2_flush_range(kvm, range->start, range->end);
+}
+
+bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ gpa_t gpa = range->start << PAGE_SHIFT;
+ pte_t hva_pte = range->pte;
+ pte_t *ptep = kvm_pte_for_gpa(kvm, NULL, gpa);
+ pte_t old_pte;
+
+ if (!ptep)
+ return false;
+
+ /* Mapping may need adjusting depending on memslot flags */
+ old_pte = *ptep;
+ if (range->slot->flags & KVM_MEM_LOG_DIRTY_PAGES && !pte_dirty(old_pte))
+ hva_pte = pte_mkclean(hva_pte);
+ else if (range->slot->flags & KVM_MEM_READONLY)
+ hva_pte = pte_wrprotect(hva_pte);
+
+ set_pte(ptep, hva_pte);
+
+ /* Replacing an absent or old page doesn't need flushes */
+ if (!pte_present(old_pte) || !pte_young(old_pte))
+ return false;
+
+ /* Pages swapped, aged, moved, or cleaned require flushes */
+ return !pte_present(hva_pte) ||
+ !pte_young(hva_pte) ||
+ pte_pfn(old_pte) != pte_pfn(hva_pte) ||
+ (pte_dirty(old_pte) && !pte_dirty(hva_pte));
+}
+
+bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ return level2_ptw_pgd(kvm->arch.gpa_mm.pgd, range->start << PAGE_SHIFT,
+ range->end << PAGE_SHIFT, level2_mkold_pte);
+}
+
+bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+ gpa_t gpa = range->start << PAGE_SHIFT;
+ pte_t *ptep = kvm_pte_for_gpa(kvm, NULL, gpa);
+
+ if (ptep && pte_present(*ptep) && pte_young(*ptep))
+ return true;
+
+ return false;
+}
+
+/**
+ * kvm_map_page_fast() - Fast path GPA fault handler.
+ * @vcpu: VCPU pointer.
+ * @gpa: Guest physical address of fault.
+ * @write: Whether the fault was due to a write.
+ *
+ * Perform fast path GPA fault handling, doing all that can be done without
+ * calling into KVM. This handles marking old pages young (for idle page
+ * tracking), and dirtying of clean pages (for dirty page logging).
+ *
+ * Returns: 0 on success, in which case we can update derived mappings and
+ * resume guest execution.
+ * -EFAULT on failure due to absent GPA mapping or write to
+ * read-only page, in which case KVM must be consulted.
+ */
+static int kvm_map_page_fast(struct kvm_vcpu *vcpu, unsigned long gpa,
+ bool write)
+{
+ struct kvm *kvm = vcpu->kvm;
+ gfn_t gfn = gpa >> PAGE_SHIFT;
+ pte_t *ptep;
+ kvm_pfn_t pfn = 0;
+ bool pfn_valid = false;
+ int ret = 0;
+
+ spin_lock(&kvm->mmu_lock);
+
+ /* Fast path - just check GPA page table for an existing entry */
+ ptep = kvm_pte_for_gpa(kvm, NULL, gpa);
+ if (!ptep || !pte_present(*ptep)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ /* Track access to pages marked old */
+ if (!pte_young(*ptep)) {
+ set_pte(ptep, pte_mkyoung(*ptep));
+ pfn = pte_pfn(*ptep);
+ pfn_valid = true;
+ /* call kvm_set_pfn_accessed() after unlock */
+ }
+ if (write && !pte_dirty(*ptep)) {
+ if (!pte_write(*ptep)) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ /* Track dirtying of writeable pages */
+ set_pte(ptep, pte_mkdirty(*ptep));
+ pfn = pte_pfn(*ptep);
+ mark_page_dirty(kvm, gfn);
+ kvm_set_pfn_dirty(pfn);
+ }
+
+out:
+ spin_unlock(&kvm->mmu_lock);
+ if (pfn_valid)
+ kvm_set_pfn_accessed(pfn);
+ return ret;
+}
+
+/**
+ * kvm_map_page() - Map a guest physical page.
+ * @vcpu: VCPU pointer.
+ * @gpa: Guest physical address of fault.
+ * @write: Whether the fault was due to a write.
+ *
+ * Handle GPA faults by creating a new GPA mapping (or updating an existing
+ * one).
+ *
+ * This takes care of marking pages young or dirty (idle/dirty page tracking),
+ * asking KVM for the corresponding PFN, and creating a mapping in the GPA page
+ * tables. Derived mappings (GVA page tables and TLBs) must be handled by the
+ * caller.
+ *
+ * Returns: 0 on success
+ * -EFAULT if there is no memory region at @gpa or a write was
+ * attempted to a read-only memory region. This is usually handled
+ * as an MMIO access.
+ */
+static int kvm_map_page(struct kvm_vcpu *vcpu, unsigned long gpa, bool write)
+{
+ bool writeable;
+ int srcu_idx, err = 0, retry_no = 0;
+ unsigned long hva;
+ unsigned long mmu_seq;
+ unsigned long prot_bits;
+ pte_t *ptep, new_pte;
+ kvm_pfn_t pfn;
+ gfn_t gfn = gpa >> PAGE_SHIFT;
+ struct vm_area_struct *vma;
+ struct kvm *kvm = vcpu->kvm;
+ struct kvm_memory_slot *memslot;
+ struct kvm_mmu_memory_cache *memcache = &vcpu->arch.mmu_page_cache;
+
+ /* Try the fast path to handle old / clean pages */
+ srcu_idx = srcu_read_lock(&kvm->srcu);
+ err = kvm_map_page_fast(vcpu, gpa, write);
+ if (!err)
+ goto out;
+
+ memslot = gfn_to_memslot(kvm, gfn);
+ hva = gfn_to_hva_memslot_prot(memslot, gfn, &writeable);
+ if (kvm_is_error_hva(hva) || (write && !writeable))
+ goto out;
+
+ mmap_read_lock(current->mm);
+ vma = find_vma_intersection(current->mm, hva, hva + 1);
+ if (unlikely(!vma)) {
+ kvm_err("Failed to find VMA for hva 0x%lx\n", hva);
+ mmap_read_unlock(current->mm);
+ err = -EFAULT;
+ goto out;
+ }
+ mmap_read_unlock(current->mm);
+
+ /* We need a minimum of cached pages ready for page table creation */
+ err = kvm_mmu_topup_memory_cache(memcache, KVM_MMU_CACHE_MIN_PAGES);
+ if (err)
+ goto out;
+
+retry:
+ /*
+ * Used to check for invalidations in progress, of the pfn that is
+ * returned by pfn_to_pfn_prot below.
+ */
+ mmu_seq = kvm->mmu_invalidate_seq;
+ /*
+ * Ensure the read of mmu_invalidate_seq isn't reordered with PTE reads in
+ * gfn_to_pfn_prot() (which calls get_user_pages()), so that we don't
+ * risk the page we get a reference to getting unmapped before we have a
+ * chance to grab the mmu_lock without mmu_invalidate_retry() noticing.
+ *
+ * This smp_rmb() pairs with the effective smp_wmb() of the combination
+ * of the pte_unmap_unlock() after the PTE is zapped, and the
+ * spin_lock() in kvm_mmu_invalidate_invalidate_<page|range_end>() before
+ * mmu_invalidate_seq is incremented.
+ */
+ smp_rmb();
+
+ /* Slow path - ask KVM core whether we can access this GPA */
+ pfn = gfn_to_pfn_prot(kvm, gfn, write, &writeable);
+ if (is_error_noslot_pfn(pfn)) {
+ err = -EFAULT;
+ goto out;
+ }
+
+ spin_lock(&kvm->mmu_lock);
+ /* Check if an invalidation has taken place since we got pfn */
+ if (mmu_invalidate_retry(kvm, mmu_seq)) {
+ /*
+ * This can happen when mappings are changed asynchronously, but
+ * also synchronously if a COW is triggered by
+ * gfn_to_pfn_prot().
+ */
+ spin_unlock(&kvm->mmu_lock);
+ kvm_set_pfn_accessed(pfn);
+ kvm_release_pfn_clean(pfn);
+ if (retry_no > 100) {
+ retry_no = 0;
+ schedule();
+ }
+ retry_no++;
+ goto retry;
+ }
+
+ /*
+ * For emulated devices such virtio device, actual cache attribute is
+ * determined by physical machine.
+ * For pass through physical device, it should be uncachable
+ */
+ prot_bits = _PAGE_PRESENT | __READABLE;
+ if (vma->vm_flags & (VM_IO | VM_PFNMAP))
+ prot_bits |= _CACHE_SUC;
+ else
+ prot_bits |= _CACHE_CC;
+
+ if (writeable) {
+ prot_bits |= _PAGE_WRITE;
+ if (write) {
+ prot_bits |= __WRITEABLE;
+ mark_page_dirty(kvm, gfn);
+ kvm_set_pfn_dirty(pfn);
+ }
+ }
+
+ /* Ensure page tables are allocated */
+ ptep = kvm_pte_for_gpa(kvm, memcache, gpa);
+ new_pte = pfn_pte(pfn, __pgprot(prot_bits));
+ set_pte(ptep, new_pte);
+
+ err = 0;
+ spin_unlock(&kvm->mmu_lock);
+ kvm_release_pfn_clean(pfn);
+ kvm_set_pfn_accessed(pfn);
+out:
+ srcu_read_unlock(&kvm->srcu, srcu_idx);
+ return err;
+}
+
+int kvm_handle_mm_fault(struct kvm_vcpu *vcpu, unsigned long gpa, bool write)
+{
+ int ret;
+
+ ret = kvm_map_page(vcpu, gpa, write);
+ if (ret)
+ return ret;
+
+ /* Invalidate this entry in the TLB */
+ return kvm_flush_tlb_gpa(vcpu, gpa);
+}
+
+void kvm_arch_sync_dirty_log(struct kvm *kvm, struct kvm_memory_slot *memslot)
+{
+
+}
+
+int kvm_arch_prepare_memory_region(struct kvm *kvm,
+ const struct kvm_memory_slot *old,
+ struct kvm_memory_slot *new,
+ enum kvm_mr_change change)
+{
+ return 0;
+}
+
+void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm,
+ const struct kvm_memory_slot *memslot)
+{
+ kvm_flush_remote_tlbs(kvm);
+}
--
2.31.1
Implement handle fpu exception, using kvm_own_fpu to enable fpu for
guest.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/exit.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index 0daf18242657..e218e7a43447 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -633,3 +633,29 @@ static int _kvm_handle_read_fault(struct kvm_vcpu *vcpu)
{
return _kvm_handle_mmu_fault(vcpu, false);
}
+
+/**
+ * _kvm_handle_fpu_disabled() - Guest used fpu however it is disabled at host
+ * @vcpu: Virtual CPU context.
+ *
+ * Handle when the guest attempts to use fpu which hasn't been allowed
+ * by the root context.
+ */
+static int _kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu)
+{
+ struct kvm_run *run = vcpu->run;
+
+ /*
+ * If guest FPU not present, the FPU operation should have been
+ * treated as a reserved instruction!
+ * If FPU already in use, we shouldn't get this at all.
+ */
+ if (WARN_ON(vcpu->arch.aux_inuse & KVM_LARCH_FPU)) {
+ kvm_err("%s internal error\n", __func__);
+ run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ return RESUME_HOST;
+ }
+
+ kvm_own_fpu(vcpu);
+ return RESUME_GUEST;
+}
--
2.31.1
Implement handle mmio exception, setting the mmio info into vcpu_run and
return to user space to handle it.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/exit.c | 308 ++++++++++++++++++++++++++++++++++++++
1 file changed, 308 insertions(+)
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index 717ef01338db..0daf18242657 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -211,6 +211,265 @@ int _kvm_emu_idle(struct kvm_vcpu *vcpu)
return EMULATE_DONE;
}
+int _kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst)
+{
+ struct kvm_run *run = vcpu->run;
+ unsigned int rd, op8, opcode;
+ unsigned long rd_val = 0;
+ void *data = run->mmio.data;
+ unsigned long curr_pc;
+ int ret;
+
+ /*
+ * Update PC and hold onto current PC in case there is
+ * an error and we want to rollback the PC
+ */
+ curr_pc = vcpu->arch.pc;
+ update_pc(&vcpu->arch);
+
+ op8 = (inst.word >> 24) & 0xff;
+ run->mmio.phys_addr = vcpu->arch.badv;
+ ret = EMULATE_DO_MMIO;
+ if (op8 < 0x28) {
+ /* stptrw/d process */
+ rd = inst.reg2i14_format.rd;
+ opcode = inst.reg2i14_format.opcode;
+
+ switch (opcode) {
+ case stptrd_op:
+ run->mmio.len = 8;
+ *(unsigned long *)data = vcpu->arch.gprs[rd];
+ break;
+ case stptrw_op:
+ run->mmio.len = 4;
+ *(unsigned int *)data = vcpu->arch.gprs[rd];
+ break;
+ default:
+ ret = EMULATE_FAIL;
+ break;
+ }
+ } else if (op8 < 0x30) {
+ /* st.b/h/w/d process */
+ rd = inst.reg2i12_format.rd;
+ opcode = inst.reg2i12_format.opcode;
+ rd_val = vcpu->arch.gprs[rd];
+
+ switch (opcode) {
+ case std_op:
+ run->mmio.len = 8;
+ *(unsigned long *)data = rd_val;
+ break;
+ case stw_op:
+ run->mmio.len = 4;
+ *(unsigned int *)data = rd_val;
+ break;
+ case sth_op:
+ run->mmio.len = 2;
+ *(unsigned short *)data = rd_val;
+ break;
+ case stb_op:
+ run->mmio.len = 1;
+ *(unsigned char *)data = rd_val;
+ break;
+ default:
+ ret = EMULATE_FAIL;
+ break;
+ }
+ } else if (op8 == 0x38) {
+ /* stxb/h/w/d process */
+ rd = inst.reg3_format.rd;
+ opcode = inst.reg3_format.opcode;
+
+ switch (opcode) {
+ case stxb_op:
+ run->mmio.len = 1;
+ *(unsigned char *)data = vcpu->arch.gprs[rd];
+ break;
+ case stxh_op:
+ run->mmio.len = 2;
+ *(unsigned short *)data = vcpu->arch.gprs[rd];
+ break;
+ case stxw_op:
+ run->mmio.len = 4;
+ *(unsigned int *)data = vcpu->arch.gprs[rd];
+ break;
+ case stxd_op:
+ run->mmio.len = 8;
+ *(unsigned long *)data = vcpu->arch.gprs[rd];
+ break;
+ default:
+ ret = EMULATE_FAIL;
+ break;
+ }
+ } else
+ ret = EMULATE_FAIL;
+
+ if (ret == EMULATE_DO_MMIO) {
+ run->mmio.is_write = 1;
+ vcpu->mmio_needed = 1;
+ vcpu->mmio_is_write = 1;
+ } else {
+ vcpu->arch.pc = curr_pc;
+ kvm_err("Write not supporded inst=0x%08x @%lx BadVaddr:%#lx\n",
+ inst.word, vcpu->arch.pc, vcpu->arch.badv);
+ kvm_arch_vcpu_dump_regs(vcpu);
+ /* Rollback PC if emulation was unsuccessful */
+ }
+
+ return ret;
+}
+
+int _kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst)
+{
+ unsigned int op8, opcode, rd;
+ struct kvm_run *run = vcpu->run;
+ int ret;
+
+ run->mmio.phys_addr = vcpu->arch.badv;
+ vcpu->mmio_needed = 2; /* signed */
+ op8 = (inst.word >> 24) & 0xff;
+ ret = EMULATE_DO_MMIO;
+
+ if (op8 < 0x28) {
+ /* ldptr.w/d process */
+ rd = inst.reg2i14_format.rd;
+ opcode = inst.reg2i14_format.opcode;
+
+ switch (opcode) {
+ case ldptrd_op:
+ run->mmio.len = 8;
+ break;
+ case ldptrw_op:
+ run->mmio.len = 4;
+ break;
+ default:
+ break;
+ }
+ } else if (op8 < 0x2f) {
+ /* ld.b/h/w/d, ld.bu/hu/wu process */
+ rd = inst.reg2i12_format.rd;
+ opcode = inst.reg2i12_format.opcode;
+
+ switch (opcode) {
+ case ldd_op:
+ run->mmio.len = 8;
+ break;
+ case ldwu_op:
+ vcpu->mmio_needed = 1; /* unsigned */
+ run->mmio.len = 4;
+ break;
+ case ldw_op:
+ run->mmio.len = 4;
+ break;
+ case ldhu_op:
+ vcpu->mmio_needed = 1; /* unsigned */
+ run->mmio.len = 2;
+ break;
+ case ldh_op:
+ run->mmio.len = 2;
+ break;
+ case ldbu_op:
+ vcpu->mmio_needed = 1; /* unsigned */
+ run->mmio.len = 1;
+ break;
+ case ldb_op:
+ run->mmio.len = 1;
+ break;
+ default:
+ ret = EMULATE_FAIL;
+ break;
+ }
+ } else if (op8 == 0x38) {
+ /* ldxb/h/w/d, ldxb/h/wu, ldgtb/h/w/d, ldleb/h/w/d process */
+ rd = inst.reg3_format.rd;
+ opcode = inst.reg3_format.opcode;
+
+ switch (opcode) {
+ case ldxb_op:
+ run->mmio.len = 1;
+ break;
+ case ldxbu_op:
+ run->mmio.len = 1;
+ vcpu->mmio_needed = 1; /* unsigned */
+ break;
+ case ldxh_op:
+ run->mmio.len = 2;
+ break;
+ case ldxhu_op:
+ run->mmio.len = 2;
+ vcpu->mmio_needed = 1; /* unsigned */
+ break;
+ case ldxw_op:
+ run->mmio.len = 4;
+ break;
+ case ldxwu_op:
+ run->mmio.len = 4;
+ vcpu->mmio_needed = 1; /* unsigned */
+ break;
+ case ldxd_op:
+ run->mmio.len = 8;
+ break;
+ default:
+ ret = EMULATE_FAIL;
+ break;
+ }
+ } else
+ ret = EMULATE_FAIL;
+
+ if (ret == EMULATE_DO_MMIO) {
+ /* Set for _kvm_complete_mmio_read use */
+ vcpu->arch.io_gpr = rd;
+ run->mmio.is_write = 0;
+ vcpu->mmio_is_write = 0;
+ } else {
+ kvm_err("Load not supporded inst=0x%08x @%lx BadVaddr:%#lx\n",
+ inst.word, vcpu->arch.pc, vcpu->arch.badv);
+ kvm_arch_vcpu_dump_regs(vcpu);
+ vcpu->mmio_needed = 0;
+ }
+ return ret;
+}
+
+int _kvm_complete_mmio_read(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+ unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr];
+ enum emulation_result er = EMULATE_DONE;
+
+ /* update with new PC */
+ update_pc(&vcpu->arch);
+ switch (run->mmio.len) {
+ case 8:
+ *gpr = *(s64 *)run->mmio.data;
+ break;
+ case 4:
+ if (vcpu->mmio_needed == 2)
+ *gpr = *(int *)run->mmio.data;
+ else
+ *gpr = *(unsigned int *)run->mmio.data;
+ break;
+ case 2:
+ if (vcpu->mmio_needed == 2)
+ *gpr = *(short *) run->mmio.data;
+ else
+ *gpr = *(unsigned short *)run->mmio.data;
+
+ break;
+ case 1:
+ if (vcpu->mmio_needed == 2)
+ *gpr = *(char *) run->mmio.data;
+ else
+ *gpr = *(unsigned char *) run->mmio.data;
+ break;
+ default:
+ kvm_err("Bad MMIO length: %d,addr is 0x%lx",
+ run->mmio.len, vcpu->arch.badv);
+ er = EMULATE_FAIL;
+ break;
+ }
+
+ return er;
+}
+
static int _kvm_trap_handle_gspr(struct kvm_vcpu *vcpu)
{
enum emulation_result er = EMULATE_DONE;
@@ -325,3 +584,52 @@ static int _kvm_handle_gspr(struct kvm_vcpu *vcpu)
}
return ret;
}
+
+static int _kvm_handle_mmu_fault(struct kvm_vcpu *vcpu, bool write)
+{
+ struct kvm_run *run = vcpu->run;
+ unsigned long badv = vcpu->arch.badv;
+ larch_inst inst;
+ enum emulation_result er = EMULATE_DONE;
+ int ret;
+
+ ret = kvm_handle_mm_fault(vcpu, badv, write);
+ if (ret) {
+ /* Treat as MMIO */
+ inst.word = vcpu->arch.badi;
+ if (write) {
+ er = _kvm_emu_mmio_write(vcpu, inst);
+ } else {
+ /* A code fetch fault doesn't count as an MMIO */
+ if (kvm_is_ifetch_fault(&vcpu->arch)) {
+ kvm_err("%s ifetch error addr:%lx\n", __func__, badv);
+ run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ return RESUME_HOST;
+ }
+
+ er = _kvm_emu_mmio_read(vcpu, inst);
+ }
+ }
+
+ if (er == EMULATE_DONE) {
+ ret = RESUME_GUEST;
+ } else if (er == EMULATE_DO_MMIO) {
+ run->exit_reason = KVM_EXIT_MMIO;
+ ret = RESUME_HOST;
+ } else {
+ run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ ret = RESUME_HOST;
+ }
+
+ return ret;
+}
+
+static int _kvm_handle_write_fault(struct kvm_vcpu *vcpu)
+{
+ return _kvm_handle_mmu_fault(vcpu, true);
+}
+
+static int _kvm_handle_read_fault(struct kvm_vcpu *vcpu)
+{
+ return _kvm_handle_mmu_fault(vcpu, false);
+}
--
2.31.1
Implement kvm exception vector, using _kvm_fault_tables array to save
the handle function pointer and it is used when vcpu handle exit.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/exit.c | 48 +++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index e218e7a43447..1a4be2af3926 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -659,3 +659,51 @@ static int _kvm_handle_fpu_disabled(struct kvm_vcpu *vcpu)
kvm_own_fpu(vcpu);
return RESUME_GUEST;
}
+
+/*
+ * Loongarch KVM callback handling for not implemented guest exiting
+ */
+static int _kvm_fault_ni(struct kvm_vcpu *vcpu)
+{
+ unsigned long estat, badv;
+ unsigned int exccode, inst;
+
+ /*
+ * Fetch the instruction.
+ */
+ badv = vcpu->arch.badv;
+ estat = vcpu->arch.host_estat;
+ exccode = (estat & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT;
+ inst = vcpu->arch.badi;
+ kvm_err("Exccode: %d PC=%#lx inst=0x%08x BadVaddr=%#lx estat=%#llx\n",
+ exccode, vcpu->arch.pc, inst, badv, read_gcsr_estat());
+ kvm_arch_vcpu_dump_regs(vcpu);
+ vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+
+ return RESUME_HOST;
+}
+
+static exit_handle_fn _kvm_fault_tables[EXCCODE_INT_START] = {
+ [EXCCODE_TLBL] = _kvm_handle_read_fault,
+ [EXCCODE_TLBI] = _kvm_handle_read_fault,
+ [EXCCODE_TLBNR] = _kvm_handle_read_fault,
+ [EXCCODE_TLBNX] = _kvm_handle_read_fault,
+ [EXCCODE_TLBS] = _kvm_handle_write_fault,
+ [EXCCODE_TLBM] = _kvm_handle_write_fault,
+ [EXCCODE_FPDIS] = _kvm_handle_fpu_disabled,
+ [EXCCODE_GSPR] = _kvm_handle_gspr,
+};
+
+int _kvm_handle_fault(struct kvm_vcpu *vcpu, int fault)
+{
+ return _kvm_fault_tables[fault](vcpu);
+}
+
+void _kvm_init_fault(void)
+{
+ int i;
+
+ for (i = 0; i < EXCCODE_INT_START; i++)
+ if (!_kvm_fault_tables[i])
+ _kvm_fault_tables[i] = _kvm_fault_ni;
+}
--
2.31.1
Implement probe virtualization when loongarch cpu init, including guest
gid info, guest fpu info, etc.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kernel/cpu-probe.c | 53 +++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
index 3a3fce2d7846..9c3483d9a131 100644
--- a/arch/loongarch/kernel/cpu-probe.c
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -176,6 +176,57 @@ static void cpu_probe_common(struct cpuinfo_loongarch *c)
}
}
+static inline void cpu_probe_guestinfo(struct cpuinfo_loongarch *c)
+{
+ unsigned long guestinfo;
+
+ guestinfo = read_csr_gstat();
+ if (guestinfo & CSR_GSTAT_GIDBIT) {
+ c->options |= LOONGARCH_CPU_GUESTID;
+ write_csr_gstat(0);
+ }
+}
+
+static inline void cpu_probe_lvz(struct cpuinfo_loongarch *c)
+{
+ unsigned long gcfg, gprcfg1;
+
+ cpu_probe_guestinfo(c);
+
+ c->guest.options |= LOONGARCH_CPU_FPU;
+ c->guest.options_dyn |= LOONGARCH_CPU_FPU;
+ c->guest.options_dyn |= LOONGARCH_CPU_PMP;
+
+ c->guest.ases |= LOONGARCH_CPU_LSX;
+ c->guest.ases_dyn |= LOONGARCH_CPU_LSX;
+ gprcfg1 = read_gcsr_prcfg1();
+ c->guest.kscratch_mask = GENMASK((gprcfg1 & CSR_CONF1_KSNUM) - 1, 0);
+
+ gcfg = read_csr_gcfg();
+ if (gcfg & CSR_GCFG_MATP_GUEST)
+ c->guest_cfg |= BIT(0);
+ if (gcfg & CSR_GCFG_MATP_ROOT)
+ c->guest_cfg |= BIT(1);
+ if (gcfg & CSR_GCFG_MATP_NEST)
+ c->guest_cfg |= BIT(2);
+ if (gcfg & CSR_GCFG_SITP)
+ c->guest_cfg |= BIT(6);
+ if (gcfg & CSR_GCFG_TITP)
+ c->guest_cfg |= BIT(8);
+ if (gcfg & CSR_GCFG_TOEP)
+ c->guest_cfg |= BIT(10);
+ if (gcfg & CSR_GCFG_TOPP)
+ c->guest_cfg |= BIT(12);
+ if (gcfg & CSR_GCFG_TORUP)
+ c->guest_cfg |= BIT(14);
+ if (gcfg & CSR_GCFG_GCIP_ALL)
+ c->guest_cfg |= BIT(16);
+ if (gcfg & CSR_GCFG_GCIP_HIT)
+ c->guest_cfg |= BIT(17);
+ if (gcfg & CSR_GCFG_GCIP_SECURE)
+ c->guest_cfg |= BIT(18);
+}
+
#define MAX_NAME_LEN 32
#define VENDOR_OFFSET 0
#define CPUNAME_OFFSET 9
@@ -289,6 +340,8 @@ void cpu_probe(void)
if (cpu == 0)
__ua_limit = ~((1ull << cpu_vabits) - 1);
#endif
+ if (cpu_has_lvz)
+ cpu_probe_lvz(c);
cpu_report();
}
--
2.31.1
Implement loongarch vcpu world switch, including vcpu enter guest and
vcpu exit from guest, both operations need to save or restore the host
and guest registers.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kernel/asm-offsets.c | 32 +++
arch/loongarch/kvm/switch.S | 301 ++++++++++++++++++++++++++++
2 files changed, 333 insertions(+)
create mode 100644 arch/loongarch/kvm/switch.S
diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c
index 4bdb203fc66e..0c055f2046eb 100644
--- a/arch/loongarch/kernel/asm-offsets.c
+++ b/arch/loongarch/kernel/asm-offsets.c
@@ -9,6 +9,7 @@
#include <linux/mm.h>
#include <linux/kbuild.h>
#include <linux/suspend.h>
+#include <linux/kvm_host.h>
#include <asm/cpu-info.h>
#include <asm/ptrace.h>
#include <asm/processor.h>
@@ -272,3 +273,34 @@ void output_pbe_defines(void)
BLANK();
}
#endif
+
+static void __used output_kvm_defines(void)
+{
+ COMMENT(" KVM/LOONGARCH Specific offsets. ");
+
+ OFFSET(VCPU_FCSR0, kvm_vcpu_arch, fpu.fcsr);
+ OFFSET(VCPU_FCC, kvm_vcpu_arch, fpu.fcc);
+ BLANK();
+
+ OFFSET(KVM_VCPU_ARCH, kvm_vcpu, arch);
+ OFFSET(KVM_VCPU_KVM, kvm_vcpu, kvm);
+ OFFSET(KVM_VCPU_RUN, kvm_vcpu, run);
+ BLANK();
+
+ OFFSET(KVM_ARCH_HSTACK, kvm_vcpu_arch, host_stack);
+ OFFSET(KVM_ARCH_HGP, kvm_vcpu_arch, host_gp);
+ OFFSET(KVM_ARCH_HANDLE_EXIT, kvm_vcpu_arch, handle_exit);
+ OFFSET(KVM_ARCH_HPGD, kvm_vcpu_arch, host_pgd);
+ OFFSET(KVM_ARCH_GEENTRY, kvm_vcpu_arch, guest_eentry);
+ OFFSET(KVM_ARCH_GPC, kvm_vcpu_arch, pc);
+ OFFSET(KVM_ARCH_GGPR, kvm_vcpu_arch, gprs);
+ OFFSET(KVM_ARCH_HESTAT, kvm_vcpu_arch, host_estat);
+ OFFSET(KVM_ARCH_HBADV, kvm_vcpu_arch, badv);
+ OFFSET(KVM_ARCH_HBADI, kvm_vcpu_arch, badi);
+ OFFSET(KVM_ARCH_HECFG, kvm_vcpu_arch, host_ecfg);
+ OFFSET(KVM_ARCH_HEENTRY, kvm_vcpu_arch, host_eentry);
+ OFFSET(KVM_ARCH_HPERCPU, kvm_vcpu_arch, host_percpu);
+
+ OFFSET(KVM_GPGD, kvm, arch.gpa_mm.pgd);
+ BLANK();
+}
diff --git a/arch/loongarch/kvm/switch.S b/arch/loongarch/kvm/switch.S
new file mode 100644
index 000000000000..1f36ae84ccc3
--- /dev/null
+++ b/arch/loongarch/kvm/switch.S
@@ -0,0 +1,301 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
+ */
+
+#include <linux/linkage.h>
+#include <asm/stackframe.h>
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/regdef.h>
+#include <asm/loongarch.h>
+#include <asm/export.h>
+
+#define PT_GPR_OFFSET(x) (PT_R0 + 8*x)
+ .text
+
+.macro kvm_save_host_gpr base
+ .irp n,1,2,3,22,23,24,25,26,27,28,29,30,31
+ st.d $r\n, \base, PT_GPR_OFFSET(\n)
+ .endr
+.endm
+
+.macro kvm_restore_host_gpr base
+ .irp n,1,2,3,22,23,24,25,26,27,28,29,30,31
+ ld.d $r\n, \base, PT_GPR_OFFSET(\n)
+ .endr
+.endm
+
+/*
+ * prepare switch to guest, save host reg and restore guest reg.
+ * a2: kvm_vcpu_arch, don't touch it until 'ertn'
+ * t0, t1: temp register
+ */
+.macro kvm_switch_to_guest
+ /* set host excfg.VS=0, all exceptions share one exception entry */
+ csrrd t0, LOONGARCH_CSR_ECFG
+ bstrins.w t0, zero, CSR_ECFG_VS_SHIFT_END, CSR_ECFG_VS_SHIFT
+ csrwr t0, LOONGARCH_CSR_ECFG
+
+ /* Load up the new EENTRY */
+ ld.d t0, a2, KVM_ARCH_GEENTRY
+ csrwr t0, LOONGARCH_CSR_EENTRY
+
+ /* Set Guest ERA */
+ ld.d t0, a2, KVM_ARCH_GPC
+ csrwr t0, LOONGARCH_CSR_ERA
+
+ /* Save host PGDL */
+ csrrd t0, LOONGARCH_CSR_PGDL
+ st.d t0, a2, KVM_ARCH_HPGD
+
+ /* Switch to kvm */
+ ld.d t1, a2, KVM_VCPU_KVM - KVM_VCPU_ARCH
+
+ /* Load guest PGDL */
+ lu12i.w t0, KVM_GPGD
+ srli.w t0, t0, 12
+ ldx.d t0, t1, t0
+ csrwr t0, LOONGARCH_CSR_PGDL
+
+ /* Mix GID and RID */
+ csrrd t1, LOONGARCH_CSR_GSTAT
+ bstrpick.w t1, t1, CSR_GSTAT_GID_SHIFT_END, CSR_GSTAT_GID_SHIFT
+ csrrd t0, LOONGARCH_CSR_GTLBC
+ bstrins.w t0, t1, CSR_GTLBC_TGID_SHIFT_END, CSR_GTLBC_TGID_SHIFT
+ csrwr t0, LOONGARCH_CSR_GTLBC
+
+ /*
+ * Switch to guest:
+ * GSTAT.PGM = 1, ERRCTL.ISERR = 0, TLBRPRMD.ISTLBR = 0
+ * ertn
+ */
+
+ /*
+ * Enable intr in root mode with future ertn so that host interrupt
+ * can be responsed during VM runs
+ * guest crmd comes from separate gcsr_CRMD register
+ */
+ ori t0, zero, CSR_PRMD_PIE
+ csrxchg t0, t0, LOONGARCH_CSR_PRMD
+
+ /* Set PVM bit to setup ertn to guest context */
+ ori t0, zero, CSR_GSTAT_PVM
+ csrxchg t0, t0, LOONGARCH_CSR_GSTAT
+
+ /* Load Guest gprs */
+ ld.d ra, a2, (KVM_ARCH_GGPR + 8 * REG_RA)
+ ld.d tp, a2, (KVM_ARCH_GGPR + 8 * REG_TP)
+ ld.d sp, a2, (KVM_ARCH_GGPR + 8 * REG_SP)
+ ld.d a0, a2, (KVM_ARCH_GGPR + 8 * REG_A0)
+ ld.d a1, a2, (KVM_ARCH_GGPR + 8 * REG_A1)
+ ld.d a3, a2, (KVM_ARCH_GGPR + 8 * REG_A3)
+ ld.d a4, a2, (KVM_ARCH_GGPR + 8 * REG_A4)
+ ld.d a5, a2, (KVM_ARCH_GGPR + 8 * REG_A5)
+ ld.d a6, a2, (KVM_ARCH_GGPR + 8 * REG_A6)
+ ld.d a7, a2, (KVM_ARCH_GGPR + 8 * REG_A7)
+ ld.d t0, a2, (KVM_ARCH_GGPR + 8 * REG_T0)
+ ld.d t1, a2, (KVM_ARCH_GGPR + 8 * REG_T1)
+ ld.d t2, a2, (KVM_ARCH_GGPR + 8 * REG_T2)
+ ld.d t3, a2, (KVM_ARCH_GGPR + 8 * REG_T3)
+ ld.d t4, a2, (KVM_ARCH_GGPR + 8 * REG_T4)
+ ld.d t5, a2, (KVM_ARCH_GGPR + 8 * REG_T5)
+ ld.d t6, a2, (KVM_ARCH_GGPR + 8 * REG_T6)
+ ld.d t7, a2, (KVM_ARCH_GGPR + 8 * REG_T7)
+ ld.d t8, a2, (KVM_ARCH_GGPR + 8 * REG_T8)
+ ld.d u0, a2, (KVM_ARCH_GGPR + 8 * REG_U0)
+ ld.d fp, a2, (KVM_ARCH_GGPR + 8 * REG_FP)
+ ld.d s0, a2, (KVM_ARCH_GGPR + 8 * REG_S0)
+ ld.d s1, a2, (KVM_ARCH_GGPR + 8 * REG_S1)
+ ld.d s2, a2, (KVM_ARCH_GGPR + 8 * REG_S2)
+ ld.d s3, a2, (KVM_ARCH_GGPR + 8 * REG_S3)
+ ld.d s4, a2, (KVM_ARCH_GGPR + 8 * REG_S4)
+ ld.d s5, a2, (KVM_ARCH_GGPR + 8 * REG_S5)
+ ld.d s6, a2, (KVM_ARCH_GGPR + 8 * REG_S6)
+ ld.d s7, a2, (KVM_ARCH_GGPR + 8 * REG_S7)
+ ld.d s8, a2, (KVM_ARCH_GGPR + 8 * REG_S8)
+ /* Load KVM_ARCH register */
+ ld.d a2, a2, (KVM_ARCH_GGPR + 8 * REG_A2)
+
+ ertn
+.endm
+
+ /*
+ * exception entry for general exception from guest mode
+ * - IRQ is disabled
+ * - kernel privilege in root mode
+ * - page mode keep unchanged from previous prmd in root mode
+ * - Fixme: tlb exception cannot happen since registers relative with TLB
+ * - is still in guest mode, such as pgd table/vmid registers etc,
+ * - will fix with hw page walk enabled in future
+ * load kvm_vcpu from reserved CSR KVM_VCPU_KS, and save a2 to KVM_TEMP_KS
+ */
+ .section .text
+ .cfi_sections .debug_frame
+SYM_CODE_START(kvm_vector_entry)
+ csrwr a2, KVM_TEMP_KS
+ csrrd a2, KVM_VCPU_KS
+ addi.d a2, a2, KVM_VCPU_ARCH
+
+ /* After save gprs, free to use any gpr */
+ st.d ra, a2, (KVM_ARCH_GGPR + 8 * REG_RA)
+ st.d tp, a2, (KVM_ARCH_GGPR + 8 * REG_TP)
+ st.d sp, a2, (KVM_ARCH_GGPR + 8 * REG_SP)
+ st.d a0, a2, (KVM_ARCH_GGPR + 8 * REG_A0)
+ st.d a1, a2, (KVM_ARCH_GGPR + 8 * REG_A1)
+ st.d a3, a2, (KVM_ARCH_GGPR + 8 * REG_A3)
+ st.d a4, a2, (KVM_ARCH_GGPR + 8 * REG_A4)
+ st.d a5, a2, (KVM_ARCH_GGPR + 8 * REG_A5)
+ st.d a6, a2, (KVM_ARCH_GGPR + 8 * REG_A6)
+ st.d a7, a2, (KVM_ARCH_GGPR + 8 * REG_A7)
+ st.d t0, a2, (KVM_ARCH_GGPR + 8 * REG_T0)
+ st.d t1, a2, (KVM_ARCH_GGPR + 8 * REG_T1)
+ st.d t2, a2, (KVM_ARCH_GGPR + 8 * REG_T2)
+ st.d t3, a2, (KVM_ARCH_GGPR + 8 * REG_T3)
+ st.d t4, a2, (KVM_ARCH_GGPR + 8 * REG_T4)
+ st.d t5, a2, (KVM_ARCH_GGPR + 8 * REG_T5)
+ st.d t6, a2, (KVM_ARCH_GGPR + 8 * REG_T6)
+ st.d t7, a2, (KVM_ARCH_GGPR + 8 * REG_T7)
+ st.d t8, a2, (KVM_ARCH_GGPR + 8 * REG_T8)
+ st.d u0, a2, (KVM_ARCH_GGPR + 8 * REG_U0)
+ st.d fp, a2, (KVM_ARCH_GGPR + 8 * REG_FP)
+ st.d s0, a2, (KVM_ARCH_GGPR + 8 * REG_S0)
+ st.d s1, a2, (KVM_ARCH_GGPR + 8 * REG_S1)
+ st.d s2, a2, (KVM_ARCH_GGPR + 8 * REG_S2)
+ st.d s3, a2, (KVM_ARCH_GGPR + 8 * REG_S3)
+ st.d s4, a2, (KVM_ARCH_GGPR + 8 * REG_S4)
+ st.d s5, a2, (KVM_ARCH_GGPR + 8 * REG_S5)
+ st.d s6, a2, (KVM_ARCH_GGPR + 8 * REG_S6)
+ st.d s7, a2, (KVM_ARCH_GGPR + 8 * REG_S7)
+ st.d s8, a2, (KVM_ARCH_GGPR + 8 * REG_S8)
+ /* Save guest a2 */
+ csrrd t0, KVM_TEMP_KS
+ st.d t0, a2, (KVM_ARCH_GGPR + 8 * REG_A2)
+
+ /* a2: kvm_vcpu_arch, a1 is free to use */
+ csrrd s1, KVM_VCPU_KS
+ ld.d s0, s1, KVM_VCPU_RUN
+
+ csrrd t0, LOONGARCH_CSR_ESTAT
+ st.d t0, a2, KVM_ARCH_HESTAT
+ csrrd t0, LOONGARCH_CSR_ERA
+ st.d t0, a2, KVM_ARCH_GPC
+ csrrd t0, LOONGARCH_CSR_BADV
+ st.d t0, a2, KVM_ARCH_HBADV
+ csrrd t0, LOONGARCH_CSR_BADI
+ st.d t0, a2, KVM_ARCH_HBADI
+
+ /* Restore host excfg.VS */
+ csrrd t0, LOONGARCH_CSR_ECFG
+ ld.d t1, a2, KVM_ARCH_HECFG
+ or t0, t0, t1
+ csrwr t0, LOONGARCH_CSR_ECFG
+
+ /* Restore host eentry */
+ ld.d t0, a2, KVM_ARCH_HEENTRY
+ csrwr t0, LOONGARCH_CSR_EENTRY
+
+ /* restore host pgd table */
+ ld.d t0, a2, KVM_ARCH_HPGD
+ csrwr t0, LOONGARCH_CSR_PGDL
+
+ /*
+ * Disable PGM bit to enter root mode by default with next ertn
+ */
+ ori t0, zero, CSR_GSTAT_PVM
+ csrxchg zero, t0, LOONGARCH_CSR_GSTAT
+ /*
+ * Clear GTLBC.TGID field
+ * 0: for root tlb update in future tlb instr
+ * others: for guest tlb update like gpa to hpa in future tlb instr
+ */
+ csrrd t0, LOONGARCH_CSR_GTLBC
+ bstrins.w t0, zero, CSR_GTLBC_TGID_SHIFT_END, CSR_GTLBC_TGID_SHIFT
+ csrwr t0, LOONGARCH_CSR_GTLBC
+ ld.d tp, a2, KVM_ARCH_HGP
+ ld.d sp, a2, KVM_ARCH_HSTACK
+ /* restore per cpu register */
+ ld.d u0, a2, KVM_ARCH_HPERCPU
+ addi.d sp, sp, -PT_SIZE
+
+ /* Prepare handle exception */
+ or a0, s0, zero
+ or a1, s1, zero
+ ld.d t8, a2, KVM_ARCH_HANDLE_EXIT
+ jirl ra,t8, 0
+
+ or a2, s1, zero
+ addi.d a2, a2, KVM_VCPU_ARCH
+
+ /* resume host when ret <= 0 */
+ bge zero, a0, ret_to_host
+
+ /*
+ * return to guest
+ * save per cpu register again, maybe switched to another cpu
+ */
+ st.d u0, a2, KVM_ARCH_HPERCPU
+
+ /* Save kvm_vcpu to kscratch */
+ csrwr s1, KVM_VCPU_KS
+ kvm_switch_to_guest
+
+ret_to_host:
+ ld.d a2, a2, KVM_ARCH_HSTACK
+ addi.d a2, a2, -PT_SIZE
+ kvm_restore_host_gpr a2
+ jirl zero, ra, 0
+SYM_CODE_END(kvm_vector_entry)
+kvm_vector_entry_end:
+
+/*
+ * int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu)
+ *
+ * @register_param:
+ * a0: kvm_run* run
+ * a1: kvm_vcpu* vcpu
+ */
+SYM_FUNC_START(kvm_enter_guest)
+ /* allocate space in stack bottom */
+ addi.d a2, sp, -PT_SIZE
+ /* save host gprs */
+ kvm_save_host_gpr a2
+
+ /* save host crmd,prmd csr to stack */
+ csrrd a3, LOONGARCH_CSR_CRMD
+ st.d a3, a2, PT_CRMD
+ csrrd a3, LOONGARCH_CSR_PRMD
+ st.d a3, a2, PT_PRMD
+
+ addi.d a2, a1, KVM_VCPU_ARCH
+ st.d sp, a2, KVM_ARCH_HSTACK
+ st.d tp, a2, KVM_ARCH_HGP
+ /* Save per cpu register */
+ st.d u0, a2, KVM_ARCH_HPERCPU
+
+ /* Save kvm_vcpu to kscratch */
+ csrwr a1, KVM_VCPU_KS
+ kvm_switch_to_guest
+SYM_FUNC_END(kvm_enter_guest)
+kvm_enter_guest_end:
+
+ .section ".rodata"
+SYM_DATA(kvm_vector_size,
+ .quad kvm_vector_entry_end - kvm_vector_entry)
+SYM_DATA(kvm_enter_guest_size,
+ .quad kvm_enter_guest_end - kvm_enter_guest)
+
+
+SYM_FUNC_START(kvm_save_fpu)
+ fpu_save_csr a0 t1
+ fpu_save_double a0 t1
+ fpu_save_cc a0 t1 t2
+ jirl zero, ra, 0
+SYM_FUNC_END(kvm_save_fpu)
+
+SYM_FUNC_START(kvm_restore_fpu)
+ fpu_restore_double a0 t1
+ fpu_restore_csr a0 t1
+ fpu_restore_cc a0 t1 t2
+ jirl zero, ra, 0
+SYM_FUNC_END(kvm_restore_fpu)
--
2.31.1
Supplement kvm document about loongarch-specific part, such as add
api introduction for GET/SET_ONE_REG, GET/SET_FPU, GET/SET_MP_STATE,
etc.
Signed-off-by: Tianrui Zhao <[email protected]>
---
Documentation/virt/kvm/api.rst | 71 +++++++++++++++++++++++++++++-----
1 file changed, 62 insertions(+), 9 deletions(-)
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index a5c803f39832..84c26a577559 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -416,6 +416,12 @@ Reads the general purpose registers from the vcpu.
__u64 pc;
};
+ /* loongarch */
+ struct kvm_regs {
+ unsigned long gpr[32];
+ unsigned long pc;
+ };
+
4.12 KVM_SET_REGS
-----------------
@@ -506,7 +512,7 @@ translation mode.
------------------
:Capability: basic
-:Architectures: x86, ppc, mips, riscv
+:Architectures: x86, ppc, mips, riscv, loongarch
:Type: vcpu ioctl
:Parameters: struct kvm_interrupt (in)
:Returns: 0 on success, negative on failure.
@@ -592,6 +598,14 @@ b) KVM_INTERRUPT_UNSET
This is an asynchronous vcpu ioctl and can be invoked from any thread.
+LOONGARCH:
+^^^^^^^^^^
+
+Queues an external interrupt to be injected into the virtual CPU. A negative
+interrupt number dequeues the interrupt.
+
+This is an asynchronous vcpu ioctl and can be invoked from any thread.
+
4.17 KVM_DEBUG_GUEST
--------------------
@@ -737,7 +751,7 @@ signal mask.
----------------
:Capability: basic
-:Architectures: x86
+:Architectures: x86, loongarch
:Type: vcpu ioctl
:Parameters: struct kvm_fpu (out)
:Returns: 0 on success, -1 on error
@@ -746,7 +760,7 @@ Reads the floating point state from the vcpu.
::
- /* for KVM_GET_FPU and KVM_SET_FPU */
+ /* x86: for KVM_GET_FPU and KVM_SET_FPU */
struct kvm_fpu {
__u8 fpr[8][16];
__u16 fcw;
@@ -761,12 +775,22 @@ Reads the floating point state from the vcpu.
__u32 pad2;
};
+ /* loongarch: for KVM_GET_FPU and KVM_SET_FPU */
+ struct kvm_fpu {
+ __u32 fcsr;
+ __u32 none;
+ __u64 fcc;
+ struct kvm_fpureg {
+ __u64 val64[4];
+ }fpr[32];
+ };
+
4.23 KVM_SET_FPU
----------------
:Capability: basic
-:Architectures: x86
+:Architectures: x86, loongarch
:Type: vcpu ioctl
:Parameters: struct kvm_fpu (in)
:Returns: 0 on success, -1 on error
@@ -775,7 +799,7 @@ Writes the floating point state to the vcpu.
::
- /* for KVM_GET_FPU and KVM_SET_FPU */
+ /* x86: for KVM_GET_FPU and KVM_SET_FPU */
struct kvm_fpu {
__u8 fpr[8][16];
__u16 fcw;
@@ -790,6 +814,16 @@ Writes the floating point state to the vcpu.
__u32 pad2;
};
+ /* loongarch: for KVM_GET_FPU and KVM_SET_FPU */
+ struct kvm_fpu {
+ __u32 fcsr;
+ __u32 none;
+ __u64 fcc;
+ struct kvm_fpureg {
+ __u64 val64[4];
+ }fpr[32];
+ };
+
4.24 KVM_CREATE_IRQCHIP
-----------------------
@@ -1387,7 +1421,7 @@ documentation when it pops into existence).
-------------------
:Capability: KVM_CAP_ENABLE_CAP
-:Architectures: mips, ppc, s390, x86
+:Architectures: mips, ppc, s390, x86, loongarch
:Type: vcpu ioctl
:Parameters: struct kvm_enable_cap (in)
:Returns: 0 on success; -1 on error
@@ -1442,7 +1476,7 @@ for vm-wide capabilities.
---------------------
:Capability: KVM_CAP_MP_STATE
-:Architectures: x86, s390, arm64, riscv
+:Architectures: x86, s390, arm64, riscv, loongarch
:Type: vcpu ioctl
:Parameters: struct kvm_mp_state (out)
:Returns: 0 on success; -1 on error
@@ -1460,7 +1494,7 @@ Possible values are:
========================== ===============================================
KVM_MP_STATE_RUNNABLE the vcpu is currently running
- [x86,arm64,riscv]
+ [x86,arm64,riscv,loongarch]
KVM_MP_STATE_UNINITIALIZED the vcpu is an application processor (AP)
which has not yet received an INIT signal [x86]
KVM_MP_STATE_INIT_RECEIVED the vcpu has received an INIT signal, and is
@@ -1516,11 +1550,14 @@ For riscv:
The only states that are valid are KVM_MP_STATE_STOPPED and
KVM_MP_STATE_RUNNABLE which reflect if the vcpu is paused or not.
+On loongarch, the KVM_MP_STATE_RUNNABLE state is only used which reflect the
+vcpu is runnable.
+
4.39 KVM_SET_MP_STATE
---------------------
:Capability: KVM_CAP_MP_STATE
-:Architectures: x86, s390, arm64, riscv
+:Architectures: x86, s390, arm64, riscv, loongarch
:Type: vcpu ioctl
:Parameters: struct kvm_mp_state (in)
:Returns: 0 on success; -1 on error
@@ -1538,6 +1575,9 @@ For arm64/riscv:
The only states that are valid are KVM_MP_STATE_STOPPED and
KVM_MP_STATE_RUNNABLE which reflect if the vcpu should be paused or not.
+On loongarch, the KVM_MP_STATE_RUNNABLE state is only used which reflect the
+vcpu is runnable.
+
4.40 KVM_SET_IDENTITY_MAP_ADDR
------------------------------
@@ -2839,6 +2879,19 @@ Following are the RISC-V D-extension registers:
0x8020 0000 0600 0020 fcsr Floating point control and status register
======================= ========= =============================================
+LoongArch registers are mapped using the lower 32 bits. The upper 16 bits of
+that is the register group type.
+
+LoongArch csr registers are used to control guest cpu or get status of guest
+cpu, and they have the following id bit patterns::
+
+ 0x9030 0000 0001 00 <reg:5> <sel:3> (64-bit)
+
+LoongArch KVM control registers are used to implement some new defined functions
+such as set vcpu counter or reset vcpu, and they have the following id bit patterns::
+
+ 0x9030 0000 0002 <reg:16>
+
4.69 KVM_GET_ONE_REG
--------------------
--
2.31.1
Enable loongarch kvm config and add the makefile to support build kvm
module.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/Kbuild | 1 +
arch/loongarch/Kconfig | 2 ++
arch/loongarch/configs/loongson3_defconfig | 2 ++
arch/loongarch/kvm/Kconfig | 38 ++++++++++++++++++++++
arch/loongarch/kvm/Makefile | 22 +++++++++++++
5 files changed, 65 insertions(+)
create mode 100644 arch/loongarch/kvm/Kconfig
create mode 100644 arch/loongarch/kvm/Makefile
diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild
index b01f5cdb27e0..40be8a1696f9 100644
--- a/arch/loongarch/Kbuild
+++ b/arch/loongarch/Kbuild
@@ -2,6 +2,7 @@ obj-y += kernel/
obj-y += mm/
obj-y += net/
obj-y += vdso/
+obj-y += kvm/
# for cleaning
subdir- += boot
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 7fd51257e0ed..f9beeb2e0cc9 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -148,6 +148,7 @@ config LOONGARCH
select USE_PERCPU_NUMA_NODE_ID
select USER_STACKTRACE_SUPPORT
select ZONE_DMA32
+ select HAVE_KVM
config 32BIT
bool
@@ -588,3 +589,4 @@ source "drivers/acpi/Kconfig"
endmenu
source "drivers/firmware/Kconfig"
+source "arch/loongarch/kvm/Kconfig"
diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig
index e18213f01cc4..c99520374aef 100644
--- a/arch/loongarch/configs/loongson3_defconfig
+++ b/arch/loongarch/configs/loongson3_defconfig
@@ -63,6 +63,8 @@ CONFIG_EFI_ZBOOT=y
CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
CONFIG_EFI_CAPSULE_LOADER=m
CONFIG_EFI_TEST=m
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
CONFIG_MODULES=y
CONFIG_MODULE_FORCE_LOAD=y
CONFIG_MODULE_UNLOAD=y
diff --git a/arch/loongarch/kvm/Kconfig b/arch/loongarch/kvm/Kconfig
new file mode 100644
index 000000000000..8a999b4c0232
--- /dev/null
+++ b/arch/loongarch/kvm/Kconfig
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# KVM configuration
+#
+
+source "virt/kvm/Kconfig"
+
+menuconfig VIRTUALIZATION
+ bool "Virtualization"
+ help
+ Say Y here to get to see options for using your Linux host to run
+ other operating systems inside virtual machines (guests).
+ This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and
+ disabled.
+
+if VIRTUALIZATION
+
+config KVM
+ tristate "Kernel-based Virtual Machine (KVM) support"
+ depends on HAVE_KVM
+ select MMU_NOTIFIER
+ select ANON_INODES
+ select PREEMPT_NOTIFIERS
+ select KVM_MMIO
+ select KVM_GENERIC_DIRTYLOG_READ_PROTECT
+ select HAVE_KVM_VCPU_ASYNC_IOCTL
+ select HAVE_KVM_EVENTFD
+ select SRCU
+ help
+ Support hosting virtualized guest machines using hardware
+ virtualization extensions. You will need a fairly processor
+ equipped with virtualization extensions.
+
+ If unsure, say N.
+
+endif # VIRTUALIZATION
diff --git a/arch/loongarch/kvm/Makefile b/arch/loongarch/kvm/Makefile
new file mode 100644
index 000000000000..2335e873a6ef
--- /dev/null
+++ b/arch/loongarch/kvm/Makefile
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for LOONGARCH KVM support
+#
+
+ccflags-y += -I $(srctree)/$(src)
+
+include $(srctree)/virt/kvm/Makefile.kvm
+
+obj-$(CONFIG_KVM) += kvm.o
+
+kvm-y += main.o
+kvm-y += vm.o
+kvm-y += vmid.o
+kvm-y += tlb.o
+kvm-y += mmu.o
+kvm-y += vcpu.o
+kvm-y += exit.o
+kvm-y += interrupt.o
+kvm-y += timer.o
+kvm-y += switch.o
+kvm-y += csr_ops.o
--
2.31.1
Implement kvm handle loongarch vcpu idle exception, using kvm_vcpu_block
to emulate it.
Signed-off-by: Tianrui Zhao <[email protected]>
---
arch/loongarch/kvm/exit.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/loongarch/kvm/exit.c b/arch/loongarch/kvm/exit.c
index f151cc2dd0b9..3de08a4798fc 100644
--- a/arch/loongarch/kvm/exit.c
+++ b/arch/loongarch/kvm/exit.c
@@ -190,3 +190,23 @@ int _kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run)
return er;
}
+
+int _kvm_emu_idle(struct kvm_vcpu *vcpu)
+{
+ ++vcpu->stat.idle_exits;
+ trace_kvm_exit_idle(vcpu, KVM_TRACE_EXIT_IDLE);
+
+ if (!kvm_arch_vcpu_runnable(vcpu)) {
+ /*
+ * Switch to the software timer before halt-polling/blocking as
+ * the guest's timer may be a break event for the vCPU, and the
+ * hypervisor timer runs only when the CPU is in guest mode.
+ * Switch before halt-polling so that KVM recognizes an expired
+ * timer before blocking.
+ */
+ kvm_save_timer(vcpu);
+ kvm_vcpu_block(vcpu);
+ }
+
+ return EMULATE_DONE;
+}
--
2.31.1
Hi Tianrui,
kernel test robot noticed the following build errors:
[auto build test ERROR on kvm/queue]
[also build test ERROR on linus/master v6.3-rc6 next-20230412]
[cannot apply to kvm/linux-next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Tianrui-Zhao/LoongArch-KVM-Add-kvm-related-header-files/20230412-163935
base: https://git.kernel.org/pub/scm/virt/kvm/kvm.git queue
patch link: https://lore.kernel.org/r/20230412083023.1693910-30-zhaotianrui%40loongson.cn
patch subject: [PATCH v6 29/30] LoongArch: KVM: Enable kvm config and add the makefile
config: loongarch-randconfig-c033-20230413 (https://download.01.org/0day-ci/archive/20230413/[email protected]/config)
compiler: loongarch64-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/intel-lab-lkp/linux/commit/19c2472bf2612603ce4671e06fb185516a99e040
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Tianrui-Zhao/LoongArch-KVM-Add-kvm-related-header-files/20230412-163935
git checkout 19c2472bf2612603ce4671e06fb185516a99e040
# save the config file
mkdir build_dir && cp config build_dir/.config
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch olddefconfig
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=loongarch SHELL=/bin/bash arch/loongarch/kvm/
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All error/warnings (new ones prefixed by >>):
In file included from arch/loongarch/include/asm/cpu-info.h:11,
from arch/loongarch/include/asm/processor.h:13,
from arch/loongarch/include/asm/thread_info.h:15,
from include/linux/thread_info.h:60,
from include/asm-generic/preempt.h:5,
from ./arch/loongarch/include/generated/asm/preempt.h:1,
from include/linux/preempt.h:78,
from include/linux/percpu.h:6,
from include/linux/context_tracking_state.h:5,
from include/linux/hardirq.h:5,
from include/linux/kvm_host.h:7,
from arch/loongarch/kvm/vcpu.c:6:
In function 'gcsr_read',
inlined from 'kvm_save_hw_gcsr' at arch/loongarch/include/asm/kvm_csr.h:24:19:
>> arch/loongarch/include/asm/loongarch.h:244:9: warning: 'asm' operand 1 probably does not match constraints
244 | asm volatile (
| ^~~
>> arch/loongarch/include/asm/loongarch.h:244:9: error: impossible constraint in 'asm'
In function 'gcsr_write',
inlined from 'kvm_restore_hw_gcsr' at arch/loongarch/include/asm/kvm_csr.h:29:2:
arch/loongarch/include/asm/loongarch.h:261:9: warning: 'asm' operand 1 probably does not match constraints
261 | asm volatile (
| ^~~
vim +/asm +244 arch/loongarch/include/asm/loongarch.h
f2ac457a61389b Huacai Chen 2022-05-31 238
2c87b678639d25 Tianrui Zhao 2023-04-12 239 /* GCSR */
2c87b678639d25 Tianrui Zhao 2023-04-12 240 static inline u64 gcsr_read(u32 reg)
2c87b678639d25 Tianrui Zhao 2023-04-12 241 {
2c87b678639d25 Tianrui Zhao 2023-04-12 242 u64 val = 0;
2c87b678639d25 Tianrui Zhao 2023-04-12 243 /* Instructions will be available in binutils later */
2c87b678639d25 Tianrui Zhao 2023-04-12 @244 asm volatile (
2c87b678639d25 Tianrui Zhao 2023-04-12 245 "parse_r __reg, %[val]\n\t"
2c87b678639d25 Tianrui Zhao 2023-04-12 246 /*
2c87b678639d25 Tianrui Zhao 2023-04-12 247 * read val from guest csr register %[reg]
2c87b678639d25 Tianrui Zhao 2023-04-12 248 * gcsrrd %[val], %[reg]
2c87b678639d25 Tianrui Zhao 2023-04-12 249 */
2c87b678639d25 Tianrui Zhao 2023-04-12 250 ".word 0x5 << 24 | %[reg] << 10 | 0 << 5 | __reg\n\t"
2c87b678639d25 Tianrui Zhao 2023-04-12 251 : [val] "+r" (val)
2c87b678639d25 Tianrui Zhao 2023-04-12 252 : [reg] "i" (reg)
2c87b678639d25 Tianrui Zhao 2023-04-12 253 : "memory");
2c87b678639d25 Tianrui Zhao 2023-04-12 254
2c87b678639d25 Tianrui Zhao 2023-04-12 255 return val;
2c87b678639d25 Tianrui Zhao 2023-04-12 256 }
2c87b678639d25 Tianrui Zhao 2023-04-12 257
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests
On Thu, 2023-04-13 at 15:32 +0800, kernel test robot wrote:
> vim +/asm +244 arch/loongarch/include/asm/loongarch.h
>
> f2ac457a61389b Huacai Chen 2022-05-31 238
> 2c87b678639d25 Tianrui Zhao 2023-04-12 239 /* GCSR */
> 2c87b678639d25 Tianrui Zhao 2023-04-12 240 static inline u64 gcsr_read(u32 reg)
Try __always_inline instead of inline because this "function" will
definitely break up if it's not inlined.
And I guess we should have something like:
extern u64 __compiletime_error("gcsr_read parameter is not a constant")
__gcsr_read_non_const(void);
static __always_inline u64 gcsr_read(u32 reg)
{
u64 val = 0;
if (!__builtin_constant_p(reg))
return __gcsr_read_non_const();
asm volatile (
... ...
Or perhaps we should just rewrite gcsr_read to a macro.
> 2c87b678639d25 Tianrui Zhao 2023-04-12 241 {
> 2c87b678639d25 Tianrui Zhao 2023-04-12 242 u64 val = 0;
> 2c87b678639d25 Tianrui Zhao 2023-04-12 243 /* Instructions will be available in binutils later */
> 2c87b678639d25 Tianrui Zhao 2023-04-12 @244 asm volatile (
> 2c87b678639d25 Tianrui Zhao 2023-04-12 245 "parse_r __reg, %[val]\n\t"
> 2c87b678639d25 Tianrui Zhao 2023-04-12 246 /*
> 2c87b678639d25 Tianrui Zhao 2023-04-12 247 * read val from guest csr register %[reg]
> 2c87b678639d25 Tianrui Zhao 2023-04-12 248 * gcsrrd %[val], %[reg]
> 2c87b678639d25 Tianrui Zhao 2023-04-12 249 */
> 2c87b678639d25 Tianrui Zhao 2023-04-12 250 ".word 0x5 << 24 | %[reg] << 10 | 0 << 5 | __reg\n\t"
> 2c87b678639d25 Tianrui Zhao 2023-04-12 251 : [val] "+r" (val)
> 2c87b678639d25 Tianrui Zhao 2023-04-12 252 : [reg] "i" (reg)
> 2c87b678639d25 Tianrui Zhao 2023-04-12 253 : "memory");
> 2c87b678639d25 Tianrui Zhao 2023-04-12 254
> 2c87b678639d25 Tianrui Zhao 2023-04-12 255 return val;
> 2c87b678639d25 Tianrui Zhao 2023-04-12 256 }
> 2c87b678639d25 Tianrui Zhao 2023-04-12 257
>
--
Xi Ruoyao <[email protected]>
School of Aerospace Science and Technology, Xidian University
在 2023年04月13日 16:33, Xi Ruoyao 写道:
> On Thu, 2023-04-13 at 15:32 +0800, kernel test robot wrote:
>
>> vim +/asm +244 arch/loongarch/include/asm/loongarch.h
>>
>> f2ac457a61389b Huacai Chen 2022-05-31 238
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 239 /* GCSR */
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 240 static inline u64 gcsr_read(u32 reg)
> Try __always_inline instead of inline because this "function" will
> definitely break up if it's not inlined.
>
> And I guess we should have something like:
>
> extern u64 __compiletime_error("gcsr_read parameter is not a constant")
> __gcsr_read_non_const(void);
>
> static __always_inline u64 gcsr_read(u32 reg)
> {
> u64 val = 0;
>
> if (!__builtin_constant_p(reg))
> return __gcsr_read_non_const();
>
> asm volatile (
> ... ...
>
> Or perhaps we should just rewrite gcsr_read to a macro.
>
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 241 {
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 242 u64 val = 0;
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 243 /* Instructions will be available in binutils later */
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 @244 asm volatile (
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 245 "parse_r __reg, %[val]\n\t"
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 246 /*
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 247 * read val from guest csr register %[reg]
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 248 * gcsrrd %[val], %[reg]
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 249 */
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 250 ".word 0x5 << 24 | %[reg] << 10 | 0 << 5 | __reg\n\t"
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 251 : [val] "+r" (val)
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 252 : [reg] "i" (reg)
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 253 : "memory");
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 254
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 255 return val;
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 256 }
>> 2c87b678639d25 Tianrui Zhao 2023-04-12 257
>>
Thanks, it really should use __always_inline there, and I will reconsider to fix this function mistakes about compiling.
Thanks
Tianrui Zhao