From: Christian Borntraeger <[email protected]>

>From time to time we need to set the guest storage key. Lets
provide a helper function that handles the changes with all the
right locking and checking.

Signed-off-by: Christian Borntraeger <[email protected]>
Signed-off-by: Cornelia Huck <[email protected]>
---
 arch/s390/include/asm/pgalloc.h |  3 +++
 arch/s390/mm/pgtable.c          | 48 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/arch/s390/include/asm/pgalloc.h b/arch/s390/include/asm/pgalloc.h
index 590c321..e1408dd 100644
--- a/arch/s390/include/asm/pgalloc.h
+++ b/arch/s390/include/asm/pgalloc.h
@@ -22,6 +22,9 @@ unsigned long *page_table_alloc(struct mm_struct *, unsigned 
long);
 void page_table_free(struct mm_struct *, unsigned long *);
 void page_table_free_rcu(struct mmu_gather *, unsigned long *);
 
+int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
+                         unsigned long key, bool nq);
+
 static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
 {
        typedef struct { char _[n]; } addrtype;
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 1e0c438..44b1450 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -771,6 +771,54 @@ static inline void page_table_free_pgste(unsigned long 
*table)
        __free_page(page);
 }
 
+int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
+                         unsigned long key, bool nq)
+{
+       spinlock_t *ptl;
+       pgste_t old, new;
+       pte_t *ptep;
+
+       down_read(&mm->mmap_sem);
+       ptep = get_locked_pte(current->mm, addr, &ptl);
+       if (unlikely(!ptep)) {
+               up_read(&mm->mmap_sem);
+               return -EFAULT;
+       }
+
+       new = old = pgste_get_lock(ptep);
+       pgste_val(new) &= ~(PGSTE_GR_BIT | PGSTE_GC_BIT |
+                           PGSTE_ACC_BITS | PGSTE_FP_BIT);
+       pgste_val(new) |= (key & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48;
+       pgste_val(new) |= (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
+       if (!(pte_val(*ptep) & _PAGE_INVALID)) {
+               unsigned long address, bits;
+               unsigned char skey;
+
+               address = pte_val(*ptep) & PAGE_MASK;
+               skey = page_get_storage_key(address);
+               bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
+               /* Set storage key ACC and FP */
+               page_set_storage_key(address,
+                               (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)),
+                               !nq);
+
+               /* Merge host changed & referenced into pgste  */
+               pgste_val(new) |= bits << 52;
+               /* Transfer skey changed & referenced bit to kvm user bits */
+               pgste_val(new) |= bits << 45;   /* PGSTE_UR_BIT & PGSTE_UC_BIT 
*/
+       }
+       /* changing the guest storage key is considered a change of the page */
+       if ((pgste_val(new) ^ pgste_val(old)) &
+           (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT))
+               pgste_val(new) |= PGSTE_UC_BIT;
+
+       pgste_set_unlock(ptep, new);
+       pte_unmap_unlock(*ptep, ptl);
+       up_read(&mm->mmap_sem);
+       return 0;
+}
+EXPORT_SYMBOL(set_guest_storage_key);
+
 #else /* CONFIG_PGSTE */
 
 static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
-- 
1.8.1.6

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to