mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
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:
commit
129d6eb266
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
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);
|
||||
if (!user_mode(regs) && vmalloc_fault(addr))
|
||||
return 0;
|
||||
|
||||
bad_area:
|
||||
do_kernel_address_page_fault(current->mm, addr, fsr, regs);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user