Commit 4d349ee5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull arm64 fixes from Will Deacon:
 "The diffstat is dominated by changes to our TLB invalidation errata
  handling and the introduction of a new GCS selftest to catch one of
  the issues that is fixed here relating to PROT_NONE mappings.

   - Fix cpufreq warning due to attempting a cross-call with interrupts
     masked when reading local AMU counters

   - Fix DEBUG_PREEMPT warning from the delay loop when it tries to
     access per-cpu errata workaround state for the virtual counter

   - Re-jig and optimise our TLB invalidation errata workarounds in
     preparation for more hardware brokenness

   - Fix GCS mappings to interact properly with PROT_NONE and to avoid
     corrupting the pte on CPUs with FEAT_LPA2

   - Fix ioremap_prot() to extract only the memory attributes from the
     user pte and ignore all the other 'prot' bits"

* tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux:
  arm64: topology: Fix false warning in counters_read_on_cpu() for same-CPU reads
  arm64: Fix sampling the "stable" virtual counter in preemptible section
  arm64: tlb: Optimize ARM64_WORKAROUND_REPEAT_TLBI
  arm64: tlb: Allow XZR argument to TLBI ops
  kselftest: arm64: Check access to GCS after mprotect(PROT_NONE)
  arm64: gcs: Honour mprotect(PROT_NONE) on shadow stack mappings
  arm64: gcs: Do not set PTE_SHARED on GCS mappings if FEAT_LPA2 is enabled
  arm64: io: Extract user memory type in ioremap_prot()
  arm64: io: Rename ioremap_prot() to __ioremap_prot()
parents 1c63df24 df6e4ab6
Loading
Loading
Loading
Loading
+20 −6
Original line number Diff line number Diff line
@@ -264,19 +264,33 @@ __iowrite64_copy(void __iomem *to, const void *from, size_t count)
typedef int (*ioremap_prot_hook_t)(phys_addr_t phys_addr, size_t size,
				   pgprot_t *prot);
int arm64_ioremap_prot_hook_register(const ioremap_prot_hook_t hook);
void __iomem *__ioremap_prot(phys_addr_t phys, size_t size, pgprot_t prot);

#define ioremap_prot ioremap_prot
static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size,
					 pgprot_t user_prot)
{
	pgprot_t prot;
	ptdesc_t user_prot_val = pgprot_val(user_prot);

	if (WARN_ON_ONCE(!(user_prot_val & PTE_USER)))
		return NULL;

#define _PAGE_IOREMAP PROT_DEVICE_nGnRE
	prot = __pgprot_modify(PAGE_KERNEL, PTE_ATTRINDX_MASK,
			       user_prot_val & PTE_ATTRINDX_MASK);
	return __ioremap_prot(phys, size, prot);
}
#define ioremap_prot ioremap_prot

#define ioremap(addr, size)	\
	__ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRE))
#define ioremap_wc(addr, size)	\
	ioremap_prot((addr), (size), __pgprot(PROT_NORMAL_NC))
	__ioremap_prot((addr), (size), __pgprot(PROT_NORMAL_NC))
#define ioremap_np(addr, size)	\
	ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRnE))
	__ioremap_prot((addr), (size), __pgprot(PROT_DEVICE_nGnRnE))


#define ioremap_encrypted(addr, size)	\
	ioremap_prot((addr), (size), PAGE_KERNEL)
	__ioremap_prot((addr), (size), PAGE_KERNEL)

/*
 * io{read,write}{16,32,64}be() macros
@@ -297,7 +311,7 @@ static inline void __iomem *ioremap_cache(phys_addr_t addr, size_t size)
	if (pfn_is_map_memory(__phys_to_pfn(addr)))
		return (void __iomem *)__phys_to_virt(addr);

	return ioremap_prot(addr, size, __pgprot(PROT_NORMAL));
	return __ioremap_prot(addr, size, __pgprot(PROT_NORMAL));
}

/*
+0 −3
Original line number Diff line number Diff line
@@ -164,9 +164,6 @@ static inline bool __pure lpa2_is_enabled(void)
#define _PAGE_GCS	(_PAGE_DEFAULT | PTE_NG | PTE_UXN | PTE_WRITE | PTE_USER)
#define _PAGE_GCS_RO	(_PAGE_DEFAULT | PTE_NG | PTE_UXN | PTE_USER)

#define PAGE_GCS	__pgprot(_PAGE_GCS)
#define PAGE_GCS_RO	__pgprot(_PAGE_GCS_RO)

#define PIE_E0	( \
	PIRx_ELx_PERM_PREP(pte_pi_index(_PAGE_GCS),           PIE_GCS)  | \
	PIRx_ELx_PERM_PREP(pte_pi_index(_PAGE_GCS_RO),        PIE_R)   | \
+37 −26
Original line number Diff line number Diff line
@@ -31,19 +31,11 @@
 */
#define __TLBI_0(op, arg) asm (ARM64_ASM_PREAMBLE			       \
			       "tlbi " #op "\n"				       \
		   ALTERNATIVE("nop\n			nop",		       \
			       "dsb ish\n		tlbi " #op,	       \
			       ARM64_WORKAROUND_REPEAT_TLBI,		       \
			       CONFIG_ARM64_WORKAROUND_REPEAT_TLBI)	       \
			    : : )

#define __TLBI_1(op, arg) asm (ARM64_ASM_PREAMBLE			       \
			       "tlbi " #op ", %0\n"			       \
		   ALTERNATIVE("nop\n			nop",		       \
			       "dsb ish\n		tlbi " #op ", %0",     \
			       ARM64_WORKAROUND_REPEAT_TLBI,		       \
			       CONFIG_ARM64_WORKAROUND_REPEAT_TLBI)	       \
			    : : "r" (arg))
			       "tlbi " #op ", %x0\n"			       \
			    : : "rZ" (arg))

#define __TLBI_N(op, arg, n, ...) __TLBI_##n(op, arg)

@@ -181,6 +173,34 @@ static inline unsigned long get_trans_granule(void)
		(__pages >> (5 * (scale) + 1)) - 1;			\
	})

#define __repeat_tlbi_sync(op, arg...)						\
do {										\
	if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_REPEAT_TLBI))	\
		break;								\
	__tlbi(op, ##arg);							\
	dsb(ish);								\
} while (0)

/*
 * Complete broadcast TLB maintenance issued by the host which invalidates
 * stage 1 information in the host's own translation regime.
 */
static inline void __tlbi_sync_s1ish(void)
{
	dsb(ish);
	__repeat_tlbi_sync(vale1is, 0);
}

/*
 * Complete broadcast TLB maintenance issued by hyp code which invalidates
 * stage 1 translation information in any translation regime.
 */
static inline void __tlbi_sync_s1ish_hyp(void)
{
	dsb(ish);
	__repeat_tlbi_sync(vale2is, 0);
}

/*
 *	TLB Invalidation
 *	================
@@ -279,7 +299,7 @@ static inline void flush_tlb_all(void)
{
	dsb(ishst);
	__tlbi(vmalle1is);
	dsb(ish);
	__tlbi_sync_s1ish();
	isb();
}

@@ -291,7 +311,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
	asid = __TLBI_VADDR(0, ASID(mm));
	__tlbi(aside1is, asid);
	__tlbi_user(aside1is, asid);
	dsb(ish);
	__tlbi_sync_s1ish();
	mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
}

@@ -345,20 +365,11 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,
				  unsigned long uaddr)
{
	flush_tlb_page_nosync(vma, uaddr);
	dsb(ish);
	__tlbi_sync_s1ish();
}

static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)
{
	/*
	 * TLB flush deferral is not required on systems which are affected by
	 * ARM64_WORKAROUND_REPEAT_TLBI, as __tlbi()/__tlbi_user() implementation
	 * will have two consecutive TLBI instructions with a dsb(ish) in between
	 * defeating the purpose (i.e save overall 'dsb ish' cost).
	 */
	if (alternative_has_cap_unlikely(ARM64_WORKAROUND_REPEAT_TLBI))
		return false;

	return true;
}

@@ -374,7 +385,7 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)
 */
static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
{
	dsb(ish);
	__tlbi_sync_s1ish();
}

/*
@@ -509,7 +520,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
{
	__flush_tlb_range_nosync(vma->vm_mm, start, end, stride,
				 last_level, tlb_level);
	dsb(ish);
	__tlbi_sync_s1ish();
}

static inline void local_flush_tlb_contpte(struct vm_area_struct *vma,
@@ -557,7 +568,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
	dsb(ishst);
	__flush_tlb_range_op(vaale1is, start, pages, stride, 0,
			     TLBI_TTL_UNKNOWN, false, lpa2_is_enabled());
	dsb(ish);
	__tlbi_sync_s1ish();
	isb();
}

@@ -571,7 +582,7 @@ static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr)

	dsb(ishst);
	__tlbi(vaae1is, addr);
	dsb(ish);
	__tlbi_sync_s1ish();
	isb();
}

+1 −1
Original line number Diff line number Diff line
@@ -377,7 +377,7 @@ void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
				prot = __acpi_get_writethrough_mem_attribute();
		}
	}
	return ioremap_prot(phys, size, prot);
	return __ioremap_prot(phys, size, prot);
}

/*
+1 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ __do_compat_cache_op(unsigned long start, unsigned long end)
			 * We pick the reserved-ASID to minimise the impact.
			 */
			__tlbi(aside1is, __TLBI_VADDR(0, 0));
			dsb(ish);
			__tlbi_sync_s1ish();
		}

		ret = caches_clean_inval_user_pou(start, start + chunk);
Loading