Commit fd7bb4a2 authored by Anup Patel's avatar Anup Patel Committed by Anup Patel
Browse files

RISC-V: KVM: Implement VMID allocator



We implement a simple VMID allocator for Guests/VMs which:
1. Detects number of VMID bits at boot-time
2. Uses atomic number to track VMID version and increments
   VMID version whenever we run-out of VMIDs
3. Flushes Guest TLBs on all host CPUs whenever we run-out
   of VMIDs
4. Force updates HW Stage2 VMID for each Guest VCPU whenever
   VMID changes using VCPU request KVM_REQ_UPDATE_HGATP

Signed-off-by: default avatarAnup Patel <anup.patel@wdc.com>
Acked-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Reviewed-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Reviewed-by: default avatarAlexander Graf <graf@amazon.com>
Acked-by: default avatarPalmer Dabbelt <palmerdabbelt@google.com>
parent 5a5d79ac
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#define KVM_REQ_SLEEP \
	KVM_ARCH_REQ_FLAGS(0, KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP)
#define KVM_REQ_VCPU_RESET		KVM_ARCH_REQ(1)
#define KVM_REQ_UPDATE_HGATP		KVM_ARCH_REQ(2)

struct kvm_vm_stat {
	struct kvm_vm_stat_generic generic;
@@ -43,7 +44,19 @@ struct kvm_vcpu_stat {
struct kvm_arch_memory_slot {
};

struct kvm_vmid {
	/*
	 * Writes to vmid_version and vmid happen with vmid_lock held
	 * whereas reads happen without any lock held.
	 */
	unsigned long vmid_version;
	unsigned long vmid;
};

struct kvm_arch {
	/* stage2 vmid */
	struct kvm_vmid vmid;

	/* stage2 page table */
	pgd_t *pgd;
	phys_addr_t pgd_phys;
@@ -173,6 +186,11 @@ static inline void kvm_arch_sync_events(struct kvm *kvm) {}
static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {}
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}

void __kvm_riscv_hfence_gvma_vmid_gpa(unsigned long gpa, unsigned long vmid);
void __kvm_riscv_hfence_gvma_vmid(unsigned long vmid);
void __kvm_riscv_hfence_gvma_gpa(unsigned long gpa);
void __kvm_riscv_hfence_gvma_all(void);

int kvm_riscv_stage2_map(struct kvm_vcpu *vcpu,
			 struct kvm_memory_slot *memslot,
			 gpa_t gpa, unsigned long hva, bool is_write);
@@ -181,6 +199,12 @@ int kvm_riscv_stage2_alloc_pgd(struct kvm *kvm);
void kvm_riscv_stage2_free_pgd(struct kvm *kvm);
void kvm_riscv_stage2_update_hgatp(struct kvm_vcpu *vcpu);

void kvm_riscv_stage2_vmid_detect(void);
unsigned long kvm_riscv_stage2_vmid_bits(void);
int kvm_riscv_stage2_vmid_init(struct kvm *kvm);
bool kvm_riscv_stage2_vmid_ver_changed(struct kvm_vmid *vmid);
void kvm_riscv_stage2_vmid_update(struct kvm_vcpu *vcpu);

void __kvm_riscv_unpriv_trap(void);

unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu,
+12 −2
Original line number Diff line number Diff line
@@ -9,5 +9,15 @@ KVM := ../../../virt/kvm

obj-$(CONFIG_KVM) += kvm.o

kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o $(KVM)/binary_stats.o \
	 $(KVM)/eventfd.o main.o vm.o mmu.o vcpu.o vcpu_exit.o vcpu_switch.o
kvm-y += $(KVM)/kvm_main.o
kvm-y += $(KVM)/coalesced_mmio.o
kvm-y += $(KVM)/binary_stats.o
kvm-y += $(KVM)/eventfd.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 += vcpu_exit.o
kvm-y += vcpu_switch.o
+4 −0
Original line number Diff line number Diff line
@@ -79,8 +79,12 @@ int kvm_arch_init(void *opaque)
		return -ENODEV;
	}

	kvm_riscv_stage2_vmid_detect();

	kvm_info("hypervisor extension available\n");

	kvm_info("VMID %ld bits available\n", kvm_riscv_stage2_vmid_bits());

	return 0;
}

arch/riscv/kvm/tlb.S

0 → 100644
+74 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2019 Western Digital Corporation or its affiliates.
 *
 * Authors:
 *     Anup Patel <anup.patel@wdc.com>
 */

#include <linux/linkage.h>
#include <asm/asm.h>

	.text
	.altmacro
	.option norelax

	/*
	 * Instruction encoding of hfence.gvma is:
	 * HFENCE.GVMA rs1, rs2
	 * HFENCE.GVMA zero, rs2
	 * HFENCE.GVMA rs1
	 * HFENCE.GVMA
	 *
	 * rs1!=zero and rs2!=zero ==> HFENCE.GVMA rs1, rs2
	 * rs1==zero and rs2!=zero ==> HFENCE.GVMA zero, rs2
	 * rs1!=zero and rs2==zero ==> HFENCE.GVMA rs1
	 * rs1==zero and rs2==zero ==> HFENCE.GVMA
	 *
	 * Instruction encoding of HFENCE.GVMA is:
	 * 0110001 rs2(5) rs1(5) 000 00000 1110011
	 */

ENTRY(__kvm_riscv_hfence_gvma_vmid_gpa)
	/*
	 * rs1 = a0 (GPA)
	 * rs2 = a1 (VMID)
	 * HFENCE.GVMA a0, a1
	 * 0110001 01011 01010 000 00000 1110011
	 */
	.word 0x62b50073
	ret
ENDPROC(__kvm_riscv_hfence_gvma_vmid_gpa)

ENTRY(__kvm_riscv_hfence_gvma_vmid)
	/*
	 * rs1 = zero
	 * rs2 = a0 (VMID)
	 * HFENCE.GVMA zero, a0
	 * 0110001 01010 00000 000 00000 1110011
	 */
	.word 0x62a00073
	ret
ENDPROC(__kvm_riscv_hfence_gvma_vmid)

ENTRY(__kvm_riscv_hfence_gvma_gpa)
	/*
	 * rs1 = a0 (GPA)
	 * rs2 = zero
	 * HFENCE.GVMA a0
	 * 0110001 00000 01010 000 00000 1110011
	 */
	.word 0x62050073
	ret
ENDPROC(__kvm_riscv_hfence_gvma_gpa)

ENTRY(__kvm_riscv_hfence_gvma_all)
	/*
	 * rs1 = zero
	 * rs2 = zero
	 * HFENCE.GVMA
	 * 0110001 00000 00000 000 00000 1110011
	 */
	.word 0x62000073
	ret
ENDPROC(__kvm_riscv_hfence_gvma_all)
+9 −0
Original line number Diff line number Diff line
@@ -622,6 +622,12 @@ static void kvm_riscv_check_vcpu_requests(struct kvm_vcpu *vcpu)

		if (kvm_check_request(KVM_REQ_VCPU_RESET, vcpu))
			kvm_riscv_reset_vcpu(vcpu);

		if (kvm_check_request(KVM_REQ_UPDATE_HGATP, vcpu))
			kvm_riscv_stage2_update_hgatp(vcpu);

		if (kvm_check_request(KVM_REQ_TLB_FLUSH, vcpu))
			__kvm_riscv_hfence_gvma_all();
	}
}

@@ -667,6 +673,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
		/* Check conditions before entering the guest */
		cond_resched();

		kvm_riscv_stage2_vmid_update(vcpu);

		kvm_riscv_check_vcpu_requests(vcpu);

		preempt_disable();
@@ -703,6 +711,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
		kvm_riscv_update_hvip(vcpu);

		if (ret <= 0 ||
		    kvm_riscv_stage2_vmid_ver_changed(&vcpu->kvm->arch.vmid) ||
		    kvm_request_pending(vcpu)) {
			vcpu->mode = OUTSIDE_GUEST_MODE;
			local_irq_enable();
Loading