Skip to content

Commit 4d62e81

Browse files
author
Russell King
committed
ARM: kexec: fix oops after TLB are invalidated
Giancarlo Ferrari reports the following oops while trying to use kexec: Unable to handle kernel paging request at virtual address 80112f38 pgd = fd7ef03e [80112f38] *pgd=0001141e(bad) Internal error: Oops: 80d [#1] PREEMPT SMP ARM ... This is caused by machine_kexec() trying to set the kernel text to be read/write, so it can poke values into the relocation code before copying it - and an interrupt occuring which changes the page tables. The subsequent writes then hit read-only sections that trigger a data abort resulting in the above oops. Fix this by copying the relocation code, and then writing the variables into the destination, thereby avoiding the need to make the kernel text read/write. Reported-by: Giancarlo Ferrari <[email protected]> Tested-by: Giancarlo Ferrari <[email protected]> Signed-off-by: Russell King <[email protected]>
1 parent 9c698bf commit 4d62e81

File tree

4 files changed

+36
-39
lines changed

4 files changed

+36
-39
lines changed

arch/arm/include/asm/kexec-internal.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ARM_KEXEC_INTERNAL_H
3+
#define _ARM_KEXEC_INTERNAL_H
4+
5+
struct kexec_relocate_data {
6+
unsigned long kexec_start_address;
7+
unsigned long kexec_indirection_page;
8+
unsigned long kexec_mach_type;
9+
unsigned long kexec_r2;
10+
};
11+
12+
#endif

arch/arm/kernel/asm-offsets.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/mm.h>
1313
#include <linux/dma-mapping.h>
1414
#include <asm/cacheflush.h>
15+
#include <asm/kexec-internal.h>
1516
#include <asm/glue-df.h>
1617
#include <asm/glue-pf.h>
1718
#include <asm/mach/arch.h>
@@ -170,5 +171,9 @@ int main(void)
170171
DEFINE(MPU_RGN_PRBAR, offsetof(struct mpu_rgn, prbar));
171172
DEFINE(MPU_RGN_PRLAR, offsetof(struct mpu_rgn, prlar));
172173
#endif
174+
DEFINE(KEXEC_START_ADDR, offsetof(struct kexec_relocate_data, kexec_start_address));
175+
DEFINE(KEXEC_INDIR_PAGE, offsetof(struct kexec_relocate_data, kexec_indirection_page));
176+
DEFINE(KEXEC_MACH_TYPE, offsetof(struct kexec_relocate_data, kexec_mach_type));
177+
DEFINE(KEXEC_R2, offsetof(struct kexec_relocate_data, kexec_r2));
173178
return 0;
174179
}

arch/arm/kernel/machine_kexec.c

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/of_fdt.h>
1414
#include <asm/mmu_context.h>
1515
#include <asm/cacheflush.h>
16+
#include <asm/kexec-internal.h>
1617
#include <asm/fncpy.h>
1718
#include <asm/mach-types.h>
1819
#include <asm/smp_plat.h>
@@ -22,11 +23,6 @@
2223
extern void relocate_new_kernel(void);
2324
extern const unsigned int relocate_new_kernel_size;
2425

25-
extern unsigned long kexec_start_address;
26-
extern unsigned long kexec_indirection_page;
27-
extern unsigned long kexec_mach_type;
28-
extern unsigned long kexec_boot_atags;
29-
3026
static atomic_t waiting_for_crash_ipi;
3127

3228
/*
@@ -159,6 +155,7 @@ void (*kexec_reinit)(void);
159155
void machine_kexec(struct kimage *image)
160156
{
161157
unsigned long page_list, reboot_entry_phys;
158+
struct kexec_relocate_data *data;
162159
void (*reboot_entry)(void);
163160
void *reboot_code_buffer;
164161

@@ -174,18 +171,17 @@ void machine_kexec(struct kimage *image)
174171

175172
reboot_code_buffer = page_address(image->control_code_page);
176173

177-
/* Prepare parameters for reboot_code_buffer*/
178-
set_kernel_text_rw();
179-
kexec_start_address = image->start;
180-
kexec_indirection_page = page_list;
181-
kexec_mach_type = machine_arch_type;
182-
kexec_boot_atags = image->arch.kernel_r2;
183-
184174
/* copy our kernel relocation code to the control code page */
185175
reboot_entry = fncpy(reboot_code_buffer,
186176
&relocate_new_kernel,
187177
relocate_new_kernel_size);
188178

179+
data = reboot_code_buffer + relocate_new_kernel_size;
180+
data->kexec_start_address = image->start;
181+
data->kexec_indirection_page = page_list;
182+
data->kexec_mach_type = machine_arch_type;
183+
data->kexec_r2 = image->arch.kernel_r2;
184+
189185
/* get the identity mapping physical address for the reboot code */
190186
reboot_entry_phys = virt_to_idmap(reboot_entry);
191187

arch/arm/kernel/relocate_kernel.S

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55

66
#include <linux/linkage.h>
77
#include <asm/assembler.h>
8+
#include <asm/asm-offsets.h>
89
#include <asm/kexec.h>
910

1011
.align 3 /* not needed for this code, but keeps fncpy() happy */
1112

1213
ENTRY(relocate_new_kernel)
1314

14-
ldr r0,kexec_indirection_page
15-
ldr r1,kexec_start_address
15+
adr r7, relocate_new_kernel_end
16+
ldr r0, [r7, #KEXEC_INDIR_PAGE]
17+
ldr r1, [r7, #KEXEC_START_ADDR]
1618

1719
/*
1820
* If there is no indirection page (we are doing crashdumps)
@@ -57,34 +59,16 @@ ENTRY(relocate_new_kernel)
5759

5860
2:
5961
/* Jump to relocated kernel */
60-
mov lr,r1
61-
mov r0,#0
62-
ldr r1,kexec_mach_type
63-
ldr r2,kexec_boot_atags
64-
ARM( ret lr )
65-
THUMB( bx lr )
66-
67-
.align
68-
69-
.globl kexec_start_address
70-
kexec_start_address:
71-
.long 0x0
72-
73-
.globl kexec_indirection_page
74-
kexec_indirection_page:
75-
.long 0x0
76-
77-
.globl kexec_mach_type
78-
kexec_mach_type:
79-
.long 0x0
80-
81-
/* phy addr of the atags for the new kernel */
82-
.globl kexec_boot_atags
83-
kexec_boot_atags:
84-
.long 0x0
62+
mov lr, r1
63+
mov r0, #0
64+
ldr r1, [r7, #KEXEC_MACH_TYPE]
65+
ldr r2, [r7, #KEXEC_R2]
66+
ARM( ret lr )
67+
THUMB( bx lr )
8568

8669
ENDPROC(relocate_new_kernel)
8770

71+
.align 3
8872
relocate_new_kernel_end:
8973

9074
.globl relocate_new_kernel_size

0 commit comments

Comments
 (0)