Commit 4660e168 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull arm64 fixes from Will Deacon:
 "The main changes are a fix to the way in which we manage the access
  flag setting for mappings using the contiguous bit and a fix for a
  hang on the kexec/hibernation path.

  Summary:

   - Fix kexec/hibernation hang due to bogus read-only mappings

   - Fix sparse warnings in our cmpxchg() implementation

   - Prevent runtime-const being used in modules, just like x86

   - Fix broken elision of access flag modifications for contiguous
     entries on systems without support for hardware updates

   - Fix a broken SVE selftest that was testing the wrong instruction"

* tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux:
  selftest/arm64: Fix sve2p1_sigill() to hwcap test
  arm64: contpte: fix set_access_flags() no-op check for SMMU/ATS faults
  arm64: make runtime const not usable by modules
  arm64: mm: Add PTE_DIRTY back to PAGE_KERNEL* to fix kexec/hibernation
  arm64: Silence sparse warnings caused by the type casting in (cmp)xchg
parents e0c505cb d87c828d
Loading
Loading
Loading
Loading
+7 −5
Original line number Diff line number Diff line
@@ -91,8 +91,9 @@ __XCHG_GEN(_mb)
#define __xchg_wrapper(sfx, ptr, x)					\
({									\
	__typeof__(*(ptr)) __ret;					\
	__ret = (__typeof__(*(ptr)))					\
		__arch_xchg##sfx((unsigned long)(x), (ptr), sizeof(*(ptr))); \
	__ret = (__force __typeof__(*(ptr)))				\
		__arch_xchg##sfx((__force unsigned long)(x), (ptr),	\
				  sizeof(*(ptr)));			\
	__ret;								\
})

@@ -175,9 +176,10 @@ __CMPXCHG_GEN(_mb)
#define __cmpxchg_wrapper(sfx, ptr, o, n)				\
({									\
	__typeof__(*(ptr)) __ret;					\
	__ret = (__typeof__(*(ptr)))					\
		__cmpxchg##sfx((ptr), (unsigned long)(o),		\
				(unsigned long)(n), sizeof(*(ptr)));	\
	__ret = (__force __typeof__(*(ptr)))				\
		__cmpxchg##sfx((ptr), (__force unsigned long)(o),	\
				(__force unsigned long)(n),		\
				sizeof(*(ptr)));			\
	__ret;								\
})

+5 −5
Original line number Diff line number Diff line
@@ -50,11 +50,11 @@

#define _PAGE_DEFAULT		(_PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL))

#define _PAGE_KERNEL		(PROT_NORMAL)
#define _PAGE_KERNEL_RO		((PROT_NORMAL & ~PTE_WRITE) | PTE_RDONLY)
#define _PAGE_KERNEL_ROX	((PROT_NORMAL & ~(PTE_WRITE | PTE_PXN)) | PTE_RDONLY)
#define _PAGE_KERNEL_EXEC	(PROT_NORMAL & ~PTE_PXN)
#define _PAGE_KERNEL_EXEC_CONT	((PROT_NORMAL & ~PTE_PXN) | PTE_CONT)
#define _PAGE_KERNEL		(PROT_NORMAL | PTE_DIRTY)
#define _PAGE_KERNEL_RO		((PROT_NORMAL & ~PTE_WRITE) | PTE_RDONLY | PTE_DIRTY)
#define _PAGE_KERNEL_ROX	((PROT_NORMAL & ~(PTE_WRITE | PTE_PXN)) | PTE_RDONLY | PTE_DIRTY)
#define _PAGE_KERNEL_EXEC	((PROT_NORMAL & ~PTE_PXN) | PTE_DIRTY)
#define _PAGE_KERNEL_EXEC_CONT	((PROT_NORMAL & ~PTE_PXN) | PTE_CONT | PTE_DIRTY)

#define _PAGE_SHARED		(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE)
#define _PAGE_SHARED_EXEC	(_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_WRITE)
+4 −0
Original line number Diff line number Diff line
@@ -2,6 +2,10 @@
#ifndef _ASM_RUNTIME_CONST_H
#define _ASM_RUNTIME_CONST_H

#ifdef MODULE
  #error "Cannot use runtime-const infrastructure from modules"
#endif

#include <asm/cacheflush.h>

/* Sigh. You can still run arm64 in BE mode */
+49 −4
Original line number Diff line number Diff line
@@ -599,6 +599,27 @@ void contpte_clear_young_dirty_ptes(struct vm_area_struct *vma,
}
EXPORT_SYMBOL_GPL(contpte_clear_young_dirty_ptes);

static bool contpte_all_subptes_match_access_flags(pte_t *ptep, pte_t entry)
{
	pte_t *cont_ptep = contpte_align_down(ptep);
	/*
	 * PFNs differ per sub-PTE. Match only bits consumed by
	 * __ptep_set_access_flags(): AF, DIRTY and write permission.
	 */
	const pteval_t cmp_mask = PTE_RDONLY | PTE_AF | PTE_WRITE | PTE_DIRTY;
	pteval_t entry_cmp = pte_val(entry) & cmp_mask;
	int i;

	for (i = 0; i < CONT_PTES; i++) {
		pteval_t pte_cmp = pte_val(__ptep_get(cont_ptep + i)) & cmp_mask;

		if (pte_cmp != entry_cmp)
			return false;
	}

	return true;
}

int contpte_ptep_set_access_flags(struct vm_area_struct *vma,
					unsigned long addr, pte_t *ptep,
					pte_t entry, int dirty)
@@ -608,13 +629,37 @@ int contpte_ptep_set_access_flags(struct vm_area_struct *vma,
	int i;

	/*
	 * Gather the access/dirty bits for the contiguous range. If nothing has
	 * changed, its a noop.
	 * Check whether all sub-PTEs in the CONT block already match the
	 * requested access flags/write permission, using raw per-PTE values
	 * rather than the gathered ptep_get() view.
	 *
	 * __ptep_set_access_flags() can update AF, dirty and write
	 * permission, but only to make the mapping more permissive.
	 *
	 * ptep_get() gathers AF/dirty state across the whole CONT block,
	 * which is correct for a CPU with FEAT_HAFDBS. But page-table
	 * walkers that evaluate each descriptor individually (e.g. a CPU
	 * without DBM support, or an SMMU without HTTU, or with HA/HD
	 * disabled in CD.TCR) can keep faulting on the target sub-PTE if
	 * only a sibling has been updated. Gathering can therefore cause
	 * false no-ops when only a sibling has been updated:
	 *  - write faults: target still has PTE_RDONLY (needs PTE_RDONLY cleared)
	 *  - read faults:  target still lacks PTE_AF
	 *
	 * Per Arm ARM (DDI 0487) D8.7.1, any sub-PTE in a CONT range may
	 * become the effective cached translation, so all entries must have
	 * consistent attributes. Check the full CONT block before returning
	 * no-op, and when any sub-PTE mismatches, proceed to update the whole
	 * range.
	 */
	orig_pte = pte_mknoncont(ptep_get(ptep));
	if (pte_val(orig_pte) == pte_val(entry))
	if (contpte_all_subptes_match_access_flags(ptep, entry))
		return 0;

	/*
	 * Use raw target pte (not gathered) for write-bit unfold decision.
	 */
	orig_pte = pte_mknoncont(__ptep_get(ptep));

	/*
	 * We can fix up access/dirty bits without having to unfold the contig
	 * range. But if the write bit is changing, we must unfold.
+2 −2
Original line number Diff line number Diff line
@@ -475,8 +475,8 @@ static void sve2_sigill(void)

static void sve2p1_sigill(void)
{
	/* BFADD Z0.H, Z0.H, Z0.H */
	asm volatile(".inst 0x65000000" : : : "z0");
	/* LD1Q {Z0.Q}, P0/Z, [Z0.D, X0] */
	asm volatile(".inst 0xC400A000" : : : "z0");
}

static void sve2p2_sigill(void)