s390/kvm: 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: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
25b41a7b67
commit
24d5dd0208
2 changed files with 51 additions and 0 deletions
|
@ -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(struct mm_struct *, unsigned long *);
|
||||||
void page_table_free_rcu(struct mmu_gather *, 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)
|
static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
|
||||||
{
|
{
|
||||||
typedef struct { char _[n]; } addrtype;
|
typedef struct { char _[n]; } addrtype;
|
||||||
|
|
|
@ -771,6 +771,54 @@ static inline void page_table_free_pgste(unsigned long *table)
|
||||||
__free_page(page);
|
__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 */
|
#else /* CONFIG_PGSTE */
|
||||||
|
|
||||||
static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
|
static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,
|
||||||
|
|
Loading…
Add table
Reference in a new issue