]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
s390/mm: shadow pages with real guest requested protection
authorDavid Hildenbrand <dahi@linux.vnet.ibm.com>
Tue, 8 Mar 2016 11:21:41 +0000 (12:21 +0100)
committerChristian Borntraeger <borntraeger@de.ibm.com>
Mon, 20 Jun 2016 07:54:19 +0000 (09:54 +0200)
We really want to avoid manually handling protection for nested
virtualization. By shadowing pages with the protection the guest asked us
for, the SIE can handle most protection-related actions for us (e.g.
special handling for MVPG) and we can directly forward protection
exceptions to the guest.

PTEs will now always be shadowed with the correct _PAGE_PROTECT flag.
Unshadowing will take care of any guest changes to the parent PTE and
any host changes to the host PTE. If the host PTE doesn't have the
fitting access rights or is not available, we have to fix it up.

Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
arch/s390/include/asm/gmap.h
arch/s390/include/asm/pgtable.h
arch/s390/kvm/gaccess.c
arch/s390/mm/gmap.c
arch/s390/mm/pgtable.c

index 58e65ee5b2d25e2938258518bec3583816762249..4a47055f58d76692cd394338c1b7066250d09708 100644 (file)
@@ -110,8 +110,7 @@ int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt);
 int gmap_shadow_pgt(struct gmap *sg, unsigned long saddr, unsigned long pgt);
 int gmap_shadow_pgt_lookup(struct gmap *sg, unsigned long saddr,
                           unsigned long *pgt, int *dat_protection);
-int gmap_shadow_page(struct gmap *sg, unsigned long saddr,
-                    unsigned long paddr, int write);
+int gmap_shadow_page(struct gmap *sg, unsigned long saddr, pte_t pte);
 
 void gmap_register_pte_notifier(struct gmap_notifier *);
 void gmap_unregister_pte_notifier(struct gmap_notifier *);
index a6e7fc8f5b495cb40b1321674513291b19f67272..c7ebba483f097f07aa0c03a5a678ac5390ead75b 100644 (file)
@@ -895,7 +895,7 @@ void ptep_zap_unused(struct mm_struct *mm, unsigned long addr,
                     pte_t *ptep , int reset);
 void ptep_zap_key(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
 int ptep_shadow_pte(struct mm_struct *mm, unsigned long saddr,
-                   pte_t *sptep, pte_t *tptep, int write);
+                   pte_t *sptep, pte_t *tptep, pte_t pte);
 void ptep_unshadow_pte(struct mm_struct *mm, unsigned long saddr, pte_t *ptep);
 
 bool test_and_clear_guest_dirty(struct mm_struct *mm, unsigned long address);
index ba4985262bced39d806f8a343c00454cd5a82e97..c5f79c1205cf8fa10232e80bd15ad0ecbeea9bb0 100644 (file)
@@ -1109,7 +1109,7 @@ int kvm_s390_shadow_fault(struct gmap *sg, unsigned long saddr, int write)
        dat_protection |= pte.p;
        if (write && dat_protection)
                return PGM_PROTECTION;
-       rc = gmap_shadow_page(sg, saddr, pte.pfra * 4096, write);
+       rc = gmap_shadow_page(sg, saddr, __pte(pte.val));
        if (rc)
                return rc;
        return 0;
index b02d0d0cc641367f44eb03f9a1d1830666bfe878..a57a87bfeb27842cb2fcb0e17a7e2c550de77289 100644 (file)
@@ -1743,8 +1743,7 @@ EXPORT_SYMBOL_GPL(gmap_shadow_pgt);
  * gmap_shadow_page - create a shadow page mapping
  * @sg: pointer to the shadow guest address space structure
  * @saddr: faulting address in the shadow gmap
- * @paddr: parent gmap address to get mapped at @saddr
- * @write: =1 map r/w, =0 map r/o
+ * @pte: pte in parent gmap address space to get shadowed
  *
  * Returns 0 if successfully shadowed or already shadowed, -EAGAIN if the
  * shadow table structure is incomplete, -ENOMEM if out of memory and
@@ -1752,12 +1751,11 @@ EXPORT_SYMBOL_GPL(gmap_shadow_pgt);
  *
  * Called with sg->mm->mmap_sem in read.
  */
-int gmap_shadow_page(struct gmap *sg, unsigned long saddr,
-                    unsigned long paddr, int write)
+int gmap_shadow_page(struct gmap *sg, unsigned long saddr, pte_t pte)
 {
        struct gmap *parent;
        struct gmap_rmap *rmap;
-       unsigned long vmaddr;
+       unsigned long vmaddr, paddr;
        spinlock_t *ptl;
        pte_t *sptep, *tptep;
        int rc;
@@ -1771,6 +1769,7 @@ int gmap_shadow_page(struct gmap *sg, unsigned long saddr,
        rmap->raddr = (saddr & PAGE_MASK) | _SHADOW_RMAP_PGTABLE;
 
        while (1) {
+               paddr = pte_val(pte) & PAGE_MASK;
                vmaddr = __gmap_translate(parent, paddr);
                if (IS_ERR_VALUE(vmaddr)) {
                        rc = vmaddr;
@@ -1791,8 +1790,7 @@ int gmap_shadow_page(struct gmap *sg, unsigned long saddr,
                                radix_tree_preload_end();
                                break;
                        }
-                       rc = ptep_shadow_pte(sg->mm, saddr,
-                                            sptep, tptep, write);
+                       rc = ptep_shadow_pte(sg->mm, saddr, sptep, tptep, pte);
                        if (rc > 0) {
                                /* Success and a new mapping */
                                gmap_insert_rmap(sg, vmaddr, rmap);
index 5b02583fbf4cbc2b557c68082711e818013036b2..293130b5aee763342f209ae4be2121328f841836 100644 (file)
@@ -463,29 +463,27 @@ int ptep_force_prot(struct mm_struct *mm, unsigned long addr,
 }
 
 int ptep_shadow_pte(struct mm_struct *mm, unsigned long saddr,
-                   pte_t *sptep, pte_t *tptep, int write)
+                   pte_t *sptep, pte_t *tptep, pte_t pte)
 {
        pgste_t spgste, tpgste;
        pte_t spte, tpte;
        int rc = -EAGAIN;
 
+       if (!(pte_val(*tptep) & _PAGE_INVALID))
+               return 0;       /* already shadowed */
        spgste = pgste_get_lock(sptep);
        spte = *sptep;
        if (!(pte_val(spte) & _PAGE_INVALID) &&
-           !(pte_val(spte) & _PAGE_PROTECT)) {
-               rc = 0;
-               if (!(pte_val(*tptep) & _PAGE_INVALID))
-                       /* Update existing mapping */
-                       ptep_flush_direct(mm, saddr, tptep);
-               else
-                       rc = 1;
+           !((pte_val(spte) & _PAGE_PROTECT) &&
+             !(pte_val(pte) & _PAGE_PROTECT))) {
                pgste_val(spgste) |= PGSTE_VSIE_BIT;
                tpgste = pgste_get_lock(tptep);
                pte_val(tpte) = (pte_val(spte) & PAGE_MASK) |
-                       (write ? 0 : _PAGE_PROTECT);
+                               (pte_val(pte) & _PAGE_PROTECT);
                /* don't touch the storage key - it belongs to parent pgste */
                tpgste = pgste_set_pte(tptep, tpgste, tpte);
                pgste_set_unlock(tptep, tpgste);
+               rc = 1;
        }
        pgste_set_unlock(sptep, spgste);
        return rc;