diff options
author | Christian Borntraeger <borntraeger@de.ibm.com> | 2013-06-12 07:54:52 -0400 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2013-06-17 11:04:30 -0400 |
commit | db70ccdfb9953b984f5b95d98c50d8da335bab59 (patch) | |
tree | 0fcf0e279caf6c7b9652a7e6bc824fe926b893dd /arch/s390/mm | |
parent | 8915aa27d5efbb9185357175b0acf884325565f9 (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.c | 48 |
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 | ||
774 | int 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 | } | ||
820 | EXPORT_SYMBOL(set_guest_storage_key); | ||
821 | |||
774 | #else /* CONFIG_PGSTE */ | 822 | #else /* CONFIG_PGSTE */ |
775 | 823 | ||
776 | static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, | 824 | static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm, |