162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2019 Western Digital Corporation or its affiliates. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Authors: 662306a36Sopenharmony_ci * Anup Patel <anup.patel@wdc.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/bitops.h> 1062306a36Sopenharmony_ci#include <linux/cpumask.h> 1162306a36Sopenharmony_ci#include <linux/errno.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/smp.h> 1562306a36Sopenharmony_ci#include <linux/kvm_host.h> 1662306a36Sopenharmony_ci#include <asm/csr.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic unsigned long vmid_version = 1; 1962306a36Sopenharmony_cistatic unsigned long vmid_next; 2062306a36Sopenharmony_cistatic unsigned long vmid_bits __ro_after_init; 2162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(vmid_lock); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_civoid __init kvm_riscv_gstage_vmid_detect(void) 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci unsigned long old; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci /* Figure-out number of VMID bits in HW */ 2862306a36Sopenharmony_ci old = csr_read(CSR_HGATP); 2962306a36Sopenharmony_ci csr_write(CSR_HGATP, old | HGATP_VMID); 3062306a36Sopenharmony_ci vmid_bits = csr_read(CSR_HGATP); 3162306a36Sopenharmony_ci vmid_bits = (vmid_bits & HGATP_VMID) >> HGATP_VMID_SHIFT; 3262306a36Sopenharmony_ci vmid_bits = fls_long(vmid_bits); 3362306a36Sopenharmony_ci csr_write(CSR_HGATP, old); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* We polluted local TLB so flush all guest TLB */ 3662306a36Sopenharmony_ci kvm_riscv_local_hfence_gvma_all(); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* We don't use VMID bits if they are not sufficient */ 3962306a36Sopenharmony_ci if ((1UL << vmid_bits) < num_possible_cpus()) 4062306a36Sopenharmony_ci vmid_bits = 0; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciunsigned long kvm_riscv_gstage_vmid_bits(void) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci return vmid_bits; 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ciint kvm_riscv_gstage_vmid_init(struct kvm *kvm) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci /* Mark the initial VMID and VMID version invalid */ 5162306a36Sopenharmony_ci kvm->arch.vmid.vmid_version = 0; 5262306a36Sopenharmony_ci kvm->arch.vmid.vmid = 0; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cibool kvm_riscv_gstage_vmid_ver_changed(struct kvm_vmid *vmid) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (!vmid_bits) 6062306a36Sopenharmony_ci return false; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return unlikely(READ_ONCE(vmid->vmid_version) != 6362306a36Sopenharmony_ci READ_ONCE(vmid_version)); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void __local_hfence_gvma_all(void *info) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci kvm_riscv_local_hfence_gvma_all(); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_civoid kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned long i; 7462306a36Sopenharmony_ci struct kvm_vcpu *v; 7562306a36Sopenharmony_ci struct kvm_vmid *vmid = &vcpu->kvm->arch.vmid; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci if (!kvm_riscv_gstage_vmid_ver_changed(vmid)) 7862306a36Sopenharmony_ci return; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci spin_lock(&vmid_lock); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * We need to re-check the vmid_version here to ensure that if 8462306a36Sopenharmony_ci * another vcpu already allocated a valid vmid for this vm. 8562306a36Sopenharmony_ci */ 8662306a36Sopenharmony_ci if (!kvm_riscv_gstage_vmid_ver_changed(vmid)) { 8762306a36Sopenharmony_ci spin_unlock(&vmid_lock); 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* First user of a new VMID version? */ 9262306a36Sopenharmony_ci if (unlikely(vmid_next == 0)) { 9362306a36Sopenharmony_ci WRITE_ONCE(vmid_version, READ_ONCE(vmid_version) + 1); 9462306a36Sopenharmony_ci vmid_next = 1; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci /* 9762306a36Sopenharmony_ci * We ran out of VMIDs so we increment vmid_version and 9862306a36Sopenharmony_ci * start assigning VMIDs from 1. 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * This also means existing VMIDs assignment to all Guest 10162306a36Sopenharmony_ci * instances is invalid and we have force VMID re-assignement 10262306a36Sopenharmony_ci * for all Guest instances. The Guest instances that were not 10362306a36Sopenharmony_ci * running will automatically pick-up new VMIDs because will 10462306a36Sopenharmony_ci * call kvm_riscv_gstage_vmid_update() whenever they enter 10562306a36Sopenharmony_ci * in-kernel run loop. For Guest instances that are already 10662306a36Sopenharmony_ci * running, we force VM exits on all host CPUs using IPI and 10762306a36Sopenharmony_ci * flush all Guest TLBs. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci on_each_cpu_mask(cpu_online_mask, __local_hfence_gvma_all, 11062306a36Sopenharmony_ci NULL, 1); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci vmid->vmid = vmid_next; 11462306a36Sopenharmony_ci vmid_next++; 11562306a36Sopenharmony_ci vmid_next &= (1 << vmid_bits) - 1; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci WRITE_ONCE(vmid->vmid_version, READ_ONCE(vmid_version)); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_unlock(&vmid_lock); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* Request G-stage page table update for all VCPUs */ 12262306a36Sopenharmony_ci kvm_for_each_vcpu(i, v, vcpu->kvm) 12362306a36Sopenharmony_ci kvm_make_request(KVM_REQ_UPDATE_HGATP, v); 12462306a36Sopenharmony_ci} 125