aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm
diff options
context:
space:
mode:
authorChristian Borntraeger <borntraeger@de.ibm.com>2013-06-12 07:54:52 -0400
committerPaolo Bonzini <pbonzini@redhat.com>2013-06-17 11:04:30 -0400
commitdb70ccdfb9953b984f5b95d98c50d8da335bab59 (patch)
tree0fcf0e279caf6c7b9652a7e6bc824fe926b893dd /arch/s390/mm
parent8915aa27d5efbb9185357175b0acf884325565f9 (diff)
KVM: s390: Provide function for setting the guest storage key
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 <borntraeger@de.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'arch/s390/mm')
-rw-r--r--arch/s390/mm/pgtable.c48
1 files changed, 48 insertions, 0 deletions
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 1e0c438dbd65..44b145055f35 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)
771 __free_page(page); 771 __free_page(page);
772} 772}
773 773
774int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
775 unsigned long key, bool nq)
776{
777 spinlock_t *ptl;
778 pgste_t old, new;
779 pte_t *ptep;
780
781 down_read(&mm->mmap_sem);
782 ptep = get_locked_pte(current->mm, addr, &ptl);
783 if (unlikely(!ptep)) {
784 up_read(&mm->mmap_sem);
785 return -EFAULT;
786 }
787
788 new = old = pgste_get_lock(ptep);
789 pgste_val(new) &= ~(PGSTE_GR_BIT | PGSTE_GC_BIT |
790 PGSTE_ACC_BITS | PGSTE_FP_BIT);
791 pgste_val(new) |= (key & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48;
792 pgste_val(new) |= (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
793 if (!(pte_val(*ptep) & _PAGE_INVALID)) {
794 unsigned long address, bits;
795 unsigned char skey;
796
797 address = pte_val(*ptep) & PAGE_MASK;
798 skey = page_get_storage_key(address);
799 bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
800 /* Set storage key ACC and FP */
801 page_set_storage_key(address,
802 (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)),
803 !nq);
804
805 /* Merge host changed & referenced into pgste */
806 pgste_val(new) |= bits << 52;
807 /* Transfer skey changed & referenced bit to kvm user bits */
808 pgste_val(new) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */
809 }
810 /* changing the guest storage key is considered a change of the page */
811 if ((pgste_val(new) ^ pgste_val(old)) &
812 (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT))
813 pgste_val(new) |= PGSTE_UC_BIT;
814
815 pgste_set_unlock(ptep, new);
816 pte_unmap_unlock(*ptep, ptl);
817 up_read(&mm->mmap_sem);
818 return 0;
819}
820EXPORT_SYMBOL(set_guest_storage_key);
821
774#else /* CONFIG_PGSTE */ 822#else /* CONFIG_PGSTE */
775 823
776static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, 824static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,