From 471b5e42cc7d76678314542d0ce079e5f3cfb706 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Thu, 16 Feb 2017 18:53:29 +0100 Subject: ARM: 8659/1: l2c: allow CA9 optimizations to be disabled If a PL310 is added to a system, but the sideband signals are not connected, some Cortex A9 optimizations cannot be used. In particular, enabling Full Line of Zeros in the CA9 without sidebands connected will crash the system since the CA9 will expect the L2C to perform operations, yet the L2C never gets the commands. Early BRESP also does not work without sideband signals. Signed-off-by: Chris Brandt Signed-off-by: Russell King --- arch/arm/mm/cache-l2x0.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 2290be390f87..808efbb89b88 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -57,6 +57,9 @@ static unsigned long sync_reg_offset = L2X0_CACHE_SYNC; struct l2x0_regs l2x0_saved_regs; +static bool l2x0_bresp_disable; +static bool l2x0_flz_disable; + /* * Common code for all cache controllers. */ @@ -620,7 +623,7 @@ static void __init l2c310_enable(void __iomem *base, unsigned num_lock) u32 aux = l2x0_saved_regs.aux_ctrl; if (rev >= L310_CACHE_ID_RTL_R2P0) { - if (cortex_a9) { + if (cortex_a9 && !l2x0_bresp_disable) { aux |= L310_AUX_CTRL_EARLY_BRESP; pr_info("L2C-310 enabling early BRESP for Cortex-A9\n"); } else if (aux & L310_AUX_CTRL_EARLY_BRESP) { @@ -629,7 +632,7 @@ static void __init l2c310_enable(void __iomem *base, unsigned num_lock) } } - if (cortex_a9) { + if (cortex_a9 && !l2x0_flz_disable) { u32 aux_cur = readl_relaxed(base + L2X0_AUX_CTRL); u32 acr = get_auxcr(); @@ -1200,6 +1203,12 @@ static void __init l2c310_of_parse(const struct device_node *np, *aux_mask &= ~L2C_AUX_CTRL_PARITY_ENABLE; } + if (of_property_read_bool(np, "arm,early-bresp-disable")) + l2x0_bresp_disable = true; + + if (of_property_read_bool(np, "arm,full-line-zero-disable")) + l2x0_flz_disable = true; + prefetch = l2x0_saved_regs.prefetch_ctrl; ret = of_property_read_u32(np, "arm,double-linefill", &val); -- cgit From dd59f974bd5e04d000e38ceca1a19085d029794b Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sat, 25 Mar 2017 00:51:32 +0100 Subject: ARM: 8666/1: mm: dump: Add domain to output This adds the memory domain (on non-LPAE) to the PMD and PTE dumps. This isn't in the regular PMD bits because I couldn't find a clean way to fall back to retain some of the PMD bits when reporting PTE. So this is special-cased currently. New output example: ---[ Modules ]--- 0x7f000000-0x7f001000 4K KERNEL ro x SHD MEM/CACHED/WBWA 0x7f001000-0x7f002000 4K KERNEL ro NX SHD MEM/CACHED/WBWA 0x7f002000-0x7f004000 8K KERNEL RW NX SHD MEM/CACHED/WBWA ---[ Kernel Mapping ]--- 0x80000000-0x80100000 1M KERNEL RW NX SHD 0x80100000-0x80800000 7M KERNEL ro x SHD 0x80800000-0x80b00000 3M KERNEL ro NX SHD 0x80b00000-0xa0000000 501M KERNEL RW NX SHD ... ---[ Vectors ]--- 0xffff0000-0xffff1000 4K VECTORS USR ro x SHD MEM/CACHED/WBWA 0xffff1000-0xffff2000 4K VECTORS ro x SHD MEM/CACHED/WBWA Signed-off-by: Kees Cook Signed-off-by: Russell King --- arch/arm/mm/dump.c | 54 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 10 deletions(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/dump.c b/arch/arm/mm/dump.c index 21192d6eda40..35ff45470dbf 100644 --- a/arch/arm/mm/dump.c +++ b/arch/arm/mm/dump.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -43,6 +44,7 @@ struct pg_state { unsigned long start_address; unsigned level; u64 current_prot; + const char *current_domain; }; struct prot_bits { @@ -216,7 +218,8 @@ static void dump_prot(struct pg_state *st, const struct prot_bits *bits, size_t } } -static void note_page(struct pg_state *st, unsigned long addr, unsigned level, u64 val) +static void note_page(struct pg_state *st, unsigned long addr, + unsigned int level, u64 val, const char *domain) { static const char units[] = "KMGTPE"; u64 prot = val & pg_level[level].mask; @@ -224,8 +227,10 @@ static void note_page(struct pg_state *st, unsigned long addr, unsigned level, u if (!st->level) { st->level = level; st->current_prot = prot; + st->current_domain = domain; seq_printf(st->seq, "---[ %s ]---\n", st->marker->name); } else if (prot != st->current_prot || level != st->level || + domain != st->current_domain || addr >= st->marker[1].start_address) { const char *unit = units; unsigned long delta; @@ -240,6 +245,8 @@ static void note_page(struct pg_state *st, unsigned long addr, unsigned level, u unit++; } seq_printf(st->seq, "%9lu%c", delta, *unit); + if (st->current_domain) + seq_printf(st->seq, " %s", st->current_domain); if (pg_level[st->level].bits) dump_prot(st, pg_level[st->level].bits, pg_level[st->level].num); seq_printf(st->seq, "\n"); @@ -251,11 +258,13 @@ static void note_page(struct pg_state *st, unsigned long addr, unsigned level, u } st->start_address = addr; st->current_prot = prot; + st->current_domain = domain; st->level = level; } } -static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start) +static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start, + const char *domain) { pte_t *pte = pte_offset_kernel(pmd, 0); unsigned long addr; @@ -263,25 +272,50 @@ static void walk_pte(struct pg_state *st, pmd_t *pmd, unsigned long start) for (i = 0; i < PTRS_PER_PTE; i++, pte++) { addr = start + i * PAGE_SIZE; - note_page(st, addr, 4, pte_val(*pte)); + note_page(st, addr, 4, pte_val(*pte), domain); } } +static const char *get_domain_name(pmd_t *pmd) +{ +#ifndef CONFIG_ARM_LPAE + switch (pmd_val(*pmd) & PMD_DOMAIN_MASK) { + case PMD_DOMAIN(DOMAIN_KERNEL): + return "KERNEL "; + case PMD_DOMAIN(DOMAIN_USER): + return "USER "; + case PMD_DOMAIN(DOMAIN_IO): + return "IO "; + case PMD_DOMAIN(DOMAIN_VECTORS): + return "VECTORS"; + default: + return "unknown"; + } +#endif + return NULL; +} + static void walk_pmd(struct pg_state *st, pud_t *pud, unsigned long start) { pmd_t *pmd = pmd_offset(pud, 0); unsigned long addr; unsigned i; + const char *domain; for (i = 0; i < PTRS_PER_PMD; i++, pmd++) { addr = start + i * PMD_SIZE; + domain = get_domain_name(pmd); if (pmd_none(*pmd) || pmd_large(*pmd) || !pmd_present(*pmd)) - note_page(st, addr, 3, pmd_val(*pmd)); + note_page(st, addr, 3, pmd_val(*pmd), domain); else - walk_pte(st, pmd, addr); + walk_pte(st, pmd, addr, domain); - if (SECTION_SIZE < PMD_SIZE && pmd_large(pmd[1])) - note_page(st, addr + SECTION_SIZE, 3, pmd_val(pmd[1])); + if (SECTION_SIZE < PMD_SIZE && pmd_large(pmd[1])) { + addr += SECTION_SIZE; + pmd++; + domain = get_domain_name(pmd); + note_page(st, addr, 3, pmd_val(*pmd), domain); + } } } @@ -296,7 +330,7 @@ static void walk_pud(struct pg_state *st, pgd_t *pgd, unsigned long start) if (!pud_none(*pud)) { walk_pmd(st, pud, addr); } else { - note_page(st, addr, 2, pud_val(*pud)); + note_page(st, addr, 2, pud_val(*pud), NULL); } } } @@ -317,11 +351,11 @@ static void walk_pgd(struct seq_file *m) if (!pgd_none(*pgd)) { walk_pud(&st, pgd, addr); } else { - note_page(&st, addr, 1, pgd_val(*pgd)); + note_page(&st, addr, 1, pgd_val(*pgd), NULL); } } - note_page(&st, 0, 0, 0); + note_page(&st, 0, 0, 0, NULL); } static int ptdump_show(struct seq_file *m, void *v) -- cgit From b089c31c519c3906c14801b6ec483e18a5152a50 Mon Sep 17 00:00:00 2001 From: Jon Medhurst Date: Mon, 10 Apr 2017 11:13:59 +0100 Subject: ARM: 8667/3: Fix memory attribute inconsistencies when using fixmap To cope with the variety in ARM architectures and configurations, the pagetable attributes for kernel memory are generated at runtime to match the system the kernel finds itself on. This calculated value is stored in pgprot_kernel. However, when early fixmap support was added for ARM (commit a5f4c561b3b1) the attributes used for mappings were hard coded because pgprot_kernel is not set up early enough. Unfortunately, when fixmap is used after early boot this means the memory being mapped can have different attributes to existing mappings, potentially leading to unpredictable behaviour. A specific problem also exists due to the hard coded values not include the 'shareable' attribute which means on systems where this matters (e.g. those with multiple CPU clusters) the cache contents for a memory location can become inconsistent between CPUs. To resolve these issues we change fixmap to use the same memory attributes (from pgprot_kernel) that the rest of the kernel uses. To enable this we need to refactor the initialisation code so build_mem_type_table() is called early enough. Note, that relies on early param parsing for memory type overrides passed via the kernel command line, so we need to make sure this call is still after parse_early_params(). [ardb: keep early_fixmap_init() before param parsing, for earlycon] Fixes: a5f4c561b3b1 ("ARM: 8415/1: early fixmap support for earlycon") Cc: # v4.3+ Tested-by: afzal mohammed Signed-off-by: Jon Medhurst Signed-off-by: Ard Biesheuvel Signed-off-by: Russell King --- arch/arm/mm/mmu.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index 4e016d7f37b3..347cca965783 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -414,6 +414,11 @@ void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot) FIXADDR_END); BUG_ON(idx >= __end_of_fixed_addresses); + /* we only support device mappings until pgprot_kernel has been set */ + if (WARN_ON(pgprot_val(prot) != pgprot_val(FIXMAP_PAGE_IO) && + pgprot_val(pgprot_kernel) == 0)) + return; + if (pgprot_val(prot)) set_pte_at(NULL, vaddr, pte, pfn_pte(phys >> PAGE_SHIFT, prot)); @@ -1492,7 +1497,7 @@ pgtables_remap lpae_pgtables_remap_asm; * early_paging_init() recreates boot time page table setup, allowing machines * to switch over to a high (>4G) address space on LPAE systems */ -void __init early_paging_init(const struct machine_desc *mdesc) +static void __init early_paging_init(const struct machine_desc *mdesc) { pgtables_remap *lpae_pgtables_remap; unsigned long pa_pgd; @@ -1560,7 +1565,7 @@ void __init early_paging_init(const struct machine_desc *mdesc) #else -void __init early_paging_init(const struct machine_desc *mdesc) +static void __init early_paging_init(const struct machine_desc *mdesc) { long long offset; @@ -1616,7 +1621,6 @@ void __init paging_init(const struct machine_desc *mdesc) { void *zero_page; - build_mem_type_table(); prepare_page_table(); map_lowmem(); memblock_set_current_limit(arm_lowmem_limit); @@ -1636,3 +1640,9 @@ void __init paging_init(const struct machine_desc *mdesc) empty_zero_page = virt_to_page(zero_page); __flush_dcache_page(NULL, empty_zero_page); } + +void __init early_mm_init(const struct machine_desc *mdesc) +{ + build_mem_type_table(); + early_paging_init(mdesc); +} -- cgit From b70cd406d7fe9976962d621d8c60d324eb47d284 Mon Sep 17 00:00:00 2001 From: Vladimir Murzin Date: Mon, 24 Apr 2017 10:41:53 +0100 Subject: ARM: 8671/1: V7M: Preserve registers across switch from Thread to Handler mode According to ARMv7 ARM, when exception is taken content of r0-r3, r12 is unknown (see ExceptionTaken() pseudocode). Even though existent implementations keep these register unchanged, preserve them to be in line with architecture. Reported-by: Dobromir Stefanov Signed-off-by: Vladimir Murzin Signed-off-by: Russell King --- arch/arm/mm/proc-v7m.S | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/proc-v7m.S b/arch/arm/mm/proc-v7m.S index 8dea61640cc1..11ae6b847ad0 100644 --- a/arch/arm/mm/proc-v7m.S +++ b/arch/arm/mm/proc-v7m.S @@ -135,9 +135,11 @@ __v7m_setup_cont: dsb mov r6, lr @ save LR ldr sp, =init_thread_union + THREAD_START_SP + stmia sp, {r0-r3, r12} cpsie i svc #0 1: cpsid i + ldmia sp, {r0-r3, r12} str r5, [r12, #11 * 4] @ restore the original SVC vector entry mov lr, r6 @ restore LR -- cgit From 11ce4b33aedc65198d7bc9669344ebca5ee36a41 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Tue, 25 Apr 2017 21:20:52 +0100 Subject: ARM: 8672/1: mm: remove tasklist locking from update_sections_early() The below backtrace can be observed on -rt kernel with CONFIG_DEBUG_MODULE_RONX (4.9 kernel CONFIG_DEBUG_RODATA) option enabled: BUG: sleeping function called from invalid context at kernel/locking/rtmutex.c:993 in_atomic(): 1, irqs_disabled(): 128, pid: 14, name: migration/0 1 lock held by migration/0/14: #0: (tasklist_lock){+.+...}, at: [] update_sections_early+0x24/0xdc irq event stamp: 38 hardirqs last enabled at (37): [] _raw_spin_unlock_irq+0x24/0x68 hardirqs last disabled at (38): [] multi_cpu_stop+0xd8/0x138 softirqs last enabled at (0): [] copy_process.part.5+0x238/0x1b64 softirqs last disabled at (0): [< (null)>] (null) Preemption disabled at: [] cpu_stopper_thread+0x80/0x10c CPU: 0 PID: 14 Comm: migration/0 Not tainted 4.9.21-rt16-02220-g49e319c #15 Hardware name: Generic DRA74X (Flattened Device Tree) [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0xa8/0xd4) [] (dump_stack) from [] (___might_sleep+0x1bc/0x2ac) [] (___might_sleep) from [] (__rt_spin_lock+0x1c/0x30) [] (__rt_spin_lock) from [] (rt_read_lock+0x54/0x68) [] (rt_read_lock) from [] (update_sections_early+0x24/0xdc) [] (update_sections_early) from [] (__fix_kernmem_perms+0x10/0x1c) [] (__fix_kernmem_perms) from [] (multi_cpu_stop+0x100/0x138) [] (multi_cpu_stop) from [] (cpu_stopper_thread+0x88/0x10c) [] (cpu_stopper_thread) from [] (smpboot_thread_fn+0x174/0x31c) [] (smpboot_thread_fn) from [] (kthread+0xf0/0x108) [] (kthread) from [] (ret_from_fork+0x14/0x3c) Freeing unused kernel memory: 1024K (c0d00000 - c0e00000) The stop_machine() is called with cpus = NULL from fix_kernmem_perms() and mark_rodata_ro() which means only one CPU will execute update_sections_early() while all other CPUs will spin and wait. Hence, it's safe to remove tasklist locking from update_sections_early(). As part of this change also mark functions which are local to this module as static. Signed-off-by: Grygorii Strashko Acked-by: Laura Abbott Acked-by: Kees Cook Signed-off-by: Russell King --- arch/arm/mm/init.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'arch/arm/mm') diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index 1d8558ff9827..ad80548325fe 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -709,34 +709,37 @@ void set_section_perms(struct section_perm *perms, int n, bool set, } +/** + * update_sections_early intended to be called only through stop_machine + * framework and executed by only one CPU while all other CPUs will spin and + * wait, so no locking is required in this function. + */ static void update_sections_early(struct section_perm perms[], int n) { struct task_struct *t, *s; - read_lock(&tasklist_lock); for_each_process(t) { if (t->flags & PF_KTHREAD) continue; for_each_thread(t, s) set_section_perms(perms, n, true, s->mm); } - read_unlock(&tasklist_lock); set_section_perms(perms, n, true, current->active_mm); set_section_perms(perms, n, true, &init_mm); } -int __fix_kernmem_perms(void *unused) +static int __fix_kernmem_perms(void *unused) { update_sections_early(nx_perms, ARRAY_SIZE(nx_perms)); return 0; } -void fix_kernmem_perms(void) +static void fix_kernmem_perms(void) { stop_machine(__fix_kernmem_perms, NULL, NULL); } -int __mark_rodata_ro(void *unused) +static int __mark_rodata_ro(void *unused) { update_sections_early(ro_perms, ARRAY_SIZE(ro_perms)); return 0; -- cgit