On Fri, Jul 17, 2020 at 01:00:24AM -0700, Ram Pai wrote: > During the life of SVM, its GFNs transition through normal, secure and > shared states. Since the kernel does not track GFNs that are shared, it > is not possible to disambiguate a shared GFN from a GFN whose PFN has > not yet been migrated to a secure-PFN. Also it is not possible to > disambiguate a secure-GFN from a GFN whose GFN has been pagedout from > the ultravisor. > > The ability to identify the state of a GFN is needed to skip migration > of its PFN to secure-PFN during ESM transition. > > The code is re-organized to track the states of a GFN as explained > below. > > ************************************************************************ > 1. States of a GFN > --------------- > The GFN can be in one of the following states. > > (a) Secure - The GFN is secure. The GFN is associated with > a Secure VM, the contents of the GFN is not accessible > to the Hypervisor. This GFN can be backed by a secure-PFN, > or can be backed by a normal-PFN with contents encrypted. > The former is true when the GFN is paged-in into the > ultravisor. The latter is true when the GFN is paged-out > of the ultravisor. > > (b) Shared - The GFN is shared. The GFN is associated with a > a secure VM. The contents of the GFN is accessible to > Hypervisor. This GFN is backed by a normal-PFN and its > content is un-encrypted. > > (c) Normal - The GFN is a normal. The GFN is associated with > a normal VM. The contents of the GFN is accesible to > the Hypervisor. Its content is never encrypted. > > 2. States of a VM. > --------------- > > (a) Normal VM: A VM whose contents are always accessible to > the hypervisor. All its GFNs are normal-GFNs. > > (b) Secure VM: A VM whose contents are not accessible to the > hypervisor without the VM's consent. Its GFNs are > either Shared-GFN or Secure-GFNs. > > (c) Transient VM: A Normal VM that is transitioning to secure VM. > The transition starts on successful return of > H_SVM_INIT_START, and ends on successful return > of H_SVM_INIT_DONE. This transient VM, can have GFNs > in any of the three states; i.e Secure-GFN, Shared-GFN, > and Normal-GFN. The VM never executes in this state > in supervisor-mode. > > 3. Memory slot State. > ------------------ > The state of a memory slot mirrors the state of the > VM the memory slot is associated with. > > 4. VM State transition. > -------------------- > > A VM always starts in Normal Mode. > > H_SVM_INIT_START moves the VM into transient state. During this > time the Ultravisor may request some of its GFNs to be shared or > secured. So its GFNs can be in one of the three GFN states. > > H_SVM_INIT_DONE moves the VM entirely from transient state to > secure-state. At this point any left-over normal-GFNs are > transitioned to Secure-GFN. > > H_SVM_INIT_ABORT moves the transient VM back to normal VM. > All its GFNs are moved to Normal-GFNs. > > UV_TERMINATE transitions the secure-VM back to normal-VM. All > the secure-GFN and shared-GFNs are tranistioned to normal-GFN > Note: The contents of the normal-GFN is undefined at this point. > > 5. GFN state implementation: > ------------------------- > > Secure GFN is associated with a secure-PFN; also called uvmem_pfn, > when the GFN is paged-in. Its pfn[] has KVMPPC_GFN_UVMEM_PFN flag > set, and contains the value of the secure-PFN. > It is associated with a normal-PFN; also called mem_pfn, when > the GFN is pagedout. Its pfn[] has KVMPPC_GFN_MEM_PFN flag set. > The value of the normal-PFN is not tracked. > > Shared GFN is associated with a normal-PFN. Its pfn[] has > KVMPPC_UVMEM_SHARED_PFN flag set. The value of the normal-PFN > is not tracked. > > Normal GFN is associated with normal-PFN. Its pfn[] has > no flag set. The value of the normal-PFN is not tracked. > > 6. Life cycle of a GFN > -------------------- > -------------------------------------------------------------- > | | Share | Unshare | SVM |H_SVM_INIT_DONE| > | |operation |operation | abort/ | | > | | | | terminate | | > ------------------------------------------------------------- > | | | | | | > | Secure | Shared | Secure |Normal |Secure | > | | | | | | > | Shared | Shared | Secure |Normal |Shared | > | | | | | | > | Normal | Shared | Secure |Normal |Secure | > -------------------------------------------------------------- > > 7. Life cycle of a VM > -------------------- > -------------------------------------------------------------------- > | | start | H_SVM_ |H_SVM_ |H_SVM_ |UV_SVM_ | > | | VM |INIT_START|INIT_DONE|INIT_ABORT |TERMINATE | > | | | | | | | > --------- ---------------------------------------------------------- > | | | | | | | > | Normal | Normal | Transient|Error |Error |Normal | > | | | | | | | > | Secure | Error | Error |Error |Error |Normal | > | | | | | | | > |Transient| N/A | Error |Secure |Normal |Normal | > -------------------------------------------------------------------- > > ************************************************************************ > > Cc: Paul Mackerras <pau...@ozlabs.org> > Cc: Benjamin Herrenschmidt <b...@kernel.crashing.org> > Cc: Michael Ellerman <m...@ellerman.id.au> > Cc: Bharata B Rao <bhar...@linux.ibm.com> > Cc: Aneesh Kumar K.V <aneesh.ku...@linux.ibm.com> > Cc: Sukadev Bhattiprolu <suka...@linux.vnet.ibm.com> > Cc: Laurent Dufour <lduf...@linux.ibm.com> > Cc: Thiago Jung Bauermann <bauer...@linux.ibm.com> > Cc: David Gibson <da...@gibson.dropbear.id.au> > Cc: Claudio Carvalho <cclau...@linux.ibm.com> > Cc: kvm-...@vger.kernel.org > Cc: linuxppc-dev@lists.ozlabs.org > Reviewed-by: Thiago Jung Bauermann <bauer...@linux.ibm.com> > Signed-off-by: Ram Pai <linux...@us.ibm.com> > --- > arch/powerpc/kvm/book3s_hv_uvmem.c | 187 > +++++++++++++++++++++++++++++++++---- > 1 file changed, 168 insertions(+), 19 deletions(-) > > diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c > b/arch/powerpc/kvm/book3s_hv_uvmem.c > index 0baa293..df2e272 100644 > --- a/arch/powerpc/kvm/book3s_hv_uvmem.c > +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c > @@ -98,7 +98,127 @@ > static unsigned long *kvmppc_uvmem_bitmap; > static DEFINE_SPINLOCK(kvmppc_uvmem_bitmap_lock); > > -#define KVMPPC_UVMEM_PFN (1UL << 63) > +/* > + * States of a GFN > + * --------------- > + * The GFN can be in one of the following states. > + * > + * (a) Secure - The GFN is secure. The GFN is associated with > + * a Secure VM, the contents of the GFN is not accessible > + * to the Hypervisor. This GFN can be backed by a secure-PFN, > + * or can be backed by a normal-PFN with contents encrypted. > + * The former is true when the GFN is paged-in into the > + * ultravisor. The latter is true when the GFN is paged-out > + * of the ultravisor. > + * > + * (b) Shared - The GFN is shared. The GFN is associated with a > + * a secure VM. The contents of the GFN is accessible to > + * Hypervisor. This GFN is backed by a normal-PFN and its > + * content is un-encrypted. > + * > + * (c) Normal - The GFN is a normal. The GFN is associated with > + * a normal VM. The contents of the GFN is accesible to > + * the Hypervisor. Its content is never encrypted. > + * > + * States of a VM. > + * --------------- > + * > + * Normal VM: A VM whose contents are always accessible to > + * the hypervisor. All its GFNs are normal-GFNs. > + * > + * Secure VM: A VM whose contents are not accessible to the > + * hypervisor without the VM's consent. Its GFNs are > + * either Shared-GFN or Secure-GFNs. > + * > + * Transient VM: A Normal VM that is transitioning to secure VM. > + * The transition starts on successful return of > + * H_SVM_INIT_START, and ends on successful return > + * of H_SVM_INIT_DONE. This transient VM, can have GFNs > + * in any of the three states; i.e Secure-GFN, Shared-GFN, > + * and Normal-GFN. The VM never executes in this state > + * in supervisor-mode. > + * > + * Memory slot State. > + * ----------------------------- > + * The state of a memory slot mirrors the state of the > + * VM the memory slot is associated with. > + * > + * VM State transition. > + * -------------------- > + * > + * A VM always starts in Normal Mode. > + * > + * H_SVM_INIT_START moves the VM into transient state. During this > + * time the Ultravisor may request some of its GFNs to be shared or > + * secured. So its GFNs can be in one of the three GFN states. > + * > + * H_SVM_INIT_DONE moves the VM entirely from transient state to > + * secure-state. At this point any left-over normal-GFNs are > + * transitioned to Secure-GFN. > + * > + * H_SVM_INIT_ABORT moves the transient VM back to normal VM. > + * All its GFNs are moved to Normal-GFNs. > + * > + * UV_TERMINATE transitions the secure-VM back to normal-VM. All > + * the secure-GFN and shared-GFNs are tranistioned to normal-GFN > + * Note: The contents of the normal-GFN is undefined at this point. > + * > + * GFN state implementation: > + * ------------------------- > + * > + * Secure GFN is associated with a secure-PFN; also called uvmem_pfn, > + * when the GFN is paged-in. Its pfn[] has KVMPPC_GFN_UVMEM_PFN flag > + * set, and contains the value of the secure-PFN. > + * It is associated with a normal-PFN; also called mem_pfn, when > + * the GFN is pagedout. Its pfn[] has KVMPPC_GFN_MEM_PFN flag set. > + * The value of the normal-PFN is not tracked. > + * > + * Shared GFN is associated with a normal-PFN. Its pfn[] has > + * KVMPPC_UVMEM_SHARED_PFN flag set. The value of the normal-PFN > + * is not tracked. > + * > + * Normal GFN is associated with normal-PFN. Its pfn[] has > + * no flag set. The value of the normal-PFN is not tracked. > + * > + * Life cycle of a GFN > + * -------------------- > + * > + * -------------------------------------------------------------- > + * | | Share | Unshare | SVM |H_SVM_INIT_DONE| > + * | |operation |operation | abort/ | | > + * | | | | terminate | | > + * ------------------------------------------------------------- > + * | | | | | | > + * | Secure | Shared | Secure |Normal |Secure | > + * | | | | | | > + * | Shared | Shared | Secure |Normal |Shared | > + * | | | | | | > + * | Normal | Shared | Secure |Normal |Secure | > + * -------------------------------------------------------------- > + * > + * Life cycle of a VM > + * -------------------- > + * > + * -------------------------------------------------------------------- > + * | | start | H_SVM_ |H_SVM_ |H_SVM_ |UV_SVM_ | > + * | | VM |INIT_START|INIT_DONE|INIT_ABORT |TERMINATE | > + * | | | | | | | > + * --------- ---------------------------------------------------------- > + * | | | | | | | > + * | Normal | Normal | Transient|Error |Error |Normal | > + * | | | | | | | > + * | Secure | Error | Error |Error |Error |Normal | > + * | | | | | | | > + * |Transient| N/A | Error |Secure |Normal |Normal | > + * -------------------------------------------------------------------- > + */ > + > +#define KVMPPC_GFN_UVMEM_PFN (1UL << 63) > +#define KVMPPC_GFN_MEM_PFN (1UL << 62) > +#define KVMPPC_GFN_SHARED (1UL << 61) > +#define KVMPPC_GFN_SECURE (KVMPPC_GFN_UVMEM_PFN | KVMPPC_GFN_MEM_PFN) > +#define KVMPPC_GFN_FLAG_MASK (KVMPPC_GFN_SECURE | KVMPPC_GFN_SHARED) > +#define KVMPPC_GFN_PFN_MASK (~KVMPPC_GFN_FLAG_MASK) > > struct kvmppc_uvmem_slot { > struct list_head list; > @@ -106,11 +226,11 @@ struct kvmppc_uvmem_slot { > unsigned long base_pfn; > unsigned long *pfns; > }; > - > struct kvmppc_uvmem_page_pvt { > struct kvm *kvm; > unsigned long gpa; > bool skip_page_out; > + bool remove_gfn; > }; > > bool kvmppc_uvmem_available(void) > @@ -163,8 +283,8 @@ void kvmppc_uvmem_slot_free(struct kvm *kvm, const struct > kvm_memory_slot *slot) > mutex_unlock(&kvm->arch.uvmem_lock); > } > > -static void kvmppc_uvmem_pfn_insert(unsigned long gfn, unsigned long > uvmem_pfn, > - struct kvm *kvm) > +static void kvmppc_mark_gfn(unsigned long gfn, struct kvm *kvm, > + unsigned long flag, unsigned long uvmem_pfn) > { > struct kvmppc_uvmem_slot *p; > > @@ -172,24 +292,41 @@ static void kvmppc_uvmem_pfn_insert(unsigned long gfn, > unsigned long uvmem_pfn, > if (gfn >= p->base_pfn && gfn < p->base_pfn + p->nr_pfns) { > unsigned long index = gfn - p->base_pfn; > > - p->pfns[index] = uvmem_pfn | KVMPPC_UVMEM_PFN; > + if (flag == KVMPPC_GFN_UVMEM_PFN) > + p->pfns[index] = uvmem_pfn | flag; > + else > + p->pfns[index] = flag; > return; > } > } > } > > -static void kvmppc_uvmem_pfn_remove(unsigned long gfn, struct kvm *kvm) > +/* mark the GFN as secure-GFN associated with @uvmem pfn device-PFN. */ > +static void kvmppc_gfn_secure_uvmem_pfn(unsigned long gfn, > + unsigned long uvmem_pfn, struct kvm *kvm) > { > - struct kvmppc_uvmem_slot *p; > + kvmppc_mark_gfn(gfn, kvm, KVMPPC_GFN_UVMEM_PFN, uvmem_pfn); > +} > > - list_for_each_entry(p, &kvm->arch.uvmem_pfns, list) { > - if (gfn >= p->base_pfn && gfn < p->base_pfn + p->nr_pfns) { > - p->pfns[gfn - p->base_pfn] = 0; > - return; > - } > - } > +/* mark the GFN as secure-GFN associated with a memory-PFN. */ > +static void kvmppc_gfn_secure_mem_pfn(unsigned long gfn, struct kvm *kvm) > +{ > + kvmppc_mark_gfn(gfn, kvm, KVMPPC_GFN_MEM_PFN, 0); > +} > + > +/* mark the GFN as a shared GFN. */ > +static void kvmppc_gfn_shared(unsigned long gfn, struct kvm *kvm) > +{ > + kvmppc_mark_gfn(gfn, kvm, KVMPPC_GFN_SHARED, 0); > +} > + > +/* mark the GFN as a non-existent GFN. */ > +static void kvmppc_gfn_remove(unsigned long gfn, struct kvm *kvm) > +{ > + kvmppc_mark_gfn(gfn, kvm, 0, 0); > } > > +/* return true, if the GFN is a secure-GFN backed by a secure-PFN */ > static bool kvmppc_gfn_is_uvmem_pfn(unsigned long gfn, struct kvm *kvm, > unsigned long *uvmem_pfn) > { > @@ -199,10 +336,10 @@ static bool kvmppc_gfn_is_uvmem_pfn(unsigned long gfn, > struct kvm *kvm, > if (gfn >= p->base_pfn && gfn < p->base_pfn + p->nr_pfns) { > unsigned long index = gfn - p->base_pfn; > > - if (p->pfns[index] & KVMPPC_UVMEM_PFN) { > + if (p->pfns[index] & KVMPPC_GFN_UVMEM_PFN) { > if (uvmem_pfn) > *uvmem_pfn = p->pfns[index] & > - ~KVMPPC_UVMEM_PFN; > + KVMPPC_GFN_PFN_MASK; > return true; > } else > return false; > @@ -353,6 +490,7 @@ void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot > *free, > > mutex_lock(&kvm->arch.uvmem_lock); > if (!kvmppc_gfn_is_uvmem_pfn(gfn, kvm, &uvmem_pfn)) { > + kvmppc_gfn_remove(gfn, kvm); > mutex_unlock(&kvm->arch.uvmem_lock); > continue; > } > @@ -360,6 +498,7 @@ void kvmppc_uvmem_drop_pages(const struct kvm_memory_slot > *free, > uvmem_page = pfn_to_page(uvmem_pfn); > pvt = uvmem_page->zone_device_data; > pvt->skip_page_out = skip_page_out; > + pvt->remove_gfn = true; > mutex_unlock(&kvm->arch.uvmem_lock); > > pfn = gfn_to_pfn(kvm, gfn); > @@ -429,7 +568,7 @@ static struct page *kvmppc_uvmem_get_page(unsigned long > gpa, struct kvm *kvm) > goto out_clear; > > uvmem_pfn = bit + pfn_first; > - kvmppc_uvmem_pfn_insert(gpa >> PAGE_SHIFT, uvmem_pfn, kvm); > + kvmppc_gfn_secure_uvmem_pfn(gpa >> PAGE_SHIFT, uvmem_pfn, kvm); > > pvt->gpa = gpa; > pvt->kvm = kvm; > @@ -524,6 +663,7 @@ static unsigned long kvmppc_share_page(struct kvm *kvm, > unsigned long gpa, > uvmem_page = pfn_to_page(uvmem_pfn); > pvt = uvmem_page->zone_device_data; > pvt->skip_page_out = true; > + pvt->remove_gfn = false; > } > > retry: > @@ -537,12 +677,16 @@ static unsigned long kvmppc_share_page(struct kvm *kvm, > unsigned long gpa, > uvmem_page = pfn_to_page(uvmem_pfn); > pvt = uvmem_page->zone_device_data; > pvt->skip_page_out = true; > + pvt->remove_gfn = false;
This is the case of making an already secure page as shared page. A comment here as to why remove_gfn is set to false here will help. Also isn't it by default false? Is there a situation where it starts out by default false, becomes true later and you are required to explicitly mark it false here? Otherwise, Reviewed-by: Bharata B Rao <bhar...@linux.ibm.com> Regards, Bharata.