arm64: alternatives runtime patching
cover arm64: alternatives runtime patching
This series introduces alternatives runtime patching to arm64. This allows to patch assembly instruction at runtime to either fix hardware bugs or optimize for certain hardware features. Look at patch 5/6 for an example on how to use this.
The code is heavily based on the x86 implementation. Currently this is focussed on fixing CPU errata, but in the future runtime optimizations for new CPU features are planned.
Patch 1/6 introduces a new cpu_hwcaps bitmap, which holds kernel internal CPU feature flags. Since elf_hwcaps is also a userspace ABI, I refrained from tinkering with this (tried this, gets messy).
Based on bits in this bitmap, in patch 2/6 we provide a macro to replace (inline) assembly instructions at runtime with alternative instructions. As on x86, those bits go into separate ELF sections. The patching is done using stop_machine() to avoid mayhem in SMP.
Patch 3/6 introduces means to set cpu_hwcaps bits based on detecting a certain CPU revision. Currently this is based on the architectural bits in the MIDR register, but the code is flexible enough to easily introduce more advanced criteria.
To support a heterogenous CPU setup (e.g. big.LITTLE), we scan all CPUs in the system.
Patch 4 and 5 use the new framework to introduce workarounds for two ARM-Cortex errata. Patch 4 introduces some more framework for the detection, while patch 5 should be used as a blueprint for how to add workarounds for CPU errata in the future.
Patch 6/6 finally introduces Kconfig entries for the bugs. Those are meant to
a) document the errata and workarounds and
b) to allow system vendors to remove certain workarounds for custom build kernels.
Though this shouldn't be strictly necessary, experience shows that people will do b) anyway and I deem it better to provide official means rather than provoking random hacks.
The code can be found also in the alternatives/v1 branch of:
http://www.linux-arm.org/git?p=linux-ap.gitgit://linux-arm.org/linux-ap.git
Please review and comment!
Cheers,
Andre.
Andre Przywara (6):
arm64: add cpu_capabilities bitmap
arm64: add alternative runtime patching
arm64: detect silicon revisions and set cap bits accordingly
arm64: add Cortex-A53 cache errata workaround
arm64: add Cortex-A57 erratum 832075 workaround
arm64: protect alternatives workarounds with Kconfig options
arch/arm64/Kconfig | 107 +++++++++++++++++++++++++++++ arch/arm64/include/asm/alternative-asm.h | 29 ++++++++ arch/arm64/include/asm/alternative.h | 43 ++++++++++++ arch/arm64/include/asm/cpufeature.h | 29 ++++++++ arch/arm64/include/asm/cputype.h | 5 ++ arch/arm64/include/asm/io.h | 23 +++++-- arch/arm64/kernel/Makefile | 2 +- arch/arm64/kernel/alternative.c | 64 +++++++++++++++++ arch/arm64/kernel/cpu_errata.c | 110 ++++++++++++++++++++++++++++++ arch/arm64/kernel/cpuinfo.c | 3 + arch/arm64/kernel/setup.c | 3 + arch/arm64/kernel/smp.c | 2 + arch/arm64/kernel/vmlinux.lds.S | 11 +++ arch/arm64/mm/cache.S | 4 +- arch/arm64/mm/init.c | 2 + 15 files changed, 431 insertions(+), 6 deletions(-) create mode 100644 arch/arm64/include/asm/alternative-asm.h create mode 100644 arch/arm64/include/asm/alternative.h create mode 100644 arch/arm64/kernel/alternative.c create mode 100644 arch/arm64/kernel/cpu_errata.c
Signature · 1 line
1.7.9.5
PATCH 1/6 arm64: add cpu_capabilities bitmap
For taking note if at least one CPU in the system needs a bug workaround or would benefit from a code optimization, we create a new bitmap to hold (artificial) feature bits.
Since elf_hwcap is part of the userland ABI, we keep it alone and introduce a new data structure for that (along with some accessors).
--- arch/arm64/include/asm/cpufeature.h | 20 ++++++++++++++++++++ arch/arm64/kernel/setup.c | 3 +++ 2 files changed, 23 insertions(+)
arch/arm64/include/asm/cpufeature.h +20 -0
@@ -21,9 +21,29 @@#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))#define cpu_feature (x) ilog2(HWCAP_ ## x) +#define NCAPS 0 + +extern DECLARE_BITMAP(cpu_hwcaps, NCAPS); +static inline bool cpu_have_feature (unsigned int num) {return elf_hwcap & (1UL << num); } +static inline bool cpus_have_cap (unsigned int num) +{ +if (num >=NCAPS ) +return false; +return test_bit (num, cpu_hwcaps); +} + +static inline void cpus_set_cap (unsigned int num) +{ +if (num >=NCAPS ) +pr_warn ("Attempt to set an illegal CPU capability (%d >= %d)\n" , + num,NCAPS ); +else +__set_bit (num, cpu_hwcaps); +} +#endif
arch/arm64/kernel/setup.c +3 -0
@@ -49,6 +49,7 @@#include <asm/cputype.h> #include <asm/elf.h> #include <asm/cputable.h> +#include <asm/cpufeature.h> #include <asm/cpu_ops.h> #include <asm/sections.h> #include <asm/setup.h>
@@ -77,6 +78,8 @@ unsigned int compat_elf_hwcap __read_mostly = COMPAT_ELF_HWCAP_DEFAULT;unsigned int compat_elf_hwcap2__read_mostly ;#endif +DECLARE_BITMAP (cpu_hwcaps,NCAPS ); +static const char *cpu_name;static const char *machine_name;phys_addr_t __fdt_pointer__initdata ;
Signature · 1 line
1.7.9.5
PATCH 2/6 arm64: add alternative runtime patching
In reply to Andre PrzywaraWith a blatant copy of some x86 bits we introduce the alternative runtime patching "framework" to arm64.
This is quite basic for now and we only provide the functions we need at this time.
This is connected to the newly introduced feature bits.
--- arch/arm64/include/asm/alternative-asm.h | 16 ++++++++ arch/arm64/include/asm/alternative.h | 43 ++++++++++++++++++++ arch/arm64/kernel/Makefile | 2 +- arch/arm64/kernel/alternative.c | 64 ++++++++++++++++++++++++++++++ arch/arm64/kernel/smp.c | 2 + arch/arm64/kernel/vmlinux.lds.S | 11 +++++ arch/arm64/mm/init.c | 2 + 7 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/include/asm/alternative-asm.h create mode 100644 arch/arm64/include/asm/alternative.h create mode 100644 arch/arm64/kernel/alternative.c
arch/arm64/include/asm/alternative-asm.h new file +16 -0
@@ -0,0 +1,16 @@ +#ifndef __ASM_ALTERNATIVE_ASM_H +#define __ASM_ALTERNATIVE_ASM_H + +#ifdef __ASSEMBLY__ + +.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len + .word \orig_offset - . + .word \alt_offset - . + .hword \feature + .byte \orig_len + .byte \alt_len +.endm + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_ALTERNATIVE_ASM_H */
arch/arm64/include/asm/alternative.h new file +43 -0
43 lines not on this page · raw message
arch/arm64/kernel/Makefile +1 -1
@@ -15,7 +15,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ - cpuinfo.o + cpuinfo.o alternative.o arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o
arch/arm64/kernel/alternative.c new file +64 -0
64 lines not on this page · raw message
arch/arm64/kernel/smp.c +2 -0
@@ -37,6 +37,7 @@#include <linux/of.h> #include <linux/irq_work.h> +#include <asm/alternative.h> #include <asm/atomic.h> #include <asm/cacheflush.h> #include <asm/cpu.h>
@@ -309,6 +310,7 @@ void cpu_die(void)void __init smp_cpus_done (unsigned int max_cpus) {pr_info ("SMP: Total of %d processors activated.\n" ,num_online_cpus ()); +apply_alternatives (); }void __init smp_prepare_boot_cpu (void )
arch/arm64/kernel/vmlinux.lds.S +11 -0
@@ -100,6 +100,17 @@ SECTIONS . = ALIGN(PAGE_SIZE); __init_end = .; + . = ALIGN(4); + .altinstructions : { + __alt_instructions = .; + *(.altinstructions) + __alt_instructions_end = .; + } + .altinstr_replacement : { + *(.altinstr_replacement) + } + + . = ALIGN(PAGE_SIZE); _data = .; _sdata = .; RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE)
arch/arm64/mm/init.c +2 -0
@@ -39,6 +39,7 @@#include <asm/setup.h> #include <asm/sizes.h> #include <asm/tlb.h> +#include <asm/alternative.h> #include "mm.h"
@@ -325,6 +326,7 @@ void __init mem_init(void)void free_initmem (void ) {free_initmem_default (0 ); +free_alternatives_memory (); }#ifdef CONFIG_BLK_DEV_INITRD
Signature · 1 line
1.7.9.5
PATCH 3/6 arm64: detect silicon revisions and set cap bits accordingly
In reply to Andre PrzywaraAfter each CPU has been started, we iterate through a list of CPU features or bugs to detect CPUs which need (or could benefit from) kernel code patches.
For each feature/bug there is a function which checks if that particular CPU is affected. We will later provide some more generic functions for common things like testing for certain MIDR ranges. We do this for every CPU to cover big.LITTLE systems properly as well.
If a certain feature/bug has been detected, the capability bit will be set, so that later the call to apply_alternatives() will trigger the actual code patching.
--- arch/arm64/include/asm/cpufeature.h | 2 ++ arch/arm64/kernel/Makefile | 2 +- arch/arm64/kernel/cpu_errata.c | 59 +++++++++++++++++++++++++++++++++++ arch/arm64/kernel/cpuinfo.c | 3 ++ 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 arch/arm64/kernel/cpu_errata.c
arch/arm64/include/asm/cpufeature.h +2 -0
@@ -46,4 +46,6 @@ static inline void cpus_set_cap(unsigned int num) __set_bit(num, cpu_hwcaps); } +void check_local_cpu_errata (void ); +#endif
arch/arm64/kernel/Makefile +1 -1
@@ -15,7 +15,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \ entry-fpsimd.o process.o ptrace.o setup.o signal.o \ sys.o stacktrace.o time.o traps.o io.o vdso.o \ hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ - cpuinfo.o alternative.o + cpuinfo.o cpu_errata.o alternative.o arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ sys_compat.o
arch/arm64/kernel/cpu_errata.c new file +59 -0
59 lines not on this page · raw message
arch/arm64/kernel/cpuinfo.c +3 -0
@@ -18,6 +18,7 @@#include <asm/cachetype.h> #include <asm/cpu.h> #include <asm/cputype.h> +#include <asm/cpufeature.h> #include <linux/bitops.h> #include <linux/bug.h>
@@ -186,6 +187,8 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) info->reg_id_pfr1 =read_cpuid (ID_PFR1_EL1 );cpuinfo_detect_icache_policy (info); + +check_local_cpu_errata (); }void cpuinfo_store_cpu (void )
Signature · 1 line
1.7.9.5
PATCH 4/6 arm64: add Cortex-A53 cache errata workaround
In reply to Andre PrzywaraThe ARM errata 819472, 826319, 827319 and 824069 define the same workaround for these hardware issues in certain Cortex-A53 parts. Use the new alternatives framework and the CPU MIDR detection to patch "cache clean" into "cache clean and invalidate" instructions if an affected CPU is detected at runtime.
--- arch/arm64/include/asm/alternative-asm.h | 13 ++++++++++++ arch/arm64/include/asm/cpufeature.h | 8 +++++++- arch/arm64/include/asm/cputype.h | 5 +++++ arch/arm64/kernel/cpu_errata.c | 32 +++++++++++++++++++++++++++++- arch/arm64/mm/cache.S | 4 +++- 5 files changed, 59 insertions(+), 3 deletions(-)
arch/arm64/include/asm/alternative-asm.h +13 -0
@@ -11,6 +11,19 @@ .byte \alt_len .endm +.macro alternative_insn insn1 insn2 cap +661: \insn1 +662: .pushsection .altinstructions, "a" + altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f + .popsection + .pushsection .altinstr_replacement,"ax" +663: \insn2 +664: .popsection + .if ((664b-663b) != (662b-661b)) + .error "Alternatives instruction length mismatch" + .endif +.endm +#endif /* __ASSEMBLY__ */ #endif /* __ASM_ALTERNATIVE_ASM_H */
arch/arm64/include/asm/cpufeature.h +7 -1
@@ -21,7 +21,11 @@#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))#define cpu_feature (x) ilog2(HWCAP_ ## x) -#define NCAPS 0 +#define ARM64_WORKAROUND_CLEAN_CACHE 0 + +#define NCAPS 1 + +#ifndef __ASSEMBLY__ extern DECLARE_BITMAP(cpu_hwcaps, NCAPS);
@@ -48,4 +52,6 @@ static inline void cpus_set_cap(unsigned int num)void check_local_cpu_errata (void ); +#endif /* __ASSEMBLY__ */ +#endif
arch/arm64/include/asm/cputype.h +5 -0
@@ -57,6 +57,11 @@#define MIDR_IMPLEMENTOR (midr) \ (((midr) & MIDR_IMPLEMENTOR_MASK) >> MIDR_IMPLEMENTOR_SHIFT) +#define MIDR_CPU_PART (imp, partnum) \ + (((imp) << MIDR_IMPLEMENTOR_SHIFT) | \ + (0xf << MIDR_ARCHITECTURE_SHIFT) | \ + ((partnum) << MIDR_PARTNUM_SHIFT)) +#define ARM_CPU_IMP_ARM 0x41#define ARM_CPU_IMP_APM 0x50
arch/arm64/kernel/cpu_errata.c +31 -1
@@ -23,6 +23,8 @@#include <asm/cputype.h> #include <asm/cpufeature.h> +#define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) +/* * Add a struct or another datatype to the union below if you need * different means to detect an affected CPU.
@@ -39,8 +41,36 @@ struct arm64_cpu_capabilities { }; }; +#define CPU_MODEL_MASK (MIDR_IMPLEMENTOR_MASK | MIDR_PARTNUM_MASK | \ + MIDR_ARCHITECTURE_MASK) + +static bool is_affected_midr_range (struct arm64_cpu_capabilities *entry) +{ +u32 midr =read_cpuid_id (); + +if ((midr &CPU_MODEL_MASK ) != entry->midr_model) +return false; + + midr &=MIDR_REVISION_MASK |MIDR_VARIANT_MASK ; + +return (midr >= entry->midr_range_min && midr <= entry->midr_range_max); +} + +#define MIDR_RANGE (model, min, max) \ + .is_affected = is_affected_midr_range, \ + .midr_model = model, \ + .midr_range_min = min, \ + .midr_range_max = max +struct arm64_cpu_capabilities arm64_errata[] = { - {} + { +/* Cortex-A53 r0p[012] */ + .desc ="ARM errata 826319, 827319, 824069" , + .capability =ARM64_WORKAROUND_CLEAN_CACHE , +MIDR_RANGE (MIDR_CORTEX_A53 ,0x00 ,0x02 ), + }, + { + } };void check_local_cpu_errata (void )
arch/arm64/mm/cache.S +3 -1
@@ -20,6 +20,8 @@ #include <linux/linkage.h> #include <linux/init.h> #include <asm/assembler.h> +#include <asm/cpufeature.h> +#include <asm/alternative-asm.h> #include "proc-macros.S"
@@ -210,7 +212,7 @@ __dma_clean_range: dcache_line_size x2, x3 sub x3, x2, #1 bic x0, x0, x3 -1: dc cvac, x0 // clean D / U line +1: alternative_insn "dc cvac, x0", "dc civac, x0", ARM64_WORKAROUND_CLEAN_CACHE add x0, x0, x2 cmp x0, x1 b.lo 1b
Signature · 1 line
1.7.9.5
PATCH 5/6 arm64: add Cortex-A57 erratum 832075 workaround
In reply to Andre PrzywaraThe ARM erratum 832075 applies to certain revisions of Cortex-A57, one of the workarounds is to change device loads into using load-aquire semantics.
This is achieved using the alternatives framework.
--- arch/arm64/include/asm/cpufeature.h | 5 +++-- arch/arm64/include/asm/io.h | 23 +++++++++++++++++++---- arch/arm64/kernel/cpu_errata.c | 7 +++++++ 3 files changed, 29 insertions(+), 6 deletions(-)
arch/arm64/include/asm/cpufeature.h +3 -2
@@ -21,9 +21,10 @@#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))#define cpu_feature (x) ilog2(HWCAP_ ## x) -#define ARM64_WORKAROUND_CLEAN_CACHE 0 +#define ARM64_WORKAROUND_CLEAN_CACHE 0 +#define ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE 1 -#define NCAPS 1 +#define NCAPS 2#ifndef __ASSEMBLY__
arch/arm64/include/asm/io.h +19 -4
@@ -28,6 +28,8 @@#include <asm/barrier.h> #include <asm/pgtable.h> #include <asm/early_ioremap.h> +#include <asm/alternative.h> +#include <asm/cpufeature.h> #include <xen/xen.h>
@@ -57,28 +59,41 @@ static inline void __raw_writeq(u64 val, volatile void __iomem *addr)static inline u8 __raw_readb (const volatile void __iomem *addr) {u8 val; - asmvolatile ("ldrb %w0, [%1]" :"=r" (val) :"r" (addr)); + asm volatile(ALTERNATIVE("ldrb %w0, [%1]", + "ldarb %w0, [%1]", + ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) + : "=r" (val) : "r" (addr));return val; }static inline u16 __raw_readw (const volatile void __iomem *addr) {u16 val; - asmvolatile ("ldrh %w0, [%1]" :"=r" (val) :"r" (addr)); + + asm volatile(ALTERNATIVE("ldrh %w0, [%1]", + "ldarh %w0, [%1]", + ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) + : "=r" (val) : "r" (addr));return val; }static inline u32 __raw_readl (const volatile void __iomem *addr) {u32 val; - asmvolatile ("ldr %w0, [%1]" :"=r" (val) :"r" (addr)); + asm volatile(ALTERNATIVE("ldr %w0, [%1]", + "ldar %w0, [%1]", + ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) + : "=r" (val) : "r" (addr));return val; }static inline u64 __raw_readq (const volatile void __iomem *addr) {u64 val; - asmvolatile ("ldr %0, [%1]" :"=r" (val) :"r" (addr)); + asm volatile(ALTERNATIVE("ldr %0, [%1]", + "ldar %0, [%1]", + ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE) + : "=r" (val) : "r" (addr));return val; }
arch/arm64/kernel/cpu_errata.c +7 -0
@@ -24,6 +24,7 @@#include <asm/cpufeature.h> #define MIDR_CORTEX_A53 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53) +#define MIDR_CORTEX_A57 MIDR_CPU_PART(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57)/* * Add a struct or another datatype to the union below if you need
@@ -70,6 +71,12 @@ struct arm64_cpu_capabilities arm64_errata[] = { MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x02), }, { + /* Cortex-A57 r0p0 - r1p2 */ + .desc = "ARM erratum 832075", + .capability = ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE, + MIDR_RANGE(MIDR_CORTEX_A57, 0x00, 0x12), + }, + { } };
Signature · 1 line
1.7.9.5
PATCH 6/6 arm64: protect alternatives workarounds with Kconfig options
In reply to Andre PrzywaraNot all of the errata we have workarounds for apply necessarily to all SoCs, so people compiling a kernel for one very specific SoC may not need to patch the kernel.
Introduce a new submenu in the "Platform selection" menu to allow people to turn off certain bugs if they are not affected. By default all of them are enabled.
Normal users or distribution kernels shouldn't bother to deselect any bugs here, since the alternatives framework will take care of patching them in only if needed.
--- arch/arm64/Kconfig | 107 ++++++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/cpu_errata.c | 14 ++++++ 2 files changed, 121 insertions(+)
arch/arm64/Kconfig +107 -0
@@ -162,6 +162,113 @@ config ARCH_XGENE help This enables support for AppliedMicro X-Gene SOC Family +menu "ARM errata workarounds via the alternatives framework" + +config ARM64_ERRATUM_826319 + bool "Cortex-A53: 826319: System might deadlock if a write cannot complete until read data is accepted" + default y + help + This option adds an alternative code sequence to work around ARM + erratum 826319 on Cortex-A53 parts up to r0p2 with an AMBA 4 ACE or + AXI master interface and an L2 cache. + + If a Cortex-A53 uses an AMBA AXI4 ACE interface to other processors + and is unable to accept a certain write via this interface, it will + not progress on read data presented on the read data channel and the + system can deadlock. + + The workaround promotes data cache clean instructions to + data cache clean-and-invalidate. + Please note that this does not necessarily enable the workaround, + as it depends on the alternative framework, which will only patch + the kernel if an affected CPU is detected. + + If unsure, say Y. + +config ARM64_ERRATUM_827319 + bool "Cortex-A53: 827319: Data cache clean instructions might cause overlapping transactions to the interconnect" + default y + help + This option adds an alternative code sequence to work around ARM + erratum 827319 on Cortex-A53 parts up to r0p2 with an AMBA 5 CHI + master interface and an L2 cache. + + Under certain conditions this erratum can cause a clean line eviction + to occur at the same time as another transaction to the same address + on the AMBA 5 CHI interface, which can cause data corruption if the + interconnect reorders the two transactions. + + The workaround promotes data cache clean instructions to + data cache clean-and-invalidate. + Please note that this does not necessarily enable the workaround, + as it depends on the alternative framework, which will only patch + the kernel if an affected CPU is detected. + + If unsure, say Y. + +config ARM64_ERRATUM_824069 + bool "Cortex-A53: 824069: Cache line might not be marked as clean after a CleanShared snoop" + default y + help + This option adds an alternative code sequence to work around ARM + erratum 824069 on Cortex-A53 parts up to r0p2 when it is connected + to a coherent interconnect. + + If a Cortex-A53 processor is executing a store or prefetch for + write instruction at the same time as a processor in another + cluster is executing a cache maintenance operation to the same + address, then this erratum might cause a clean cache line to be + incorrectly marked as dirty. + + The workaround promotes data cache clean instructions to + data cache clean-and-invalidate. + Please note that this option does not necessarily enable the + workaround, as it depends on the alternative framework, which will + only patch the kernel if an affected CPU is detected. + + If unsure, say Y. + +config ARM64_ERRATUM_819472 + bool "Cortex-A53: 819472: Store exclusive instructions might cause data corruption" + default y + help + This option adds an alternative code sequence to work around ARM + erratum 819472 on Cortex-A53 parts up to r0p1 with an L2 cache + present when it is connected to a coherent interconnect. + + If the processor is executing a load and store exclusive sequence at + the same time as a processor in another cluster is executing a cache + maintenance operation to the same address, then this erratum might + cause data corruption. + + The workaround promotes data cache clean instructions to + data cache clean-and-invalidate. + Please note that this does not necessarily enable the workaround, + as it depends on the alternative framework, which will only patch + the kernel if an affected CPU is detected. + + If unsure, say Y. + +config ARM64_ERRATUM_832075 + bool "Cortex-A57: possible deadlock on mixing exclusive memory accesses with device loads" + default y + help + This option adds an alternative code sequence to work around ARM + erratum 832075 on Cortex-A57 parts up to r1p2. + + Affected Cortex-A57 parts might deadlock when exclusive load/store + instructions to Write-Back memory are mixed with Device loads. + + The workaround is to promote device loads to use Load-Acquire + semantics. + Please note that this does not necessarily enable the workaround, + as it depends on the alternative framework, which will only patch + the kernel if an affected CPU is detected. + + If unsure, say Y. + +endmenu + endmenu menu "Bus support"
arch/arm64/kernel/cpu_errata.c +14 -0
@@ -64,18 +64,32 @@ static bool is_affected_midr_range(struct arm64_cpu_capabilities *entry) .midr_range_max = max struct arm64_cpu_capabilities arm64_errata[] = { +#if defined(CONFIG_ARM64_ERRATUM_826319) || \ + defined(CONFIG_ARM64_ERRATUM_827319) || \ + defined(CONFIG_ARM64_ERRATUM_824069) { /* Cortex-A53 r0p[012] */ .desc = "ARM errata 826319, 827319, 824069", .capability = ARM64_WORKAROUND_CLEAN_CACHE, MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x02), }, +#endif +#ifdef CONFIG_ARM64_ERRATUM_819472 + { + /* Cortex-A53 r0p[01] */ + .desc = "ARM errata 819472", + .capability = ARM64_WORKAROUND_CLEAN_CACHE, + MIDR_RANGE(MIDR_CORTEX_A53, 0x00, 0x01), + }, +#endif +#ifdef CONFIG_ARM64_ERRATUM_832075 { /* Cortex-A57 r0p0 - r1p2 */ .desc = "ARM erratum 832075", .capability = ARM64_WORKAROUND_DEVICE_LOAD_ACQUIRE, MIDR_RANGE(MIDR_CORTEX_A57, 0x00, 0x12), }, +#endif { } };
Signature · 1 line
1.7.9.5
Quoted 5 lines · On Friday 14 November 2014 15:54:06 Andre Przywara wrote:
This series introduces alternatives runtime patching to arm64. This allows to patch assembly instruction at runtime to either fix hardware bugs or optimize for certain hardware features. Look at patch 5/6 for an example on how to use this.
Does it provide a measurable performance benefit? The implementation seems ok, but we should only add the complexity for things that actually need it and can't be handled just as well with a run-time conditional.
Arnd
Catalin Marinas
maintainerQuoted 10 lines · Arnd Bergmann
Quoted 4 lines · On Friday 14 November 2014 15:54:06 Andre Przywara wrote:
This series introduces alternatives runtime patching to arm64. This allows to patch assembly instruction at runtime to either fix hardware bugs or optimize for certain hardware features. Look at patch 5/6 for an example on how to use this.
Does it provide a measurable performance benefit? The implementation seems ok, but we should only add the complexity for things that actually need it and can't be handled just as well with a run-time conditional.
I'm not a fan of run-time code patching, however, I'm not sure we can avoid them long term in an optimal way.
One example is errata workarounds we cannot predict. They may require changing of a critical code path.
Another example is code like local_irq_disable which at some point we may want to implement using GICv3 priority mask rather than PSTATE.I bit change to allow NMI via standard IRQ.
And another big use-case I can't yet talk openly about is architecture extensions.
What I would like to see at some point is a way to choose alternatives at run-time via branches rather than code patching or even deciding at compile time what features we have. These would help with debugging.
Signature · 1 line
Catalin
Hi Andre,
Quoted 30 lines · Andre Przywara
This series introduces alternatives runtime patching to arm64. This allows to patch assembly instruction at runtime to either fix hardware bugs or optimize for certain hardware features. Look at patch 5/6 for an example on how to use this.
The code is heavily based on the x86 implementation. Currently this is focussed on fixing CPU errata, but in the future runtime optimizations for new CPU features are planned.
Patch 1/6 introduces a new
cpu_hwcapsbitmap, which holds kernel internal CPU feature flags. Sinceelf_hwcapsis also a userspace ABI, I refrained from tinkering with this (tried this, gets messy).Based on bits in this bitmap, in patch 2/6 we provide a macro to replace (inline) assembly instructions at runtime with alternative instructions. As on x86, those bits go into separate ELF sections. The patching is done using
stop_machine()to avoid mayhem in SMP.Patch 3/6 introduces means to set
cpu_hwcapsbits based on detecting a certain CPU revision. Currently this is based on the architectural bits in the MIDR register, but the code is flexible enough to easily introduce more advanced criteria.
To support a heterogenous CPU setup (e.g. big.LITTLE), we scan all CPUs in the system.Patch 4 and 5 use the new framework to introduce workarounds for two ARM-Cortex errata. Patch 4 introduces some more framework for the detection, while patch 5 should be used as a blueprint for how to add workarounds for CPU errata in the future.
Quoted 8 lines
Patch 6/6 finally introduces
Kconfigentries for the bugs. Those are meant to
a) document the errata and workarounds and
b) to allow system vendors to remove certain workarounds for custom build kernels.
Though this shouldn't be strictly necessary, experience shows that people will do b) anyway and I deem it better to provide official means rather than provoking random hacks.
I just merged this and encountered a couple of issues in my testing:
(1) If patching occurs in a section that is discarded at compile time (e.g. .exit.text) then we will fail to link:
.exit.text' referenced in section .altinstructions' of drivers/built-in.o: defined in discarded section `.exit.text' of drivers/built-in.o
(2) No patching occurs for modules
Please can you send me some additional patches on top of your series to address these problems? The first issue can easily be solved by adding a new Kconfig entry for the alternative framework, which causes ARM_EXIT_{KEEP,DISCARD} to change their definitions. The second patch needs a call from the module loader, in a similar manner to the SMP_ON_UP patching in arch/arm/.
In the meantime, I've applied the diff below to keep allyesconfig happy.
Thanks,
Will
--->8
arch/arm64/kernel/vmlinux.lds.S +3 -2
@@ -11,8 +11,9 @@ #include "image.h" -#define ARM_EXIT_KEEP(x) -#define ARM_EXIT_DISCARD(x) x +/* .exit.text needed in case of alternative patching */ +#define ARM_EXIT_KEEP(x) x +#define ARM_EXIT_DISCARD(x) OUTPUT_ARCH(aarch64) ENTRY(_text)
PATCH 1/1 arm64: add module support for alternatives fixups
Currently the kernel patches all necessary instructions once at boot time, so modules are not covered by this.
Change the apply_alternatives() function to take a beginning and an end pointer and introduce a new variant (apply_alternatives_all()) to cover the existing use case for the static kernel image section. Add a module_finalize() function to arm64 to check for an alternatives section in a module and patch only the instructions from that specific area.
Since that module code is not touched before the module initialization has ended, we don't need to halt the machine before doing the patching in the module's code.
---
Hi Will,
so this patch make alternatives patching now work with modules. This required some changes in the core alternatives code, so it would have looked better when folded in (but it's already too late for this, I guess). I have tested this quickly with modules with and without alternatives sections in them, all seems to work fine.
Not sure if this is too much for -next at this time, though, so I will leave this decision to you.
I will come up with a proper solution for the .exit.text issue later.
Cheers,
Andre.
arch/arm64/include/asm/alternative.h | 3 ++- arch/arm64/kernel/alternative.c | 29 +++++++++++++++++++++++++---- arch/arm64/kernel/module.c | 18 ++++++++++++++++++ arch/arm64/kernel/smp.c | 2 +- 4 files changed, 46 insertions(+), 6 deletions(-)
arch/arm64/include/asm/alternative.h +2 -1
@@ -13,7 +13,8 @@ struct alt_instr {u8 alt_len;/* size of new instruction(s), <= orig_len */ }; -void apply_alternatives (void ); +void apply_alternatives_all (void ); +void apply_alternatives (void *start,size_t length);void free_alternatives_memory (void );#define ALTINSTR_ENTRY (feature) \
arch/arm64/kernel/alternative.c +25 -4
@@ -28,12 +28,18 @@extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; -static int __apply_alternatives (void *dummy) +struct alt_region { +struct alt_instr *begin; +struct alt_instr *end; +}; + +static int __apply_alternatives (void *alt_region) {struct alt_instr *alt; +struct alt_region *region = alt_region;u8 *origptr, *replptr; -for (alt = __alt_instructions; alt < __alt_instructions_end; alt++) { +for (alt = region->begin; alt < region->end; alt++) {if (!cpus_have_cap (alt->cpufeature))continue ;
@@ -51,10 +57,25 @@ static int __apply_alternatives(void *dummy)return 0 ; } -void apply_alternatives (void ) +void apply_alternatives_all (void ) { +struct alt_region region = { + .begin = __alt_instructions, + .end = __alt_instructions_end, + }; +/* better not try code patching on a live SMP system */ -stop_machine (__apply_alternatives,NULL ,NULL ); +stop_machine (__apply_alternatives, ®ion,NULL ); +} + +void apply_alternatives (void *start,size_t length) +{ +struct alt_region region = { + .begin = start, + .end = start + length, + }; + +__apply_alternatives (®ion); }void free_alternatives_memory (void )
arch/arm64/kernel/module.c +18 -0
@@ -26,6 +26,7 @@#include <linux/moduleloader.h> #include <linux/vmalloc.h> #include <asm/insn.h> +#include <asm/sections.h> #define AARCH64_INSN_IMM_MOVNZ AARCH64_INSN_IMM_MAX#define AARCH64_INSN_IMM_MOVK AARCH64_INSN_IMM_16
@@ -394,3 +395,20 @@ overflow: me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);return -ENOEXEC ; } + +int module_finalize (const Elf_Ehdr *hdr, +const Elf_Shdr *sechdrs, +struct module *me) +{ +const Elf_Shdr *s, *se; +const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; + +for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { +if (strcmp (".altinstructions" , secstrs + s->sh_name) ==0 ) { +apply_alternatives ((void *)s->sh_addr, s->sh_size); +return 0 ; + } + } + +return 0 ; +}
arch/arm64/kernel/smp.c +1 -1
@@ -310,7 +310,7 @@ void cpu_die(void)void __init smp_cpus_done (unsigned int max_cpus) {pr_info ("SMP: Total of %d processors activated.\n" ,num_online_cpus ()); -apply_alternatives (); +apply_alternatives_all (); }void __init smp_prepare_boot_cpu (void )
Signature · 1 line
1.7.9.5