ARM development for 7.1-rc1

Several development updates for 7.1-rc1:
 - fix a race condition handling PG_dcache_clean
 - further cleanups for the fault handling, allowing RT to be enabled
 - fixing nzones validation in adfs filesystem driver
 - fix for module unwinding
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuNNh8scc2k/wOAE+9OeQG+StrGQFAmnrfHEACgkQ9OeQG+St
 rGTtMA/+JmEQuVvtJ4ZJ+PhFYdI/VVPTpTwf4tP1dhlLEKaeX4vzlw09+mW/dYeM
 jH65rkQ3wcLC4gRH7ZI1SRMm6n0lAf0ROLjpwagd+4U+xnhLCmIHxo8FM2v3Rmyl
 34WnCmWitiEtUN41t0EfbUBxDoX5pV42vpPkYIdnQtLkjObCcXsVFMhegBPCTfp2
 UOozgH7yNlkFwWEOCEacNne22MmAheOU0R4Jb/OiFAaE3z4i/9+nFoHd7NCKpmDf
 hgWaOalg+jsclxn5kyE0KbraP7Uvx44eX8OeQk1rdvL+F9ItGFXtXKD7FUUj+71/
 S5yHT3YrRYvAEM1GDKAieZhVWFxKPm5osnlJwrrrnfLWR7JvwrsU7SGS8FqSsqqf
 i/Ie5rY1hV9faXJikY8MDSzKC7gwdX0wtZ8JZanZtGwFlPvI17oaE4K3hbiD13Dk
 WIJpd7kfYEWG0SjWtyrW1E2plqy3lfVN4MZfjW2ID18uo0PwVtgE5GFf/HFGxugN
 WLV+/DhSkc40EUC4bVflWDLtnJYhsQYCJoHwZXVI8YpNK+tShia0BVCSj+Llv8Tv
 f33Ae7IARZad+ty2MsTW1oG0zcIjHI8A4OIoRfbRwf3z9Bs8S3o6chlTOi7YYV5y
 fbSxCg9AIYRcUZ0ACsnrIgrVd/YF57FD8AvJ/BHvTm7nVTeoLKY=
 =arB6
 -----END PGP SIGNATURE-----

Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rmk/linux

Pull ARM updates from Russell King:

 - fix a race condition handling PG_dcache_clean

 - further cleanups for the fault handling, allowing RT to be enabled

 - fixing nzones validation in adfs filesystem driver

 - fix for module unwinding

* tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rmk/linux:
  ARM: 9463/1: Allow to enable RT
  ARM: 9472/1: fix race condition on PG_dcache_clean in __sync_icache_dcache()
  ARM: 9471/1: module: fix unwind section relocation out of range error
  fs/adfs: validate nzones in adfs_validate_bblk()
  ARM: provide individual is_translation_fault() and is_permission_fault()
  ARM: move FSR fault status definitions before fsr_fs()
  ARM: use BIT() and GENMASK() for fault status register fields
  ARM: move is_permission_fault() and is_translation_fault() to fault.h
  ARM: move vmalloc() lazy-page table population
  ARM: ensure interrupts are enabled in __do_user_fault()
This commit is contained in:
Linus Torvalds 2026-04-25 07:44:26 -07:00
commit 129d6eb266
6 changed files with 128 additions and 92 deletions

View File

@ -42,6 +42,7 @@ config ARM
select ARCH_SUPPORTS_CFI
select ARCH_SUPPORTS_HUGETLBFS if ARM_LPAE
select ARCH_SUPPORTS_PER_VMA_LOCK
select ARCH_SUPPORTS_RT
select ARCH_USE_BUILTIN_BSWAP
select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_USE_MEMTEST

View File

@ -225,6 +225,18 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
mod->arch.init.plt = s;
else if (s->sh_type == SHT_SYMTAB)
syms = (Elf32_Sym *)s->sh_addr;
#if defined(CONFIG_ARM_UNWIND) && !defined(CONFIG_VMSPLIT_3G)
else if (s->sh_type == ELF_SECTION_UNWIND ||
(strncmp(".ARM.extab", secstrings + s->sh_name, 10) == 0)) {
/*
* To avoid the possible relocation out of range issue for
* R_ARM_PREL31, mark unwind section .ARM.extab and .ARM.exidx as
* executable so they will be allocated along with .text section to
* meet +/-1GB range requirement of the R_ARM_PREL31 relocation
*/
s->sh_flags |= SHF_EXECINSTR;
}
#endif
}
if (!mod->arch.core.plt || !mod->arch.init.plt) {

View File

@ -115,32 +115,6 @@ static inline bool is_write_fault(unsigned int fsr)
return (fsr & FSR_WRITE) && !(fsr & FSR_CM);
}
static inline bool is_translation_fault(unsigned int fsr)
{
int fs = fsr_fs(fsr);
#ifdef CONFIG_ARM_LPAE
if ((fs & FS_MMU_NOLL_MASK) == FS_TRANS_NOLL)
return true;
#else
if (fs == FS_L1_TRANS || fs == FS_L2_TRANS)
return true;
#endif
return false;
}
static inline bool is_permission_fault(unsigned int fsr)
{
int fs = fsr_fs(fsr);
#ifdef CONFIG_ARM_LPAE
if ((fs & FS_MMU_NOLL_MASK) == FS_PERM_NOLL)
return true;
#else
if (fs == FS_L1_PERM || fs == FS_L2_PERM)
return true;
#endif
return false;
}
static void die_kernel_fault(const char *msg, struct mm_struct *mm,
unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
@ -190,7 +164,8 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
/*
* Something tried to access memory that isn't in our memory map..
* User mode accesses just cause a SIGSEGV
* User mode accesses just cause a SIGSEGV. Ensure interrupts are enabled
* for preempt RT.
*/
static void
__do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig,
@ -198,6 +173,8 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig,
{
struct task_struct *tsk = current;
local_irq_enable();
#ifdef CONFIG_DEBUG_USER
if (((user_debug & UDBG_SEGV) && (sig == SIGSEGV)) ||
((user_debug & UDBG_BUS) && (sig == SIGBUS))) {
@ -258,6 +235,70 @@ static inline bool ttbr0_usermode_access_allowed(struct pt_regs *regs)
}
#endif
/*
* Handle a vmalloc fault, copying the non-leaf page table entries from
* init_mm.pgd. Any kernel context can trigger this, so we must not sleep
* or enable interrupts. Having two CPUs execute this for the same page is
* no problem, we'll just copy the same data twice.
*
* Returns false on failure.
*/
static bool __kprobes __maybe_unused vmalloc_fault(unsigned long addr)
{
unsigned int index;
pgd_t *pgd, *pgd_k;
p4d_t *p4d, *p4d_k;
pud_t *pud, *pud_k;
pmd_t *pmd, *pmd_k;
index = pgd_index(addr);
pgd = cpu_get_pgd() + index;
pgd_k = init_mm.pgd + index;
p4d = p4d_offset(pgd, addr);
p4d_k = p4d_offset(pgd_k, addr);
if (p4d_none(*p4d_k))
return false;
if (!p4d_present(*p4d))
set_p4d(p4d, *p4d_k);
pud = pud_offset(p4d, addr);
pud_k = pud_offset(p4d_k, addr);
if (pud_none(*pud_k))
return false;
if (!pud_present(*pud))
set_pud(pud, *pud_k);
pmd = pmd_offset(pud, addr);
pmd_k = pmd_offset(pud_k, addr);
#ifdef CONFIG_ARM_LPAE
/*
* Only one hardware entry per PMD with LPAE.
*/
index = 0;
#else
/*
* On ARM one Linux PGD entry contains two hardware entries (see page
* tables layout in pgtable.h). We normally guarantee that we always
* fill both L1 entries. But create_mapping() doesn't follow the rule.
* It can create inidividual L1 entries, so here we have to call
* pmd_none() check for the entry really corresponded to address, not
* for the first of pair.
*/
index = (addr >> SECTION_SHIFT) & 1;
#endif
if (pmd_none(pmd_k[index]))
return false;
copy_pmd(pmd, pmd_k);
return true;
}
static int __kprobes
do_kernel_address_page_fault(struct mm_struct *mm, unsigned long addr,
unsigned int fsr, struct pt_regs *regs)
@ -268,6 +309,7 @@ do_kernel_address_page_fault(struct mm_struct *mm, unsigned long addr,
* should not be faulting in kernel space, which includes the
* vector/khelper page. Handle the branch predictor hardening
* while interrupts are still disabled, then send a SIGSEGV.
* Note that __do_user_fault() will enable interrupts.
*/
harden_branch_predictor();
__do_user_fault(addr, fsr, SIGSEGV, SEGV_MAPERR, regs);
@ -492,10 +534,9 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
* directly to do_kernel_address_page_fault() to handle.
*
* Otherwise, we're probably faulting in the vmalloc() area, so try to fix
* that up. Note that we must not take any locks or enable interrupts in
* this case.
* that up via vmalloc_fault().
*
* If vmalloc() fixup fails, that means the non-leaf page tables did not
* If vmalloc_fault() fails, that means the non-leaf page tables did not
* contain an entry for this address, so handle this via
* do_kernel_address_page_fault().
*/
@ -504,65 +545,12 @@ static int __kprobes
do_translation_fault(unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
{
unsigned int index;
pgd_t *pgd, *pgd_k;
p4d_t *p4d, *p4d_k;
pud_t *pud, *pud_k;
pmd_t *pmd, *pmd_k;
if (addr < TASK_SIZE)
return do_page_fault(addr, fsr, regs);
if (user_mode(regs))
goto bad_area;
if (!user_mode(regs) && vmalloc_fault(addr))
return 0;
index = pgd_index(addr);
pgd = cpu_get_pgd() + index;
pgd_k = init_mm.pgd + index;
p4d = p4d_offset(pgd, addr);
p4d_k = p4d_offset(pgd_k, addr);
if (p4d_none(*p4d_k))
goto bad_area;
if (!p4d_present(*p4d))
set_p4d(p4d, *p4d_k);
pud = pud_offset(p4d, addr);
pud_k = pud_offset(p4d_k, addr);
if (pud_none(*pud_k))
goto bad_area;
if (!pud_present(*pud))
set_pud(pud, *pud_k);
pmd = pmd_offset(pud, addr);
pmd_k = pmd_offset(pud_k, addr);
#ifdef CONFIG_ARM_LPAE
/*
* Only one hardware entry per PMD with LPAE.
*/
index = 0;
#else
/*
* On ARM one Linux PGD entry contains two hardware entries (see page
* tables layout in pgtable.h). We normally guarantee that we always
* fill both L1 entries. But create_mapping() doesn't follow the rule.
* It can create inidividual L1 entries, so here we have to call
* pmd_none() check for the entry really corresponded to address, not
* for the first of pair.
*/
index = (addr >> SECTION_SHIFT) & 1;
#endif
if (pmd_none(pmd_k[index]))
goto bad_area;
copy_pmd(pmd, pmd_k);
return 0;
bad_area:
do_kernel_address_page_fault(current->mm, addr, fsr, regs);
return 0;

View File

@ -5,12 +5,9 @@
/*
* Fault status register encodings. We steal bit 31 for our own purposes.
*/
#define FSR_LNX_PF (1 << 31)
#define FSR_CM (1 << 13)
#define FSR_WRITE (1 << 11)
#define FSR_FS4 (1 << 10)
#define FSR_FS3_0 (15)
#define FSR_FS5_0 (0x3f)
#define FSR_LNX_PF BIT(31)
#define FSR_CM BIT(13)
#define FSR_WRITE BIT(11)
#ifdef CONFIG_ARM_LPAE
#define FSR_FS_AEA 17
@ -18,10 +15,26 @@
#define FS_PERM_NOLL 0xC
#define FS_MMU_NOLL_MASK 0x3C
#define FSR_FS5_0 GENMASK(5, 0)
static inline int fsr_fs(unsigned int fsr)
{
return fsr & FSR_FS5_0;
}
static inline bool is_translation_fault(unsigned int fsr)
{
int fs = fsr_fs(fsr);
return (fs & FS_MMU_NOLL_MASK) == FS_TRANS_NOLL;
}
static inline bool is_permission_fault(unsigned int fsr)
{
int fs = fsr_fs(fsr);
return (fs & FS_MMU_NOLL_MASK) == FS_PERM_NOLL;
}
#else
#define FSR_FS_AEA 22
#define FS_L1_TRANS 0x5
@ -29,10 +42,27 @@ static inline int fsr_fs(unsigned int fsr)
#define FS_L1_PERM 0xD
#define FS_L2_PERM 0xF
#define FSR_FS4 BIT(10)
#define FSR_FS3_0 GENMASK(3, 0)
static inline int fsr_fs(unsigned int fsr)
{
return (fsr & FSR_FS3_0) | (fsr & FSR_FS4) >> 6;
}
static inline bool is_translation_fault(unsigned int fsr)
{
int fs = fsr_fs(fsr);
return fs == FS_L1_TRANS || fs == FS_L2_TRANS;
}
static inline bool is_permission_fault(unsigned int fsr)
{
int fs = fsr_fs(fsr);
return fs == FS_L1_PERM || fs == FS_L2_PERM;
}
#endif
void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs);

View File

@ -304,8 +304,10 @@ void __sync_icache_dcache(pte_t pteval)
else
mapping = NULL;
if (!test_and_set_bit(PG_dcache_clean, &folio->flags.f))
if (!test_bit(PG_dcache_clean, &folio->flags.f)) {
__flush_dcache_folio(mapping, folio);
set_bit(PG_dcache_clean, &folio->flags.f);
}
if (pte_exec(pteval))
__flush_icache_all();

View File

@ -317,6 +317,9 @@ static int adfs_validate_bblk(struct super_block *sb, struct buffer_head *bh,
if (adfs_checkdiscrecord(dr))
return -EILSEQ;
if ((dr->nzones | dr->nzones_high << 8) == 0)
return -EILSEQ;
*drp = dr;
return 0;
}