From patchwork Thu Feb 27 00:33:08 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Quentin Perret X-Patchwork-Id: 13993380 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 8AEE8C021B8 for ; Thu, 27 Feb 2025 00:41:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender:List-Subscribe:List-Help :List-Post:List-Archive:List-Unsubscribe:List-Id:Content-Type:Cc:To:From: Subject:Message-ID:References:Mime-Version:In-Reply-To:Date:Reply-To: Content-Transfer-Encoding:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=y4cLgcKO1PUW4FpDNffQ+Ql+VwqpG2i64q1nOyfOcxQ=; b=bWsJpSjXFsnXa7Ln2KstbeJqcX WupIjCJmWHzKyY2tl4B9nwlTiJIgKscNKDlrCOYn9K39Gb4yKsBie2MNuQDLDE7upoCGHmjnZv64d Nwv316MAAymtdDY6TXARNxw5BfA3eS+lud/8P8uI6ep3PPK/xCEYp0Wb2vWnKpA01QmWrCTls4Hn2 ush7A2PGbHqsOGWZ3/vUvHkhS3crJf1Jkw9x0xWa0WrZMflr7Z1VnANXptq/Zk2XUhAx0Ac7wJCEq me9RT+BHcuvcz+BMwtHGatOOEpFgZV6hIwIUJQgS+/3Xr7jaeRDfmGMAYuzfWwsVffDt/NpOwy6HT 2eI66qPw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tnRxZ-00000005q9d-0oUD; Thu, 27 Feb 2025 00:41:05 +0000 Received: from mail-ej1-x64a.google.com ([2a00:1450:4864:20::64a]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tnRq6-00000005osK-30U7 for linux-arm-kernel@lists.infradead.org; Thu, 27 Feb 2025 00:33:23 +0000 Received: by mail-ej1-x64a.google.com with SMTP id a640c23a62f3a-abb8f65af3dso32603666b.1 for ; Wed, 26 Feb 2025 16:33:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1740616401; x=1741221201; darn=lists.infradead.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=y4cLgcKO1PUW4FpDNffQ+Ql+VwqpG2i64q1nOyfOcxQ=; b=l27PQjivt1nhxSF1LXZsRrxx2/rg52ldl8aW+rjVstP6b8XAXrPKbSamhaQXL814h0 ZMqDMkg5AZM2aABO6Ao1nuz1faql1g0cWCRNppcHDN5gGAeQlmZZE4V+4MlqW3S4tv08 1CpJVcl8ct9zTcrK+c9jCoSQzUO7AGKH2RKB/P0esGMKAtY8Xt4PAJncfYt3iOUr26HB 2PaG0/lTKgVbGIpC21VCKCrOsxTo99dRM/2V2uXRurEA0QHBzHXGRxZRbonIQq8DEOKc AS/RPRQO2UO1vCg3Kb2MxuUI8j3HDqaxHCBccYdvu8ppvGNdO24aSpq5Xo0mgWh3q+mL 2q8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740616401; x=1741221201; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=y4cLgcKO1PUW4FpDNffQ+Ql+VwqpG2i64q1nOyfOcxQ=; b=ng7ZquevU//RJcxf0yUkUNcbLKyes89FjLPsFmeOmWuBjnmo7ny4GFHaBAPN01a88m 3RavkmVE2RsScTnWe7CyiXmxym4lXnqJ1sJJ4koIPb4fWpXhgYbHnEAiAjJ1G9c+Pg5+ RuOsHVlgbF+ujRWj3FGuvWMrkXNEpk8AwlcRPLrPKe8de2GxCCNplZV2AxJ9LRFAtNEa VXZ0FvuaievEcCscFM5pAUbG9LAHc0ZQWk5evQRMLanmr8Vu8E+da6T9pP0G8WAZXqZ0 z9B3/ZbYmYjRR6ythq2APbBvwRmHvOd7CpLnHcDKIqHh7M3ZEG8e6z4Kc/6fs3C/B6cM MGOQ== X-Forwarded-Encrypted: i=1; AJvYcCXVb/6oduPtExlFr5xht6Nhf0mwlvOgD3/c1ZMfl8auPs4dgXl427zlGYEAbfmbJGQIRCPyjlR0qfIF073Kr43G@lists.infradead.org X-Gm-Message-State: AOJu0YzdmdabGw069+cRKGnr+GvXfoWpcLdo01+cphYuSjoat9/Zcuis vGLQyXvoirEzF36sLSRFuRhhHV3V1WVqMmZmRW8zb6PrJkfY4/gSbwaoJ1mJbC+xB6xvaAxro7C qHdPB4w== X-Google-Smtp-Source: AGHT+IGWCpUoU+SAED/xQN8A5aBnRG3yloyg3hA6+NnceXAeQRYPTf0IxPy3Oss2RrNOg12kmMv5qagT6Wh9 X-Received: from ejcux9.prod.google.com ([2002:a17:907:cf89:b0:abb:7df3:8192]) (user=qperret job=prod-delivery.src-stubby-dispatcher) by 2002:a17:907:2ce4:b0:abb:bcef:837c with SMTP id a640c23a62f3a-abeeef81b48mr736438766b.56.1740616401210; Wed, 26 Feb 2025 16:33:21 -0800 (PST) Date: Thu, 27 Feb 2025 00:33:08 +0000 In-Reply-To: <20250227003310.367350-1-qperret@google.com> Mime-Version: 1.0 References: <20250227003310.367350-1-qperret@google.com> X-Mailer: git-send-email 2.48.1.658.g4767266eb4-goog Message-ID: <20250227003310.367350-5-qperret@google.com> Subject: [PATCH 4/6] KVM: arm64: Move hyp state to hyp_vmemmap From: Quentin Perret To: Marc Zyngier , Oliver Upton , Joey Gouly , Suzuki K Poulose , Zenghui Yu , Catalin Marinas , Will Deacon Cc: Vincent Donnefort , Quentin Perret , linux-arm-kernel@lists.infradead.org, kvmarm@lists.linux.dev, linux-kernel@vger.kernel.org X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20250226_163322_758208_F611BD9D X-CRM114-Status: GOOD ( 18.29 ) 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 Tracking the hypervisor's ownership state into struct hyp_page has several benefits, including allowing far more efficient lookups (no page-table walk needed) and de-corelating the state from the presence of a mapping. This will later allow to map pages into EL2 stage-1 less proactively which is generally a good thing for security. And in the future this will help with tracking the state of pages mapped into the hypervisor's private range without requiring an alias into the 'linear map' range. Signed-off-by: Quentin Perret --- arch/arm64/kvm/hyp/include/nvhe/memory.h | 20 +++++++++- arch/arm64/kvm/hyp/nvhe/mem_protect.c | 51 ++++++++++++------------ arch/arm64/kvm/hyp/nvhe/setup.c | 6 ++- 3 files changed, 49 insertions(+), 28 deletions(-) diff --git a/arch/arm64/kvm/hyp/include/nvhe/memory.h b/arch/arm64/kvm/hyp/include/nvhe/memory.h index 4a3c55d26ef3..cc4c01158368 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/memory.h +++ b/arch/arm64/kvm/hyp/include/nvhe/memory.h @@ -22,6 +22,7 @@ enum pkvm_page_state { /* Meta-states which aren't encoded directly in the PTE's SW bits */ PKVM_NOPAGE = BIT(0) | BIT(1), }; +#define PKVM_PAGE_STATE_MASK (BIT(0) | BIT(1)) #define PKVM_PAGE_STATE_PROT_MASK (KVM_PGTABLE_PROT_SW0 | KVM_PGTABLE_PROT_SW1) static inline enum kvm_pgtable_prot pkvm_mkstate(enum kvm_pgtable_prot prot, @@ -42,7 +43,14 @@ struct hyp_page { u8 order; /* Host (non-meta) state. Guarded by the host stage-2 lock. */ - unsigned __host_state : 8; + unsigned __host_state : 4; + + /* + * Complement of the hyp (non-meta) state. Guarded by the hyp stage-1 lock. We use the + * complement so that the initial 0 in __hyp_state_comp (due to the entire vmemmap starting + * off zeroed) encodes PKVM_NOPAGE. + */ + unsigned __hyp_state_comp : 4; u32 host_share_guest_count; }; @@ -89,6 +97,16 @@ static inline void set_host_state(phys_addr_t phys, enum pkvm_page_state state) hyp_phys_to_page(phys)->__host_state = state; } +static inline enum pkvm_page_state get_hyp_state(phys_addr_t phys) +{ + return hyp_phys_to_page(phys)->__hyp_state_comp ^ PKVM_PAGE_STATE_MASK; +} + +static inline void set_hyp_state(phys_addr_t phys, enum pkvm_page_state state) +{ + hyp_phys_to_page(phys)->__hyp_state_comp = state ^ PKVM_PAGE_STATE_MASK; +} + /* * Refcounting for 'struct hyp_page'. * hyp_pool::lock must be held if atomic access to the refcount is required. diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index a45ffdec7612..3ab8c81500c2 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -642,24 +642,24 @@ static int __host_set_page_state_range(u64 addr, u64 size, return 0; } -static enum pkvm_page_state hyp_get_page_state(kvm_pte_t pte, u64 addr) +static void __hyp_set_page_state_range(phys_addr_t phys, u64 size, enum pkvm_page_state state) { - if (!kvm_pte_valid(pte)) - return PKVM_NOPAGE; + phys_addr_t end = phys + size; - return pkvm_getstate(kvm_pgtable_hyp_pte_prot(pte)); + for (; phys < end; phys += PAGE_SIZE) + set_hyp_state(phys, state); } -static int __hyp_check_page_state_range(u64 addr, u64 size, - enum pkvm_page_state state) +static int __hyp_check_page_state_range(phys_addr_t phys, u64 size, enum pkvm_page_state state) { - struct check_walk_data d = { - .desired = state, - .get_page_state = hyp_get_page_state, - }; + phys_addr_t end = phys + size; + + for (; phys < end; phys += PAGE_SIZE) { + if (get_hyp_state(phys) != state) + return -EPERM; + } - hyp_assert_lock_held(&pkvm_pgd_lock); - return check_page_state_range(&pkvm_pgtable, addr, size, &d); + return 0; } static enum pkvm_page_state guest_get_page_state(kvm_pte_t pte, u64 addr) @@ -687,7 +687,6 @@ int __pkvm_host_share_hyp(u64 pfn) { u64 phys = hyp_pfn_to_phys(pfn); void *virt = __hyp_va(phys); - enum kvm_pgtable_prot prot; u64 size = PAGE_SIZE; int ret; @@ -698,13 +697,13 @@ int __pkvm_host_share_hyp(u64 pfn) if (ret) goto unlock; if (IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) { - ret = __hyp_check_page_state_range((u64)virt, size, PKVM_NOPAGE); + ret = __hyp_check_page_state_range(phys, size, PKVM_NOPAGE); if (ret) goto unlock; } - prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_SHARED_BORROWED); - WARN_ON(pkvm_create_mappings_locked(virt, virt + size, prot)); + __hyp_set_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED); + WARN_ON(pkvm_create_mappings_locked(virt, virt + size, PAGE_HYP)); WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_SHARED_OWNED)); unlock: @@ -727,7 +726,7 @@ int __pkvm_host_unshare_hyp(u64 pfn) ret = __host_check_page_state_range(phys, size, PKVM_PAGE_SHARED_OWNED); if (ret) goto unlock; - ret = __hyp_check_page_state_range(virt, size, PKVM_PAGE_SHARED_BORROWED); + ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED); if (ret) goto unlock; if (hyp_page_count((void *)virt)) { @@ -735,6 +734,7 @@ int __pkvm_host_unshare_hyp(u64 pfn) goto unlock; } + __hyp_set_page_state_range(phys, size, PKVM_NOPAGE); WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, size) != size); WARN_ON(__host_set_page_state_range(phys, size, PKVM_PAGE_OWNED)); @@ -750,7 +750,6 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages) u64 phys = hyp_pfn_to_phys(pfn); u64 size = PAGE_SIZE * nr_pages; void *virt = __hyp_va(phys); - enum kvm_pgtable_prot prot; int ret; host_lock_component(); @@ -760,13 +759,13 @@ int __pkvm_host_donate_hyp(u64 pfn, u64 nr_pages) if (ret) goto unlock; if (IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) { - ret = __hyp_check_page_state_range((u64)virt, size, PKVM_NOPAGE); + ret = __hyp_check_page_state_range(phys, size, PKVM_NOPAGE); if (ret) goto unlock; } - prot = pkvm_mkstate(PAGE_HYP, PKVM_PAGE_OWNED); - WARN_ON(pkvm_create_mappings_locked(virt, virt + size, prot)); + __hyp_set_page_state_range(phys, size, PKVM_PAGE_OWNED); + WARN_ON(pkvm_create_mappings_locked(virt, virt + size, PAGE_HYP)); WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_HYP)); unlock: @@ -786,7 +785,7 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages) host_lock_component(); hyp_lock_component(); - ret = __hyp_check_page_state_range(virt, size, PKVM_PAGE_OWNED); + ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_OWNED); if (ret) goto unlock; if (IS_ENABLED(CONFIG_NVHE_EL2_DEBUG)) { @@ -795,6 +794,7 @@ int __pkvm_hyp_donate_host(u64 pfn, u64 nr_pages) goto unlock; } + __hyp_set_page_state_range(phys, size, PKVM_NOPAGE); WARN_ON(kvm_pgtable_hyp_unmap(&pkvm_pgtable, virt, size) != size); WARN_ON(host_stage2_set_owner_locked(phys, size, PKVM_ID_HOST)); @@ -809,19 +809,18 @@ int hyp_pin_shared_mem(void *from, void *to) { u64 cur, start = ALIGN_DOWN((u64)from, PAGE_SIZE); u64 end = PAGE_ALIGN((u64)to); + u64 phys = __hyp_pa(start); u64 size = end - start; int ret; host_lock_component(); hyp_lock_component(); - ret = __host_check_page_state_range(__hyp_pa(start), size, - PKVM_PAGE_SHARED_OWNED); + ret = __host_check_page_state_range(phys, size, PKVM_PAGE_SHARED_OWNED); if (ret) goto unlock; - ret = __hyp_check_page_state_range(start, size, - PKVM_PAGE_SHARED_BORROWED); + ret = __hyp_check_page_state_range(phys, size, PKVM_PAGE_SHARED_BORROWED); if (ret) goto unlock; diff --git a/arch/arm64/kvm/hyp/nvhe/setup.c b/arch/arm64/kvm/hyp/nvhe/setup.c index 1a414288fe8c..955c431af5d0 100644 --- a/arch/arm64/kvm/hyp/nvhe/setup.c +++ b/arch/arm64/kvm/hyp/nvhe/setup.c @@ -194,16 +194,20 @@ static int fix_host_ownership_walker(const struct kvm_pgtable_visit_ctx *ctx, /* * Adjust the host stage-2 mappings to match the ownership attributes - * configured in the hypervisor stage-1. + * configured in the hypervisor stage-1, and make sure to propagate them + * to the hyp_vmemmap state. */ state = pkvm_getstate(kvm_pgtable_hyp_pte_prot(ctx->old)); switch (state) { case PKVM_PAGE_OWNED: + set_hyp_state(phys, PKVM_PAGE_OWNED); return host_stage2_set_owner_locked(phys, PAGE_SIZE, PKVM_ID_HYP); case PKVM_PAGE_SHARED_OWNED: + set_hyp_state(phys, PKVM_PAGE_SHARED_OWNED); set_host_state(phys, PKVM_PAGE_SHARED_BORROWED); break; case PKVM_PAGE_SHARED_BORROWED: + set_hyp_state(phys, PKVM_PAGE_SHARED_BORROWED); set_host_state(phys, PKVM_PAGE_SHARED_OWNED); break; default: