Message ID | 20230915014949.1222777-3-zhaotianrui@loongson.cn |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:172:b0:3f2:4152:657d with SMTP id h50csp743328vqi; Thu, 14 Sep 2023 18:50:30 -0700 (PDT) X-Google-Smtp-Source: AGHT+IFELShJPMN6RKhLGQqVJRqjxcJmn6Suc/kQ+q/S79Lat1Guxg7TRaC3d0uNJYPTZgYvqq0s X-Received: by 2002:a67:f890:0:b0:44d:4dd6:7965 with SMTP id h16-20020a67f890000000b0044d4dd67965mr392133vso.5.1694742629957; Thu, 14 Sep 2023 18:50:29 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1694742629; cv=none; d=google.com; s=arc-20160816; b=Y36U8Ul/Uhxn68ftcRrmM5uHqG5BavI2Duw8/zSB5Cd/30Bu+grm2NKKhMs7cVdrY7 GLbsyPI83y9MLKG5bYn3Z42nmBzK9yjY8BhI5qNCe9sRouTr+m9ClTbQgs9l5JU0sTX0 0FSNbifp0bdk2TXsyZUz/wkBg1hplyGtirmo0ZJUy2qwrKCEwJi8j6O8in+LC7khridF KCB5YaVN8pn7EbLHU5q8jMIhnTjnApwGeKhVMPRhxAxnDewLsgqFEuM1EUasfMpVU15m zylVoyuJ5bGyhOnV//BqrDu6tHaWdohI2zyL8pHZ64Q+03l/MRDhmECuWdA62AqiPGAH i21A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from; bh=W2Lbfl7gYDNdPNdLkqpA8B4V4nHIB+Dpkqe/AWl70zo=; fh=ejoaVAUUbBXsA/6jM/beuI9xCYtV2osdR9SpI7hjY84=; b=wNmvzXqk2SZmggJY7arep5vivpnZWK4jamhoodSvkyGrSIOyuZ+yOV+sXDCO0j/GdC FTO+wGgTR7iIcWJYS/dCNxGIrC5qwW1Evv6ofCdvAYBGI0r/VSyskBT41VEjUj2y0vL/ yufd9KMPgGKl8Bba592huVkEOCKp4/4nc62BwSWT2YnygHX4gs40h3baGlD1GR3iBn2m /etS5wS9DgPLWyW5/fnLJz1xwNvcDTLKXpbMibBfAp+pIhIzBOffJq060tOraSGiiwKY BsFg+GHF3jr578MpoUN5LQmg6sITQhJuHtCWvQWIkjbqN2ygmu5xL5rvQVG94C/aYJwf A2xA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.31 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from morse.vger.email (morse.vger.email. [23.128.96.31]) by mx.google.com with ESMTPS id f16-20020a63e310000000b0056ad77ddd4csi2300087pgh.438.2023.09.14.18.50.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 14 Sep 2023 18:50:29 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.31 as permitted sender) client-ip=23.128.96.31; Authentication-Results: mx.google.com; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.31 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by morse.vger.email (Postfix) with ESMTP id E04708468316; Thu, 14 Sep 2023 18:50:22 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at morse.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229584AbjIOBuF (ORCPT <rfc822;pwkd43@gmail.com> + 33 others); Thu, 14 Sep 2023 21:50:05 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53656 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231319AbjIOBuB (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Thu, 14 Sep 2023 21:50:01 -0400 Received: from mail.loongson.cn (mail.loongson.cn [114.242.206.163]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id EBEF22708; Thu, 14 Sep 2023 18:49:56 -0700 (PDT) Received: from loongson.cn (unknown [10.2.5.185]) by gateway (Coremail) with SMTP id _____8CxfOpCuANlg_4nAA--.49980S3; Fri, 15 Sep 2023 09:49:54 +0800 (CST) Received: from localhost.localdomain (unknown [10.2.5.185]) by localhost.localdomain (Coremail) with SMTP id AQAAf8Axndw9uANl+ioGAA--.11927S4; Fri, 15 Sep 2023 09:49:54 +0800 (CST) From: Tianrui Zhao <zhaotianrui@loongson.cn> To: linux-kernel@vger.kernel.org, kvm@vger.kernel.org Cc: Paolo Bonzini <pbonzini@redhat.com>, Huacai Chen <chenhuacai@kernel.org>, WANG Xuerui <kernel@xen0n.name>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, loongarch@lists.linux.dev, Jens Axboe <axboe@kernel.dk>, Mark Brown <broonie@kernel.org>, Alex Deucher <alexander.deucher@amd.com>, Oliver Upton <oliver.upton@linux.dev>, maobibo@loongson.cn, Xi Ruoyao <xry111@xry111.site>, zhaotianrui@loongson.cn Subject: [PATCH v21 02/29] LoongArch: KVM: Implement kvm module related interface Date: Fri, 15 Sep 2023 09:49:22 +0800 Message-Id: <20230915014949.1222777-3-zhaotianrui@loongson.cn> X-Mailer: git-send-email 2.39.1 In-Reply-To: <20230915014949.1222777-1-zhaotianrui@loongson.cn> References: <20230915014949.1222777-1-zhaotianrui@loongson.cn> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-CM-TRANSID: AQAAf8Axndw9uANl+ioGAA--.11927S4 X-CM-SenderInfo: p2kd03xldq233l6o00pqjv00gofq/ X-Coremail-Antispam: 1Uk129KBjDUn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7KY7 ZEXasCq-sGcSsGvfJ3UbIjqfuFe4nvWSU5nxnvy29KBjDU0xBIdaVrnUUvcSsGvfC2Kfnx nUUI43ZEXa7xR_UUUUUUUUU== Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (morse.vger.email [0.0.0.0]); Thu, 14 Sep 2023 18:50:22 -0700 (PDT) X-Spam-Status: No, score=-0.8 required=5.0 tests=HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on morse.vger.email X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1777066448205026129 X-GMAIL-MSGID: 1777066448205026129 |
Series |
Add KVM LoongArch support
|
|
Commit Message
zhaotianrui
Sept. 15, 2023, 1:49 a.m. UTC
Implement LoongArch kvm module init, module exit interface, using kvm context to save the vpid info and vcpu world switch interface pointer. Reviewed-by: Bibo Mao <maobibo@loongson.cn> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> --- arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 arch/loongarch/kvm/main.c
Comments
Hi, Tianrui, On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > > Implement LoongArch kvm module init, module exit interface, > using kvm context to save the vpid info and vcpu world switch > interface pointer. > > Reviewed-by: Bibo Mao <maobibo@loongson.cn> > Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > --- > arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 367 insertions(+) > create mode 100644 arch/loongarch/kvm/main.c > > diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > new file mode 100644 > index 0000000000..0deb9273d8 > --- /dev/null > +++ b/arch/loongarch/kvm/main.c > @@ -0,0 +1,367 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > + */ > + > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/kvm_host.h> > +#include <asm/cacheflush.h> > +#include <asm/cpufeature.h> > +#include <asm/kvm_csr.h> > +#include "trace.h" > + > +static struct kvm_context __percpu *vmcs; > +struct kvm_world_switch *kvm_loongarch_ops; > +unsigned long vpid_mask; > +static int gcsr_flag[CSR_MAX_NUMS]; > + > +int get_gcsr_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + return gcsr_flag[csr]; > + > + return INVALID_GCSR; > +} > + > +static inline void set_gcsr_sw_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + gcsr_flag[csr] |= SW_GCSR; > +} > + > +static inline void set_gcsr_hw_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + gcsr_flag[csr] |= HW_GCSR; > +} > + > +/* > + * The default value of gcsr_flag[CSR] is 0, and we use this > + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > + * gcsr is software or hardware. It will be used by get/set_gcsr, > + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > + * else use sw csr to emulate it. > + */ > +static void kvm_init_gcsr_flag(void) > +{ > + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > +} > + > +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 */ "64 bit" means what? > + if (unlikely(!vpid)) > + vpid = vpid_mask + 1; > + > + /* vpid 0 reserved for root */ Is this line really a comment about "++vpid"? Huacai > + ++vpid; > + > + /* start new vpid cycle */ > + kvm_flush_tlb_all(); > + } > + > + context->vpid_cache = vpid; > + vcpu->arch.vpid = vpid; > +} > + > +void kvm_check_vpid(struct kvm_vcpu *vcpu) > +{ > + struct kvm_context *context; > + bool migrated; > + unsigned long ver, old, vpid; > + int cpu; > + > + cpu = smp_processor_id(); > + /* > + * 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->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); > + vcpu->cpu = cpu; > + } > + > + /* Restore GSTAT(0x50).vpid */ > + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > +} > + > +static int kvm_loongarch_env_init(void) > +{ > + struct kvm_context *context; > + int cpu, order; > + void *addr; > + > + vmcs = alloc_percpu(struct kvm_context); > + if (!vmcs) { > + pr_err("kvm: failed to allocate percpu kvm_context\n"); > + return -ENOMEM; > + } > + > + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > + if (!kvm_loongarch_ops) { > + free_percpu(vmcs); > + vmcs = NULL; > + return -ENOMEM; > + } > + /* > + * There will be problem in world switch code if there > + * is page fault reenter, since pgd register is shared > + * between root kernel and kvm hypervisor. World switch > + * entry need be unmapped area, cannot be tlb mapped area. > + * In future if hw pagetable walking is supported, or there > + * is separate pgd registers between root kernel and kvm > + * hypervisor, copying about world switch code will not be used. > + */ > + > + order = get_order(kvm_vector_size + kvm_enter_guest_size); > + addr = (void *)__get_free_pages(GFP_KERNEL, order); > + if (!addr) { > + free_percpu(vmcs); > + vmcs = NULL; > + kfree(kvm_loongarch_ops); > + kvm_loongarch_ops = NULL; > + return -ENOMEM; > + } > + > + memcpy(addr, kvm_vector_entry, kvm_vector_size); > + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > + flush_icache_range((unsigned long)addr, (unsigned long)addr + > + kvm_vector_size + kvm_enter_guest_size); > + kvm_loongarch_ops->guest_eentry = addr; > + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > + kvm_loongarch_ops->page_order = order; > + > + vpid_mask = read_csr_gstat(); > + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > + if (vpid_mask) > + vpid_mask = GENMASK(vpid_mask - 1, 0); > + > + for_each_possible_cpu(cpu) { > + context = per_cpu_ptr(vmcs, cpu); > + context->vpid_cache = vpid_mask + 1; > + context->last_vcpu = NULL; > + } > + > + kvm_init_fault(); > + kvm_init_gcsr_flag(); > + > + return 0; > +} > + > +static void kvm_loongarch_env_exit(void) > +{ > + unsigned long addr; > + > + if (vmcs) > + free_percpu(vmcs); > + > + if (kvm_loongarch_ops) { > + if (kvm_loongarch_ops->guest_eentry) { > + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > + free_pages(addr, kvm_loongarch_ops->page_order); > + } > + kfree(kvm_loongarch_ops); > + } > +} > + > +static int kvm_loongarch_init(void) > +{ > + int r; > + > + if (!cpu_has_lvz) { > + kvm_info("hardware virtualization not available\n"); > + return -ENODEV; > + } > + r = kvm_loongarch_env_init(); > + if (r) > + return r; > + > + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > +} > + > +static void kvm_loongarch_exit(void) > +{ > + kvm_exit(); > + kvm_loongarch_env_exit(); > +} > + > +module_init(kvm_loongarch_init); > +module_exit(kvm_loongarch_exit); > + > +#ifdef MODULE > +static const struct cpu_feature loongarch_kvm_feature[] = { > + { .feature = cpu_feature(LOONGARCH_LVZ) }, > + {}, > +}; > +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > +#endif > -- > 2.39.1 >
Hi, Tianrui, On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > > Implement LoongArch kvm module init, module exit interface, > using kvm context to save the vpid info and vcpu world switch > interface pointer. > > Reviewed-by: Bibo Mao <maobibo@loongson.cn> > Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > --- > arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 367 insertions(+) > create mode 100644 arch/loongarch/kvm/main.c > > diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > new file mode 100644 > index 0000000000..0deb9273d8 > --- /dev/null > +++ b/arch/loongarch/kvm/main.c > @@ -0,0 +1,367 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > + */ > + > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/kvm_host.h> > +#include <asm/cacheflush.h> > +#include <asm/cpufeature.h> > +#include <asm/kvm_csr.h> > +#include "trace.h" > + > +static struct kvm_context __percpu *vmcs; > +struct kvm_world_switch *kvm_loongarch_ops; > +unsigned long vpid_mask; > +static int gcsr_flag[CSR_MAX_NUMS]; > + > +int get_gcsr_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + return gcsr_flag[csr]; > + > + return INVALID_GCSR; > +} > + > +static inline void set_gcsr_sw_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + gcsr_flag[csr] |= SW_GCSR; > +} > + > +static inline void set_gcsr_hw_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + gcsr_flag[csr] |= HW_GCSR; > +} > + > +/* > + * The default value of gcsr_flag[CSR] is 0, and we use this > + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > + * gcsr is software or hardware. It will be used by get/set_gcsr, > + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > + * else use sw csr to emulate it. > + */ > +static void kvm_init_gcsr_flag(void) > +{ > + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); FWPS and MWPS are both HW CSR and SW CSR? Huacai > + > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > +} > + > +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_vpid(struct kvm_vcpu *vcpu) > +{ > + struct kvm_context *context; > + bool migrated; > + unsigned long ver, old, vpid; > + int cpu; > + > + cpu = smp_processor_id(); > + /* > + * 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->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); > + vcpu->cpu = cpu; > + } > + > + /* Restore GSTAT(0x50).vpid */ > + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > +} > + > +static int kvm_loongarch_env_init(void) > +{ > + struct kvm_context *context; > + int cpu, order; > + void *addr; > + > + vmcs = alloc_percpu(struct kvm_context); > + if (!vmcs) { > + pr_err("kvm: failed to allocate percpu kvm_context\n"); > + return -ENOMEM; > + } > + > + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > + if (!kvm_loongarch_ops) { > + free_percpu(vmcs); > + vmcs = NULL; > + return -ENOMEM; > + } > + /* > + * There will be problem in world switch code if there > + * is page fault reenter, since pgd register is shared > + * between root kernel and kvm hypervisor. World switch > + * entry need be unmapped area, cannot be tlb mapped area. > + * In future if hw pagetable walking is supported, or there > + * is separate pgd registers between root kernel and kvm > + * hypervisor, copying about world switch code will not be used. > + */ > + > + order = get_order(kvm_vector_size + kvm_enter_guest_size); > + addr = (void *)__get_free_pages(GFP_KERNEL, order); > + if (!addr) { > + free_percpu(vmcs); > + vmcs = NULL; > + kfree(kvm_loongarch_ops); > + kvm_loongarch_ops = NULL; > + return -ENOMEM; > + } > + > + memcpy(addr, kvm_vector_entry, kvm_vector_size); > + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > + flush_icache_range((unsigned long)addr, (unsigned long)addr + > + kvm_vector_size + kvm_enter_guest_size); > + kvm_loongarch_ops->guest_eentry = addr; > + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > + kvm_loongarch_ops->page_order = order; > + > + vpid_mask = read_csr_gstat(); > + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > + if (vpid_mask) > + vpid_mask = GENMASK(vpid_mask - 1, 0); > + > + for_each_possible_cpu(cpu) { > + context = per_cpu_ptr(vmcs, cpu); > + context->vpid_cache = vpid_mask + 1; > + context->last_vcpu = NULL; > + } > + > + kvm_init_fault(); > + kvm_init_gcsr_flag(); > + > + return 0; > +} > + > +static void kvm_loongarch_env_exit(void) > +{ > + unsigned long addr; > + > + if (vmcs) > + free_percpu(vmcs); > + > + if (kvm_loongarch_ops) { > + if (kvm_loongarch_ops->guest_eentry) { > + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > + free_pages(addr, kvm_loongarch_ops->page_order); > + } > + kfree(kvm_loongarch_ops); > + } > +} > + > +static int kvm_loongarch_init(void) > +{ > + int r; > + > + if (!cpu_has_lvz) { > + kvm_info("hardware virtualization not available\n"); > + return -ENODEV; > + } > + r = kvm_loongarch_env_init(); > + if (r) > + return r; > + > + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > +} > + > +static void kvm_loongarch_exit(void) > +{ > + kvm_exit(); > + kvm_loongarch_env_exit(); > +} > + > +module_init(kvm_loongarch_init); > +module_exit(kvm_loongarch_exit); > + > +#ifdef MODULE > +static const struct cpu_feature loongarch_kvm_feature[] = { > + { .feature = cpu_feature(LOONGARCH_LVZ) }, > + {}, > +}; > +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > +#endif > -- > 2.39.1 >
在 2023/9/16 下午2:57, Huacai Chen 写道: > Hi, Tianrui, > > On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >> Implement LoongArch kvm module init, module exit interface, >> using kvm context to save the vpid info and vcpu world switch >> interface pointer. >> >> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >> --- >> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 367 insertions(+) >> create mode 100644 arch/loongarch/kvm/main.c >> >> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >> new file mode 100644 >> index 0000000000..0deb9273d8 >> --- /dev/null >> +++ b/arch/loongarch/kvm/main.c >> @@ -0,0 +1,367 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >> + */ >> + >> +#include <linux/err.h> >> +#include <linux/module.h> >> +#include <linux/kvm_host.h> >> +#include <asm/cacheflush.h> >> +#include <asm/cpufeature.h> >> +#include <asm/kvm_csr.h> >> +#include "trace.h" >> + >> +static struct kvm_context __percpu *vmcs; >> +struct kvm_world_switch *kvm_loongarch_ops; >> +unsigned long vpid_mask; >> +static int gcsr_flag[CSR_MAX_NUMS]; >> + >> +int get_gcsr_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + return gcsr_flag[csr]; >> + >> + return INVALID_GCSR; >> +} >> + >> +static inline void set_gcsr_sw_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + gcsr_flag[csr] |= SW_GCSR; >> +} >> + >> +static inline void set_gcsr_hw_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + gcsr_flag[csr] |= HW_GCSR; >> +} >> + >> +/* >> + * The default value of gcsr_flag[CSR] is 0, and we use this >> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >> + * gcsr is software or hardware. It will be used by get/set_gcsr, >> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >> + * else use sw csr to emulate it. >> + */ >> +static void kvm_init_gcsr_flag(void) >> +{ >> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >> +} >> + >> +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 */ > "64 bit" means what? It should be /* finish round of vpid loop */ > >> + if (unlikely(!vpid)) >> + vpid = vpid_mask + 1; >> + >> + /* vpid 0 reserved for root */ > Is this line really a comment about "++vpid"? Yes, vpid should not be 0, so it should plus 1. Thanks Tianrui Zhao > > Huacai > >> + ++vpid; >> + >> + /* start new vpid cycle */ >> + kvm_flush_tlb_all(); >> + } >> + >> + context->vpid_cache = vpid; >> + vcpu->arch.vpid = vpid; >> +} >> + >> +void kvm_check_vpid(struct kvm_vcpu *vcpu) >> +{ >> + struct kvm_context *context; >> + bool migrated; >> + unsigned long ver, old, vpid; >> + int cpu; >> + >> + cpu = smp_processor_id(); >> + /* >> + * 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->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); >> + vcpu->cpu = cpu; >> + } >> + >> + /* Restore GSTAT(0x50).vpid */ >> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >> +} >> + >> +static int kvm_loongarch_env_init(void) >> +{ >> + struct kvm_context *context; >> + int cpu, order; >> + void *addr; >> + >> + vmcs = alloc_percpu(struct kvm_context); >> + if (!vmcs) { >> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >> + return -ENOMEM; >> + } >> + >> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >> + if (!kvm_loongarch_ops) { >> + free_percpu(vmcs); >> + vmcs = NULL; >> + return -ENOMEM; >> + } >> + /* >> + * There will be problem in world switch code if there >> + * is page fault reenter, since pgd register is shared >> + * between root kernel and kvm hypervisor. World switch >> + * entry need be unmapped area, cannot be tlb mapped area. >> + * In future if hw pagetable walking is supported, or there >> + * is separate pgd registers between root kernel and kvm >> + * hypervisor, copying about world switch code will not be used. >> + */ >> + >> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >> + if (!addr) { >> + free_percpu(vmcs); >> + vmcs = NULL; >> + kfree(kvm_loongarch_ops); >> + kvm_loongarch_ops = NULL; >> + return -ENOMEM; >> + } >> + >> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >> + kvm_vector_size + kvm_enter_guest_size); >> + kvm_loongarch_ops->guest_eentry = addr; >> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >> + kvm_loongarch_ops->page_order = order; >> + >> + vpid_mask = read_csr_gstat(); >> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >> + if (vpid_mask) >> + vpid_mask = GENMASK(vpid_mask - 1, 0); >> + >> + for_each_possible_cpu(cpu) { >> + context = per_cpu_ptr(vmcs, cpu); >> + context->vpid_cache = vpid_mask + 1; >> + context->last_vcpu = NULL; >> + } >> + >> + kvm_init_fault(); >> + kvm_init_gcsr_flag(); >> + >> + return 0; >> +} >> + >> +static void kvm_loongarch_env_exit(void) >> +{ >> + unsigned long addr; >> + >> + if (vmcs) >> + free_percpu(vmcs); >> + >> + if (kvm_loongarch_ops) { >> + if (kvm_loongarch_ops->guest_eentry) { >> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >> + free_pages(addr, kvm_loongarch_ops->page_order); >> + } >> + kfree(kvm_loongarch_ops); >> + } >> +} >> + >> +static int kvm_loongarch_init(void) >> +{ >> + int r; >> + >> + if (!cpu_has_lvz) { >> + kvm_info("hardware virtualization not available\n"); >> + return -ENODEV; >> + } >> + r = kvm_loongarch_env_init(); >> + if (r) >> + return r; >> + >> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >> +} >> + >> +static void kvm_loongarch_exit(void) >> +{ >> + kvm_exit(); >> + kvm_loongarch_env_exit(); >> +} >> + >> +module_init(kvm_loongarch_init); >> +module_exit(kvm_loongarch_exit); >> + >> +#ifdef MODULE >> +static const struct cpu_feature loongarch_kvm_feature[] = { >> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >> +#endif >> -- >> 2.39.1 >>
Hi, Tianrui, On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > > Implement LoongArch kvm module init, module exit interface, > using kvm context to save the vpid info and vcpu world switch > interface pointer. > > Reviewed-by: Bibo Mao <maobibo@loongson.cn> > Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > --- > arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > 1 file changed, 367 insertions(+) > create mode 100644 arch/loongarch/kvm/main.c > > diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > new file mode 100644 > index 0000000000..0deb9273d8 > --- /dev/null > +++ b/arch/loongarch/kvm/main.c > @@ -0,0 +1,367 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > + */ > + > +#include <linux/err.h> > +#include <linux/module.h> > +#include <linux/kvm_host.h> > +#include <asm/cacheflush.h> > +#include <asm/cpufeature.h> > +#include <asm/kvm_csr.h> > +#include "trace.h" > + > +static struct kvm_context __percpu *vmcs; > +struct kvm_world_switch *kvm_loongarch_ops; > +unsigned long vpid_mask; > +static int gcsr_flag[CSR_MAX_NUMS]; > + > +int get_gcsr_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + return gcsr_flag[csr]; > + > + return INVALID_GCSR; > +} > + > +static inline void set_gcsr_sw_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + gcsr_flag[csr] |= SW_GCSR; > +} > + > +static inline void set_gcsr_hw_flag(int csr) > +{ > + if (csr < CSR_MAX_NUMS) > + gcsr_flag[csr] |= HW_GCSR; > +} > + > +/* > + * The default value of gcsr_flag[CSR] is 0, and we use this > + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > + * gcsr is software or hardware. It will be used by get/set_gcsr, > + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > + * else use sw csr to emulate it. > + */ > +static void kvm_init_gcsr_flag(void) > +{ > + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > + > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > +} > + > +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_vpid(struct kvm_vcpu *vcpu) > +{ > + struct kvm_context *context; > + bool migrated; > + unsigned long ver, old, vpid; > + int cpu; > + > + cpu = smp_processor_id(); > + /* > + * 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->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); > + vcpu->cpu = cpu; > + } > + > + /* Restore GSTAT(0x50).vpid */ > + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > +} > + > +static int kvm_loongarch_env_init(void) > +{ > + struct kvm_context *context; > + int cpu, order; > + void *addr; > + > + vmcs = alloc_percpu(struct kvm_context); > + if (!vmcs) { > + pr_err("kvm: failed to allocate percpu kvm_context\n"); > + return -ENOMEM; > + } > + > + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > + if (!kvm_loongarch_ops) { > + free_percpu(vmcs); > + vmcs = NULL; > + return -ENOMEM; > + } > + /* > + * There will be problem in world switch code if there > + * is page fault reenter, since pgd register is shared > + * between root kernel and kvm hypervisor. World switch > + * entry need be unmapped area, cannot be tlb mapped area. > + * In future if hw pagetable walking is supported, or there > + * is separate pgd registers between root kernel and kvm > + * hypervisor, copying about world switch code will not be used. > + */ > + > + order = get_order(kvm_vector_size + kvm_enter_guest_size); > + addr = (void *)__get_free_pages(GFP_KERNEL, order); > + if (!addr) { > + free_percpu(vmcs); > + vmcs = NULL; > + kfree(kvm_loongarch_ops); > + kvm_loongarch_ops = NULL; > + return -ENOMEM; > + } > + > + memcpy(addr, kvm_vector_entry, kvm_vector_size); > + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); Why memcpy? In our internal repo, we use kvm_vector_entry and kvm_enter_guest directly. The long comments above make me nervous because Loongson-3A6000 already supports hardware pagetable walker. Huacai > + flush_icache_range((unsigned long)addr, (unsigned long)addr + > + kvm_vector_size + kvm_enter_guest_size); > + kvm_loongarch_ops->guest_eentry = addr; > + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > + kvm_loongarch_ops->page_order = order; > + > + vpid_mask = read_csr_gstat(); > + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > + if (vpid_mask) > + vpid_mask = GENMASK(vpid_mask - 1, 0); > + > + for_each_possible_cpu(cpu) { > + context = per_cpu_ptr(vmcs, cpu); > + context->vpid_cache = vpid_mask + 1; > + context->last_vcpu = NULL; > + } > + > + kvm_init_fault(); > + kvm_init_gcsr_flag(); > + > + return 0; > +} > + > +static void kvm_loongarch_env_exit(void) > +{ > + unsigned long addr; > + > + if (vmcs) > + free_percpu(vmcs); > + > + if (kvm_loongarch_ops) { > + if (kvm_loongarch_ops->guest_eentry) { > + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > + free_pages(addr, kvm_loongarch_ops->page_order); > + } > + kfree(kvm_loongarch_ops); > + } > +} > + > +static int kvm_loongarch_init(void) > +{ > + int r; > + > + if (!cpu_has_lvz) { > + kvm_info("hardware virtualization not available\n"); > + return -ENODEV; > + } > + r = kvm_loongarch_env_init(); > + if (r) > + return r; > + > + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > +} > + > +static void kvm_loongarch_exit(void) > +{ > + kvm_exit(); > + kvm_loongarch_env_exit(); > +} > + > +module_init(kvm_loongarch_init); > +module_exit(kvm_loongarch_exit); > + > +#ifdef MODULE > +static const struct cpu_feature loongarch_kvm_feature[] = { > + { .feature = cpu_feature(LOONGARCH_LVZ) }, > + {}, > +}; > +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > +#endif > -- > 2.39.1 >
在 2023/9/16 下午4:51, Huacai Chen 写道: > Hi, Tianrui, > > On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >> Implement LoongArch kvm module init, module exit interface, >> using kvm context to save the vpid info and vcpu world switch >> interface pointer. >> >> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >> --- >> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 367 insertions(+) >> create mode 100644 arch/loongarch/kvm/main.c >> >> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >> new file mode 100644 >> index 0000000000..0deb9273d8 >> --- /dev/null >> +++ b/arch/loongarch/kvm/main.c >> @@ -0,0 +1,367 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >> + */ >> + >> +#include <linux/err.h> >> +#include <linux/module.h> >> +#include <linux/kvm_host.h> >> +#include <asm/cacheflush.h> >> +#include <asm/cpufeature.h> >> +#include <asm/kvm_csr.h> >> +#include "trace.h" >> + >> +static struct kvm_context __percpu *vmcs; >> +struct kvm_world_switch *kvm_loongarch_ops; >> +unsigned long vpid_mask; >> +static int gcsr_flag[CSR_MAX_NUMS]; >> + >> +int get_gcsr_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + return gcsr_flag[csr]; >> + >> + return INVALID_GCSR; >> +} >> + >> +static inline void set_gcsr_sw_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + gcsr_flag[csr] |= SW_GCSR; >> +} >> + >> +static inline void set_gcsr_hw_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + gcsr_flag[csr] |= HW_GCSR; >> +} >> + >> +/* >> + * The default value of gcsr_flag[CSR] is 0, and we use this >> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >> + * gcsr is software or hardware. It will be used by get/set_gcsr, >> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >> + * else use sw csr to emulate it. >> + */ >> +static void kvm_init_gcsr_flag(void) >> +{ >> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > FWPS and MWPS are both HW CSR and SW CSR? > > Huacai The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it is my mistake. Thanks Tianrui Zhao > >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >> +} >> + >> +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_vpid(struct kvm_vcpu *vcpu) >> +{ >> + struct kvm_context *context; >> + bool migrated; >> + unsigned long ver, old, vpid; >> + int cpu; >> + >> + cpu = smp_processor_id(); >> + /* >> + * 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->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); >> + vcpu->cpu = cpu; >> + } >> + >> + /* Restore GSTAT(0x50).vpid */ >> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >> +} >> + >> +static int kvm_loongarch_env_init(void) >> +{ >> + struct kvm_context *context; >> + int cpu, order; >> + void *addr; >> + >> + vmcs = alloc_percpu(struct kvm_context); >> + if (!vmcs) { >> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >> + return -ENOMEM; >> + } >> + >> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >> + if (!kvm_loongarch_ops) { >> + free_percpu(vmcs); >> + vmcs = NULL; >> + return -ENOMEM; >> + } >> + /* >> + * There will be problem in world switch code if there >> + * is page fault reenter, since pgd register is shared >> + * between root kernel and kvm hypervisor. World switch >> + * entry need be unmapped area, cannot be tlb mapped area. >> + * In future if hw pagetable walking is supported, or there >> + * is separate pgd registers between root kernel and kvm >> + * hypervisor, copying about world switch code will not be used. >> + */ >> + >> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >> + if (!addr) { >> + free_percpu(vmcs); >> + vmcs = NULL; >> + kfree(kvm_loongarch_ops); >> + kvm_loongarch_ops = NULL; >> + return -ENOMEM; >> + } >> + >> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >> + kvm_vector_size + kvm_enter_guest_size); >> + kvm_loongarch_ops->guest_eentry = addr; >> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >> + kvm_loongarch_ops->page_order = order; >> + >> + vpid_mask = read_csr_gstat(); >> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >> + if (vpid_mask) >> + vpid_mask = GENMASK(vpid_mask - 1, 0); >> + >> + for_each_possible_cpu(cpu) { >> + context = per_cpu_ptr(vmcs, cpu); >> + context->vpid_cache = vpid_mask + 1; >> + context->last_vcpu = NULL; >> + } >> + >> + kvm_init_fault(); >> + kvm_init_gcsr_flag(); >> + >> + return 0; >> +} >> + >> +static void kvm_loongarch_env_exit(void) >> +{ >> + unsigned long addr; >> + >> + if (vmcs) >> + free_percpu(vmcs); >> + >> + if (kvm_loongarch_ops) { >> + if (kvm_loongarch_ops->guest_eentry) { >> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >> + free_pages(addr, kvm_loongarch_ops->page_order); >> + } >> + kfree(kvm_loongarch_ops); >> + } >> +} >> + >> +static int kvm_loongarch_init(void) >> +{ >> + int r; >> + >> + if (!cpu_has_lvz) { >> + kvm_info("hardware virtualization not available\n"); >> + return -ENODEV; >> + } >> + r = kvm_loongarch_env_init(); >> + if (r) >> + return r; >> + >> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >> +} >> + >> +static void kvm_loongarch_exit(void) >> +{ >> + kvm_exit(); >> + kvm_loongarch_env_exit(); >> +} >> + >> +module_init(kvm_loongarch_init); >> +module_exit(kvm_loongarch_exit); >> + >> +#ifdef MODULE >> +static const struct cpu_feature loongarch_kvm_feature[] = { >> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >> +#endif >> -- >> 2.39.1 >>
On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > > > 在 2023/9/16 下午4:51, Huacai Chen 写道: > > Hi, Tianrui, > > > > On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > >> Implement LoongArch kvm module init, module exit interface, > >> using kvm context to save the vpid info and vcpu world switch > >> interface pointer. > >> > >> Reviewed-by: Bibo Mao <maobibo@loongson.cn> > >> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > >> --- > >> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > >> 1 file changed, 367 insertions(+) > >> create mode 100644 arch/loongarch/kvm/main.c > >> > >> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > >> new file mode 100644 > >> index 0000000000..0deb9273d8 > >> --- /dev/null > >> +++ b/arch/loongarch/kvm/main.c > >> @@ -0,0 +1,367 @@ > >> +// SPDX-License-Identifier: GPL-2.0 > >> +/* > >> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > >> + */ > >> + > >> +#include <linux/err.h> > >> +#include <linux/module.h> > >> +#include <linux/kvm_host.h> > >> +#include <asm/cacheflush.h> > >> +#include <asm/cpufeature.h> > >> +#include <asm/kvm_csr.h> > >> +#include "trace.h" > >> + > >> +static struct kvm_context __percpu *vmcs; > >> +struct kvm_world_switch *kvm_loongarch_ops; > >> +unsigned long vpid_mask; > >> +static int gcsr_flag[CSR_MAX_NUMS]; > >> + > >> +int get_gcsr_flag(int csr) > >> +{ > >> + if (csr < CSR_MAX_NUMS) > >> + return gcsr_flag[csr]; > >> + > >> + return INVALID_GCSR; > >> +} > >> + > >> +static inline void set_gcsr_sw_flag(int csr) > >> +{ > >> + if (csr < CSR_MAX_NUMS) > >> + gcsr_flag[csr] |= SW_GCSR; > >> +} > >> + > >> +static inline void set_gcsr_hw_flag(int csr) > >> +{ > >> + if (csr < CSR_MAX_NUMS) > >> + gcsr_flag[csr] |= HW_GCSR; > >> +} > >> + > >> +/* > >> + * The default value of gcsr_flag[CSR] is 0, and we use this > >> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > >> + * gcsr is software or hardware. It will be used by get/set_gcsr, > >> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > >> + * else use sw csr to emulate it. > >> + */ > >> +static void kvm_init_gcsr_flag(void) > >> +{ > >> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > > FWPS and MWPS are both HW CSR and SW CSR? > > > > Huacai > The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it > is my mistake. But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW GCSR, while DBxxxx and IBxxxx are SW GCSR. Huacai > > Thanks > Tianrui Zhao > > > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > >> +} > >> + > >> +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_vpid(struct kvm_vcpu *vcpu) > >> +{ > >> + struct kvm_context *context; > >> + bool migrated; > >> + unsigned long ver, old, vpid; > >> + int cpu; > >> + > >> + cpu = smp_processor_id(); > >> + /* > >> + * 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->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); > >> + vcpu->cpu = cpu; > >> + } > >> + > >> + /* Restore GSTAT(0x50).vpid */ > >> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > >> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > >> +} > >> + > >> +static int kvm_loongarch_env_init(void) > >> +{ > >> + struct kvm_context *context; > >> + int cpu, order; > >> + void *addr; > >> + > >> + vmcs = alloc_percpu(struct kvm_context); > >> + if (!vmcs) { > >> + pr_err("kvm: failed to allocate percpu kvm_context\n"); > >> + return -ENOMEM; > >> + } > >> + > >> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > >> + if (!kvm_loongarch_ops) { > >> + free_percpu(vmcs); > >> + vmcs = NULL; > >> + return -ENOMEM; > >> + } > >> + /* > >> + * There will be problem in world switch code if there > >> + * is page fault reenter, since pgd register is shared > >> + * between root kernel and kvm hypervisor. World switch > >> + * entry need be unmapped area, cannot be tlb mapped area. > >> + * In future if hw pagetable walking is supported, or there > >> + * is separate pgd registers between root kernel and kvm > >> + * hypervisor, copying about world switch code will not be used. > >> + */ > >> + > >> + order = get_order(kvm_vector_size + kvm_enter_guest_size); > >> + addr = (void *)__get_free_pages(GFP_KERNEL, order); > >> + if (!addr) { > >> + free_percpu(vmcs); > >> + vmcs = NULL; > >> + kfree(kvm_loongarch_ops); > >> + kvm_loongarch_ops = NULL; > >> + return -ENOMEM; > >> + } > >> + > >> + memcpy(addr, kvm_vector_entry, kvm_vector_size); > >> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > >> + flush_icache_range((unsigned long)addr, (unsigned long)addr + > >> + kvm_vector_size + kvm_enter_guest_size); > >> + kvm_loongarch_ops->guest_eentry = addr; > >> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > >> + kvm_loongarch_ops->page_order = order; > >> + > >> + vpid_mask = read_csr_gstat(); > >> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > >> + if (vpid_mask) > >> + vpid_mask = GENMASK(vpid_mask - 1, 0); > >> + > >> + for_each_possible_cpu(cpu) { > >> + context = per_cpu_ptr(vmcs, cpu); > >> + context->vpid_cache = vpid_mask + 1; > >> + context->last_vcpu = NULL; > >> + } > >> + > >> + kvm_init_fault(); > >> + kvm_init_gcsr_flag(); > >> + > >> + return 0; > >> +} > >> + > >> +static void kvm_loongarch_env_exit(void) > >> +{ > >> + unsigned long addr; > >> + > >> + if (vmcs) > >> + free_percpu(vmcs); > >> + > >> + if (kvm_loongarch_ops) { > >> + if (kvm_loongarch_ops->guest_eentry) { > >> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > >> + free_pages(addr, kvm_loongarch_ops->page_order); > >> + } > >> + kfree(kvm_loongarch_ops); > >> + } > >> +} > >> + > >> +static int kvm_loongarch_init(void) > >> +{ > >> + int r; > >> + > >> + if (!cpu_has_lvz) { > >> + kvm_info("hardware virtualization not available\n"); > >> + return -ENODEV; > >> + } > >> + r = kvm_loongarch_env_init(); > >> + if (r) > >> + return r; > >> + > >> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > >> +} > >> + > >> +static void kvm_loongarch_exit(void) > >> +{ > >> + kvm_exit(); > >> + kvm_loongarch_env_exit(); > >> +} > >> + > >> +module_init(kvm_loongarch_init); > >> +module_exit(kvm_loongarch_exit); > >> + > >> +#ifdef MODULE > >> +static const struct cpu_feature loongarch_kvm_feature[] = { > >> + { .feature = cpu_feature(LOONGARCH_LVZ) }, > >> + {}, > >> +}; > >> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > >> +#endif > >> -- > >> 2.39.1 > >> > >
在 2023/9/18 上午9:45, Huacai Chen 写道: > On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >> >> 在 2023/9/16 下午4:51, Huacai Chen 写道: >>> Hi, Tianrui, >>> >>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >>>> Implement LoongArch kvm module init, module exit interface, >>>> using kvm context to save the vpid info and vcpu world switch >>>> interface pointer. >>>> >>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >>>> --- >>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >>>> 1 file changed, 367 insertions(+) >>>> create mode 100644 arch/loongarch/kvm/main.c >>>> >>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >>>> new file mode 100644 >>>> index 0000000000..0deb9273d8 >>>> --- /dev/null >>>> +++ b/arch/loongarch/kvm/main.c >>>> @@ -0,0 +1,367 @@ >>>> +// SPDX-License-Identifier: GPL-2.0 >>>> +/* >>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >>>> + */ >>>> + >>>> +#include <linux/err.h> >>>> +#include <linux/module.h> >>>> +#include <linux/kvm_host.h> >>>> +#include <asm/cacheflush.h> >>>> +#include <asm/cpufeature.h> >>>> +#include <asm/kvm_csr.h> >>>> +#include "trace.h" >>>> + >>>> +static struct kvm_context __percpu *vmcs; >>>> +struct kvm_world_switch *kvm_loongarch_ops; >>>> +unsigned long vpid_mask; >>>> +static int gcsr_flag[CSR_MAX_NUMS]; >>>> + >>>> +int get_gcsr_flag(int csr) >>>> +{ >>>> + if (csr < CSR_MAX_NUMS) >>>> + return gcsr_flag[csr]; >>>> + >>>> + return INVALID_GCSR; >>>> +} >>>> + >>>> +static inline void set_gcsr_sw_flag(int csr) >>>> +{ >>>> + if (csr < CSR_MAX_NUMS) >>>> + gcsr_flag[csr] |= SW_GCSR; >>>> +} >>>> + >>>> +static inline void set_gcsr_hw_flag(int csr) >>>> +{ >>>> + if (csr < CSR_MAX_NUMS) >>>> + gcsr_flag[csr] |= HW_GCSR; >>>> +} >>>> + >>>> +/* >>>> + * The default value of gcsr_flag[CSR] is 0, and we use this >>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, >>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >>>> + * else use sw csr to emulate it. >>>> + */ >>>> +static void kvm_init_gcsr_flag(void) >>>> +{ >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>> FWPS and MWPS are both HW CSR and SW CSR? >>> >>> Huacai >> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it >> is my mistake. > But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW > GCSR, while DBxxxx and IBxxxx are SW GCSR. Ok, It is my misunderstanding, as the FWPC and MWPC can control guest debug register numbers, but I know they are all HW GCSR when I look up the manual again. Thanks Tianrui Zhao > > Huacai > >> Thanks >> Tianrui Zhao >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >>>> +} >>>> + >>>> +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_vpid(struct kvm_vcpu *vcpu) >>>> +{ >>>> + struct kvm_context *context; >>>> + bool migrated; >>>> + unsigned long ver, old, vpid; >>>> + int cpu; >>>> + >>>> + cpu = smp_processor_id(); >>>> + /* >>>> + * 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->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); >>>> + vcpu->cpu = cpu; >>>> + } >>>> + >>>> + /* Restore GSTAT(0x50).vpid */ >>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >>>> +} >>>> + >>>> +static int kvm_loongarch_env_init(void) >>>> +{ >>>> + struct kvm_context *context; >>>> + int cpu, order; >>>> + void *addr; >>>> + >>>> + vmcs = alloc_percpu(struct kvm_context); >>>> + if (!vmcs) { >>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >>>> + return -ENOMEM; >>>> + } >>>> + >>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >>>> + if (!kvm_loongarch_ops) { >>>> + free_percpu(vmcs); >>>> + vmcs = NULL; >>>> + return -ENOMEM; >>>> + } >>>> + /* >>>> + * There will be problem in world switch code if there >>>> + * is page fault reenter, since pgd register is shared >>>> + * between root kernel and kvm hypervisor. World switch >>>> + * entry need be unmapped area, cannot be tlb mapped area. >>>> + * In future if hw pagetable walking is supported, or there >>>> + * is separate pgd registers between root kernel and kvm >>>> + * hypervisor, copying about world switch code will not be used. >>>> + */ >>>> + >>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >>>> + if (!addr) { >>>> + free_percpu(vmcs); >>>> + vmcs = NULL; >>>> + kfree(kvm_loongarch_ops); >>>> + kvm_loongarch_ops = NULL; >>>> + return -ENOMEM; >>>> + } >>>> + >>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >>>> + kvm_vector_size + kvm_enter_guest_size); >>>> + kvm_loongarch_ops->guest_eentry = addr; >>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >>>> + kvm_loongarch_ops->page_order = order; >>>> + >>>> + vpid_mask = read_csr_gstat(); >>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >>>> + if (vpid_mask) >>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); >>>> + >>>> + for_each_possible_cpu(cpu) { >>>> + context = per_cpu_ptr(vmcs, cpu); >>>> + context->vpid_cache = vpid_mask + 1; >>>> + context->last_vcpu = NULL; >>>> + } >>>> + >>>> + kvm_init_fault(); >>>> + kvm_init_gcsr_flag(); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static void kvm_loongarch_env_exit(void) >>>> +{ >>>> + unsigned long addr; >>>> + >>>> + if (vmcs) >>>> + free_percpu(vmcs); >>>> + >>>> + if (kvm_loongarch_ops) { >>>> + if (kvm_loongarch_ops->guest_eentry) { >>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >>>> + free_pages(addr, kvm_loongarch_ops->page_order); >>>> + } >>>> + kfree(kvm_loongarch_ops); >>>> + } >>>> +} >>>> + >>>> +static int kvm_loongarch_init(void) >>>> +{ >>>> + int r; >>>> + >>>> + if (!cpu_has_lvz) { >>>> + kvm_info("hardware virtualization not available\n"); >>>> + return -ENODEV; >>>> + } >>>> + r = kvm_loongarch_env_init(); >>>> + if (r) >>>> + return r; >>>> + >>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >>>> +} >>>> + >>>> +static void kvm_loongarch_exit(void) >>>> +{ >>>> + kvm_exit(); >>>> + kvm_loongarch_env_exit(); >>>> +} >>>> + >>>> +module_init(kvm_loongarch_init); >>>> +module_exit(kvm_loongarch_exit); >>>> + >>>> +#ifdef MODULE >>>> +static const struct cpu_feature loongarch_kvm_feature[] = { >>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >>>> + {}, >>>> +}; >>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >>>> +#endif >>>> -- >>>> 2.39.1 >>>> >>
On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > > > 在 2023/9/18 上午9:45, Huacai Chen 写道: > > On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >> > >> 在 2023/9/16 下午4:51, Huacai Chen 写道: > >>> Hi, Tianrui, > >>> > >>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > >>>> Implement LoongArch kvm module init, module exit interface, > >>>> using kvm context to save the vpid info and vcpu world switch > >>>> interface pointer. > >>>> > >>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> > >>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > >>>> --- > >>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > >>>> 1 file changed, 367 insertions(+) > >>>> create mode 100644 arch/loongarch/kvm/main.c > >>>> > >>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > >>>> new file mode 100644 > >>>> index 0000000000..0deb9273d8 > >>>> --- /dev/null > >>>> +++ b/arch/loongarch/kvm/main.c > >>>> @@ -0,0 +1,367 @@ > >>>> +// SPDX-License-Identifier: GPL-2.0 > >>>> +/* > >>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > >>>> + */ > >>>> + > >>>> +#include <linux/err.h> > >>>> +#include <linux/module.h> > >>>> +#include <linux/kvm_host.h> > >>>> +#include <asm/cacheflush.h> > >>>> +#include <asm/cpufeature.h> > >>>> +#include <asm/kvm_csr.h> > >>>> +#include "trace.h" > >>>> + > >>>> +static struct kvm_context __percpu *vmcs; > >>>> +struct kvm_world_switch *kvm_loongarch_ops; > >>>> +unsigned long vpid_mask; > >>>> +static int gcsr_flag[CSR_MAX_NUMS]; > >>>> + > >>>> +int get_gcsr_flag(int csr) > >>>> +{ > >>>> + if (csr < CSR_MAX_NUMS) > >>>> + return gcsr_flag[csr]; > >>>> + > >>>> + return INVALID_GCSR; > >>>> +} > >>>> + > >>>> +static inline void set_gcsr_sw_flag(int csr) > >>>> +{ > >>>> + if (csr < CSR_MAX_NUMS) > >>>> + gcsr_flag[csr] |= SW_GCSR; > >>>> +} > >>>> + > >>>> +static inline void set_gcsr_hw_flag(int csr) > >>>> +{ > >>>> + if (csr < CSR_MAX_NUMS) > >>>> + gcsr_flag[csr] |= HW_GCSR; > >>>> +} > >>>> + > >>>> +/* > >>>> + * The default value of gcsr_flag[CSR] is 0, and we use this > >>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > >>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, > >>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > >>>> + * else use sw csr to emulate it. > >>>> + */ > >>>> +static void kvm_init_gcsr_flag(void) > >>>> +{ > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > >>>> + > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > >>>> + > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > >>> FWPS and MWPS are both HW CSR and SW CSR? > >>> > >>> Huacai > >> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it > >> is my mistake. > > But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW > > GCSR, while DBxxxx and IBxxxx are SW GCSR. > Ok, It is my misunderstanding, as the FWPC and MWPC can control guest > debug register numbers, but I know they are all HW GCSR when I look up > the manual again. So these lines can be removed? set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); And add FWPC/MWPC to hw list? Huacai > > Thanks > Tianrui Zhao > > > > Huacai > > > >> Thanks > >> Tianrui Zhao > >>>> + > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > >>>> + > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > >>>> + > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > >>>> +} > >>>> + > >>>> +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_vpid(struct kvm_vcpu *vcpu) > >>>> +{ > >>>> + struct kvm_context *context; > >>>> + bool migrated; > >>>> + unsigned long ver, old, vpid; > >>>> + int cpu; > >>>> + > >>>> + cpu = smp_processor_id(); > >>>> + /* > >>>> + * 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->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); > >>>> + vcpu->cpu = cpu; > >>>> + } > >>>> + > >>>> + /* Restore GSTAT(0x50).vpid */ > >>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > >>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > >>>> +} > >>>> + > >>>> +static int kvm_loongarch_env_init(void) > >>>> +{ > >>>> + struct kvm_context *context; > >>>> + int cpu, order; > >>>> + void *addr; > >>>> + > >>>> + vmcs = alloc_percpu(struct kvm_context); > >>>> + if (!vmcs) { > >>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); > >>>> + return -ENOMEM; > >>>> + } > >>>> + > >>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > >>>> + if (!kvm_loongarch_ops) { > >>>> + free_percpu(vmcs); > >>>> + vmcs = NULL; > >>>> + return -ENOMEM; > >>>> + } > >>>> + /* > >>>> + * There will be problem in world switch code if there > >>>> + * is page fault reenter, since pgd register is shared > >>>> + * between root kernel and kvm hypervisor. World switch > >>>> + * entry need be unmapped area, cannot be tlb mapped area. > >>>> + * In future if hw pagetable walking is supported, or there > >>>> + * is separate pgd registers between root kernel and kvm > >>>> + * hypervisor, copying about world switch code will not be used. > >>>> + */ > >>>> + > >>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); > >>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); > >>>> + if (!addr) { > >>>> + free_percpu(vmcs); > >>>> + vmcs = NULL; > >>>> + kfree(kvm_loongarch_ops); > >>>> + kvm_loongarch_ops = NULL; > >>>> + return -ENOMEM; > >>>> + } > >>>> + > >>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); > >>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > >>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + > >>>> + kvm_vector_size + kvm_enter_guest_size); > >>>> + kvm_loongarch_ops->guest_eentry = addr; > >>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > >>>> + kvm_loongarch_ops->page_order = order; > >>>> + > >>>> + vpid_mask = read_csr_gstat(); > >>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > >>>> + if (vpid_mask) > >>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); > >>>> + > >>>> + for_each_possible_cpu(cpu) { > >>>> + context = per_cpu_ptr(vmcs, cpu); > >>>> + context->vpid_cache = vpid_mask + 1; > >>>> + context->last_vcpu = NULL; > >>>> + } > >>>> + > >>>> + kvm_init_fault(); > >>>> + kvm_init_gcsr_flag(); > >>>> + > >>>> + return 0; > >>>> +} > >>>> + > >>>> +static void kvm_loongarch_env_exit(void) > >>>> +{ > >>>> + unsigned long addr; > >>>> + > >>>> + if (vmcs) > >>>> + free_percpu(vmcs); > >>>> + > >>>> + if (kvm_loongarch_ops) { > >>>> + if (kvm_loongarch_ops->guest_eentry) { > >>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > >>>> + free_pages(addr, kvm_loongarch_ops->page_order); > >>>> + } > >>>> + kfree(kvm_loongarch_ops); > >>>> + } > >>>> +} > >>>> + > >>>> +static int kvm_loongarch_init(void) > >>>> +{ > >>>> + int r; > >>>> + > >>>> + if (!cpu_has_lvz) { > >>>> + kvm_info("hardware virtualization not available\n"); > >>>> + return -ENODEV; > >>>> + } > >>>> + r = kvm_loongarch_env_init(); > >>>> + if (r) > >>>> + return r; > >>>> + > >>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > >>>> +} > >>>> + > >>>> +static void kvm_loongarch_exit(void) > >>>> +{ > >>>> + kvm_exit(); > >>>> + kvm_loongarch_env_exit(); > >>>> +} > >>>> + > >>>> +module_init(kvm_loongarch_init); > >>>> +module_exit(kvm_loongarch_exit); > >>>> + > >>>> +#ifdef MODULE > >>>> +static const struct cpu_feature loongarch_kvm_feature[] = { > >>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, > >>>> + {}, > >>>> +}; > >>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > >>>> +#endif > >>>> -- > >>>> 2.39.1 > >>>> > >> >
在 2023/9/18 上午11:12, Huacai Chen 写道: > On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >> >> 在 2023/9/18 上午9:45, Huacai Chen 写道: >>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: >>>>> Hi, Tianrui, >>>>> >>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >>>>>> Implement LoongArch kvm module init, module exit interface, >>>>>> using kvm context to save the vpid info and vcpu world switch >>>>>> interface pointer. >>>>>> >>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >>>>>> --- >>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >>>>>> 1 file changed, 367 insertions(+) >>>>>> create mode 100644 arch/loongarch/kvm/main.c >>>>>> >>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >>>>>> new file mode 100644 >>>>>> index 0000000000..0deb9273d8 >>>>>> --- /dev/null >>>>>> +++ b/arch/loongarch/kvm/main.c >>>>>> @@ -0,0 +1,367 @@ >>>>>> +// SPDX-License-Identifier: GPL-2.0 >>>>>> +/* >>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >>>>>> + */ >>>>>> + >>>>>> +#include <linux/err.h> >>>>>> +#include <linux/module.h> >>>>>> +#include <linux/kvm_host.h> >>>>>> +#include <asm/cacheflush.h> >>>>>> +#include <asm/cpufeature.h> >>>>>> +#include <asm/kvm_csr.h> >>>>>> +#include "trace.h" >>>>>> + >>>>>> +static struct kvm_context __percpu *vmcs; >>>>>> +struct kvm_world_switch *kvm_loongarch_ops; >>>>>> +unsigned long vpid_mask; >>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; >>>>>> + >>>>>> +int get_gcsr_flag(int csr) >>>>>> +{ >>>>>> + if (csr < CSR_MAX_NUMS) >>>>>> + return gcsr_flag[csr]; >>>>>> + >>>>>> + return INVALID_GCSR; >>>>>> +} >>>>>> + >>>>>> +static inline void set_gcsr_sw_flag(int csr) >>>>>> +{ >>>>>> + if (csr < CSR_MAX_NUMS) >>>>>> + gcsr_flag[csr] |= SW_GCSR; >>>>>> +} >>>>>> + >>>>>> +static inline void set_gcsr_hw_flag(int csr) >>>>>> +{ >>>>>> + if (csr < CSR_MAX_NUMS) >>>>>> + gcsr_flag[csr] |= HW_GCSR; >>>>>> +} >>>>>> + >>>>>> +/* >>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this >>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, >>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >>>>>> + * else use sw csr to emulate it. >>>>>> + */ >>>>>> +static void kvm_init_gcsr_flag(void) >>>>>> +{ >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >>>>>> + >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >>>>>> + >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>>> FWPS and MWPS are both HW CSR and SW CSR? >>>>> >>>>> Huacai >>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it >>>> is my mistake. >>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW >>> GCSR, while DBxxxx and IBxxxx are SW GCSR. >> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest >> debug register numbers, but I know they are all HW GCSR when I look up >> the manual again. > So these lines can be removed? > > set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > > And add FWPC/MWPC to hw list? > > Huacai Yes, It is. Thanks Tianrui Zhao > >> Thanks >> Tianrui Zhao >>> Huacai >>> >>>> Thanks >>>> Tianrui Zhao >>>>>> + >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >>>>>> + >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >>>>>> + >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >>>>>> +} >>>>>> + >>>>>> +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_vpid(struct kvm_vcpu *vcpu) >>>>>> +{ >>>>>> + struct kvm_context *context; >>>>>> + bool migrated; >>>>>> + unsigned long ver, old, vpid; >>>>>> + int cpu; >>>>>> + >>>>>> + cpu = smp_processor_id(); >>>>>> + /* >>>>>> + * 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->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); >>>>>> + vcpu->cpu = cpu; >>>>>> + } >>>>>> + >>>>>> + /* Restore GSTAT(0x50).vpid */ >>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >>>>>> +} >>>>>> + >>>>>> +static int kvm_loongarch_env_init(void) >>>>>> +{ >>>>>> + struct kvm_context *context; >>>>>> + int cpu, order; >>>>>> + void *addr; >>>>>> + >>>>>> + vmcs = alloc_percpu(struct kvm_context); >>>>>> + if (!vmcs) { >>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >>>>>> + return -ENOMEM; >>>>>> + } >>>>>> + >>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >>>>>> + if (!kvm_loongarch_ops) { >>>>>> + free_percpu(vmcs); >>>>>> + vmcs = NULL; >>>>>> + return -ENOMEM; >>>>>> + } >>>>>> + /* >>>>>> + * There will be problem in world switch code if there >>>>>> + * is page fault reenter, since pgd register is shared >>>>>> + * between root kernel and kvm hypervisor. World switch >>>>>> + * entry need be unmapped area, cannot be tlb mapped area. >>>>>> + * In future if hw pagetable walking is supported, or there >>>>>> + * is separate pgd registers between root kernel and kvm >>>>>> + * hypervisor, copying about world switch code will not be used. >>>>>> + */ >>>>>> + >>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >>>>>> + if (!addr) { >>>>>> + free_percpu(vmcs); >>>>>> + vmcs = NULL; >>>>>> + kfree(kvm_loongarch_ops); >>>>>> + kvm_loongarch_ops = NULL; >>>>>> + return -ENOMEM; >>>>>> + } >>>>>> + >>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >>>>>> + kvm_vector_size + kvm_enter_guest_size); >>>>>> + kvm_loongarch_ops->guest_eentry = addr; >>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >>>>>> + kvm_loongarch_ops->page_order = order; >>>>>> + >>>>>> + vpid_mask = read_csr_gstat(); >>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >>>>>> + if (vpid_mask) >>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); >>>>>> + >>>>>> + for_each_possible_cpu(cpu) { >>>>>> + context = per_cpu_ptr(vmcs, cpu); >>>>>> + context->vpid_cache = vpid_mask + 1; >>>>>> + context->last_vcpu = NULL; >>>>>> + } >>>>>> + >>>>>> + kvm_init_fault(); >>>>>> + kvm_init_gcsr_flag(); >>>>>> + >>>>>> + return 0; >>>>>> +} >>>>>> + >>>>>> +static void kvm_loongarch_env_exit(void) >>>>>> +{ >>>>>> + unsigned long addr; >>>>>> + >>>>>> + if (vmcs) >>>>>> + free_percpu(vmcs); >>>>>> + >>>>>> + if (kvm_loongarch_ops) { >>>>>> + if (kvm_loongarch_ops->guest_eentry) { >>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); >>>>>> + } >>>>>> + kfree(kvm_loongarch_ops); >>>>>> + } >>>>>> +} >>>>>> + >>>>>> +static int kvm_loongarch_init(void) >>>>>> +{ >>>>>> + int r; >>>>>> + >>>>>> + if (!cpu_has_lvz) { >>>>>> + kvm_info("hardware virtualization not available\n"); >>>>>> + return -ENODEV; >>>>>> + } >>>>>> + r = kvm_loongarch_env_init(); >>>>>> + if (r) >>>>>> + return r; >>>>>> + >>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >>>>>> +} >>>>>> + >>>>>> +static void kvm_loongarch_exit(void) >>>>>> +{ >>>>>> + kvm_exit(); >>>>>> + kvm_loongarch_env_exit(); >>>>>> +} >>>>>> + >>>>>> +module_init(kvm_loongarch_init); >>>>>> +module_exit(kvm_loongarch_exit); >>>>>> + >>>>>> +#ifdef MODULE >>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { >>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >>>>>> + {}, >>>>>> +}; >>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >>>>>> +#endif >>>>>> -- >>>>>> 2.39.1 >>>>>>
On Mon, Sep 18, 2023 at 11:20 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > > > 在 2023/9/18 上午11:12, Huacai Chen 写道: > > On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >> > >> 在 2023/9/18 上午9:45, Huacai Chen 写道: > >>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: > >>>>> Hi, Tianrui, > >>>>> > >>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > >>>>>> Implement LoongArch kvm module init, module exit interface, > >>>>>> using kvm context to save the vpid info and vcpu world switch > >>>>>> interface pointer. > >>>>>> > >>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> > >>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > >>>>>> --- > >>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > >>>>>> 1 file changed, 367 insertions(+) > >>>>>> create mode 100644 arch/loongarch/kvm/main.c > >>>>>> > >>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > >>>>>> new file mode 100644 > >>>>>> index 0000000000..0deb9273d8 > >>>>>> --- /dev/null > >>>>>> +++ b/arch/loongarch/kvm/main.c > >>>>>> @@ -0,0 +1,367 @@ > >>>>>> +// SPDX-License-Identifier: GPL-2.0 > >>>>>> +/* > >>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > >>>>>> + */ > >>>>>> + > >>>>>> +#include <linux/err.h> > >>>>>> +#include <linux/module.h> > >>>>>> +#include <linux/kvm_host.h> > >>>>>> +#include <asm/cacheflush.h> > >>>>>> +#include <asm/cpufeature.h> > >>>>>> +#include <asm/kvm_csr.h> > >>>>>> +#include "trace.h" > >>>>>> + > >>>>>> +static struct kvm_context __percpu *vmcs; > >>>>>> +struct kvm_world_switch *kvm_loongarch_ops; > >>>>>> +unsigned long vpid_mask; > >>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; > >>>>>> + > >>>>>> +int get_gcsr_flag(int csr) > >>>>>> +{ > >>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>> + return gcsr_flag[csr]; > >>>>>> + > >>>>>> + return INVALID_GCSR; > >>>>>> +} > >>>>>> + > >>>>>> +static inline void set_gcsr_sw_flag(int csr) > >>>>>> +{ > >>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>> + gcsr_flag[csr] |= SW_GCSR; > >>>>>> +} > >>>>>> + > >>>>>> +static inline void set_gcsr_hw_flag(int csr) > >>>>>> +{ > >>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>> + gcsr_flag[csr] |= HW_GCSR; > >>>>>> +} > >>>>>> + > >>>>>> +/* > >>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this > >>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > >>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, > >>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > >>>>>> + * else use sw csr to emulate it. > >>>>>> + */ > >>>>>> +static void kvm_init_gcsr_flag(void) > >>>>>> +{ > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > >>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > >>>>>> + > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > >>>>>> + > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > >>>>> FWPS and MWPS are both HW CSR and SW CSR? > >>>>> > >>>>> Huacai > >>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it > >>>> is my mistake. > >>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW > >>> GCSR, while DBxxxx and IBxxxx are SW GCSR. > >> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest > >> debug register numbers, but I know they are all HW GCSR when I look up > >> the manual again. > > So these lines can be removed? > > > > set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > > set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > > set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > > set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > > > > And add FWPC/MWPC to hw list? Can you discuss more with our hw engineers? I found in section 1.8: PGD, TINTCLR, PRCFG1~3 are all HW GCSR. Huacai > > > > Huacai > Yes, It is. > > Thanks > Tianrui Zhao > > > >> Thanks > >> Tianrui Zhao > >>> Huacai > >>> > >>>> Thanks > >>>> Tianrui Zhao > >>>>>> + > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > >>>>>> + > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > >>>>>> + > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > >>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > >>>>>> +} > >>>>>> + > >>>>>> +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_vpid(struct kvm_vcpu *vcpu) > >>>>>> +{ > >>>>>> + struct kvm_context *context; > >>>>>> + bool migrated; > >>>>>> + unsigned long ver, old, vpid; > >>>>>> + int cpu; > >>>>>> + > >>>>>> + cpu = smp_processor_id(); > >>>>>> + /* > >>>>>> + * 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->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); > >>>>>> + vcpu->cpu = cpu; > >>>>>> + } > >>>>>> + > >>>>>> + /* Restore GSTAT(0x50).vpid */ > >>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > >>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > >>>>>> +} > >>>>>> + > >>>>>> +static int kvm_loongarch_env_init(void) > >>>>>> +{ > >>>>>> + struct kvm_context *context; > >>>>>> + int cpu, order; > >>>>>> + void *addr; > >>>>>> + > >>>>>> + vmcs = alloc_percpu(struct kvm_context); > >>>>>> + if (!vmcs) { > >>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); > >>>>>> + return -ENOMEM; > >>>>>> + } > >>>>>> + > >>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > >>>>>> + if (!kvm_loongarch_ops) { > >>>>>> + free_percpu(vmcs); > >>>>>> + vmcs = NULL; > >>>>>> + return -ENOMEM; > >>>>>> + } > >>>>>> + /* > >>>>>> + * There will be problem in world switch code if there > >>>>>> + * is page fault reenter, since pgd register is shared > >>>>>> + * between root kernel and kvm hypervisor. World switch > >>>>>> + * entry need be unmapped area, cannot be tlb mapped area. > >>>>>> + * In future if hw pagetable walking is supported, or there > >>>>>> + * is separate pgd registers between root kernel and kvm > >>>>>> + * hypervisor, copying about world switch code will not be used. > >>>>>> + */ > >>>>>> + > >>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); > >>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); > >>>>>> + if (!addr) { > >>>>>> + free_percpu(vmcs); > >>>>>> + vmcs = NULL; > >>>>>> + kfree(kvm_loongarch_ops); > >>>>>> + kvm_loongarch_ops = NULL; > >>>>>> + return -ENOMEM; > >>>>>> + } > >>>>>> + > >>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); > >>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > >>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + > >>>>>> + kvm_vector_size + kvm_enter_guest_size); > >>>>>> + kvm_loongarch_ops->guest_eentry = addr; > >>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > >>>>>> + kvm_loongarch_ops->page_order = order; > >>>>>> + > >>>>>> + vpid_mask = read_csr_gstat(); > >>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > >>>>>> + if (vpid_mask) > >>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); > >>>>>> + > >>>>>> + for_each_possible_cpu(cpu) { > >>>>>> + context = per_cpu_ptr(vmcs, cpu); > >>>>>> + context->vpid_cache = vpid_mask + 1; > >>>>>> + context->last_vcpu = NULL; > >>>>>> + } > >>>>>> + > >>>>>> + kvm_init_fault(); > >>>>>> + kvm_init_gcsr_flag(); > >>>>>> + > >>>>>> + return 0; > >>>>>> +} > >>>>>> + > >>>>>> +static void kvm_loongarch_env_exit(void) > >>>>>> +{ > >>>>>> + unsigned long addr; > >>>>>> + > >>>>>> + if (vmcs) > >>>>>> + free_percpu(vmcs); > >>>>>> + > >>>>>> + if (kvm_loongarch_ops) { > >>>>>> + if (kvm_loongarch_ops->guest_eentry) { > >>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > >>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); > >>>>>> + } > >>>>>> + kfree(kvm_loongarch_ops); > >>>>>> + } > >>>>>> +} > >>>>>> + > >>>>>> +static int kvm_loongarch_init(void) > >>>>>> +{ > >>>>>> + int r; > >>>>>> + > >>>>>> + if (!cpu_has_lvz) { > >>>>>> + kvm_info("hardware virtualization not available\n"); > >>>>>> + return -ENODEV; > >>>>>> + } > >>>>>> + r = kvm_loongarch_env_init(); > >>>>>> + if (r) > >>>>>> + return r; > >>>>>> + > >>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > >>>>>> +} > >>>>>> + > >>>>>> +static void kvm_loongarch_exit(void) > >>>>>> +{ > >>>>>> + kvm_exit(); > >>>>>> + kvm_loongarch_env_exit(); > >>>>>> +} > >>>>>> + > >>>>>> +module_init(kvm_loongarch_init); > >>>>>> +module_exit(kvm_loongarch_exit); > >>>>>> + > >>>>>> +#ifdef MODULE > >>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { > >>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, > >>>>>> + {}, > >>>>>> +}; > >>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > >>>>>> +#endif > >>>>>> -- > >>>>>> 2.39.1 > >>>>>> > >
在 2023/9/18 下午12:03, Huacai Chen 写道: > On Mon, Sep 18, 2023 at 11:20 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >> >> 在 2023/9/18 上午11:12, Huacai Chen 写道: >>> On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>> 在 2023/9/18 上午9:45, Huacai Chen 写道: >>>>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: >>>>>>> Hi, Tianrui, >>>>>>> >>>>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >>>>>>>> Implement LoongArch kvm module init, module exit interface, >>>>>>>> using kvm context to save the vpid info and vcpu world switch >>>>>>>> interface pointer. >>>>>>>> >>>>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >>>>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >>>>>>>> --- >>>>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >>>>>>>> 1 file changed, 367 insertions(+) >>>>>>>> create mode 100644 arch/loongarch/kvm/main.c >>>>>>>> >>>>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >>>>>>>> new file mode 100644 >>>>>>>> index 0000000000..0deb9273d8 >>>>>>>> --- /dev/null >>>>>>>> +++ b/arch/loongarch/kvm/main.c >>>>>>>> @@ -0,0 +1,367 @@ >>>>>>>> +// SPDX-License-Identifier: GPL-2.0 >>>>>>>> +/* >>>>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >>>>>>>> + */ >>>>>>>> + >>>>>>>> +#include <linux/err.h> >>>>>>>> +#include <linux/module.h> >>>>>>>> +#include <linux/kvm_host.h> >>>>>>>> +#include <asm/cacheflush.h> >>>>>>>> +#include <asm/cpufeature.h> >>>>>>>> +#include <asm/kvm_csr.h> >>>>>>>> +#include "trace.h" >>>>>>>> + >>>>>>>> +static struct kvm_context __percpu *vmcs; >>>>>>>> +struct kvm_world_switch *kvm_loongarch_ops; >>>>>>>> +unsigned long vpid_mask; >>>>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; >>>>>>>> + >>>>>>>> +int get_gcsr_flag(int csr) >>>>>>>> +{ >>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>> + return gcsr_flag[csr]; >>>>>>>> + >>>>>>>> + return INVALID_GCSR; >>>>>>>> +} >>>>>>>> + >>>>>>>> +static inline void set_gcsr_sw_flag(int csr) >>>>>>>> +{ >>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>> + gcsr_flag[csr] |= SW_GCSR; >>>>>>>> +} >>>>>>>> + >>>>>>>> +static inline void set_gcsr_hw_flag(int csr) >>>>>>>> +{ >>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>> + gcsr_flag[csr] |= HW_GCSR; >>>>>>>> +} >>>>>>>> + >>>>>>>> +/* >>>>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this >>>>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >>>>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, >>>>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >>>>>>>> + * else use sw csr to emulate it. >>>>>>>> + */ >>>>>>>> +static void kvm_init_gcsr_flag(void) >>>>>>>> +{ >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >>>>>>>> + >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >>>>>>>> + >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>>>>> FWPS and MWPS are both HW CSR and SW CSR? >>>>>>> >>>>>>> Huacai >>>>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it >>>>>> is my mistake. >>>>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW >>>>> GCSR, while DBxxxx and IBxxxx are SW GCSR. >>>> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest >>>> debug register numbers, but I know they are all HW GCSR when I look up >>>> the manual again. >>> So these lines can be removed? >>> >>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>> >>> And add FWPC/MWPC to hw list? > Can you discuss more with our hw engineers? I found in section 1.8: > PGD, TINTCLR, PRCFG1~3 are all HW GCSR. If guest can access hw CSR directly and do not cause a exception, it means hw GCSR. on the other hand, if cause a exception to return to KVM to emulate it by software, it means SW GCSR. And I re-check the PGD, TINTCLR, PRCFG1~3 CSR, they should be hw GCSR. Thanks Tianrui Zhao > > Huacai > >>> Huacai >> Yes, It is. >> >> Thanks >> Tianrui Zhao >>>> Thanks >>>> Tianrui Zhao >>>>> Huacai >>>>> >>>>>> Thanks >>>>>> Tianrui Zhao >>>>>>>> + >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >>>>>>>> + >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >>>>>>>> + >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >>>>>>>> +} >>>>>>>> + >>>>>>>> +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_vpid(struct kvm_vcpu *vcpu) >>>>>>>> +{ >>>>>>>> + struct kvm_context *context; >>>>>>>> + bool migrated; >>>>>>>> + unsigned long ver, old, vpid; >>>>>>>> + int cpu; >>>>>>>> + >>>>>>>> + cpu = smp_processor_id(); >>>>>>>> + /* >>>>>>>> + * 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->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); >>>>>>>> + vcpu->cpu = cpu; >>>>>>>> + } >>>>>>>> + >>>>>>>> + /* Restore GSTAT(0x50).vpid */ >>>>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >>>>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >>>>>>>> +} >>>>>>>> + >>>>>>>> +static int kvm_loongarch_env_init(void) >>>>>>>> +{ >>>>>>>> + struct kvm_context *context; >>>>>>>> + int cpu, order; >>>>>>>> + void *addr; >>>>>>>> + >>>>>>>> + vmcs = alloc_percpu(struct kvm_context); >>>>>>>> + if (!vmcs) { >>>>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >>>>>>>> + return -ENOMEM; >>>>>>>> + } >>>>>>>> + >>>>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >>>>>>>> + if (!kvm_loongarch_ops) { >>>>>>>> + free_percpu(vmcs); >>>>>>>> + vmcs = NULL; >>>>>>>> + return -ENOMEM; >>>>>>>> + } >>>>>>>> + /* >>>>>>>> + * There will be problem in world switch code if there >>>>>>>> + * is page fault reenter, since pgd register is shared >>>>>>>> + * between root kernel and kvm hypervisor. World switch >>>>>>>> + * entry need be unmapped area, cannot be tlb mapped area. >>>>>>>> + * In future if hw pagetable walking is supported, or there >>>>>>>> + * is separate pgd registers between root kernel and kvm >>>>>>>> + * hypervisor, copying about world switch code will not be used. >>>>>>>> + */ >>>>>>>> + >>>>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >>>>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >>>>>>>> + if (!addr) { >>>>>>>> + free_percpu(vmcs); >>>>>>>> + vmcs = NULL; >>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>> + kvm_loongarch_ops = NULL; >>>>>>>> + return -ENOMEM; >>>>>>>> + } >>>>>>>> + >>>>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >>>>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >>>>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >>>>>>>> + kvm_vector_size + kvm_enter_guest_size); >>>>>>>> + kvm_loongarch_ops->guest_eentry = addr; >>>>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >>>>>>>> + kvm_loongarch_ops->page_order = order; >>>>>>>> + >>>>>>>> + vpid_mask = read_csr_gstat(); >>>>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >>>>>>>> + if (vpid_mask) >>>>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); >>>>>>>> + >>>>>>>> + for_each_possible_cpu(cpu) { >>>>>>>> + context = per_cpu_ptr(vmcs, cpu); >>>>>>>> + context->vpid_cache = vpid_mask + 1; >>>>>>>> + context->last_vcpu = NULL; >>>>>>>> + } >>>>>>>> + >>>>>>>> + kvm_init_fault(); >>>>>>>> + kvm_init_gcsr_flag(); >>>>>>>> + >>>>>>>> + return 0; >>>>>>>> +} >>>>>>>> + >>>>>>>> +static void kvm_loongarch_env_exit(void) >>>>>>>> +{ >>>>>>>> + unsigned long addr; >>>>>>>> + >>>>>>>> + if (vmcs) >>>>>>>> + free_percpu(vmcs); >>>>>>>> + >>>>>>>> + if (kvm_loongarch_ops) { >>>>>>>> + if (kvm_loongarch_ops->guest_eentry) { >>>>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >>>>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); >>>>>>>> + } >>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>> + } >>>>>>>> +} >>>>>>>> + >>>>>>>> +static int kvm_loongarch_init(void) >>>>>>>> +{ >>>>>>>> + int r; >>>>>>>> + >>>>>>>> + if (!cpu_has_lvz) { >>>>>>>> + kvm_info("hardware virtualization not available\n"); >>>>>>>> + return -ENODEV; >>>>>>>> + } >>>>>>>> + r = kvm_loongarch_env_init(); >>>>>>>> + if (r) >>>>>>>> + return r; >>>>>>>> + >>>>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >>>>>>>> +} >>>>>>>> + >>>>>>>> +static void kvm_loongarch_exit(void) >>>>>>>> +{ >>>>>>>> + kvm_exit(); >>>>>>>> + kvm_loongarch_env_exit(); >>>>>>>> +} >>>>>>>> + >>>>>>>> +module_init(kvm_loongarch_init); >>>>>>>> +module_exit(kvm_loongarch_exit); >>>>>>>> + >>>>>>>> +#ifdef MODULE >>>>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { >>>>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >>>>>>>> + {}, >>>>>>>> +}; >>>>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >>>>>>>> +#endif >>>>>>>> -- >>>>>>>> 2.39.1 >>>>>>>> >>
在 2023/9/18 14:25, zhaotianrui 写道: > > 在 2023/9/18 下午12:03, Huacai Chen 写道: >> On Mon, Sep 18, 2023 at 11:20 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>> >>> 在 2023/9/18 上午11:12, Huacai Chen 写道: >>>> On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>> 在 2023/9/18 上午9:45, Huacai Chen 写道: >>>>>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: >>>>>>>> Hi, Tianrui, >>>>>>>> >>>>>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >>>>>>>>> Implement LoongArch kvm module init, module exit interface, >>>>>>>>> using kvm context to save the vpid info and vcpu world switch >>>>>>>>> interface pointer. >>>>>>>>> >>>>>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >>>>>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >>>>>>>>> --- >>>>>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >>>>>>>>> 1 file changed, 367 insertions(+) >>>>>>>>> create mode 100644 arch/loongarch/kvm/main.c >>>>>>>>> >>>>>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >>>>>>>>> new file mode 100644 >>>>>>>>> index 0000000000..0deb9273d8 >>>>>>>>> --- /dev/null >>>>>>>>> +++ b/arch/loongarch/kvm/main.c >>>>>>>>> @@ -0,0 +1,367 @@ >>>>>>>>> +// SPDX-License-Identifier: GPL-2.0 >>>>>>>>> +/* >>>>>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >>>>>>>>> + */ >>>>>>>>> + >>>>>>>>> +#include <linux/err.h> >>>>>>>>> +#include <linux/module.h> >>>>>>>>> +#include <linux/kvm_host.h> >>>>>>>>> +#include <asm/cacheflush.h> >>>>>>>>> +#include <asm/cpufeature.h> >>>>>>>>> +#include <asm/kvm_csr.h> >>>>>>>>> +#include "trace.h" >>>>>>>>> + >>>>>>>>> +static struct kvm_context __percpu *vmcs; >>>>>>>>> +struct kvm_world_switch *kvm_loongarch_ops; >>>>>>>>> +unsigned long vpid_mask; >>>>>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; >>>>>>>>> + >>>>>>>>> +int get_gcsr_flag(int csr) >>>>>>>>> +{ >>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>> + return gcsr_flag[csr]; >>>>>>>>> + >>>>>>>>> + return INVALID_GCSR; >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +static inline void set_gcsr_sw_flag(int csr) >>>>>>>>> +{ >>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>> + gcsr_flag[csr] |= SW_GCSR; >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +static inline void set_gcsr_hw_flag(int csr) >>>>>>>>> +{ >>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>> + gcsr_flag[csr] |= HW_GCSR; >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +/* >>>>>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this >>>>>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >>>>>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, >>>>>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >>>>>>>>> + * else use sw csr to emulate it. >>>>>>>>> + */ >>>>>>>>> +static void kvm_init_gcsr_flag(void) >>>>>>>>> +{ >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >>>>>>>>> + >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >>>>>>>>> + >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>>>>>> FWPS and MWPS are both HW CSR and SW CSR? >>>>>>>> >>>>>>>> Huacai >>>>>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it >>>>>>> is my mistake. >>>>>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW >>>>>> GCSR, while DBxxxx and IBxxxx are SW GCSR. >>>>> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest >>>>> debug register numbers, but I know they are all HW GCSR when I look up >>>>> the manual again. >>>> So these lines can be removed? >>>> >>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>> >>>> And add FWPC/MWPC to hw list? >> Can you discuss more with our hw engineers? I found in section 1.8: >> PGD, TINTCLR, PRCFG1~3 are all HW GCSR. > If guest can access hw CSR directly and do not cause a exception, it means hw GCSR. on the other hand, if cause a exception to return to KVM to emulate it by software, it means SW GCSR. And I re-check the PGD, TINTCLR, PRCFG1~3 CSR, they should be hw GCSR. CSR_PGD is logical read only register instead, its content is the same CSR_PGDL if highest bit of BADV is 0, else its content is the same with CSR_PGDH. TINTCLR is write only register, write 1 to clear timer interrupt and read value is 0. So CSR_PGD and TINTCLR need not save and restore during vcpu switching or vm migration, it is not necessary to set CSR_PGD/TINTCLR from qemu user space. PRCFG1~3 is read only registers, it is decided by hardware and can not be set by kvm, it is not necessary to save or retore. However there maybe brings problems for vm migration on diferent hardwares if PRCFG1~3 is different. Regards Bibo Mao > > Thanks > Tianrui Zhao >> >> Huacai >> >>>> Huacai >>> Yes, It is. >>> >>> Thanks >>> Tianrui Zhao >>>>> Thanks >>>>> Tianrui Zhao >>>>>> Huacai >>>>>> >>>>>>> Thanks >>>>>>> Tianrui Zhao >>>>>>>>> + >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >>>>>>>>> + >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >>>>>>>>> + >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +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_vpid(struct kvm_vcpu *vcpu) >>>>>>>>> +{ >>>>>>>>> + struct kvm_context *context; >>>>>>>>> + bool migrated; >>>>>>>>> + unsigned long ver, old, vpid; >>>>>>>>> + int cpu; >>>>>>>>> + >>>>>>>>> + cpu = smp_processor_id(); >>>>>>>>> + /* >>>>>>>>> + * 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->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); >>>>>>>>> + vcpu->cpu = cpu; >>>>>>>>> + } >>>>>>>>> + >>>>>>>>> + /* Restore GSTAT(0x50).vpid */ >>>>>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >>>>>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +static int kvm_loongarch_env_init(void) >>>>>>>>> +{ >>>>>>>>> + struct kvm_context *context; >>>>>>>>> + int cpu, order; >>>>>>>>> + void *addr; >>>>>>>>> + >>>>>>>>> + vmcs = alloc_percpu(struct kvm_context); >>>>>>>>> + if (!vmcs) { >>>>>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >>>>>>>>> + return -ENOMEM; >>>>>>>>> + } >>>>>>>>> + >>>>>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >>>>>>>>> + if (!kvm_loongarch_ops) { >>>>>>>>> + free_percpu(vmcs); >>>>>>>>> + vmcs = NULL; >>>>>>>>> + return -ENOMEM; >>>>>>>>> + } >>>>>>>>> + /* >>>>>>>>> + * There will be problem in world switch code if there >>>>>>>>> + * is page fault reenter, since pgd register is shared >>>>>>>>> + * between root kernel and kvm hypervisor. World switch >>>>>>>>> + * entry need be unmapped area, cannot be tlb mapped area. >>>>>>>>> + * In future if hw pagetable walking is supported, or there >>>>>>>>> + * is separate pgd registers between root kernel and kvm >>>>>>>>> + * hypervisor, copying about world switch code will not be used. >>>>>>>>> + */ >>>>>>>>> + >>>>>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >>>>>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >>>>>>>>> + if (!addr) { >>>>>>>>> + free_percpu(vmcs); >>>>>>>>> + vmcs = NULL; >>>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>>> + kvm_loongarch_ops = NULL; >>>>>>>>> + return -ENOMEM; >>>>>>>>> + } >>>>>>>>> + >>>>>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >>>>>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >>>>>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >>>>>>>>> + kvm_vector_size + kvm_enter_guest_size); >>>>>>>>> + kvm_loongarch_ops->guest_eentry = addr; >>>>>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >>>>>>>>> + kvm_loongarch_ops->page_order = order; >>>>>>>>> + >>>>>>>>> + vpid_mask = read_csr_gstat(); >>>>>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >>>>>>>>> + if (vpid_mask) >>>>>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); >>>>>>>>> + >>>>>>>>> + for_each_possible_cpu(cpu) { >>>>>>>>> + context = per_cpu_ptr(vmcs, cpu); >>>>>>>>> + context->vpid_cache = vpid_mask + 1; >>>>>>>>> + context->last_vcpu = NULL; >>>>>>>>> + } >>>>>>>>> + >>>>>>>>> + kvm_init_fault(); >>>>>>>>> + kvm_init_gcsr_flag(); >>>>>>>>> + >>>>>>>>> + return 0; >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +static void kvm_loongarch_env_exit(void) >>>>>>>>> +{ >>>>>>>>> + unsigned long addr; >>>>>>>>> + >>>>>>>>> + if (vmcs) >>>>>>>>> + free_percpu(vmcs); >>>>>>>>> + >>>>>>>>> + if (kvm_loongarch_ops) { >>>>>>>>> + if (kvm_loongarch_ops->guest_eentry) { >>>>>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >>>>>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); >>>>>>>>> + } >>>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>>> + } >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +static int kvm_loongarch_init(void) >>>>>>>>> +{ >>>>>>>>> + int r; >>>>>>>>> + >>>>>>>>> + if (!cpu_has_lvz) { >>>>>>>>> + kvm_info("hardware virtualization not available\n"); >>>>>>>>> + return -ENODEV; >>>>>>>>> + } >>>>>>>>> + r = kvm_loongarch_env_init(); >>>>>>>>> + if (r) >>>>>>>>> + return r; >>>>>>>>> + >>>>>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +static void kvm_loongarch_exit(void) >>>>>>>>> +{ >>>>>>>>> + kvm_exit(); >>>>>>>>> + kvm_loongarch_env_exit(); >>>>>>>>> +} >>>>>>>>> + >>>>>>>>> +module_init(kvm_loongarch_init); >>>>>>>>> +module_exit(kvm_loongarch_exit); >>>>>>>>> + >>>>>>>>> +#ifdef MODULE >>>>>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { >>>>>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >>>>>>>>> + {}, >>>>>>>>> +}; >>>>>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >>>>>>>>> +#endif >>>>>>>>> -- >>>>>>>>> 2.39.1 >>>>>>>>> >>>
On Mon, Sep 18, 2023 at 4:39 PM bibo mao <maobibo@loongson.cn> wrote: > > > > 在 2023/9/18 14:25, zhaotianrui 写道: > > > > 在 2023/9/18 下午12:03, Huacai Chen 写道: > >> On Mon, Sep 18, 2023 at 11:20 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >>> > >>> 在 2023/9/18 上午11:12, Huacai Chen 写道: > >>>> On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >>>>> 在 2023/9/18 上午9:45, Huacai Chen 写道: > >>>>>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >>>>>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: > >>>>>>>> Hi, Tianrui, > >>>>>>>> > >>>>>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > >>>>>>>>> Implement LoongArch kvm module init, module exit interface, > >>>>>>>>> using kvm context to save the vpid info and vcpu world switch > >>>>>>>>> interface pointer. > >>>>>>>>> > >>>>>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> > >>>>>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > >>>>>>>>> --- > >>>>>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > >>>>>>>>> 1 file changed, 367 insertions(+) > >>>>>>>>> create mode 100644 arch/loongarch/kvm/main.c > >>>>>>>>> > >>>>>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > >>>>>>>>> new file mode 100644 > >>>>>>>>> index 0000000000..0deb9273d8 > >>>>>>>>> --- /dev/null > >>>>>>>>> +++ b/arch/loongarch/kvm/main.c > >>>>>>>>> @@ -0,0 +1,367 @@ > >>>>>>>>> +// SPDX-License-Identifier: GPL-2.0 > >>>>>>>>> +/* > >>>>>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > >>>>>>>>> + */ > >>>>>>>>> + > >>>>>>>>> +#include <linux/err.h> > >>>>>>>>> +#include <linux/module.h> > >>>>>>>>> +#include <linux/kvm_host.h> > >>>>>>>>> +#include <asm/cacheflush.h> > >>>>>>>>> +#include <asm/cpufeature.h> > >>>>>>>>> +#include <asm/kvm_csr.h> > >>>>>>>>> +#include "trace.h" > >>>>>>>>> + > >>>>>>>>> +static struct kvm_context __percpu *vmcs; > >>>>>>>>> +struct kvm_world_switch *kvm_loongarch_ops; > >>>>>>>>> +unsigned long vpid_mask; > >>>>>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; > >>>>>>>>> + > >>>>>>>>> +int get_gcsr_flag(int csr) > >>>>>>>>> +{ > >>>>>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>>>>> + return gcsr_flag[csr]; > >>>>>>>>> + > >>>>>>>>> + return INVALID_GCSR; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static inline void set_gcsr_sw_flag(int csr) > >>>>>>>>> +{ > >>>>>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>>>>> + gcsr_flag[csr] |= SW_GCSR; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static inline void set_gcsr_hw_flag(int csr) > >>>>>>>>> +{ > >>>>>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>>>>> + gcsr_flag[csr] |= HW_GCSR; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +/* > >>>>>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this > >>>>>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > >>>>>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, > >>>>>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > >>>>>>>>> + * else use sw csr to emulate it. > >>>>>>>>> + */ > >>>>>>>>> +static void kvm_init_gcsr_flag(void) > >>>>>>>>> +{ > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > >>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > >>>>>>>>> + > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > >>>>>>>>> + > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > >>>>>>>> FWPS and MWPS are both HW CSR and SW CSR? > >>>>>>>> > >>>>>>>> Huacai > >>>>>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it > >>>>>>> is my mistake. > >>>>>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW > >>>>>> GCSR, while DBxxxx and IBxxxx are SW GCSR. > >>>>> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest > >>>>> debug register numbers, but I know they are all HW GCSR when I look up > >>>>> the manual again. > >>>> So these lines can be removed? > >>>> > >>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > >>>> > >>>> And add FWPC/MWPC to hw list? > >> Can you discuss more with our hw engineers? I found in section 1.8: > >> PGD, TINTCLR, PRCFG1~3 are all HW GCSR. > > If guest can access hw CSR directly and do not cause a exception, it means hw GCSR. on the other hand, if cause a exception to return to KVM to emulate it by software, it means SW GCSR. And I re-check the PGD, TINTCLR, PRCFG1~3 CSR, they should be hw GCSR. > > CSR_PGD is logical read only register instead, its content is the same > CSR_PGDL if highest bit of BADV is 0, else its content is the same with > CSR_PGDH. > > TINTCLR is write only register, write 1 to clear timer interrupt and read > value is 0. > > So CSR_PGD and TINTCLR need not save and restore during vcpu switching or > vm migration, it is not necessary to set CSR_PGD/TINTCLR from qemu user space. > > PRCFG1~3 is read only registers, it is decided by hardware and can not be > set by kvm, it is not necessary to save or retore. However there maybe brings > problems for vm migration on diferent hardwares if PRCFG1~3 is different. During my tests, reading FWPC/FWPS/MWPC/MWPS causes guest exit, but user manual vol 3 says they are HW GCSR, who can tell me why? Huacai > > Regards > Bibo Mao > > > > Thanks > > Tianrui Zhao > >> > >> Huacai > >> > >>>> Huacai > >>> Yes, It is. > >>> > >>> Thanks > >>> Tianrui Zhao > >>>>> Thanks > >>>>> Tianrui Zhao > >>>>>> Huacai > >>>>>> > >>>>>>> Thanks > >>>>>>> Tianrui Zhao > >>>>>>>>> + > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > >>>>>>>>> + > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > >>>>>>>>> + > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > >>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +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_vpid(struct kvm_vcpu *vcpu) > >>>>>>>>> +{ > >>>>>>>>> + struct kvm_context *context; > >>>>>>>>> + bool migrated; > >>>>>>>>> + unsigned long ver, old, vpid; > >>>>>>>>> + int cpu; > >>>>>>>>> + > >>>>>>>>> + cpu = smp_processor_id(); > >>>>>>>>> + /* > >>>>>>>>> + * 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->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); > >>>>>>>>> + vcpu->cpu = cpu; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + /* Restore GSTAT(0x50).vpid */ > >>>>>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > >>>>>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static int kvm_loongarch_env_init(void) > >>>>>>>>> +{ > >>>>>>>>> + struct kvm_context *context; > >>>>>>>>> + int cpu, order; > >>>>>>>>> + void *addr; > >>>>>>>>> + > >>>>>>>>> + vmcs = alloc_percpu(struct kvm_context); > >>>>>>>>> + if (!vmcs) { > >>>>>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); > >>>>>>>>> + return -ENOMEM; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > >>>>>>>>> + if (!kvm_loongarch_ops) { > >>>>>>>>> + free_percpu(vmcs); > >>>>>>>>> + vmcs = NULL; > >>>>>>>>> + return -ENOMEM; > >>>>>>>>> + } > >>>>>>>>> + /* > >>>>>>>>> + * There will be problem in world switch code if there > >>>>>>>>> + * is page fault reenter, since pgd register is shared > >>>>>>>>> + * between root kernel and kvm hypervisor. World switch > >>>>>>>>> + * entry need be unmapped area, cannot be tlb mapped area. > >>>>>>>>> + * In future if hw pagetable walking is supported, or there > >>>>>>>>> + * is separate pgd registers between root kernel and kvm > >>>>>>>>> + * hypervisor, copying about world switch code will not be used. > >>>>>>>>> + */ > >>>>>>>>> + > >>>>>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); > >>>>>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); > >>>>>>>>> + if (!addr) { > >>>>>>>>> + free_percpu(vmcs); > >>>>>>>>> + vmcs = NULL; > >>>>>>>>> + kfree(kvm_loongarch_ops); > >>>>>>>>> + kvm_loongarch_ops = NULL; > >>>>>>>>> + return -ENOMEM; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); > >>>>>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > >>>>>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + > >>>>>>>>> + kvm_vector_size + kvm_enter_guest_size); > >>>>>>>>> + kvm_loongarch_ops->guest_eentry = addr; > >>>>>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > >>>>>>>>> + kvm_loongarch_ops->page_order = order; > >>>>>>>>> + > >>>>>>>>> + vpid_mask = read_csr_gstat(); > >>>>>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > >>>>>>>>> + if (vpid_mask) > >>>>>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); > >>>>>>>>> + > >>>>>>>>> + for_each_possible_cpu(cpu) { > >>>>>>>>> + context = per_cpu_ptr(vmcs, cpu); > >>>>>>>>> + context->vpid_cache = vpid_mask + 1; > >>>>>>>>> + context->last_vcpu = NULL; > >>>>>>>>> + } > >>>>>>>>> + > >>>>>>>>> + kvm_init_fault(); > >>>>>>>>> + kvm_init_gcsr_flag(); > >>>>>>>>> + > >>>>>>>>> + return 0; > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static void kvm_loongarch_env_exit(void) > >>>>>>>>> +{ > >>>>>>>>> + unsigned long addr; > >>>>>>>>> + > >>>>>>>>> + if (vmcs) > >>>>>>>>> + free_percpu(vmcs); > >>>>>>>>> + > >>>>>>>>> + if (kvm_loongarch_ops) { > >>>>>>>>> + if (kvm_loongarch_ops->guest_eentry) { > >>>>>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > >>>>>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); > >>>>>>>>> + } > >>>>>>>>> + kfree(kvm_loongarch_ops); > >>>>>>>>> + } > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static int kvm_loongarch_init(void) > >>>>>>>>> +{ > >>>>>>>>> + int r; > >>>>>>>>> + > >>>>>>>>> + if (!cpu_has_lvz) { > >>>>>>>>> + kvm_info("hardware virtualization not available\n"); > >>>>>>>>> + return -ENODEV; > >>>>>>>>> + } > >>>>>>>>> + r = kvm_loongarch_env_init(); > >>>>>>>>> + if (r) > >>>>>>>>> + return r; > >>>>>>>>> + > >>>>>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +static void kvm_loongarch_exit(void) > >>>>>>>>> +{ > >>>>>>>>> + kvm_exit(); > >>>>>>>>> + kvm_loongarch_env_exit(); > >>>>>>>>> +} > >>>>>>>>> + > >>>>>>>>> +module_init(kvm_loongarch_init); > >>>>>>>>> +module_exit(kvm_loongarch_exit); > >>>>>>>>> + > >>>>>>>>> +#ifdef MODULE > >>>>>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { > >>>>>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, > >>>>>>>>> + {}, > >>>>>>>>> +}; > >>>>>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > >>>>>>>>> +#endif > >>>>>>>>> -- > >>>>>>>>> 2.39.1 > >>>>>>>>> > >>> > >
在 2023/9/17 下午12:21, Huacai Chen 写道: > Hi, Tianrui, > > On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >> Implement LoongArch kvm module init, module exit interface, >> using kvm context to save the vpid info and vcpu world switch >> interface pointer. >> >> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >> --- >> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >> 1 file changed, 367 insertions(+) >> create mode 100644 arch/loongarch/kvm/main.c >> >> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >> new file mode 100644 >> index 0000000000..0deb9273d8 >> --- /dev/null >> +++ b/arch/loongarch/kvm/main.c >> @@ -0,0 +1,367 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >> + */ >> + >> +#include <linux/err.h> >> +#include <linux/module.h> >> +#include <linux/kvm_host.h> >> +#include <asm/cacheflush.h> >> +#include <asm/cpufeature.h> >> +#include <asm/kvm_csr.h> >> +#include "trace.h" >> + >> +static struct kvm_context __percpu *vmcs; >> +struct kvm_world_switch *kvm_loongarch_ops; >> +unsigned long vpid_mask; >> +static int gcsr_flag[CSR_MAX_NUMS]; >> + >> +int get_gcsr_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + return gcsr_flag[csr]; >> + >> + return INVALID_GCSR; >> +} >> + >> +static inline void set_gcsr_sw_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + gcsr_flag[csr] |= SW_GCSR; >> +} >> + >> +static inline void set_gcsr_hw_flag(int csr) >> +{ >> + if (csr < CSR_MAX_NUMS) >> + gcsr_flag[csr] |= HW_GCSR; >> +} >> + >> +/* >> + * The default value of gcsr_flag[CSR] is 0, and we use this >> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >> + * gcsr is software or hardware. It will be used by get/set_gcsr, >> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >> + * else use sw csr to emulate it. >> + */ >> +static void kvm_init_gcsr_flag(void) >> +{ >> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >> + >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >> +} >> + >> +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_vpid(struct kvm_vcpu *vcpu) >> +{ >> + struct kvm_context *context; >> + bool migrated; >> + unsigned long ver, old, vpid; >> + int cpu; >> + >> + cpu = smp_processor_id(); >> + /* >> + * 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->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); >> + vcpu->cpu = cpu; >> + } >> + >> + /* Restore GSTAT(0x50).vpid */ >> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >> +} >> + >> +static int kvm_loongarch_env_init(void) >> +{ >> + struct kvm_context *context; >> + int cpu, order; >> + void *addr; >> + >> + vmcs = alloc_percpu(struct kvm_context); >> + if (!vmcs) { >> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >> + return -ENOMEM; >> + } >> + >> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >> + if (!kvm_loongarch_ops) { >> + free_percpu(vmcs); >> + vmcs = NULL; >> + return -ENOMEM; >> + } >> + /* >> + * There will be problem in world switch code if there >> + * is page fault reenter, since pgd register is shared >> + * between root kernel and kvm hypervisor. World switch >> + * entry need be unmapped area, cannot be tlb mapped area. >> + * In future if hw pagetable walking is supported, or there >> + * is separate pgd registers between root kernel and kvm >> + * hypervisor, copying about world switch code will not be used. >> + */ >> + >> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >> + if (!addr) { >> + free_percpu(vmcs); >> + vmcs = NULL; >> + kfree(kvm_loongarch_ops); >> + kvm_loongarch_ops = NULL; >> + return -ENOMEM; >> + } >> + >> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > Why memcpy? In our internal repo, we use kvm_vector_entry and > kvm_enter_guest directly. The long comments above make me nervous > because Loongson-3A6000 already supports hardware pagetable walker. > > Huacai As mentioned in the comments, it need not this memcpy if hardware page walk is supported in 3A6000. Thanks Tianrui Zhao > >> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >> + kvm_vector_size + kvm_enter_guest_size); >> + kvm_loongarch_ops->guest_eentry = addr; >> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >> + kvm_loongarch_ops->page_order = order; >> + >> + vpid_mask = read_csr_gstat(); >> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >> + if (vpid_mask) >> + vpid_mask = GENMASK(vpid_mask - 1, 0); >> + >> + for_each_possible_cpu(cpu) { >> + context = per_cpu_ptr(vmcs, cpu); >> + context->vpid_cache = vpid_mask + 1; >> + context->last_vcpu = NULL; >> + } >> + >> + kvm_init_fault(); >> + kvm_init_gcsr_flag(); >> + >> + return 0; >> +} >> + >> +static void kvm_loongarch_env_exit(void) >> +{ >> + unsigned long addr; >> + >> + if (vmcs) >> + free_percpu(vmcs); >> + >> + if (kvm_loongarch_ops) { >> + if (kvm_loongarch_ops->guest_eentry) { >> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >> + free_pages(addr, kvm_loongarch_ops->page_order); >> + } >> + kfree(kvm_loongarch_ops); >> + } >> +} >> + >> +static int kvm_loongarch_init(void) >> +{ >> + int r; >> + >> + if (!cpu_has_lvz) { >> + kvm_info("hardware virtualization not available\n"); >> + return -ENODEV; >> + } >> + r = kvm_loongarch_env_init(); >> + if (r) >> + return r; >> + >> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >> +} >> + >> +static void kvm_loongarch_exit(void) >> +{ >> + kvm_exit(); >> + kvm_loongarch_env_exit(); >> +} >> + >> +module_init(kvm_loongarch_init); >> +module_exit(kvm_loongarch_exit); >> + >> +#ifdef MODULE >> +static const struct cpu_feature loongarch_kvm_feature[] = { >> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >> +#endif >> -- >> 2.39.1 >>
On Tue, Sep 19, 2023 at 10:38 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > > > 在 2023/9/17 下午12:21, Huacai Chen 写道: > > Hi, Tianrui, > > > > On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > >> Implement LoongArch kvm module init, module exit interface, > >> using kvm context to save the vpid info and vcpu world switch > >> interface pointer. > >> > >> Reviewed-by: Bibo Mao <maobibo@loongson.cn> > >> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > >> --- > >> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > >> 1 file changed, 367 insertions(+) > >> create mode 100644 arch/loongarch/kvm/main.c > >> > >> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > >> new file mode 100644 > >> index 0000000000..0deb9273d8 > >> --- /dev/null > >> +++ b/arch/loongarch/kvm/main.c > >> @@ -0,0 +1,367 @@ > >> +// SPDX-License-Identifier: GPL-2.0 > >> +/* > >> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > >> + */ > >> + > >> +#include <linux/err.h> > >> +#include <linux/module.h> > >> +#include <linux/kvm_host.h> > >> +#include <asm/cacheflush.h> > >> +#include <asm/cpufeature.h> > >> +#include <asm/kvm_csr.h> > >> +#include "trace.h" > >> + > >> +static struct kvm_context __percpu *vmcs; > >> +struct kvm_world_switch *kvm_loongarch_ops; > >> +unsigned long vpid_mask; > >> +static int gcsr_flag[CSR_MAX_NUMS]; > >> + > >> +int get_gcsr_flag(int csr) > >> +{ > >> + if (csr < CSR_MAX_NUMS) > >> + return gcsr_flag[csr]; > >> + > >> + return INVALID_GCSR; > >> +} > >> + > >> +static inline void set_gcsr_sw_flag(int csr) > >> +{ > >> + if (csr < CSR_MAX_NUMS) > >> + gcsr_flag[csr] |= SW_GCSR; > >> +} > >> + > >> +static inline void set_gcsr_hw_flag(int csr) > >> +{ > >> + if (csr < CSR_MAX_NUMS) > >> + gcsr_flag[csr] |= HW_GCSR; > >> +} > >> + > >> +/* > >> + * The default value of gcsr_flag[CSR] is 0, and we use this > >> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > >> + * gcsr is software or hardware. It will be used by get/set_gcsr, > >> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > >> + * else use sw csr to emulate it. > >> + */ > >> +static void kvm_init_gcsr_flag(void) > >> +{ > >> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > >> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > >> + > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > >> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > >> +} > >> + > >> +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_vpid(struct kvm_vcpu *vcpu) > >> +{ > >> + struct kvm_context *context; > >> + bool migrated; > >> + unsigned long ver, old, vpid; > >> + int cpu; > >> + > >> + cpu = smp_processor_id(); > >> + /* > >> + * 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->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); > >> + vcpu->cpu = cpu; > >> + } > >> + > >> + /* Restore GSTAT(0x50).vpid */ > >> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > >> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > >> +} > >> + > >> +static int kvm_loongarch_env_init(void) > >> +{ > >> + struct kvm_context *context; > >> + int cpu, order; > >> + void *addr; > >> + > >> + vmcs = alloc_percpu(struct kvm_context); > >> + if (!vmcs) { > >> + pr_err("kvm: failed to allocate percpu kvm_context\n"); > >> + return -ENOMEM; > >> + } > >> + > >> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > >> + if (!kvm_loongarch_ops) { > >> + free_percpu(vmcs); > >> + vmcs = NULL; > >> + return -ENOMEM; > >> + } > >> + /* > >> + * There will be problem in world switch code if there > >> + * is page fault reenter, since pgd register is shared > >> + * between root kernel and kvm hypervisor. World switch > >> + * entry need be unmapped area, cannot be tlb mapped area. > >> + * In future if hw pagetable walking is supported, or there > >> + * is separate pgd registers between root kernel and kvm > >> + * hypervisor, copying about world switch code will not be used. > >> + */ > >> + > >> + order = get_order(kvm_vector_size + kvm_enter_guest_size); > >> + addr = (void *)__get_free_pages(GFP_KERNEL, order); > >> + if (!addr) { > >> + free_percpu(vmcs); > >> + vmcs = NULL; > >> + kfree(kvm_loongarch_ops); > >> + kvm_loongarch_ops = NULL; > >> + return -ENOMEM; > >> + } > >> + > >> + memcpy(addr, kvm_vector_entry, kvm_vector_size); > >> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > > Why memcpy? In our internal repo, we use kvm_vector_entry and > > kvm_enter_guest directly. The long comments above make me nervous > > because Loongson-3A6000 already supports hardware pagetable walker. > > > > Huacai > As mentioned in the comments, it need not this memcpy if hardware page > walk is supported in 3A6000. But why in our internal repo we don't use memcpy() but kvm can still work on Loongson-3A5000? Huacai > > Thanks > Tianrui Zhao > > > >> + flush_icache_range((unsigned long)addr, (unsigned long)addr + > >> + kvm_vector_size + kvm_enter_guest_size); > >> + kvm_loongarch_ops->guest_eentry = addr; > >> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > >> + kvm_loongarch_ops->page_order = order; > >> + > >> + vpid_mask = read_csr_gstat(); > >> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > >> + if (vpid_mask) > >> + vpid_mask = GENMASK(vpid_mask - 1, 0); > >> + > >> + for_each_possible_cpu(cpu) { > >> + context = per_cpu_ptr(vmcs, cpu); > >> + context->vpid_cache = vpid_mask + 1; > >> + context->last_vcpu = NULL; > >> + } > >> + > >> + kvm_init_fault(); > >> + kvm_init_gcsr_flag(); > >> + > >> + return 0; > >> +} > >> + > >> +static void kvm_loongarch_env_exit(void) > >> +{ > >> + unsigned long addr; > >> + > >> + if (vmcs) > >> + free_percpu(vmcs); > >> + > >> + if (kvm_loongarch_ops) { > >> + if (kvm_loongarch_ops->guest_eentry) { > >> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > >> + free_pages(addr, kvm_loongarch_ops->page_order); > >> + } > >> + kfree(kvm_loongarch_ops); > >> + } > >> +} > >> + > >> +static int kvm_loongarch_init(void) > >> +{ > >> + int r; > >> + > >> + if (!cpu_has_lvz) { > >> + kvm_info("hardware virtualization not available\n"); > >> + return -ENODEV; > >> + } > >> + r = kvm_loongarch_env_init(); > >> + if (r) > >> + return r; > >> + > >> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > >> +} > >> + > >> +static void kvm_loongarch_exit(void) > >> +{ > >> + kvm_exit(); > >> + kvm_loongarch_env_exit(); > >> +} > >> + > >> +module_init(kvm_loongarch_init); > >> +module_exit(kvm_loongarch_exit); > >> + > >> +#ifdef MODULE > >> +static const struct cpu_feature loongarch_kvm_feature[] = { > >> + { .feature = cpu_feature(LOONGARCH_LVZ) }, > >> + {}, > >> +}; > >> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > >> +#endif > >> -- > >> 2.39.1 > >> > >
在 2023/9/19 上午10:14, Huacai Chen 写道: > On Mon, Sep 18, 2023 at 4:39 PM bibo mao <maobibo@loongson.cn> wrote: >> >> >> 在 2023/9/18 14:25, zhaotianrui 写道: >>> 在 2023/9/18 下午12:03, Huacai Chen 写道: >>>> On Mon, Sep 18, 2023 at 11:20 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>> 在 2023/9/18 上午11:12, Huacai Chen 写道: >>>>>> On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>>>> 在 2023/9/18 上午9:45, Huacai Chen 写道: >>>>>>>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>>>>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: >>>>>>>>>> Hi, Tianrui, >>>>>>>>>> >>>>>>>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >>>>>>>>>>> Implement LoongArch kvm module init, module exit interface, >>>>>>>>>>> using kvm context to save the vpid info and vcpu world switch >>>>>>>>>>> interface pointer. >>>>>>>>>>> >>>>>>>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >>>>>>>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >>>>>>>>>>> --- >>>>>>>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >>>>>>>>>>> 1 file changed, 367 insertions(+) >>>>>>>>>>> create mode 100644 arch/loongarch/kvm/main.c >>>>>>>>>>> >>>>>>>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >>>>>>>>>>> new file mode 100644 >>>>>>>>>>> index 0000000000..0deb9273d8 >>>>>>>>>>> --- /dev/null >>>>>>>>>>> +++ b/arch/loongarch/kvm/main.c >>>>>>>>>>> @@ -0,0 +1,367 @@ >>>>>>>>>>> +// SPDX-License-Identifier: GPL-2.0 >>>>>>>>>>> +/* >>>>>>>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >>>>>>>>>>> + */ >>>>>>>>>>> + >>>>>>>>>>> +#include <linux/err.h> >>>>>>>>>>> +#include <linux/module.h> >>>>>>>>>>> +#include <linux/kvm_host.h> >>>>>>>>>>> +#include <asm/cacheflush.h> >>>>>>>>>>> +#include <asm/cpufeature.h> >>>>>>>>>>> +#include <asm/kvm_csr.h> >>>>>>>>>>> +#include "trace.h" >>>>>>>>>>> + >>>>>>>>>>> +static struct kvm_context __percpu *vmcs; >>>>>>>>>>> +struct kvm_world_switch *kvm_loongarch_ops; >>>>>>>>>>> +unsigned long vpid_mask; >>>>>>>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; >>>>>>>>>>> + >>>>>>>>>>> +int get_gcsr_flag(int csr) >>>>>>>>>>> +{ >>>>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>>>> + return gcsr_flag[csr]; >>>>>>>>>>> + >>>>>>>>>>> + return INVALID_GCSR; >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +static inline void set_gcsr_sw_flag(int csr) >>>>>>>>>>> +{ >>>>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>>>> + gcsr_flag[csr] |= SW_GCSR; >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +static inline void set_gcsr_hw_flag(int csr) >>>>>>>>>>> +{ >>>>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>>>> + gcsr_flag[csr] |= HW_GCSR; >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +/* >>>>>>>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this >>>>>>>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >>>>>>>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, >>>>>>>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >>>>>>>>>>> + * else use sw csr to emulate it. >>>>>>>>>>> + */ >>>>>>>>>>> +static void kvm_init_gcsr_flag(void) >>>>>>>>>>> +{ >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >>>>>>>>>>> + >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >>>>>>>>>>> + >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>>>>>>>> FWPS and MWPS are both HW CSR and SW CSR? >>>>>>>>>> >>>>>>>>>> Huacai >>>>>>>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it >>>>>>>>> is my mistake. >>>>>>>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW >>>>>>>> GCSR, while DBxxxx and IBxxxx are SW GCSR. >>>>>>> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest >>>>>>> debug register numbers, but I know they are all HW GCSR when I look up >>>>>>> the manual again. >>>>>> So these lines can be removed? >>>>>> >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>>>> >>>>>> And add FWPC/MWPC to hw list? >>>> Can you discuss more with our hw engineers? I found in section 1.8: >>>> PGD, TINTCLR, PRCFG1~3 are all HW GCSR. >>> If guest can access hw CSR directly and do not cause a exception, it means hw GCSR. on the other hand, if cause a exception to return to KVM to emulate it by software, it means SW GCSR. And I re-check the PGD, TINTCLR, PRCFG1~3 CSR, they should be hw GCSR. >> CSR_PGD is logical read only register instead, its content is the same >> CSR_PGDL if highest bit of BADV is 0, else its content is the same with >> CSR_PGDH. >> >> TINTCLR is write only register, write 1 to clear timer interrupt and read >> value is 0. >> >> So CSR_PGD and TINTCLR need not save and restore during vcpu switching or >> vm migration, it is not necessary to set CSR_PGD/TINTCLR from qemu user space. >> >> PRCFG1~3 is read only registers, it is decided by hardware and can not be >> set by kvm, it is not necessary to save or retore. However there maybe brings >> problems for vm migration on diferent hardwares if PRCFG1~3 is different. > During my tests, reading FWPC/FWPS/MWPC/MWPS causes guest exit, but > user manual vol 3 says they are HW GCSR, who can tell me why? It will cause guest exiting when guest visit these registers above for the first time, and KVM could config FWPC, MWPC to enable the debug registers for guest, so that it will not cause exiting at next time. And we will add this handling later when we want to use this function in guest. Thanks Tianrui Zhao > > Huacai > >> Regards >> Bibo Mao >>> Thanks >>> Tianrui Zhao >>>> Huacai >>>> >>>>>> Huacai >>>>> Yes, It is. >>>>> >>>>> Thanks >>>>> Tianrui Zhao >>>>>>> Thanks >>>>>>> Tianrui Zhao >>>>>>>> Huacai >>>>>>>> >>>>>>>>> Thanks >>>>>>>>> Tianrui Zhao >>>>>>>>>>> + >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >>>>>>>>>>> + >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >>>>>>>>>>> + >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +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_vpid(struct kvm_vcpu *vcpu) >>>>>>>>>>> +{ >>>>>>>>>>> + struct kvm_context *context; >>>>>>>>>>> + bool migrated; >>>>>>>>>>> + unsigned long ver, old, vpid; >>>>>>>>>>> + int cpu; >>>>>>>>>>> + >>>>>>>>>>> + cpu = smp_processor_id(); >>>>>>>>>>> + /* >>>>>>>>>>> + * 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->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); >>>>>>>>>>> + vcpu->cpu = cpu; >>>>>>>>>>> + } >>>>>>>>>>> + >>>>>>>>>>> + /* Restore GSTAT(0x50).vpid */ >>>>>>>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >>>>>>>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +static int kvm_loongarch_env_init(void) >>>>>>>>>>> +{ >>>>>>>>>>> + struct kvm_context *context; >>>>>>>>>>> + int cpu, order; >>>>>>>>>>> + void *addr; >>>>>>>>>>> + >>>>>>>>>>> + vmcs = alloc_percpu(struct kvm_context); >>>>>>>>>>> + if (!vmcs) { >>>>>>>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >>>>>>>>>>> + return -ENOMEM; >>>>>>>>>>> + } >>>>>>>>>>> + >>>>>>>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >>>>>>>>>>> + if (!kvm_loongarch_ops) { >>>>>>>>>>> + free_percpu(vmcs); >>>>>>>>>>> + vmcs = NULL; >>>>>>>>>>> + return -ENOMEM; >>>>>>>>>>> + } >>>>>>>>>>> + /* >>>>>>>>>>> + * There will be problem in world switch code if there >>>>>>>>>>> + * is page fault reenter, since pgd register is shared >>>>>>>>>>> + * between root kernel and kvm hypervisor. World switch >>>>>>>>>>> + * entry need be unmapped area, cannot be tlb mapped area. >>>>>>>>>>> + * In future if hw pagetable walking is supported, or there >>>>>>>>>>> + * is separate pgd registers between root kernel and kvm >>>>>>>>>>> + * hypervisor, copying about world switch code will not be used. >>>>>>>>>>> + */ >>>>>>>>>>> + >>>>>>>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >>>>>>>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >>>>>>>>>>> + if (!addr) { >>>>>>>>>>> + free_percpu(vmcs); >>>>>>>>>>> + vmcs = NULL; >>>>>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>>>>> + kvm_loongarch_ops = NULL; >>>>>>>>>>> + return -ENOMEM; >>>>>>>>>>> + } >>>>>>>>>>> + >>>>>>>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >>>>>>>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >>>>>>>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >>>>>>>>>>> + kvm_vector_size + kvm_enter_guest_size); >>>>>>>>>>> + kvm_loongarch_ops->guest_eentry = addr; >>>>>>>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >>>>>>>>>>> + kvm_loongarch_ops->page_order = order; >>>>>>>>>>> + >>>>>>>>>>> + vpid_mask = read_csr_gstat(); >>>>>>>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >>>>>>>>>>> + if (vpid_mask) >>>>>>>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); >>>>>>>>>>> + >>>>>>>>>>> + for_each_possible_cpu(cpu) { >>>>>>>>>>> + context = per_cpu_ptr(vmcs, cpu); >>>>>>>>>>> + context->vpid_cache = vpid_mask + 1; >>>>>>>>>>> + context->last_vcpu = NULL; >>>>>>>>>>> + } >>>>>>>>>>> + >>>>>>>>>>> + kvm_init_fault(); >>>>>>>>>>> + kvm_init_gcsr_flag(); >>>>>>>>>>> + >>>>>>>>>>> + return 0; >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +static void kvm_loongarch_env_exit(void) >>>>>>>>>>> +{ >>>>>>>>>>> + unsigned long addr; >>>>>>>>>>> + >>>>>>>>>>> + if (vmcs) >>>>>>>>>>> + free_percpu(vmcs); >>>>>>>>>>> + >>>>>>>>>>> + if (kvm_loongarch_ops) { >>>>>>>>>>> + if (kvm_loongarch_ops->guest_eentry) { >>>>>>>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >>>>>>>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); >>>>>>>>>>> + } >>>>>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>>>>> + } >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +static int kvm_loongarch_init(void) >>>>>>>>>>> +{ >>>>>>>>>>> + int r; >>>>>>>>>>> + >>>>>>>>>>> + if (!cpu_has_lvz) { >>>>>>>>>>> + kvm_info("hardware virtualization not available\n"); >>>>>>>>>>> + return -ENODEV; >>>>>>>>>>> + } >>>>>>>>>>> + r = kvm_loongarch_env_init(); >>>>>>>>>>> + if (r) >>>>>>>>>>> + return r; >>>>>>>>>>> + >>>>>>>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +static void kvm_loongarch_exit(void) >>>>>>>>>>> +{ >>>>>>>>>>> + kvm_exit(); >>>>>>>>>>> + kvm_loongarch_env_exit(); >>>>>>>>>>> +} >>>>>>>>>>> + >>>>>>>>>>> +module_init(kvm_loongarch_init); >>>>>>>>>>> +module_exit(kvm_loongarch_exit); >>>>>>>>>>> + >>>>>>>>>>> +#ifdef MODULE >>>>>>>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { >>>>>>>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >>>>>>>>>>> + {}, >>>>>>>>>>> +}; >>>>>>>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >>>>>>>>>>> +#endif >>>>>>>>>>> -- >>>>>>>>>>> 2.39.1 >>>>>>>>>>> >>
On Tue, Sep 19, 2023 at 10:53 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > > > 在 2023/9/19 上午10:14, Huacai Chen 写道: > > On Mon, Sep 18, 2023 at 4:39 PM bibo mao <maobibo@loongson.cn> wrote: > >> > >> > >> 在 2023/9/18 14:25, zhaotianrui 写道: > >>> 在 2023/9/18 下午12:03, Huacai Chen 写道: > >>>> On Mon, Sep 18, 2023 at 11:20 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >>>>> 在 2023/9/18 上午11:12, Huacai Chen 写道: > >>>>>> On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >>>>>>> 在 2023/9/18 上午9:45, Huacai Chen 写道: > >>>>>>>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: > >>>>>>>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: > >>>>>>>>>> Hi, Tianrui, > >>>>>>>>>> > >>>>>>>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: > >>>>>>>>>>> Implement LoongArch kvm module init, module exit interface, > >>>>>>>>>>> using kvm context to save the vpid info and vcpu world switch > >>>>>>>>>>> interface pointer. > >>>>>>>>>>> > >>>>>>>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> > >>>>>>>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> > >>>>>>>>>>> --- > >>>>>>>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ > >>>>>>>>>>> 1 file changed, 367 insertions(+) > >>>>>>>>>>> create mode 100644 arch/loongarch/kvm/main.c > >>>>>>>>>>> > >>>>>>>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c > >>>>>>>>>>> new file mode 100644 > >>>>>>>>>>> index 0000000000..0deb9273d8 > >>>>>>>>>>> --- /dev/null > >>>>>>>>>>> +++ b/arch/loongarch/kvm/main.c > >>>>>>>>>>> @@ -0,0 +1,367 @@ > >>>>>>>>>>> +// SPDX-License-Identifier: GPL-2.0 > >>>>>>>>>>> +/* > >>>>>>>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited > >>>>>>>>>>> + */ > >>>>>>>>>>> + > >>>>>>>>>>> +#include <linux/err.h> > >>>>>>>>>>> +#include <linux/module.h> > >>>>>>>>>>> +#include <linux/kvm_host.h> > >>>>>>>>>>> +#include <asm/cacheflush.h> > >>>>>>>>>>> +#include <asm/cpufeature.h> > >>>>>>>>>>> +#include <asm/kvm_csr.h> > >>>>>>>>>>> +#include "trace.h" > >>>>>>>>>>> + > >>>>>>>>>>> +static struct kvm_context __percpu *vmcs; > >>>>>>>>>>> +struct kvm_world_switch *kvm_loongarch_ops; > >>>>>>>>>>> +unsigned long vpid_mask; > >>>>>>>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; > >>>>>>>>>>> + > >>>>>>>>>>> +int get_gcsr_flag(int csr) > >>>>>>>>>>> +{ > >>>>>>>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>>>>>>> + return gcsr_flag[csr]; > >>>>>>>>>>> + > >>>>>>>>>>> + return INVALID_GCSR; > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +static inline void set_gcsr_sw_flag(int csr) > >>>>>>>>>>> +{ > >>>>>>>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>>>>>>> + gcsr_flag[csr] |= SW_GCSR; > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +static inline void set_gcsr_hw_flag(int csr) > >>>>>>>>>>> +{ > >>>>>>>>>>> + if (csr < CSR_MAX_NUMS) > >>>>>>>>>>> + gcsr_flag[csr] |= HW_GCSR; > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +/* > >>>>>>>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this > >>>>>>>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the > >>>>>>>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, > >>>>>>>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, > >>>>>>>>>>> + * else use sw csr to emulate it. > >>>>>>>>>>> + */ > >>>>>>>>>>> +static void kvm_init_gcsr_flag(void) > >>>>>>>>>>> +{ > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); > >>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); > >>>>>>>>>>> + > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); > >>>>>>>>>>> + > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > >>>>>>>>>> FWPS and MWPS are both HW CSR and SW CSR? > >>>>>>>>>> > >>>>>>>>>> Huacai > >>>>>>>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it > >>>>>>>>> is my mistake. > >>>>>>>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW > >>>>>>>> GCSR, while DBxxxx and IBxxxx are SW GCSR. > >>>>>>> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest > >>>>>>> debug register numbers, but I know they are all HW GCSR when I look up > >>>>>>> the manual again. > >>>>>> So these lines can be removed? > >>>>>> > >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); > >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); > >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); > >>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); > >>>>>> > >>>>>> And add FWPC/MWPC to hw list? > >>>> Can you discuss more with our hw engineers? I found in section 1.8: > >>>> PGD, TINTCLR, PRCFG1~3 are all HW GCSR. > >>> If guest can access hw CSR directly and do not cause a exception, it means hw GCSR. on the other hand, if cause a exception to return to KVM to emulate it by software, it means SW GCSR. And I re-check the PGD, TINTCLR, PRCFG1~3 CSR, they should be hw GCSR. > >> CSR_PGD is logical read only register instead, its content is the same > >> CSR_PGDL if highest bit of BADV is 0, else its content is the same with > >> CSR_PGDH. > >> > >> TINTCLR is write only register, write 1 to clear timer interrupt and read > >> value is 0. > >> > >> So CSR_PGD and TINTCLR need not save and restore during vcpu switching or > >> vm migration, it is not necessary to set CSR_PGD/TINTCLR from qemu user space. > >> > >> PRCFG1~3 is read only registers, it is decided by hardware and can not be > >> set by kvm, it is not necessary to save or retore. However there maybe brings > >> problems for vm migration on diferent hardwares if PRCFG1~3 is different. > > During my tests, reading FWPC/FWPS/MWPC/MWPS causes guest exit, but > > user manual vol 3 says they are HW GCSR, who can tell me why? > It will cause guest exiting when guest visit these registers above for > the first time, and KVM could config FWPC, MWPC to enable the debug > registers for guest, so that it will not cause exiting at next time. And > we will add this handling later when we want to use this function in guest. Then what type should we mark them? HW GCSR or SW GCSR? Huacai > > Thanks > Tianrui Zhao > > > > Huacai > > > >> Regards > >> Bibo Mao > >>> Thanks > >>> Tianrui Zhao > >>>> Huacai > >>>> > >>>>>> Huacai > >>>>> Yes, It is. > >>>>> > >>>>> Thanks > >>>>> Tianrui Zhao > >>>>>>> Thanks > >>>>>>> Tianrui Zhao > >>>>>>>> Huacai > >>>>>>>> > >>>>>>>>> Thanks > >>>>>>>>> Tianrui Zhao > >>>>>>>>>>> + > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); > >>>>>>>>>>> + > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); > >>>>>>>>>>> + > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); > >>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +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_vpid(struct kvm_vcpu *vcpu) > >>>>>>>>>>> +{ > >>>>>>>>>>> + struct kvm_context *context; > >>>>>>>>>>> + bool migrated; > >>>>>>>>>>> + unsigned long ver, old, vpid; > >>>>>>>>>>> + int cpu; > >>>>>>>>>>> + > >>>>>>>>>>> + cpu = smp_processor_id(); > >>>>>>>>>>> + /* > >>>>>>>>>>> + * 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->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); > >>>>>>>>>>> + vcpu->cpu = cpu; > >>>>>>>>>>> + } > >>>>>>>>>>> + > >>>>>>>>>>> + /* Restore GSTAT(0x50).vpid */ > >>>>>>>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; > >>>>>>>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +static int kvm_loongarch_env_init(void) > >>>>>>>>>>> +{ > >>>>>>>>>>> + struct kvm_context *context; > >>>>>>>>>>> + int cpu, order; > >>>>>>>>>>> + void *addr; > >>>>>>>>>>> + > >>>>>>>>>>> + vmcs = alloc_percpu(struct kvm_context); > >>>>>>>>>>> + if (!vmcs) { > >>>>>>>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); > >>>>>>>>>>> + return -ENOMEM; > >>>>>>>>>>> + } > >>>>>>>>>>> + > >>>>>>>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); > >>>>>>>>>>> + if (!kvm_loongarch_ops) { > >>>>>>>>>>> + free_percpu(vmcs); > >>>>>>>>>>> + vmcs = NULL; > >>>>>>>>>>> + return -ENOMEM; > >>>>>>>>>>> + } > >>>>>>>>>>> + /* > >>>>>>>>>>> + * There will be problem in world switch code if there > >>>>>>>>>>> + * is page fault reenter, since pgd register is shared > >>>>>>>>>>> + * between root kernel and kvm hypervisor. World switch > >>>>>>>>>>> + * entry need be unmapped area, cannot be tlb mapped area. > >>>>>>>>>>> + * In future if hw pagetable walking is supported, or there > >>>>>>>>>>> + * is separate pgd registers between root kernel and kvm > >>>>>>>>>>> + * hypervisor, copying about world switch code will not be used. > >>>>>>>>>>> + */ > >>>>>>>>>>> + > >>>>>>>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); > >>>>>>>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); > >>>>>>>>>>> + if (!addr) { > >>>>>>>>>>> + free_percpu(vmcs); > >>>>>>>>>>> + vmcs = NULL; > >>>>>>>>>>> + kfree(kvm_loongarch_ops); > >>>>>>>>>>> + kvm_loongarch_ops = NULL; > >>>>>>>>>>> + return -ENOMEM; > >>>>>>>>>>> + } > >>>>>>>>>>> + > >>>>>>>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); > >>>>>>>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); > >>>>>>>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + > >>>>>>>>>>> + kvm_vector_size + kvm_enter_guest_size); > >>>>>>>>>>> + kvm_loongarch_ops->guest_eentry = addr; > >>>>>>>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; > >>>>>>>>>>> + kvm_loongarch_ops->page_order = order; > >>>>>>>>>>> + > >>>>>>>>>>> + vpid_mask = read_csr_gstat(); > >>>>>>>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; > >>>>>>>>>>> + if (vpid_mask) > >>>>>>>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); > >>>>>>>>>>> + > >>>>>>>>>>> + for_each_possible_cpu(cpu) { > >>>>>>>>>>> + context = per_cpu_ptr(vmcs, cpu); > >>>>>>>>>>> + context->vpid_cache = vpid_mask + 1; > >>>>>>>>>>> + context->last_vcpu = NULL; > >>>>>>>>>>> + } > >>>>>>>>>>> + > >>>>>>>>>>> + kvm_init_fault(); > >>>>>>>>>>> + kvm_init_gcsr_flag(); > >>>>>>>>>>> + > >>>>>>>>>>> + return 0; > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +static void kvm_loongarch_env_exit(void) > >>>>>>>>>>> +{ > >>>>>>>>>>> + unsigned long addr; > >>>>>>>>>>> + > >>>>>>>>>>> + if (vmcs) > >>>>>>>>>>> + free_percpu(vmcs); > >>>>>>>>>>> + > >>>>>>>>>>> + if (kvm_loongarch_ops) { > >>>>>>>>>>> + if (kvm_loongarch_ops->guest_eentry) { > >>>>>>>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; > >>>>>>>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); > >>>>>>>>>>> + } > >>>>>>>>>>> + kfree(kvm_loongarch_ops); > >>>>>>>>>>> + } > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +static int kvm_loongarch_init(void) > >>>>>>>>>>> +{ > >>>>>>>>>>> + int r; > >>>>>>>>>>> + > >>>>>>>>>>> + if (!cpu_has_lvz) { > >>>>>>>>>>> + kvm_info("hardware virtualization not available\n"); > >>>>>>>>>>> + return -ENODEV; > >>>>>>>>>>> + } > >>>>>>>>>>> + r = kvm_loongarch_env_init(); > >>>>>>>>>>> + if (r) > >>>>>>>>>>> + return r; > >>>>>>>>>>> + > >>>>>>>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +static void kvm_loongarch_exit(void) > >>>>>>>>>>> +{ > >>>>>>>>>>> + kvm_exit(); > >>>>>>>>>>> + kvm_loongarch_env_exit(); > >>>>>>>>>>> +} > >>>>>>>>>>> + > >>>>>>>>>>> +module_init(kvm_loongarch_init); > >>>>>>>>>>> +module_exit(kvm_loongarch_exit); > >>>>>>>>>>> + > >>>>>>>>>>> +#ifdef MODULE > >>>>>>>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { > >>>>>>>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, > >>>>>>>>>>> + {}, > >>>>>>>>>>> +}; > >>>>>>>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); > >>>>>>>>>>> +#endif > >>>>>>>>>>> -- > >>>>>>>>>>> 2.39.1 > >>>>>>>>>>> > >> > >
在 2023/9/19 上午10:57, Huacai Chen 写道: > On Tue, Sep 19, 2023 at 10:53 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >> >> 在 2023/9/19 上午10:14, Huacai Chen 写道: >>> On Mon, Sep 18, 2023 at 4:39 PM bibo mao <maobibo@loongson.cn> wrote: >>>> >>>> 在 2023/9/18 14:25, zhaotianrui 写道: >>>>> 在 2023/9/18 下午12:03, Huacai Chen 写道: >>>>>> On Mon, Sep 18, 2023 at 11:20 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>>>> 在 2023/9/18 上午11:12, Huacai Chen 写道: >>>>>>>> On Mon, Sep 18, 2023 at 11:08 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>>>>>> 在 2023/9/18 上午9:45, Huacai Chen 写道: >>>>>>>>>> On Mon, Sep 18, 2023 at 9:21 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >>>>>>>>>>> 在 2023/9/16 下午4:51, Huacai Chen 写道: >>>>>>>>>>>> Hi, Tianrui, >>>>>>>>>>>> >>>>>>>>>>>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >>>>>>>>>>>>> Implement LoongArch kvm module init, module exit interface, >>>>>>>>>>>>> using kvm context to save the vpid info and vcpu world switch >>>>>>>>>>>>> interface pointer. >>>>>>>>>>>>> >>>>>>>>>>>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >>>>>>>>>>>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >>>>>>>>>>>>> --- >>>>>>>>>>>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >>>>>>>>>>>>> 1 file changed, 367 insertions(+) >>>>>>>>>>>>> create mode 100644 arch/loongarch/kvm/main.c >>>>>>>>>>>>> >>>>>>>>>>>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >>>>>>>>>>>>> new file mode 100644 >>>>>>>>>>>>> index 0000000000..0deb9273d8 >>>>>>>>>>>>> --- /dev/null >>>>>>>>>>>>> +++ b/arch/loongarch/kvm/main.c >>>>>>>>>>>>> @@ -0,0 +1,367 @@ >>>>>>>>>>>>> +// SPDX-License-Identifier: GPL-2.0 >>>>>>>>>>>>> +/* >>>>>>>>>>>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >>>>>>>>>>>>> + */ >>>>>>>>>>>>> + >>>>>>>>>>>>> +#include <linux/err.h> >>>>>>>>>>>>> +#include <linux/module.h> >>>>>>>>>>>>> +#include <linux/kvm_host.h> >>>>>>>>>>>>> +#include <asm/cacheflush.h> >>>>>>>>>>>>> +#include <asm/cpufeature.h> >>>>>>>>>>>>> +#include <asm/kvm_csr.h> >>>>>>>>>>>>> +#include "trace.h" >>>>>>>>>>>>> + >>>>>>>>>>>>> +static struct kvm_context __percpu *vmcs; >>>>>>>>>>>>> +struct kvm_world_switch *kvm_loongarch_ops; >>>>>>>>>>>>> +unsigned long vpid_mask; >>>>>>>>>>>>> +static int gcsr_flag[CSR_MAX_NUMS]; >>>>>>>>>>>>> + >>>>>>>>>>>>> +int get_gcsr_flag(int csr) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>>>>>> + return gcsr_flag[csr]; >>>>>>>>>>>>> + >>>>>>>>>>>>> + return INVALID_GCSR; >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +static inline void set_gcsr_sw_flag(int csr) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>>>>>> + gcsr_flag[csr] |= SW_GCSR; >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +static inline void set_gcsr_hw_flag(int csr) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + if (csr < CSR_MAX_NUMS) >>>>>>>>>>>>> + gcsr_flag[csr] |= HW_GCSR; >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +/* >>>>>>>>>>>>> + * The default value of gcsr_flag[CSR] is 0, and we use this >>>>>>>>>>>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >>>>>>>>>>>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, >>>>>>>>>>>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >>>>>>>>>>>>> + * else use sw csr to emulate it. >>>>>>>>>>>>> + */ >>>>>>>>>>>>> +static void kvm_init_gcsr_flag(void) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >>>>>>>>>>>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >>>>>>>>>>>>> + >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >>>>>>>>>>>>> + >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>>>>>>>>>> FWPS and MWPS are both HW CSR and SW CSR? >>>>>>>>>>>> >>>>>>>>>>>> Huacai >>>>>>>>>>> The FWPC and MWPC should be SW GCSR, FWPS and MWPS should be HW GCSR, it >>>>>>>>>>> is my mistake. >>>>>>>>>> But in user manual vol 3, section 1.5, FWPC/FWPS/MWPC/MWPS are all HW >>>>>>>>>> GCSR, while DBxxxx and IBxxxx are SW GCSR. >>>>>>>>> Ok, It is my misunderstanding, as the FWPC and MWPC can control guest >>>>>>>>> debug register numbers, but I know they are all HW GCSR when I look up >>>>>>>>> the manual again. >>>>>>>> So these lines can be removed? >>>>>>>> >>>>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>>>>>> set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>>>>>> >>>>>>>> And add FWPC/MWPC to hw list? >>>>>> Can you discuss more with our hw engineers? I found in section 1.8: >>>>>> PGD, TINTCLR, PRCFG1~3 are all HW GCSR. >>>>> If guest can access hw CSR directly and do not cause a exception, it means hw GCSR. on the other hand, if cause a exception to return to KVM to emulate it by software, it means SW GCSR. And I re-check the PGD, TINTCLR, PRCFG1~3 CSR, they should be hw GCSR. >>>> CSR_PGD is logical read only register instead, its content is the same >>>> CSR_PGDL if highest bit of BADV is 0, else its content is the same with >>>> CSR_PGDH. >>>> >>>> TINTCLR is write only register, write 1 to clear timer interrupt and read >>>> value is 0. >>>> >>>> So CSR_PGD and TINTCLR need not save and restore during vcpu switching or >>>> vm migration, it is not necessary to set CSR_PGD/TINTCLR from qemu user space. >>>> >>>> PRCFG1~3 is read only registers, it is decided by hardware and can not be >>>> set by kvm, it is not necessary to save or retore. However there maybe brings >>>> problems for vm migration on diferent hardwares if PRCFG1~3 is different. >>> During my tests, reading FWPC/FWPS/MWPC/MWPS causes guest exit, but >>> user manual vol 3 says they are HW GCSR, who can tell me why? >> It will cause guest exiting when guest visit these registers above for >> the first time, and KVM could config FWPC, MWPC to enable the debug >> registers for guest, so that it will not cause exiting at next time. And >> we will add this handling later when we want to use this function in guest. > Then what type should we mark them? HW GCSR or SW GCSR? > > Huacai It should be marked by SW GCSR at the initial time in this function, and when guest visit it, we should change the flag to HW GCSR. Thanks Tianrui Zhao > >> Thanks >> Tianrui Zhao >>> Huacai >>> >>>> Regards >>>> Bibo Mao >>>>> Thanks >>>>> Tianrui Zhao >>>>>> Huacai >>>>>> >>>>>>>> Huacai >>>>>>> Yes, It is. >>>>>>> >>>>>>> Thanks >>>>>>> Tianrui Zhao >>>>>>>>> Thanks >>>>>>>>> Tianrui Zhao >>>>>>>>>> Huacai >>>>>>>>>> >>>>>>>>>>> Thanks >>>>>>>>>>> Tianrui Zhao >>>>>>>>>>>>> + >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >>>>>>>>>>>>> + >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >>>>>>>>>>>>> + >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >>>>>>>>>>>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +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_vpid(struct kvm_vcpu *vcpu) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + struct kvm_context *context; >>>>>>>>>>>>> + bool migrated; >>>>>>>>>>>>> + unsigned long ver, old, vpid; >>>>>>>>>>>>> + int cpu; >>>>>>>>>>>>> + >>>>>>>>>>>>> + cpu = smp_processor_id(); >>>>>>>>>>>>> + /* >>>>>>>>>>>>> + * 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->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); >>>>>>>>>>>>> + vcpu->cpu = cpu; >>>>>>>>>>>>> + } >>>>>>>>>>>>> + >>>>>>>>>>>>> + /* Restore GSTAT(0x50).vpid */ >>>>>>>>>>>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >>>>>>>>>>>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +static int kvm_loongarch_env_init(void) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + struct kvm_context *context; >>>>>>>>>>>>> + int cpu, order; >>>>>>>>>>>>> + void *addr; >>>>>>>>>>>>> + >>>>>>>>>>>>> + vmcs = alloc_percpu(struct kvm_context); >>>>>>>>>>>>> + if (!vmcs) { >>>>>>>>>>>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >>>>>>>>>>>>> + return -ENOMEM; >>>>>>>>>>>>> + } >>>>>>>>>>>>> + >>>>>>>>>>>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >>>>>>>>>>>>> + if (!kvm_loongarch_ops) { >>>>>>>>>>>>> + free_percpu(vmcs); >>>>>>>>>>>>> + vmcs = NULL; >>>>>>>>>>>>> + return -ENOMEM; >>>>>>>>>>>>> + } >>>>>>>>>>>>> + /* >>>>>>>>>>>>> + * There will be problem in world switch code if there >>>>>>>>>>>>> + * is page fault reenter, since pgd register is shared >>>>>>>>>>>>> + * between root kernel and kvm hypervisor. World switch >>>>>>>>>>>>> + * entry need be unmapped area, cannot be tlb mapped area. >>>>>>>>>>>>> + * In future if hw pagetable walking is supported, or there >>>>>>>>>>>>> + * is separate pgd registers between root kernel and kvm >>>>>>>>>>>>> + * hypervisor, copying about world switch code will not be used. >>>>>>>>>>>>> + */ >>>>>>>>>>>>> + >>>>>>>>>>>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >>>>>>>>>>>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >>>>>>>>>>>>> + if (!addr) { >>>>>>>>>>>>> + free_percpu(vmcs); >>>>>>>>>>>>> + vmcs = NULL; >>>>>>>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>>>>>>> + kvm_loongarch_ops = NULL; >>>>>>>>>>>>> + return -ENOMEM; >>>>>>>>>>>>> + } >>>>>>>>>>>>> + >>>>>>>>>>>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >>>>>>>>>>>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >>>>>>>>>>>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >>>>>>>>>>>>> + kvm_vector_size + kvm_enter_guest_size); >>>>>>>>>>>>> + kvm_loongarch_ops->guest_eentry = addr; >>>>>>>>>>>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >>>>>>>>>>>>> + kvm_loongarch_ops->page_order = order; >>>>>>>>>>>>> + >>>>>>>>>>>>> + vpid_mask = read_csr_gstat(); >>>>>>>>>>>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >>>>>>>>>>>>> + if (vpid_mask) >>>>>>>>>>>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); >>>>>>>>>>>>> + >>>>>>>>>>>>> + for_each_possible_cpu(cpu) { >>>>>>>>>>>>> + context = per_cpu_ptr(vmcs, cpu); >>>>>>>>>>>>> + context->vpid_cache = vpid_mask + 1; >>>>>>>>>>>>> + context->last_vcpu = NULL; >>>>>>>>>>>>> + } >>>>>>>>>>>>> + >>>>>>>>>>>>> + kvm_init_fault(); >>>>>>>>>>>>> + kvm_init_gcsr_flag(); >>>>>>>>>>>>> + >>>>>>>>>>>>> + return 0; >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +static void kvm_loongarch_env_exit(void) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + unsigned long addr; >>>>>>>>>>>>> + >>>>>>>>>>>>> + if (vmcs) >>>>>>>>>>>>> + free_percpu(vmcs); >>>>>>>>>>>>> + >>>>>>>>>>>>> + if (kvm_loongarch_ops) { >>>>>>>>>>>>> + if (kvm_loongarch_ops->guest_eentry) { >>>>>>>>>>>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >>>>>>>>>>>>> + free_pages(addr, kvm_loongarch_ops->page_order); >>>>>>>>>>>>> + } >>>>>>>>>>>>> + kfree(kvm_loongarch_ops); >>>>>>>>>>>>> + } >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +static int kvm_loongarch_init(void) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + int r; >>>>>>>>>>>>> + >>>>>>>>>>>>> + if (!cpu_has_lvz) { >>>>>>>>>>>>> + kvm_info("hardware virtualization not available\n"); >>>>>>>>>>>>> + return -ENODEV; >>>>>>>>>>>>> + } >>>>>>>>>>>>> + r = kvm_loongarch_env_init(); >>>>>>>>>>>>> + if (r) >>>>>>>>>>>>> + return r; >>>>>>>>>>>>> + >>>>>>>>>>>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +static void kvm_loongarch_exit(void) >>>>>>>>>>>>> +{ >>>>>>>>>>>>> + kvm_exit(); >>>>>>>>>>>>> + kvm_loongarch_env_exit(); >>>>>>>>>>>>> +} >>>>>>>>>>>>> + >>>>>>>>>>>>> +module_init(kvm_loongarch_init); >>>>>>>>>>>>> +module_exit(kvm_loongarch_exit); >>>>>>>>>>>>> + >>>>>>>>>>>>> +#ifdef MODULE >>>>>>>>>>>>> +static const struct cpu_feature loongarch_kvm_feature[] = { >>>>>>>>>>>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >>>>>>>>>>>>> + {}, >>>>>>>>>>>>> +}; >>>>>>>>>>>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >>>>>>>>>>>>> +#endif >>>>>>>>>>>>> -- >>>>>>>>>>>>> 2.39.1 >>>>>>>>>>>>> >>
在 2023/9/19 上午10:41, Huacai Chen 写道: > On Tue, Sep 19, 2023 at 10:38 AM zhaotianrui <zhaotianrui@loongson.cn> wrote: >> >> 在 2023/9/17 下午12:21, Huacai Chen 写道: >>> Hi, Tianrui, >>> >>> On Fri, Sep 15, 2023 at 9:50 AM Tianrui Zhao <zhaotianrui@loongson.cn> wrote: >>>> Implement LoongArch kvm module init, module exit interface, >>>> using kvm context to save the vpid info and vcpu world switch >>>> interface pointer. >>>> >>>> Reviewed-by: Bibo Mao <maobibo@loongson.cn> >>>> Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn> >>>> --- >>>> arch/loongarch/kvm/main.c | 367 ++++++++++++++++++++++++++++++++++++++ >>>> 1 file changed, 367 insertions(+) >>>> create mode 100644 arch/loongarch/kvm/main.c >>>> >>>> diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c >>>> new file mode 100644 >>>> index 0000000000..0deb9273d8 >>>> --- /dev/null >>>> +++ b/arch/loongarch/kvm/main.c >>>> @@ -0,0 +1,367 @@ >>>> +// SPDX-License-Identifier: GPL-2.0 >>>> +/* >>>> + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited >>>> + */ >>>> + >>>> +#include <linux/err.h> >>>> +#include <linux/module.h> >>>> +#include <linux/kvm_host.h> >>>> +#include <asm/cacheflush.h> >>>> +#include <asm/cpufeature.h> >>>> +#include <asm/kvm_csr.h> >>>> +#include "trace.h" >>>> + >>>> +static struct kvm_context __percpu *vmcs; >>>> +struct kvm_world_switch *kvm_loongarch_ops; >>>> +unsigned long vpid_mask; >>>> +static int gcsr_flag[CSR_MAX_NUMS]; >>>> + >>>> +int get_gcsr_flag(int csr) >>>> +{ >>>> + if (csr < CSR_MAX_NUMS) >>>> + return gcsr_flag[csr]; >>>> + >>>> + return INVALID_GCSR; >>>> +} >>>> + >>>> +static inline void set_gcsr_sw_flag(int csr) >>>> +{ >>>> + if (csr < CSR_MAX_NUMS) >>>> + gcsr_flag[csr] |= SW_GCSR; >>>> +} >>>> + >>>> +static inline void set_gcsr_hw_flag(int csr) >>>> +{ >>>> + if (csr < CSR_MAX_NUMS) >>>> + gcsr_flag[csr] |= HW_GCSR; >>>> +} >>>> + >>>> +/* >>>> + * The default value of gcsr_flag[CSR] is 0, and we use this >>>> + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the >>>> + * gcsr is software or hardware. It will be used by get/set_gcsr, >>>> + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, >>>> + * else use sw csr to emulate it. >>>> + */ >>>> +static void kvm_init_gcsr_flag(void) >>>> +{ >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); >>>> + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); >>>> + >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); >>>> + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); >>>> +} >>>> + >>>> +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_vpid(struct kvm_vcpu *vcpu) >>>> +{ >>>> + struct kvm_context *context; >>>> + bool migrated; >>>> + unsigned long ver, old, vpid; >>>> + int cpu; >>>> + >>>> + cpu = smp_processor_id(); >>>> + /* >>>> + * 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->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); >>>> + vcpu->cpu = cpu; >>>> + } >>>> + >>>> + /* Restore GSTAT(0x50).vpid */ >>>> + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; >>>> + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); >>>> +} >>>> + >>>> +static int kvm_loongarch_env_init(void) >>>> +{ >>>> + struct kvm_context *context; >>>> + int cpu, order; >>>> + void *addr; >>>> + >>>> + vmcs = alloc_percpu(struct kvm_context); >>>> + if (!vmcs) { >>>> + pr_err("kvm: failed to allocate percpu kvm_context\n"); >>>> + return -ENOMEM; >>>> + } >>>> + >>>> + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); >>>> + if (!kvm_loongarch_ops) { >>>> + free_percpu(vmcs); >>>> + vmcs = NULL; >>>> + return -ENOMEM; >>>> + } >>>> + /* >>>> + * There will be problem in world switch code if there >>>> + * is page fault reenter, since pgd register is shared >>>> + * between root kernel and kvm hypervisor. World switch >>>> + * entry need be unmapped area, cannot be tlb mapped area. >>>> + * In future if hw pagetable walking is supported, or there >>>> + * is separate pgd registers between root kernel and kvm >>>> + * hypervisor, copying about world switch code will not be used. >>>> + */ >>>> + >>>> + order = get_order(kvm_vector_size + kvm_enter_guest_size); >>>> + addr = (void *)__get_free_pages(GFP_KERNEL, order); >>>> + if (!addr) { >>>> + free_percpu(vmcs); >>>> + vmcs = NULL; >>>> + kfree(kvm_loongarch_ops); >>>> + kvm_loongarch_ops = NULL; >>>> + return -ENOMEM; >>>> + } >>>> + >>>> + memcpy(addr, kvm_vector_entry, kvm_vector_size); >>>> + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); >>> Why memcpy? In our internal repo, we use kvm_vector_entry and >>> kvm_enter_guest directly. The long comments above make me nervous >>> because Loongson-3A6000 already supports hardware pagetable walker. >>> >>> Huacai >> As mentioned in the comments, it need not this memcpy if hardware page >> walk is supported in 3A6000. > But why in our internal repo we don't use memcpy() but kvm can still > work on Loongson-3A5000? > > Huacai Because in our internal repo, the KVM is build along with the linux kernel, however, this KVM patch series is build in module. Thanks Tianrui Zhao > >> Thanks >> Tianrui Zhao >>>> + flush_icache_range((unsigned long)addr, (unsigned long)addr + >>>> + kvm_vector_size + kvm_enter_guest_size); >>>> + kvm_loongarch_ops->guest_eentry = addr; >>>> + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; >>>> + kvm_loongarch_ops->page_order = order; >>>> + >>>> + vpid_mask = read_csr_gstat(); >>>> + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; >>>> + if (vpid_mask) >>>> + vpid_mask = GENMASK(vpid_mask - 1, 0); >>>> + >>>> + for_each_possible_cpu(cpu) { >>>> + context = per_cpu_ptr(vmcs, cpu); >>>> + context->vpid_cache = vpid_mask + 1; >>>> + context->last_vcpu = NULL; >>>> + } >>>> + >>>> + kvm_init_fault(); >>>> + kvm_init_gcsr_flag(); >>>> + >>>> + return 0; >>>> +} >>>> + >>>> +static void kvm_loongarch_env_exit(void) >>>> +{ >>>> + unsigned long addr; >>>> + >>>> + if (vmcs) >>>> + free_percpu(vmcs); >>>> + >>>> + if (kvm_loongarch_ops) { >>>> + if (kvm_loongarch_ops->guest_eentry) { >>>> + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; >>>> + free_pages(addr, kvm_loongarch_ops->page_order); >>>> + } >>>> + kfree(kvm_loongarch_ops); >>>> + } >>>> +} >>>> + >>>> +static int kvm_loongarch_init(void) >>>> +{ >>>> + int r; >>>> + >>>> + if (!cpu_has_lvz) { >>>> + kvm_info("hardware virtualization not available\n"); >>>> + return -ENODEV; >>>> + } >>>> + r = kvm_loongarch_env_init(); >>>> + if (r) >>>> + return r; >>>> + >>>> + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); >>>> +} >>>> + >>>> +static void kvm_loongarch_exit(void) >>>> +{ >>>> + kvm_exit(); >>>> + kvm_loongarch_env_exit(); >>>> +} >>>> + >>>> +module_init(kvm_loongarch_init); >>>> +module_exit(kvm_loongarch_exit); >>>> + >>>> +#ifdef MODULE >>>> +static const struct cpu_feature loongarch_kvm_feature[] = { >>>> + { .feature = cpu_feature(LOONGARCH_LVZ) }, >>>> + {}, >>>> +}; >>>> +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); >>>> +#endif >>>> -- >>>> 2.39.1 >>>> >>
diff --git a/arch/loongarch/kvm/main.c b/arch/loongarch/kvm/main.c new file mode 100644 index 0000000000..0deb9273d8 --- /dev/null +++ b/arch/loongarch/kvm/main.c @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020-2023 Loongson Technology Corporation Limited + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/kvm_host.h> +#include <asm/cacheflush.h> +#include <asm/cpufeature.h> +#include <asm/kvm_csr.h> +#include "trace.h" + +static struct kvm_context __percpu *vmcs; +struct kvm_world_switch *kvm_loongarch_ops; +unsigned long vpid_mask; +static int gcsr_flag[CSR_MAX_NUMS]; + +int get_gcsr_flag(int csr) +{ + if (csr < CSR_MAX_NUMS) + return gcsr_flag[csr]; + + return INVALID_GCSR; +} + +static inline void set_gcsr_sw_flag(int csr) +{ + if (csr < CSR_MAX_NUMS) + gcsr_flag[csr] |= SW_GCSR; +} + +static inline void set_gcsr_hw_flag(int csr) +{ + if (csr < CSR_MAX_NUMS) + gcsr_flag[csr] |= HW_GCSR; +} + +/* + * The default value of gcsr_flag[CSR] is 0, and we use this + * function to set the flag to 1(SW_GCSR) or 2(HW_GCSR) if the + * gcsr is software or hardware. It will be used by get/set_gcsr, + * if gcsr_flag is HW we should use gcsrrd/gcsrwr to access it, + * else use sw csr to emulate it. + */ +static void kvm_init_gcsr_flag(void) +{ + set_gcsr_hw_flag(LOONGARCH_CSR_CRMD); + set_gcsr_hw_flag(LOONGARCH_CSR_PRMD); + set_gcsr_hw_flag(LOONGARCH_CSR_EUEN); + set_gcsr_hw_flag(LOONGARCH_CSR_MISC); + set_gcsr_hw_flag(LOONGARCH_CSR_ECFG); + set_gcsr_hw_flag(LOONGARCH_CSR_ESTAT); + set_gcsr_hw_flag(LOONGARCH_CSR_ERA); + set_gcsr_hw_flag(LOONGARCH_CSR_BADV); + set_gcsr_hw_flag(LOONGARCH_CSR_BADI); + set_gcsr_hw_flag(LOONGARCH_CSR_EENTRY); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBIDX); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBEHI); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO0); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBELO1); + set_gcsr_hw_flag(LOONGARCH_CSR_ASID); + set_gcsr_hw_flag(LOONGARCH_CSR_PGDL); + set_gcsr_hw_flag(LOONGARCH_CSR_PGDH); + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL0); + set_gcsr_hw_flag(LOONGARCH_CSR_PWCTL1); + set_gcsr_hw_flag(LOONGARCH_CSR_STLBPGSIZE); + set_gcsr_hw_flag(LOONGARCH_CSR_RVACFG); + set_gcsr_hw_flag(LOONGARCH_CSR_CPUID); + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG1); + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG2); + set_gcsr_hw_flag(LOONGARCH_CSR_PRCFG3); + set_gcsr_hw_flag(LOONGARCH_CSR_KS0); + set_gcsr_hw_flag(LOONGARCH_CSR_KS1); + set_gcsr_hw_flag(LOONGARCH_CSR_KS2); + set_gcsr_hw_flag(LOONGARCH_CSR_KS3); + set_gcsr_hw_flag(LOONGARCH_CSR_KS4); + set_gcsr_hw_flag(LOONGARCH_CSR_KS5); + set_gcsr_hw_flag(LOONGARCH_CSR_KS6); + set_gcsr_hw_flag(LOONGARCH_CSR_KS7); + set_gcsr_hw_flag(LOONGARCH_CSR_TMID); + set_gcsr_hw_flag(LOONGARCH_CSR_TCFG); + set_gcsr_hw_flag(LOONGARCH_CSR_TVAL); + set_gcsr_hw_flag(LOONGARCH_CSR_CNTC); + set_gcsr_hw_flag(LOONGARCH_CSR_LLBCTL); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRENTRY); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRBADV); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRERA); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRSAVE); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO0); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRELO1); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBREHI); + set_gcsr_hw_flag(LOONGARCH_CSR_TLBRPRMD); + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN0); + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN1); + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN2); + set_gcsr_hw_flag(LOONGARCH_CSR_DMWIN3); + set_gcsr_hw_flag(LOONGARCH_CSR_MWPS); + set_gcsr_hw_flag(LOONGARCH_CSR_FWPS); + + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL1); + set_gcsr_sw_flag(LOONGARCH_CSR_IMPCTL2); + set_gcsr_sw_flag(LOONGARCH_CSR_MERRCTL); + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO1); + set_gcsr_sw_flag(LOONGARCH_CSR_MERRINFO2); + set_gcsr_sw_flag(LOONGARCH_CSR_MERRENTRY); + set_gcsr_sw_flag(LOONGARCH_CSR_MERRERA); + set_gcsr_sw_flag(LOONGARCH_CSR_MERRSAVE); + set_gcsr_sw_flag(LOONGARCH_CSR_CTAG); + set_gcsr_sw_flag(LOONGARCH_CSR_DEBUG); + set_gcsr_sw_flag(LOONGARCH_CSR_DERA); + set_gcsr_sw_flag(LOONGARCH_CSR_DESAVE); + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG1); + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG2); + set_gcsr_sw_flag(LOONGARCH_CSR_PRCFG3); + set_gcsr_sw_flag(LOONGARCH_CSR_PGD); + set_gcsr_sw_flag(LOONGARCH_CSR_TINTCLR); + + set_gcsr_sw_flag(LOONGARCH_CSR_FWPS); + set_gcsr_sw_flag(LOONGARCH_CSR_FWPC); + set_gcsr_sw_flag(LOONGARCH_CSR_MWPS); + set_gcsr_sw_flag(LOONGARCH_CSR_MWPC); + + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB0MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB0CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB0ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB1MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB1CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB1ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB2MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB2CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB2ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB3MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB3CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB3ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB4MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB4CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB4ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB5MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB5CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB5ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB6MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB6CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB6ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_DB7MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_DB7CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_DB7ASID); + + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB0MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB0CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB0ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB1MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB1CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB1ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB2MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB2CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB2ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB3MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB3CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB3ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB4MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB4CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB4ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB5MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB5CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB5ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB6MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB6CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB6ASID); + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ADDR); + set_gcsr_sw_flag(LOONGARCH_CSR_IB7MASK); + set_gcsr_sw_flag(LOONGARCH_CSR_IB7CTRL); + set_gcsr_sw_flag(LOONGARCH_CSR_IB7ASID); + + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL0); + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR0); + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL1); + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR1); + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL2); + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR2); + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCTRL3); + set_gcsr_sw_flag(LOONGARCH_CSR_PERFCNTR3); +} + +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_vpid(struct kvm_vcpu *vcpu) +{ + struct kvm_context *context; + bool migrated; + unsigned long ver, old, vpid; + int cpu; + + cpu = smp_processor_id(); + /* + * 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->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); + vcpu->cpu = cpu; + } + + /* Restore GSTAT(0x50).vpid */ + vpid = (vcpu->arch.vpid & vpid_mask) << CSR_GSTAT_GID_SHIFT; + change_csr_gstat(vpid_mask << CSR_GSTAT_GID_SHIFT, vpid); +} + +static int kvm_loongarch_env_init(void) +{ + struct kvm_context *context; + int cpu, order; + void *addr; + + vmcs = alloc_percpu(struct kvm_context); + if (!vmcs) { + pr_err("kvm: failed to allocate percpu kvm_context\n"); + return -ENOMEM; + } + + kvm_loongarch_ops = kzalloc(sizeof(*kvm_loongarch_ops), GFP_KERNEL); + if (!kvm_loongarch_ops) { + free_percpu(vmcs); + vmcs = NULL; + return -ENOMEM; + } + /* + * There will be problem in world switch code if there + * is page fault reenter, since pgd register is shared + * between root kernel and kvm hypervisor. World switch + * entry need be unmapped area, cannot be tlb mapped area. + * In future if hw pagetable walking is supported, or there + * is separate pgd registers between root kernel and kvm + * hypervisor, copying about world switch code will not be used. + */ + + order = get_order(kvm_vector_size + kvm_enter_guest_size); + addr = (void *)__get_free_pages(GFP_KERNEL, order); + if (!addr) { + free_percpu(vmcs); + vmcs = NULL; + kfree(kvm_loongarch_ops); + kvm_loongarch_ops = NULL; + return -ENOMEM; + } + + memcpy(addr, kvm_vector_entry, kvm_vector_size); + memcpy(addr + kvm_vector_size, kvm_enter_guest, kvm_enter_guest_size); + flush_icache_range((unsigned long)addr, (unsigned long)addr + + kvm_vector_size + kvm_enter_guest_size); + kvm_loongarch_ops->guest_eentry = addr; + kvm_loongarch_ops->enter_guest = addr + kvm_vector_size; + kvm_loongarch_ops->page_order = order; + + vpid_mask = read_csr_gstat(); + vpid_mask = (vpid_mask & CSR_GSTAT_GIDBIT) >> CSR_GSTAT_GIDBIT_SHIFT; + if (vpid_mask) + vpid_mask = GENMASK(vpid_mask - 1, 0); + + for_each_possible_cpu(cpu) { + context = per_cpu_ptr(vmcs, cpu); + context->vpid_cache = vpid_mask + 1; + context->last_vcpu = NULL; + } + + kvm_init_fault(); + kvm_init_gcsr_flag(); + + return 0; +} + +static void kvm_loongarch_env_exit(void) +{ + unsigned long addr; + + if (vmcs) + free_percpu(vmcs); + + if (kvm_loongarch_ops) { + if (kvm_loongarch_ops->guest_eentry) { + addr = (unsigned long)kvm_loongarch_ops->guest_eentry; + free_pages(addr, kvm_loongarch_ops->page_order); + } + kfree(kvm_loongarch_ops); + } +} + +static int kvm_loongarch_init(void) +{ + int r; + + if (!cpu_has_lvz) { + kvm_info("hardware virtualization not available\n"); + return -ENODEV; + } + r = kvm_loongarch_env_init(); + if (r) + return r; + + return kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE); +} + +static void kvm_loongarch_exit(void) +{ + kvm_exit(); + kvm_loongarch_env_exit(); +} + +module_init(kvm_loongarch_init); +module_exit(kvm_loongarch_exit); + +#ifdef MODULE +static const struct cpu_feature loongarch_kvm_feature[] = { + { .feature = cpu_feature(LOONGARCH_LVZ) }, + {}, +}; +MODULE_DEVICE_TABLE(cpu, loongarch_kvm_feature); +#endif