From b7ede5a1f5905ac394cc8e61712a13e3c5cb7b8f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 22 Feb 2017 19:40:12 +0100 Subject: ARM: 8662/1: module: split core and init PLT sections Since commit 35fa91eed817 ("ARM: kernel: merge core and init PLTs"), the ARM module PLT code allocates all PLT entries in a single core section, since the overhead of having a separate init PLT section is not justified by the small number of PLT entries usually required for init code. However, the core and init module regions are allocated independently, and there is a corner case where the core region may be allocated from the VMALLOC region if the dedicated module region is exhausted, but the init region, being much smaller, can still be allocated from the module region. This puts the PLT entries out of reach of the relocated branch instructions, defeating the whole purpose of PLTs. So split the core and init PLT regions, and name the latter ".init.plt" so it gets allocated along with (and sufficiently close to) the .init sections that it serves. Also, given that init PLT entries may need to be emitted for branches that target the core module, modify the logic that disregards defined symbols to only disregard symbols that are defined in the same section. Fixes: 35fa91eed817 ("ARM: kernel: merge core and init PLTs") Cc: # v4.9+ Reported-by: Angus Clark Tested-by: Angus Clark Signed-off-by: Ard Biesheuvel Signed-off-by: Russell King --- arch/arm/include/asm/module.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'arch/arm/include') diff --git a/arch/arm/include/asm/module.h b/arch/arm/include/asm/module.h index 464748b9fd7d..ed2319663a1e 100644 --- a/arch/arm/include/asm/module.h +++ b/arch/arm/include/asm/module.h @@ -18,13 +18,18 @@ enum { }; #endif +struct mod_plt_sec { + struct elf32_shdr *plt; + int plt_count; +}; + struct mod_arch_specific { #ifdef CONFIG_ARM_UNWIND struct unwind_table *unwind[ARM_SEC_MAX]; #endif #ifdef CONFIG_ARM_MODULE_PLTS - struct elf32_shdr *plt; - int plt_count; + struct mod_plt_sec core; + struct mod_plt_sec init; #endif }; -- cgit v1.2.3 From ea2d9a96b6abe57d72d39a7af24e0120f5df0a8c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sun, 19 Mar 2017 17:23:31 +0100 Subject: ARM: 8663/1: wire up HWCAP/HWCAP2 feature bits to the CPU modalias Wire up the generic support for exposing CPU feature bits via the modalias in /sys/device/system/cpu. This allows udev to automatically load modules for things like crypto algorithms that are implemented using optional instructions. Since it is non-trivial to transparantly support both HWCAP and HWCAP2 capabilities in the cpu_feature() macro (which allows a module's hwcap dependency and init routine to be declared using a single invocation of module_cpu_feature_match()), support only HWCAP2 for now, which covers the capabilities that are most likely to be useful in this manner. Module dependencies on HWCAP will need to be declared explicitly via a MODULE_DEVICE_TABLE(cpu, ...) declaration. Signed-off-by: Ard Biesheuvel Signed-off-by: Russell King --- arch/arm/Kconfig | 1 + arch/arm/include/asm/cpufeature.h | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 arch/arm/include/asm/cpufeature.h (limited to 'arch/arm/include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 0d4e71b42c77..64b43a84a1eb 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -27,6 +27,7 @@ config ARM select GENERIC_ALLOCATOR select GENERIC_ATOMIC64 if (CPU_V7M || CPU_V6 || !CPU_32v6K || !AEABI) select GENERIC_CLOCKEVENTS_BROADCAST if SMP + select GENERIC_CPU_AUTOPROBE select GENERIC_EARLY_IOREMAP select GENERIC_IDLE_POLL_SETUP select GENERIC_IRQ_PROBE diff --git a/arch/arm/include/asm/cpufeature.h b/arch/arm/include/asm/cpufeature.h new file mode 100644 index 000000000000..6d425191d01d --- /dev/null +++ b/arch/arm/include/asm/cpufeature.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __ASM_CPUFEATURE_H +#define __ASM_CPUFEATURE_H + +#include +#include + +/* + * Due to the fact that ELF_HWCAP is a 32-bit type on ARM, and given the number + * of optional CPU features it defines, ARM's CPU hardware capability bits have + * been distributed over separate elf_hwcap and elf_hwcap2 variables, each of + * which covers a subset of the available CPU features. + * + * Currently, only a few of those are suitable for automatic module loading + * (which is the primary use case of this facility) and those happen to be all + * covered by HWCAP2. So let's only cover those via the cpu_feature() + * convenience macro for now (which is used by module_cpu_feature_match()). + * However, all capabilities are exposed via the modalias, and can be matched + * using an explicit MODULE_DEVICE_TABLE() that uses __hwcap_feature() directly. + */ +#define MAX_CPU_FEATURES 64 +#define __hwcap_feature(x) ilog2(HWCAP_ ## x) +#define __hwcap2_feature(x) (32 + ilog2(HWCAP2_ ## x)) +#define cpu_feature(x) __hwcap2_feature(x) + +static inline bool cpu_have_feature(unsigned int num) +{ + return num < 32 ? elf_hwcap & BIT(num) : elf_hwcap2 & BIT(num - 32); +} + +#endif -- cgit v1.2.3 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/include/asm/fixmap.h | 2 +- arch/arm/kernel/setup.c | 4 ++-- arch/arm/mm/mmu.c | 16 +++++++++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) (limited to 'arch/arm/include') diff --git a/arch/arm/include/asm/fixmap.h b/arch/arm/include/asm/fixmap.h index 5c17d2dec777..8f967d1373f6 100644 --- a/arch/arm/include/asm/fixmap.h +++ b/arch/arm/include/asm/fixmap.h @@ -41,7 +41,7 @@ static const enum fixed_addresses __end_of_fixed_addresses = #define FIXMAP_PAGE_COMMON (L_PTE_YOUNG | L_PTE_PRESENT | L_PTE_XN | L_PTE_DIRTY) -#define FIXMAP_PAGE_NORMAL (FIXMAP_PAGE_COMMON | L_PTE_MT_WRITEBACK) +#define FIXMAP_PAGE_NORMAL (pgprot_kernel | L_PTE_XN) #define FIXMAP_PAGE_RO (FIXMAP_PAGE_NORMAL | L_PTE_RDONLY) /* Used by set_fixmap_(io|nocache), both meant for mapping a device */ diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index f4e54503afa9..32e1a9513dc7 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -80,7 +80,7 @@ __setup("fpe=", fpe_setup); extern void init_default_cache_policy(unsigned long); extern void paging_init(const struct machine_desc *desc); -extern void early_paging_init(const struct machine_desc *); +extern void early_mm_init(const struct machine_desc *); extern void adjust_lowmem_bounds(void); extern enum reboot_mode reboot_mode; extern void setup_dma_zone(const struct machine_desc *desc); @@ -1088,7 +1088,7 @@ void __init setup_arch(char **cmdline_p) parse_early_param(); #ifdef CONFIG_MMU - early_paging_init(mdesc); + early_mm_init(mdesc); #endif setup_dma_zone(mdesc); xen_early_init(); 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 v1.2.3