From: Shaoqin Huang <shaoqin.huang@xxxxxxxxx> One component sharing a memory region with another can be achieved using a "share" operation, which results in the page state of sharer become from PAGE_OWNED to PAGE_SHARED_OWNED, and the page state of being shared become from NOPAGE to PAGE_SHARED_BORROWED. The two components now can access the page both. Introduce a do_share() helper for safely sharing a memory region from one component to another. Currently, only host_to_guest sharing 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 | 155 ++++++++++++++++++++++++ arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h | 18 +++ 2 files changed, 173 insertions(+) diff --git a/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c b/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c index fad81f91794c..987fe172f6a6 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c +++ b/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.c @@ -17,6 +17,7 @@ struct check_walk_data { enum pkvm_component_id { PKVM_ID_HYP, PKVM_ID_HOST, + PKVM_ID_GUEST, }; struct pkvm_mem_trans_desc { @@ -29,6 +30,12 @@ struct pkvm_mem_trans_desc { struct { u64 addr; } hyp; + + struct { + struct pkvm_pgtable *pgt; + u64 addr; + u64 phys; + } guest; }; u64 prot; }; @@ -118,6 +125,13 @@ static pkvm_id completer_owner_id(const struct pkvm_mem_transition *tx) return __pkvm_owner_id(&tx->completer); } +static int __guest_check_page_state_range(struct pkvm_pgtable *pgt, + u64 addr, u64 size, + enum pkvm_page_state state) +{ + return check_page_state_range(pgt, addr, size, state); +} + static int host_request_donation(const struct pkvm_mem_transition *tx) { u64 addr = tx->initiator.host.addr; @@ -298,3 +312,144 @@ int __pkvm_hyp_donate_host(u64 hpa, u64 size) return ret; } + +static int host_request_share(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 guest_ack_share(const struct pkvm_mem_transition *tx) +{ + u64 addr = tx->completer.guest.addr; + u64 size = tx->size; + + return __guest_check_page_state_range(tx->completer.guest.pgt, addr, + size, PKVM_NOPAGE); +} + +static int check_share(const struct pkvm_mem_transition *tx) +{ + int ret; + + switch (tx->initiator.id) { + case PKVM_ID_HOST: + ret = host_request_share(tx); + break; + default: + ret = -EINVAL; + } + + if (ret) + return ret; + + switch (tx->completer.id) { + case PKVM_ID_GUEST: + ret = guest_ack_share(tx); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int host_initiate_share(const struct pkvm_mem_transition *tx) +{ + u64 addr = tx->initiator.host.addr; + u64 size = tx->size; + u64 prot = pkvm_mkstate(tx->initiator.prot, PKVM_PAGE_SHARED_OWNED); + + return host_ept_create_idmap_locked(addr, size, 0, prot); +} + +static int guest_complete_share(const struct pkvm_mem_transition *tx) +{ + struct pkvm_pgtable *pgt = tx->completer.guest.pgt; + u64 addr = tx->completer.guest.addr; + u64 size = tx->size; + u64 phys = tx->completer.guest.phys; + u64 prot = tx->completer.prot; + + prot = pkvm_mkstate(prot, PKVM_PAGE_SHARED_BORROWED); + return pkvm_pgtable_map(pgt, addr, phys, size, 0, prot); +} + +static int __do_share(const struct pkvm_mem_transition *tx) +{ + int ret; + + switch (tx->initiator.id) { + case PKVM_ID_HOST: + ret = host_initiate_share(tx); + break; + default: + ret = -EINVAL; + } + + if (ret) + return ret; + + switch (tx->completer.id) { + case PKVM_ID_GUEST: + ret = guest_complete_share(tx); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +/* + * do_share() - The page owner grants access to another component with a given + * set of permissions. + * + * Initiator: OWNED => SHARED_OWNED + * Completer: NOPAGE => SHARED_BORROWED + */ +static int do_share(const struct pkvm_mem_transition *share) +{ + int ret; + + ret = check_share(share); + if (ret) + return ret; + + return WARN_ON(__do_share(share)); +} + +int __pkvm_host_share_guest(u64 hpa, struct pkvm_pgtable *guest_pgt, + u64 gpa, u64 size, u64 prot) +{ + int ret; + struct pkvm_mem_transition share = { + .size = size, + .initiator = { + .id = PKVM_ID_HOST, + .host = { + .addr = hpa, + }, + .prot = HOST_EPT_DEF_MEM_PROT, + }, + .completer = { + .id = PKVM_ID_GUEST, + .guest = { + .pgt = guest_pgt, + .addr = gpa, + .phys = hpa, + }, + .prot = prot, + }, + }; + + host_ept_lock(); + + ret = do_share(&share); + + 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 efb3b3895f58..549cc5246620 100644 --- a/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h +++ b/arch/x86/kvm/vmx/pkvm/hyp/mem_protect.h @@ -82,4 +82,22 @@ int __pkvm_host_donate_hyp(u64 hpa, u64 size); */ int __pkvm_hyp_donate_host(u64 hpa, u64 size); +/* + * __pkvm_host_share_guest() - Share pages between host and guest. Host still + * ownes the page and guest will have temporary access to these pages. + * + * @hpa: Start hpa of being shared pages, must be continuous. + * @guest_pgt: The guest ept pagetable. + * @gpa: Start gpa that will be used for mapping into the guest ept. + * @size: The size of pages to be shared. + * @prot: The prot that will be used for creating mapping for guest ept. + * + * A range of pages [hpa, hpa + size) in host ept that their page state + * will be modified from PAGE_OWNED to PAGE_SHARED_OWNED. There will be + * mapping from gfn to pfn to be created in guest ept. The @prot + * and PAGE_SHARED_BORROWED will be used to create such mapping. + */ +int __pkvm_host_share_guest(u64 hpa, struct pkvm_pgtable *guest_pgt, + u64 gpa, u64 size, u64 prot); + #endif -- 2.25.1