diff options
Diffstat (limited to 'arch/s390/mm/pgtable.c')
| -rw-r--r-- | arch/s390/mm/pgtable.c | 109 | 
1 files changed, 77 insertions, 32 deletions
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c index 034721a68d8f..c16232cd0ec5 100644 --- a/arch/s390/mm/pgtable.c +++ b/arch/s390/mm/pgtable.c @@ -429,22 +429,36 @@ static inline pmd_t pmdp_flush_lazy(struct mm_struct *mm,  }  #ifdef CONFIG_PGSTE -static pmd_t *pmd_alloc_map(struct mm_struct *mm, unsigned long addr) +static int pmd_lookup(struct mm_struct *mm, unsigned long addr, pmd_t **pmdp)  { +	struct vm_area_struct *vma;  	pgd_t *pgd;  	p4d_t *p4d;  	pud_t *pud; -	pmd_t *pmd; + +	/* We need a valid VMA, otherwise this is clearly a fault. */ +	vma = vma_lookup(mm, addr); +	if (!vma) +		return -EFAULT;  	pgd = pgd_offset(mm, addr); -	p4d = p4d_alloc(mm, pgd, addr); -	if (!p4d) -		return NULL; -	pud = pud_alloc(mm, p4d, addr); -	if (!pud) -		return NULL; -	pmd = pmd_alloc(mm, pud, addr); -	return pmd; +	if (!pgd_present(*pgd)) +		return -ENOENT; + +	p4d = p4d_offset(pgd, addr); +	if (!p4d_present(*p4d)) +		return -ENOENT; + +	pud = pud_offset(p4d, addr); +	if (!pud_present(*pud)) +		return -ENOENT; + +	/* Large PUDs are not supported yet. */ +	if (pud_large(*pud)) +		return -EFAULT; + +	*pmdp = pmd_offset(pud, addr); +	return 0;  }  #endif @@ -778,14 +792,23 @@ int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,  	pmd_t *pmdp;  	pte_t *ptep; -	pmdp = pmd_alloc_map(mm, addr); -	if (unlikely(!pmdp)) +	/* +	 * If we don't have a PTE table and if there is no huge page mapped, +	 * we can ignore attempts to set the key to 0, because it already is 0. +	 */ +	switch (pmd_lookup(mm, addr, &pmdp)) { +	case -ENOENT: +		return key ? -EFAULT : 0; +	case 0: +		break; +	default:  		return -EFAULT; +	}  	ptl = pmd_lock(mm, pmdp);  	if (!pmd_present(*pmdp)) {  		spin_unlock(ptl); -		return -EFAULT; +		return key ? -EFAULT : 0;  	}  	if (pmd_large(*pmdp)) { @@ -801,10 +824,7 @@ int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,  	}  	spin_unlock(ptl); -	ptep = pte_alloc_map_lock(mm, pmdp, addr, &ptl); -	if (unlikely(!ptep)) -		return -EFAULT; - +	ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl);  	new = old = pgste_get_lock(ptep);  	pgste_val(new) &= ~(PGSTE_GR_BIT | PGSTE_GC_BIT |  			    PGSTE_ACC_BITS | PGSTE_FP_BIT); @@ -881,14 +901,23 @@ int reset_guest_reference_bit(struct mm_struct *mm, unsigned long addr)  	pte_t *ptep;  	int cc = 0; -	pmdp = pmd_alloc_map(mm, addr); -	if (unlikely(!pmdp)) +	/* +	 * If we don't have a PTE table and if there is no huge page mapped, +	 * the storage key is 0 and there is nothing for us to do. +	 */ +	switch (pmd_lookup(mm, addr, &pmdp)) { +	case -ENOENT: +		return 0; +	case 0: +		break; +	default:  		return -EFAULT; +	}  	ptl = pmd_lock(mm, pmdp);  	if (!pmd_present(*pmdp)) {  		spin_unlock(ptl); -		return -EFAULT; +		return 0;  	}  	if (pmd_large(*pmdp)) { @@ -900,10 +929,7 @@ int reset_guest_reference_bit(struct mm_struct *mm, unsigned long addr)  	}  	spin_unlock(ptl); -	ptep = pte_alloc_map_lock(mm, pmdp, addr, &ptl); -	if (unlikely(!ptep)) -		return -EFAULT; - +	ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl);  	new = old = pgste_get_lock(ptep);  	/* Reset guest reference bit only */  	pgste_val(new) &= ~PGSTE_GR_BIT; @@ -935,15 +961,24 @@ int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,  	pmd_t *pmdp;  	pte_t *ptep; -	pmdp = pmd_alloc_map(mm, addr); -	if (unlikely(!pmdp)) +	/* +	 * If we don't have a PTE table and if there is no huge page mapped, +	 * the storage key is 0. +	 */ +	*key = 0; + +	switch (pmd_lookup(mm, addr, &pmdp)) { +	case -ENOENT: +		return 0; +	case 0: +		break; +	default:  		return -EFAULT; +	}  	ptl = pmd_lock(mm, pmdp);  	if (!pmd_present(*pmdp)) { -		/* Not yet mapped memory has a zero key */  		spin_unlock(ptl); -		*key = 0;  		return 0;  	} @@ -956,10 +991,7 @@ int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,  	}  	spin_unlock(ptl); -	ptep = pte_alloc_map_lock(mm, pmdp, addr, &ptl); -	if (unlikely(!ptep)) -		return -EFAULT; - +	ptep = pte_offset_map_lock(mm, pmdp, addr, &ptl);  	pgste = pgste_get_lock(ptep);  	*key = (pgste_val(pgste) & (PGSTE_ACC_BITS | PGSTE_FP_BIT)) >> 56;  	paddr = pte_val(*ptep) & PAGE_MASK; @@ -988,6 +1020,7 @@ EXPORT_SYMBOL(get_guest_storage_key);  int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc,  			unsigned long *oldpte, unsigned long *oldpgste)  { +	struct vm_area_struct *vma;  	unsigned long pgstev;  	spinlock_t *ptl;  	pgste_t pgste; @@ -997,6 +1030,10 @@ int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc,  	WARN_ON_ONCE(orc > ESSA_MAX);  	if (unlikely(orc > ESSA_MAX))  		return -EINVAL; + +	vma = vma_lookup(mm, hva); +	if (!vma || is_vm_hugetlb_page(vma)) +		return -EFAULT;  	ptep = get_locked_pte(mm, hva, &ptl);  	if (unlikely(!ptep))  		return -EFAULT; @@ -1089,10 +1126,14 @@ EXPORT_SYMBOL(pgste_perform_essa);  int set_pgste_bits(struct mm_struct *mm, unsigned long hva,  			unsigned long bits, unsigned long value)  { +	struct vm_area_struct *vma;  	spinlock_t *ptl;  	pgste_t new;  	pte_t *ptep; +	vma = vma_lookup(mm, hva); +	if (!vma || is_vm_hugetlb_page(vma)) +		return -EFAULT;  	ptep = get_locked_pte(mm, hva, &ptl);  	if (unlikely(!ptep))  		return -EFAULT; @@ -1117,9 +1158,13 @@ EXPORT_SYMBOL(set_pgste_bits);   */  int get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep)  { +	struct vm_area_struct *vma;  	spinlock_t *ptl;  	pte_t *ptep; +	vma = vma_lookup(mm, hva); +	if (!vma || is_vm_hugetlb_page(vma)) +		return -EFAULT;  	ptep = get_locked_pte(mm, hva, &ptl);  	if (unlikely(!ptep))  		return -EFAULT;  |