| From 7eacf3456b1d20454f12e34c131073fd2a7c264c Mon Sep 17 00:00:00 2001 |
| From: Ard Biesheuvel <ardb@kernel.org> |
| Date: Tue, 30 Jun 2020 10:19:21 +0200 |
| Subject: [PATCH] arm64/alternatives: use subsections for replacement sequences |
| |
| commit f7b93d42945cc71e1346dd5ae07c59061d56745e upstream. |
| |
| When building very large kernels, the logic that emits replacement |
| sequences for alternatives fails when relative branches are present |
| in the code that is emitted into the .altinstr_replacement section |
| and patched in at the original site and fixed up. The reason is that |
| the linker will insert veneers if relative branches go out of range, |
| and due to the relative distance of the .altinstr_replacement from |
| the .text section where its branch targets usually live, veneers |
| may be emitted at the end of the .altinstr_replacement section, with |
| the relative branches in the sequence pointed at the veneers instead |
| of the actual target. |
| |
| The alternatives patching logic will attempt to fix up the branch to |
| point to its original target, which will be the veneer in this case, |
| but given that the patch site is likely to be far away as well, it |
| will be out of range and so patching will fail. There are other cases |
| where these veneers are problematic, e.g., when the target of the |
| branch is in .text while the patch site is in .init.text, in which |
| case putting the replacement sequence inside .text may not help either. |
| |
| So let's use subsections to emit the replacement code as closely as |
| possible to the patch site, to ensure that veneers are only likely to |
| be emitted if they are required at the patch site as well, in which |
| case they will be in range for the replacement sequence both before |
| and after it is transported to the patch site. |
| |
| This will prevent alternative sequences in non-init code from being |
| released from memory after boot, but this is tolerable given that the |
| entire section is only 512 KB on an allyesconfig build (which weighs in |
| at 500+ MB for the entire Image). Also, note that modules today carry |
| the replacement sequences in non-init sections as well, and any of |
| those that target init code will be emitted into init sections after |
| this change. |
| |
| This fixes an early crash when booting an allyesconfig kernel on a |
| system where any of the alternatives sequences containing relative |
| branches are activated at boot (e.g., ARM64_HAS_PAN on TX2) |
| |
| Signed-off-by: Ard Biesheuvel <ardb@kernel.org> |
| Cc: Suzuki K Poulose <suzuki.poulose@arm.com> |
| Cc: James Morse <james.morse@arm.com> |
| Cc: Andre Przywara <andre.przywara@arm.com> |
| Cc: Dave P Martin <dave.martin@arm.com> |
| Link: https://lore.kernel.org/r/20200630081921.13443-1-ardb@kernel.org |
| Signed-off-by: Will Deacon <will@kernel.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h |
| index 97624ee1aaa3..f7facb4eda9a 100644 |
| --- a/arch/arm64/include/asm/alternative.h |
| +++ b/arch/arm64/include/asm/alternative.h |
| @@ -71,11 +71,11 @@ static inline void apply_alternatives_module(void *start, size_t length) { } |
| ALTINSTR_ENTRY(feature,cb) \ |
| ".popsection\n" \ |
| " .if " __stringify(cb) " == 0\n" \ |
| - ".pushsection .altinstr_replacement, \"a\"\n" \ |
| + ".subsection 1\n" \ |
| "663:\n\t" \ |
| newinstr "\n" \ |
| "664:\n\t" \ |
| - ".popsection\n\t" \ |
| + ".previous\n\t" \ |
| ".org . - (664b-663b) + (662b-661b)\n\t" \ |
| ".org . - (662b-661b) + (664b-663b)\n" \ |
| ".else\n\t" \ |
| @@ -107,9 +107,9 @@ static inline void apply_alternatives_module(void *start, size_t length) { } |
| 662: .pushsection .altinstructions, "a" |
| altinstruction_entry 661b, 663f, \cap, 662b-661b, 664f-663f |
| .popsection |
| - .pushsection .altinstr_replacement, "ax" |
| + .subsection 1 |
| 663: \insn2 |
| -664: .popsection |
| +664: .previous |
| .org . - (664b-663b) + (662b-661b) |
| .org . - (662b-661b) + (664b-663b) |
| .endif |
| @@ -150,7 +150,7 @@ static inline void apply_alternatives_module(void *start, size_t length) { } |
| .pushsection .altinstructions, "a" |
| altinstruction_entry 663f, 661f, \cap, 664f-663f, 662f-661f |
| .popsection |
| - .pushsection .altinstr_replacement, "ax" |
| + .subsection 1 |
| .align 2 /* So GAS knows label 661 is suitably aligned */ |
| 661: |
| .endm |
| @@ -169,9 +169,9 @@ static inline void apply_alternatives_module(void *start, size_t length) { } |
| .macro alternative_else |
| 662: |
| .if .Lasm_alt_mode==0 |
| - .pushsection .altinstr_replacement, "ax" |
| + .subsection 1 |
| .else |
| - .popsection |
| + .previous |
| .endif |
| 663: |
| .endm |
| @@ -182,7 +182,7 @@ static inline void apply_alternatives_module(void *start, size_t length) { } |
| .macro alternative_endif |
| 664: |
| .if .Lasm_alt_mode==0 |
| - .popsection |
| + .previous |
| .endif |
| .org . - (664b-663b) + (662b-661b) |
| .org . - (662b-661b) + (664b-663b) |
| diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S |
| index 6ef635c35ff3..61d63657ab59 100644 |
| --- a/arch/arm64/kernel/vmlinux.lds.S |
| +++ b/arch/arm64/kernel/vmlinux.lds.S |
| @@ -172,9 +172,6 @@ SECTIONS |
| *(.altinstructions) |
| __alt_instructions_end = .; |
| } |
| - .altinstr_replacement : { |
| - *(.altinstr_replacement) |
| - } |
| |
| . = ALIGN(PAGE_SIZE); |
| __inittext_end = .; |
| -- |
| 2.27.0 |
| |