[RFC PATCH part-7 06/12] pkvm: x86: Implement do_donate() helper for donating memory

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Shaoqin Huang <shaoqin.huang@xxxxxxxxx>

Transferring ownership information of a memory region from one component
to another can be achieved using a "donate" operation, which results in
the previous owner losing access to the underlying pages entirely, and
current owner will control the pages.

Introduce a do_donate() helper for safely donating a memory region from
one component to another. Currently, only host_to_hyp donating is
implemented, but the code is easily extended to handle other
combinations and the permission checks for each component are reusable.

Signed-off-by: Shaoqin Huang <shaoqin.huang@xxxxxxxxx>
---
 arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c | 200 ++++++++++++++++++++++++
 arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h |  15 ++
 2 files changed, 215 insertions(+)

diff --git a/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c b/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c
index 625c138addfb..b040a109f87d 100644
--- a/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c
+++ b/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c
@@ -4,9 +4,39 @@
  */
 #include <linux/bitfield.h>
 #include <pkvm.h>
+#include <gfp.h>
 #include "pkvm_hyp.h"
 #include "mem_protect.h"
 #include "pgtable.h"
+#include "ept.h"
+
+struct check_walk_data {
+	enum pkvm_page_state	desired;
+};
+
+enum pkvm_component_id {
+	PKVM_ID_HYP,
+	PKVM_ID_HOST,
+};
+
+struct pkvm_mem_trans_desc {
+	enum pkvm_component_id	id;
+	union {
+		struct {
+			u64	addr;
+		} host;
+
+		struct {
+			u64	addr;
+		} hyp;
+	};
+};
+
+struct pkvm_mem_transition {
+	u64				size;
+	struct pkvm_mem_trans_desc	initiator;
+	struct pkvm_mem_trans_desc	completer;
+};
 
 static u64 pkvm_init_invalid_leaf_owner(pkvm_id owner_id)
 {
@@ -33,3 +63,173 @@ static int host_ept_set_owner_locked(phys_addr_t addr, u64 size, pkvm_id owner_i
 
 	return ret;
 }
+
+static int
+__check_page_state_walker(struct pkvm_pgtable *pgt, unsigned long vaddr,
+			  unsigned long vaddr_end, int level, void *ptep,
+			  unsigned long flags, struct pgt_flush_data *flush_data,
+			  void *const arg)
+{
+	struct check_walk_data *data = arg;
+
+	return pkvm_getstate(*(u64 *)ptep) == data->desired ? 0 : -EPERM;
+}
+
+static int check_page_state_range(struct pkvm_pgtable *pgt, u64 addr, u64 size,
+				  enum pkvm_page_state state)
+{
+	struct check_walk_data data = {
+		.desired		= state,
+	};
+	struct pkvm_pgtable_walker walker = {
+		.cb		= __check_page_state_walker,
+		.flags		= PKVM_PGTABLE_WALK_LEAF,
+		.arg		= &data,
+	};
+
+	return pgtable_walk(pgt, addr, size, &walker);
+}
+
+static int __host_check_page_state_range(u64 addr, u64 size,
+					 enum pkvm_page_state state)
+{
+	return check_page_state_range(pkvm_hyp->host_vm.ept, addr, size, state);
+}
+
+static pkvm_id __pkvm_owner_id(const struct pkvm_mem_trans_desc *desc)
+{
+	switch (desc->id) {
+	case PKVM_ID_HYP:
+		return OWNER_ID_HYP;
+	default:
+		WARN_ON(1);
+		return OWNER_ID_INV;
+	}
+}
+
+static pkvm_id completer_owner_id(const struct pkvm_mem_transition *tx)
+{
+	return __pkvm_owner_id(&tx->completer);
+}
+
+static int host_request_donation(const struct pkvm_mem_transition *tx)
+{
+	u64 addr = tx->initiator.host.addr;
+	u64 size = tx->size;
+
+	return __host_check_page_state_range(addr, size, PKVM_PAGE_OWNED);
+}
+
+static int check_donation(const struct pkvm_mem_transition *tx)
+{
+	int ret;
+
+	switch (tx->initiator.id) {
+	case PKVM_ID_HOST:
+		ret = host_request_donation(tx);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (ret)
+		return ret;
+
+	switch (tx->completer.id) {
+	case PKVM_ID_HYP:
+		ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int host_initiate_donation(const struct pkvm_mem_transition *tx)
+{
+	pkvm_id owner_id = completer_owner_id(tx);
+	u64 addr = tx->initiator.host.addr;
+	u64 size = tx->size;
+
+	if (owner_id == OWNER_ID_INV)
+		return -EINVAL;
+	else
+		return host_ept_set_owner_locked(addr, size, owner_id);
+}
+
+static int __do_donate(const struct pkvm_mem_transition *tx)
+{
+	int ret;
+
+	switch (tx->initiator.id) {
+	case PKVM_ID_HOST:
+		ret = host_initiate_donation(tx);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	if (ret)
+		return ret;
+
+	switch (tx->completer.id) {
+	case PKVM_ID_HYP:
+		ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * do_donate - the page owner transfer ownership to another component.
+ *
+ * Initiator: OWNED	=> NO_PAGE
+ * Completer: NO_APGE	=> OWNED
+ *
+ * The special component is pkvm_hyp. Since pkvm_hyp can access all the
+ * memory, nothing needs to be done if the page owner is transferred to
+ * hyp or hyp transfers the ownership to other entities.
+ */
+static int do_donate(const struct pkvm_mem_transition *donation)
+{
+	int ret;
+
+	ret = check_donation(donation);
+	if (ret)
+		return ret;
+
+	return WARN_ON(__do_donate(donation));
+}
+
+int __pkvm_host_donate_hyp(u64 hpa, u64 size)
+{
+	int ret;
+	u64 hyp_addr = (u64)__pkvm_va(hpa);
+	struct pkvm_mem_transition donation = {
+		.size		= size,
+		.initiator	= {
+			.id	= PKVM_ID_HOST,
+			.host	= {
+				.addr	= hpa,
+			},
+		},
+		.completer	= {
+			.id	= PKVM_ID_HYP,
+			.hyp	= {
+				.addr = hyp_addr,
+			},
+		},
+	};
+
+	host_ept_lock();
+
+	ret = do_donate(&donation);
+
+	host_ept_unlock();
+
+	return ret;
+}
diff --git a/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h b/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h
index 6b5e0fcbdda0..e7e632270688 100644
--- a/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h
+++ b/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h
@@ -53,4 +53,19 @@ typedef u32 pkvm_id;
 #define OWNER_ID_HOST	1UL
 #define OWNER_ID_INV	(~(u32)0UL)
 
+/*
+ * __pkvm_host_donate_hyp() - Donate pages from host to hyp, then host cannot
+ * access these donated pages.
+ *
+ * @hpa:	Start hpa of being donated pages, must be continuous.
+ * @size:	The size of memory to be donated.
+ *
+ * A range of pages [hpa, hpa + size) will be donated from host to hyp. And
+ * this will unmap these pages from host ept and set the page owner as hyp_id
+ * in the pte in host ept. For hyp mmu, it will do nothing as hyp mmu can
+ * access all the memory by default, but modifying host ept is necessary because
+ * a page used by pkvm is private and can't be accessed by host.
+ */
+int __pkvm_host_donate_hyp(u64 hpa, u64 size);
+
 #endif
-- 
2.25.1




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux