]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - arch/s390/include/asm/pgtable.h
s390/kvm: Kick guests out of sie if prefix page host pte is touched
[karo-tx-linux.git] / arch / s390 / include / asm / pgtable.h
index b4622915bd1588fc34a3e89af3885b48df7e9d8d..1d0ad7d2d29a433e6a1de4981ca5a2e6279b57a8 100644 (file)
@@ -299,17 +299,16 @@ extern unsigned long MODULES_END;
 #define _SEGMENT_ENTRY_EMPTY   (_SEGMENT_ENTRY_INV)
 
 /* Page status table bits for virtualization */
-#define RCP_ACC_BITS   0xf0000000UL
-#define RCP_FP_BIT     0x08000000UL
-#define RCP_PCL_BIT    0x00800000UL
-#define RCP_HR_BIT     0x00400000UL
-#define RCP_HC_BIT     0x00200000UL
-#define RCP_GR_BIT     0x00040000UL
-#define RCP_GC_BIT     0x00020000UL
-
-/* User dirty / referenced bit for KVM's migration feature */
-#define KVM_UR_BIT     0x00008000UL
-#define KVM_UC_BIT     0x00004000UL
+#define PGSTE_ACC_BITS 0xf0000000UL
+#define PGSTE_FP_BIT   0x08000000UL
+#define PGSTE_PCL_BIT  0x00800000UL
+#define PGSTE_HR_BIT   0x00400000UL
+#define PGSTE_HC_BIT   0x00200000UL
+#define PGSTE_GR_BIT   0x00040000UL
+#define PGSTE_GC_BIT   0x00020000UL
+#define PGSTE_UR_BIT   0x00008000UL
+#define PGSTE_UC_BIT   0x00004000UL    /* user dirty (migration) */
+#define PGSTE_IN_BIT   0x00002000UL    /* IPTE notify bit */
 
 #else /* CONFIG_64BIT */
 
@@ -366,17 +365,16 @@ extern unsigned long MODULES_END;
                                 | _SEGMENT_ENTRY_SPLIT | _SEGMENT_ENTRY_CO)
 
 /* Page status table bits for virtualization */
-#define RCP_ACC_BITS   0xf000000000000000UL
-#define RCP_FP_BIT     0x0800000000000000UL
-#define RCP_PCL_BIT    0x0080000000000000UL
-#define RCP_HR_BIT     0x0040000000000000UL
-#define RCP_HC_BIT     0x0020000000000000UL
-#define RCP_GR_BIT     0x0004000000000000UL
-#define RCP_GC_BIT     0x0002000000000000UL
-
-/* User dirty / referenced bit for KVM's migration feature */
-#define KVM_UR_BIT     0x0000800000000000UL
-#define KVM_UC_BIT     0x0000400000000000UL
+#define PGSTE_ACC_BITS 0xf000000000000000UL
+#define PGSTE_FP_BIT   0x0800000000000000UL
+#define PGSTE_PCL_BIT  0x0080000000000000UL
+#define PGSTE_HR_BIT   0x0040000000000000UL
+#define PGSTE_HC_BIT   0x0020000000000000UL
+#define PGSTE_GR_BIT   0x0004000000000000UL
+#define PGSTE_GC_BIT   0x0002000000000000UL
+#define PGSTE_UR_BIT   0x0000800000000000UL
+#define PGSTE_UC_BIT   0x0000400000000000UL    /* user dirty (migration) */
+#define PGSTE_IN_BIT   0x0000200000000000UL    /* IPTE notify bit */
 
 #endif /* CONFIG_64BIT */
 
@@ -616,8 +614,8 @@ static inline pgste_t pgste_get_lock(pte_t *ptep)
        asm(
                "       lg      %0,%2\n"
                "0:     lgr     %1,%0\n"
-               "       nihh    %0,0xff7f\n"    /* clear RCP_PCL_BIT in old */
-               "       oihh    %1,0x0080\n"    /* set RCP_PCL_BIT in new */
+               "       nihh    %0,0xff7f\n"    /* clear PCL bit in old */
+               "       oihh    %1,0x0080\n"    /* set PCL bit in new */
                "       csg     %0,%1,%2\n"
                "       jl      0b\n"
                : "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE])
@@ -630,7 +628,7 @@ static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
 {
 #ifdef CONFIG_PGSTE
        asm(
-               "       nihh    %1,0xff7f\n"    /* clear RCP_PCL_BIT */
+               "       nihh    %1,0xff7f\n"    /* clear PCL bit */
                "       stg     %1,%0\n"
                : "=Q" (ptep[PTRS_PER_PTE])
                : "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE]) : "cc");
@@ -655,14 +653,14 @@ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste)
        else if (bits)
                page_reset_referenced(address);
        /* Transfer page changed & referenced bit to guest bits in pgste */
-       pgste_val(pgste) |= bits << 48;         /* RCP_GR_BIT & RCP_GC_BIT */
+       pgste_val(pgste) |= bits << 48;         /* GR bit & GC bit */
        /* Get host changed & referenced bits from pgste */
-       bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52;
+       bits |= (pgste_val(pgste) & (PGSTE_HR_BIT | PGSTE_HC_BIT)) >> 52;
        /* Transfer page changed & referenced bit to kvm user bits */
-       pgste_val(pgste) |= bits << 45;         /* KVM_UR_BIT & KVM_UC_BIT */
+       pgste_val(pgste) |= bits << 45;         /* PGSTE_UR_BIT & PGSTE_UC_BIT */
        /* Clear relevant host bits in pgste. */
-       pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT);
-       pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT);
+       pgste_val(pgste) &= ~(PGSTE_HR_BIT | PGSTE_HC_BIT);
+       pgste_val(pgste) &= ~(PGSTE_ACC_BITS | PGSTE_FP_BIT);
        /* Copy page access key and fetch protection bit to pgste */
        pgste_val(pgste) |=
                (unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
@@ -683,15 +681,15 @@ static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste)
        /* Get referenced bit from storage key */
        young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK);
        if (young)
-               pgste_val(pgste) |= RCP_GR_BIT;
+               pgste_val(pgste) |= PGSTE_GR_BIT;
        /* Get host referenced bit from pgste */
-       if (pgste_val(pgste) & RCP_HR_BIT) {
-               pgste_val(pgste) &= ~RCP_HR_BIT;
+       if (pgste_val(pgste) & PGSTE_HR_BIT) {
+               pgste_val(pgste) &= ~PGSTE_HR_BIT;
                young = 1;
        }
        /* Transfer referenced bit to kvm user bits and pte */
        if (young) {
-               pgste_val(pgste) |= KVM_UR_BIT;
+               pgste_val(pgste) |= PGSTE_UR_BIT;
                pte_val(*ptep) |= _PAGE_SWR;
        }
 #endif
@@ -710,7 +708,7 @@ static inline void pgste_set_key(pte_t *ptep, pgste_t pgste, pte_t entry)
        okey = nkey = page_get_storage_key(address);
        nkey &= ~(_PAGE_ACC_BITS | _PAGE_FP_BIT);
        /* Set page access key and fetch protection bit from pgste */
-       nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56;
+       nkey |= (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56;
        if (okey != nkey)
                page_set_storage_key(address, nkey, 0);
 #endif
@@ -741,35 +739,48 @@ struct gmap {
        struct mm_struct *mm;
        unsigned long *table;
        unsigned long asce;
+       void *private;
        struct list_head crst_list;
 };
 
 /**
  * struct gmap_rmap - reverse mapping for segment table entries
- * @next: pointer to the next gmap_rmap structure in the list
+ * @gmap: pointer to the gmap_struct
  * @entry: pointer to a segment table entry
+ * @vmaddr: virtual address in the guest address space
  */
 struct gmap_rmap {
        struct list_head list;
+       struct gmap *gmap;
        unsigned long *entry;
+       unsigned long vmaddr;
 };
 
 /**
  * struct gmap_pgtable - gmap information attached to a page table
  * @vmaddr: address of the 1MB segment in the process virtual memory
- * @mapper: list of segment table entries maping a page table
+ * @mapper: list of segment table entries mapping a page table
  */
 struct gmap_pgtable {
        unsigned long vmaddr;
        struct list_head mapper;
 };
 
+/**
+ * struct gmap_notifier - notify function block for page invalidation
+ * @notifier_call: address of callback function
+ */
+struct gmap_notifier {
+       struct list_head list;
+       void (*notifier_call)(struct gmap *gmap, unsigned long address);
+};
+
 struct gmap *gmap_alloc(struct mm_struct *mm);
 void gmap_free(struct gmap *gmap);
 void gmap_enable(struct gmap *gmap);
 void gmap_disable(struct gmap *gmap);
 int gmap_map_segment(struct gmap *gmap, unsigned long from,
-                    unsigned long to, unsigned long length);
+                    unsigned long to, unsigned long len);
 int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len);
 unsigned long __gmap_translate(unsigned long address, struct gmap *);
 unsigned long gmap_translate(unsigned long address, struct gmap *);
@@ -777,6 +788,24 @@ unsigned long __gmap_fault(unsigned long address, struct gmap *);
 unsigned long gmap_fault(unsigned long address, struct gmap *);
 void gmap_discard(unsigned long from, unsigned long to, struct gmap *);
 
+void gmap_register_ipte_notifier(struct gmap_notifier *);
+void gmap_unregister_ipte_notifier(struct gmap_notifier *);
+int gmap_ipte_notify(struct gmap *, unsigned long start, unsigned long len);
+void gmap_do_ipte_notify(struct mm_struct *, unsigned long addr, pte_t *);
+
+static inline pgste_t pgste_ipte_notify(struct mm_struct *mm,
+                                       unsigned long addr,
+                                       pte_t *ptep, pgste_t pgste)
+{
+#ifdef CONFIG_PGSTE
+       if (pgste_val(pgste) & PGSTE_IN_BIT) {
+               pgste_val(pgste) &= ~PGSTE_IN_BIT;
+               gmap_do_ipte_notify(mm, addr, ptep);
+       }
+#endif
+       return pgste;
+}
+
 /*
  * Certain architectures need to do special things when PTEs
  * within a page table are directly modified.  Thus, the following
@@ -938,8 +967,8 @@ static inline int ptep_test_and_clear_user_dirty(struct mm_struct *mm,
        if (mm_has_pgste(mm)) {
                pgste = pgste_get_lock(ptep);
                pgste = pgste_update_all(ptep, pgste);
-               dirty = !!(pgste_val(pgste) & KVM_UC_BIT);
-               pgste_val(pgste) &= ~KVM_UC_BIT;
+               dirty = !!(pgste_val(pgste) & PGSTE_UC_BIT);
+               pgste_val(pgste) &= ~PGSTE_UC_BIT;
                pgste_set_unlock(ptep, pgste);
                return dirty;
        }
@@ -958,8 +987,8 @@ static inline int ptep_test_and_clear_user_young(struct mm_struct *mm,
        if (mm_has_pgste(mm)) {
                pgste = pgste_get_lock(ptep);
                pgste = pgste_update_young(ptep, pgste);
-               young = !!(pgste_val(pgste) & KVM_UR_BIT);
-               pgste_val(pgste) &= ~KVM_UR_BIT;
+               young = !!(pgste_val(pgste) & PGSTE_UR_BIT);
+               pgste_val(pgste) &= ~PGSTE_UR_BIT;
                pgste_set_unlock(ptep, pgste);
        }
        return young;
@@ -1032,8 +1061,10 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
        pte_t pte;
 
        mm->context.flush_mm = 1;
-       if (mm_has_pgste(mm))
+       if (mm_has_pgste(mm)) {
                pgste = pgste_get_lock(ptep);
+               pgste = pgste_ipte_notify(mm, address, ptep, pgste);
+       }
 
        pte = *ptep;
        if (!mm_exclusive(mm))
@@ -1052,11 +1083,14 @@ static inline pte_t ptep_modify_prot_start(struct mm_struct *mm,
                                           unsigned long address,
                                           pte_t *ptep)
 {
+       pgste_t pgste;
        pte_t pte;
 
        mm->context.flush_mm = 1;
-       if (mm_has_pgste(mm))
-               pgste_get_lock(ptep);
+       if (mm_has_pgste(mm)) {
+               pgste = pgste_get_lock(ptep);
+               pgste_ipte_notify(mm, address, ptep, pgste);
+       }
 
        pte = *ptep;
        if (!mm_exclusive(mm))
@@ -1082,8 +1116,10 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
        pgste_t pgste;
        pte_t pte;
 
-       if (mm_has_pgste(vma->vm_mm))
+       if (mm_has_pgste(vma->vm_mm)) {
                pgste = pgste_get_lock(ptep);
+               pgste = pgste_ipte_notify(vma->vm_mm, address, ptep, pgste);
+       }
 
        pte = *ptep;
        __ptep_ipte(address, ptep);
@@ -1111,8 +1147,11 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
        pgste_t pgste;
        pte_t pte;
 
-       if (mm_has_pgste(mm))
+       if (mm_has_pgste(mm)) {
                pgste = pgste_get_lock(ptep);
+               if (!full)
+                       pgste = pgste_ipte_notify(mm, address, ptep, pgste);
+       }
 
        pte = *ptep;
        if (!full)
@@ -1135,8 +1174,10 @@ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm,
 
        if (pte_write(pte)) {
                mm->context.flush_mm = 1;
-               if (mm_has_pgste(mm))
+               if (mm_has_pgste(mm)) {
                        pgste = pgste_get_lock(ptep);
+                       pgste = pgste_ipte_notify(mm, address, ptep, pgste);
+               }
 
                if (!mm_exclusive(mm))
                        __ptep_ipte(address, ptep);
@@ -1160,8 +1201,10 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma,
 
        if (pte_same(*ptep, entry))
                return 0;
-       if (mm_has_pgste(vma->vm_mm))
+       if (mm_has_pgste(vma->vm_mm)) {
                pgste = pgste_get_lock(ptep);
+               pgste = pgste_ipte_notify(vma->vm_mm, address, ptep, pgste);
+       }
 
        __ptep_ipte(address, ptep);