From patchwork Tue Apr 19 06:55:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Reiji Watanabe X-Patchwork-Id: 12817514 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id D97CBC433F5 for ; Tue, 19 Apr 2022 07:04:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Cc:To:From:Subject:References: Mime-Version:Message-Id:In-Reply-To:Date:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=mh4SPjDfREAbYgKkyg+urQTSuWvtF3wv3ZtQozom9og=; b=4hMCLfylMln1sGPUdX+yR3ReS5 Njh8RLw/bv4RqRIjkURhn9kp9WGqlwSYyKFGW/7b4oZODHbHHU7X/3GnCXNWiGvUb8PrLQrYOe5xJ uKybELisVoys6uQ3iY0wiUgN4XISDt2M1SFIN0r8bm8DyaySg5r7TiBaPEzaMbJ1HrkklSbBdBZ8+ MDMyeEOq/6QFD4H/yfEw0o10UIJLgHrVILOpACOTVpw2FYNgX3dP+j4zxQvbfltZxhK/5HgGJL6Hw QIkr+YllQfFZNRsE5LFvyU3RNGIF/9BOSTATiSg3kloVVUx53OQTXLoM8gJg+qMuJ/MkYQGnBW0WE bMM77sjA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1nghtS-001psF-IE; Tue, 19 Apr 2022 07:03:23 +0000 Received: from mail-pl1-x649.google.com ([2607:f8b0:4864:20::649]) by bombadil.infradead.org with esmtps (Exim 4.94.2 #2 (Red Hat Linux)) id 1nghnn-001nVm-Jy for linux-arm-kernel@lists.infradead.org; Tue, 19 Apr 2022 06:57:33 +0000 Received: by mail-pl1-x649.google.com with SMTP id n2-20020a170903404200b00158db7879ddso5461887pla.13 for ; Mon, 18 Apr 2022 23:57:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=YXalEvL5V6OCiUslrdBdC3x5nYT2WOf4a/IV/CCw06k=; b=PBolxCNPqF/rYwCSGkME7PiAyjXXHuCvjT0iZNEZCb4UsNO/plPhzccwgGr3BMSa8c qYIv3k9YmvEm0jnHH8aOEfDd+Jz3wW8Pal3dbZm9Zr89ptohuxfunoyyo690KChdXylr BAnxcpKTeQqbIxZeQsKehjYF/qG93pGRQEtb3wEri0PgRyej+PlRhFlyhZhU1VMM25FG 2XNAvsn6BDxTp6+Ol1AO6/cMdXZMNHkoiHvX1S+zju41nFgQs7zJ6P+1qxSqqFMeeyPb 8wU07LHBjIKEM+a0DsU8p638g7ca57j5iBENAizVjw6KVT8brSDjAjq4sQnRneTW4F9r LlRg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=YXalEvL5V6OCiUslrdBdC3x5nYT2WOf4a/IV/CCw06k=; b=QZu/b734lzLyRcWCfnOo/dapp3HgaM/mfoyohzkfT+EyRomJkCWj0bTWBeh6bF1dyE B8sMcK43iYk2HA3oJmmW6Ofz9L+SH0sRi+FBuayV17GtTOfd5Ud1V77E8IGBKhiQo/tW vzjDsc0bKek2oWLzui/1pSH1nVlhgvHvhbRj5alHJpB4SQonwmjdVUZxO3AJ8VUj7t8O hAP6fNaMOHRHdqk6t/4TYbeyJsoKEctMezkS2Jhnvf/a0j+mh9/t5iPbYFzMcZZTKQQn fAwjBbL8m3LpNohc2OZWerFwYIniuKO8je9nZnA5VafnvYjiLgeQ3HJSjSPfMG+D4eL/ Pq7Q== X-Gm-Message-State: AOAM532mVjurWH4voT+KtiXLKlvCCsu7+0HLbN21WD/3JU9GAEjMk/7W wjP8qY84EYrwgJx+vBcVdf1fJP7CTHs= X-Google-Smtp-Source: ABdhPJwNeyv4uuU7jneD4R263Q3CDhNQoNxJyLlw5PFrvBvjhbMrkb5ffdVyboN5y95J7aLLHm8T4JnHZ9A= X-Received: from reiji-vws-sp.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:3d59]) (user=reijiw job=sendgmr) by 2002:a05:6a00:1f0b:b0:50a:8181:fecb with SMTP id be11-20020a056a001f0b00b0050a8181fecbmr6190053pfb.12.1650351450390; Mon, 18 Apr 2022 23:57:30 -0700 (PDT) Date: Mon, 18 Apr 2022 23:55:21 -0700 In-Reply-To: <20220419065544.3616948-1-reijiw@google.com> Message-Id: <20220419065544.3616948-16-reijiw@google.com> Mime-Version: 1.0 References: <20220419065544.3616948-1-reijiw@google.com> X-Mailer: git-send-email 2.36.0.rc0.470.gd361397f0d-goog Subject: [PATCH v7 15/38] KVM: arm64: Make ID_AA64DFR0_EL1/ID_DFR0_EL1 writable From: Reiji Watanabe To: Marc Zyngier , kvmarm@lists.cs.columbia.edu Cc: kvm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, James Morse , Alexandru Elisei , Suzuki K Poulose , Paolo Bonzini , Will Deacon , Andrew Jones , Fuad Tabba , Peng Liang , Peter Shier , Ricardo Koller , Oliver Upton , Jing Zhang , Raghavendra Rao Anata , Reiji Watanabe X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20220418_235731_747946_09C9A53C X-CRM114-Status: GOOD ( 23.75 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org This patch adds id_reg_desc for ID_AA64DFR0_EL1 and ID_DFR0_EL1 to make them writable by userspace. Return an error if userspace tries to set PMUVER/PerfMon field of ID_AA64DFR0_EL1/ID_DFR0_EL1 to a value that conflicts with the PMU configuration. When a value of ID_AA64DFR0_EL1.PMUVER or ID_DFR0_EL1.PERFMON on the host is 0xf, which means IMPLEMENTATION DEFINED PMU supported, KVM erroneously expose the value for the guest as it is even though KVM doesn't support it for the guest. In that case, since KVM should expose 0x0 (PMU is not implemented), change the initial value of ID_AA64DFR0_EL1.PMUVER and ID_DFR0_EL1.PERFMON for the guest to 0x0. If userspace requests KVM to set them to 0xf, which shouldn't be allowed as KVM doesn't support IMPLEMENTATION DEFINED PMU for the guest, ignore the request (set the fields to 0x0 instead) so that a live migration from the older kernel works fine. Since number of context-aware breakpoints must be no more than number of supported breakpoints according to Arm ARM, return an error if userspace tries to set CTX_CMPS field to such value. Fixes: 8e35aa642ee4 ("arm64: cpufeature: Extract capped perfmon fields") Signed-off-by: Reiji Watanabe --- arch/arm64/include/asm/cpufeature.h | 2 +- arch/arm64/kvm/sys_regs.c | 164 ++++++++++++++++++++++++---- 2 files changed, 143 insertions(+), 23 deletions(-) diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 7a009d4e18a6..7ed2d32b3854 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -554,7 +554,7 @@ cpuid_feature_cap_perfmon_field(u64 features, int field, u64 cap) /* Treat IMPLEMENTATION DEFINED functionality as unimplemented */ if (val == ID_AA64DFR0_PMUVER_IMP_DEF) - val = 0; + return (features & ~mask); if (val > cap) { features &= ~mask; diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index 400fa7ff582f..9eca085886f5 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -654,6 +654,75 @@ static int validate_id_aa64mmfr0_el1(struct kvm_vcpu *vcpu, return 0; } +static int validate_id_aa64dfr0_el1(struct kvm_vcpu *vcpu, + const struct id_reg_desc *id_reg, u64 val) +{ + unsigned int brps, ctx_cmps; + u64 pmu, lim_pmu; + u64 lim = id_reg->vcpu_limit_val; + + brps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_BRPS_SHIFT); + ctx_cmps = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_CTX_CMPS_SHIFT); + + /* + * Number of context-aware breakpoints can be no more than number of + * supported breakpoints. + */ + if (ctx_cmps > brps) + return -EINVAL; + + /* + * KVM will not set PMUVER to 0xf (IMPLEMENTATION DEFINED PMU) + * for the guest because KVM doesn't support it. + * If userspace requests KVM to set the field to 0xf, KVM will treat + * that as 0 instead of returning an error since userspace might do + * that when the guest is migrated from a host with older KVM, + * which sets the field to 0xf when the host value is 0xf. + */ + pmu = cpuid_feature_extract_unsigned_field(val, ID_AA64DFR0_PMUVER_SHIFT); + pmu = (pmu == 0xf) ? 0 : pmu; + lim_pmu = cpuid_feature_extract_unsigned_field(lim, ID_AA64DFR0_PMUVER_SHIFT); + if (pmu > lim_pmu) + return -E2BIG; + + /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */ + if (kvm_vcpu_has_pmu(vcpu) ^ (pmu >= ID_AA64DFR0_PMUVER_8_0)) + return -EPERM; + + return 0; +} + +static int validate_id_dfr0_el1(struct kvm_vcpu *vcpu, + const struct id_reg_desc *id_reg, u64 val) +{ + u64 pmon, lim_pmon; + u64 lim = id_reg->vcpu_limit_val; + + /* + * KVM will not set PERFMON to 0xf (IMPLEMENTATION DEFINED PERFMON) + * for the guest because KVM doesn't support it. + * If userspace requests KVM to set the field to 0xf, KVM will treat + * that as 0 instead of returning an error since userspace might do + * that when the guest is migrated from a host with older KVM, + * which sets the field to 0xf when the host value is 0xf. + */ + pmon = cpuid_feature_extract_unsigned_field(val, ID_DFR0_PERFMON_SHIFT); + pmon = (pmon == 0xf) ? 0 : pmon; + lim_pmon = cpuid_feature_extract_unsigned_field(lim, ID_DFR0_PERFMON_SHIFT); + if (pmon > lim_pmon) + return -E2BIG; + + if (pmon == 1 || pmon == 2) + /* PMUv1 or PMUv2 is not allowed on ARMv8. */ + return -EINVAL; + + /* Check if there is a conflict with a request via KVM_ARM_VCPU_INIT */ + if (kvm_vcpu_has_pmu(vcpu) ^ (pmon >= ID_DFR0_PERFMON_8_0)) + return -EPERM; + + return 0; +} + static void init_id_aa64pfr0_el1_desc(struct id_reg_desc *id_reg) { u64 limit = id_reg->vcpu_limit_val; @@ -703,6 +772,31 @@ static void init_id_aa64isar2_el1_desc(struct id_reg_desc *id_reg) id_reg->vcpu_limit_val &= ~ISAR2_PTRAUTH_MASK; } +static void init_id_aa64dfr0_el1_desc(struct id_reg_desc *id_reg) +{ + u64 limit = id_reg->vcpu_limit_val; + + /* Limit guests to PMUv3 for ARMv8.4 */ + limit = cpuid_feature_cap_perfmon_field(limit, ID_AA64DFR0_PMUVER_SHIFT, + ID_AA64DFR0_PMUVER_8_4); + /* Limit debug to ARMv8.0 */ + limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER); + limit |= (FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6)); + + /* Hide SPE from guests */ + limit &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER); + + id_reg->vcpu_limit_val = limit; +} + +static void init_id_dfr0_el1_desc(struct id_reg_desc *id_reg) +{ + /* Limit guests to PMUv3 for ARMv8.4 */ + id_reg->vcpu_limit_val = + cpuid_feature_cap_perfmon_field(id_reg->vcpu_limit_val, + ID_DFR0_PERFMON_SHIFT, + ID_DFR0_PERFMON_8_4); +} static u64 vcpu_mask_id_aa64pfr0_el1(const struct kvm_vcpu *vcpu, const struct id_reg_desc *idr) @@ -729,6 +823,18 @@ static u64 vcpu_mask_id_aa64isar2_el1(const struct kvm_vcpu *vcpu, } +static u64 vcpu_mask_id_aa64dfr0_el1(const struct kvm_vcpu *vcpu, + const struct id_reg_desc *idr) +{ + return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER); +} + +static u64 vcpu_mask_id_dfr0_el1(const struct kvm_vcpu *vcpu, + const struct id_reg_desc *idr) +{ + return kvm_vcpu_has_pmu(vcpu) ? 0 : ARM64_FEATURE_MASK(ID_DFR0_PERFMON); +} + static int validate_id_reg(struct kvm_vcpu *vcpu, const struct id_reg_desc *id_reg, u64 val) { @@ -2186,28 +2292,9 @@ static u64 read_id_reg_with_encoding(const struct kvm_vcpu *vcpu, u32 id) const struct id_reg_desc *id_reg = get_id_reg_desc(id); if (id_reg) - return __read_id_reg(vcpu, id_reg); - - val = read_kvm_id_reg(vcpu->kvm, id); - switch (id) { - case SYS_ID_AA64DFR0_EL1: - /* Limit debug to ARMv8.0 */ - val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER); - val |= FIELD_PREP(ARM64_FEATURE_MASK(ID_AA64DFR0_DEBUGVER), 6); - /* Limit guests to PMUv3 for ARMv8.4 */ - val = cpuid_feature_cap_perfmon_field(val, - ID_AA64DFR0_PMUVER_SHIFT, - kvm_vcpu_has_pmu(vcpu) ? ID_AA64DFR0_PMUVER_8_4 : 0); - /* Hide SPE from guests */ - val &= ~ARM64_FEATURE_MASK(ID_AA64DFR0_PMSVER); - break; - case SYS_ID_DFR0_EL1: - /* Limit guests to PMUv3 for ARMv8.4 */ - val = cpuid_feature_cap_perfmon_field(val, - ID_DFR0_PERFMON_SHIFT, - kvm_vcpu_has_pmu(vcpu) ? ID_DFR0_PERFMON_8_4 : 0); - break; - } + val = __read_id_reg(vcpu, id_reg); + else + val = read_kvm_id_reg(vcpu->kvm, id); return val; } @@ -4028,15 +4115,48 @@ static struct id_reg_desc id_aa64mmfr0_el1_desc = { }, }; +static struct id_reg_desc id_aa64dfr0_el1_desc = { + .reg_desc = ID_SANITISED(ID_AA64DFR0_EL1), + /* + * PMUVER doesn't follow the ID scheme for fields in ID registers. + * So, it will be validated by validate_id_aa64dfr0_el1. + */ + .ignore_mask = ARM64_FEATURE_MASK(ID_AA64DFR0_PMUVER), + .init = init_id_aa64dfr0_el1_desc, + .validate = validate_id_aa64dfr0_el1, + .vcpu_mask = vcpu_mask_id_aa64dfr0_el1, + .ftr_bits = { + S_FTR_BITS(FTR_LOWER_SAFE, ID_AA64DFR0_DOUBLELOCK_SHIFT, 0xf), + }, +}; + +static struct id_reg_desc id_dfr0_el1_desc = { + .reg_desc = ID_SANITISED(ID_DFR0_EL1), + /* + * PERFMON doesn't follow the ID scheme for fields in ID registers. + * So, it will be validated by validate_id_dfr0_el1. + */ + .ignore_mask = ARM64_FEATURE_MASK(ID_DFR0_PERFMON), + .init = init_id_dfr0_el1_desc, + .validate = validate_id_dfr0_el1, + .vcpu_mask = vcpu_mask_id_dfr0_el1, +}; + #define ID_DESC(id_reg_name, id_reg_desc) \ [IDREG_IDX(SYS_##id_reg_name)] = (id_reg_desc) /* A table for ID registers's information. */ static struct id_reg_desc *id_reg_desc_table[KVM_ARM_ID_REG_MAX_NUM] = { + /* CRm=1 */ + ID_DESC(ID_DFR0_EL1, &id_dfr0_el1_desc), + /* CRm=4 */ ID_DESC(ID_AA64PFR0_EL1, &id_aa64pfr0_el1_desc), ID_DESC(ID_AA64PFR1_EL1, &id_aa64pfr1_el1_desc), + /* CRm=5 */ + ID_DESC(ID_AA64DFR0_EL1, &id_aa64dfr0_el1_desc), + /* CRm=6 */ ID_DESC(ID_AA64ISAR0_EL1, &id_aa64isar0_el1_desc), ID_DESC(ID_AA64ISAR1_EL1, &id_aa64isar1_el1_desc),