Commit 4d62e81b authored by Russell King's avatar Russell King

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: default avatarGiancarlo Ferrari <giancarlo.ferrari89@gmail.com>
Tested-by: default avatarGiancarlo Ferrari <giancarlo.ferrari89@gmail.com>
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
parent 9c698bff
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ARM_KEXEC_INTERNAL_H
#define _ARM_KEXEC_INTERNAL_H
struct kexec_relocate_data {
unsigned long kexec_start_address;
unsigned long kexec_indirection_page;
unsigned long kexec_mach_type;
unsigned long kexec_r2;
};
#endif
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/kexec-internal.h>
#include <asm/glue-df.h> #include <asm/glue-df.h>
#include <asm/glue-pf.h> #include <asm/glue-pf.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
...@@ -170,5 +171,9 @@ int main(void) ...@@ -170,5 +171,9 @@ int main(void)
DEFINE(MPU_RGN_PRBAR, offsetof(struct mpu_rgn, prbar)); DEFINE(MPU_RGN_PRBAR, offsetof(struct mpu_rgn, prbar));
DEFINE(MPU_RGN_PRLAR, offsetof(struct mpu_rgn, prlar)); DEFINE(MPU_RGN_PRLAR, offsetof(struct mpu_rgn, prlar));
#endif #endif
DEFINE(KEXEC_START_ADDR, offsetof(struct kexec_relocate_data, kexec_start_address));
DEFINE(KEXEC_INDIR_PAGE, offsetof(struct kexec_relocate_data, kexec_indirection_page));
DEFINE(KEXEC_MACH_TYPE, offsetof(struct kexec_relocate_data, kexec_mach_type));
DEFINE(KEXEC_R2, offsetof(struct kexec_relocate_data, kexec_r2));
return 0; return 0;
} }
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/of_fdt.h> #include <linux/of_fdt.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/kexec-internal.h>
#include <asm/fncpy.h> #include <asm/fncpy.h>
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <asm/smp_plat.h> #include <asm/smp_plat.h>
...@@ -22,11 +23,6 @@ ...@@ -22,11 +23,6 @@
extern void relocate_new_kernel(void); extern void relocate_new_kernel(void);
extern const unsigned int relocate_new_kernel_size; extern const unsigned int relocate_new_kernel_size;
extern unsigned long kexec_start_address;
extern unsigned long kexec_indirection_page;
extern unsigned long kexec_mach_type;
extern unsigned long kexec_boot_atags;
static atomic_t waiting_for_crash_ipi; static atomic_t waiting_for_crash_ipi;
/* /*
...@@ -159,6 +155,7 @@ void (*kexec_reinit)(void); ...@@ -159,6 +155,7 @@ void (*kexec_reinit)(void);
void machine_kexec(struct kimage *image) void machine_kexec(struct kimage *image)
{ {
unsigned long page_list, reboot_entry_phys; unsigned long page_list, reboot_entry_phys;
struct kexec_relocate_data *data;
void (*reboot_entry)(void); void (*reboot_entry)(void);
void *reboot_code_buffer; void *reboot_code_buffer;
...@@ -174,18 +171,17 @@ void machine_kexec(struct kimage *image) ...@@ -174,18 +171,17 @@ void machine_kexec(struct kimage *image)
reboot_code_buffer = page_address(image->control_code_page); reboot_code_buffer = page_address(image->control_code_page);
/* Prepare parameters for reboot_code_buffer*/
set_kernel_text_rw();
kexec_start_address = image->start;
kexec_indirection_page = page_list;
kexec_mach_type = machine_arch_type;
kexec_boot_atags = image->arch.kernel_r2;
/* copy our kernel relocation code to the control code page */ /* copy our kernel relocation code to the control code page */
reboot_entry = fncpy(reboot_code_buffer, reboot_entry = fncpy(reboot_code_buffer,
&relocate_new_kernel, &relocate_new_kernel,
relocate_new_kernel_size); relocate_new_kernel_size);
data = reboot_code_buffer + relocate_new_kernel_size;
data->kexec_start_address = image->start;
data->kexec_indirection_page = page_list;
data->kexec_mach_type = machine_arch_type;
data->kexec_r2 = image->arch.kernel_r2;
/* get the identity mapping physical address for the reboot code */ /* get the identity mapping physical address for the reboot code */
reboot_entry_phys = virt_to_idmap(reboot_entry); reboot_entry_phys = virt_to_idmap(reboot_entry);
......
...@@ -5,14 +5,16 @@ ...@@ -5,14 +5,16 @@
#include <linux/linkage.h> #include <linux/linkage.h>
#include <asm/assembler.h> #include <asm/assembler.h>
#include <asm/asm-offsets.h>
#include <asm/kexec.h> #include <asm/kexec.h>
.align 3 /* not needed for this code, but keeps fncpy() happy */ .align 3 /* not needed for this code, but keeps fncpy() happy */
ENTRY(relocate_new_kernel) ENTRY(relocate_new_kernel)
ldr r0,kexec_indirection_page adr r7, relocate_new_kernel_end
ldr r1,kexec_start_address ldr r0, [r7, #KEXEC_INDIR_PAGE]
ldr r1, [r7, #KEXEC_START_ADDR]
/* /*
* If there is no indirection page (we are doing crashdumps) * If there is no indirection page (we are doing crashdumps)
...@@ -57,34 +59,16 @@ ENTRY(relocate_new_kernel) ...@@ -57,34 +59,16 @@ ENTRY(relocate_new_kernel)
2: 2:
/* Jump to relocated kernel */ /* Jump to relocated kernel */
mov lr,r1 mov lr, r1
mov r0,#0 mov r0, #0
ldr r1,kexec_mach_type ldr r1, [r7, #KEXEC_MACH_TYPE]
ldr r2,kexec_boot_atags ldr r2, [r7, #KEXEC_R2]
ARM( ret lr ) ARM( ret lr )
THUMB( bx lr ) THUMB( bx lr )
.align
.globl kexec_start_address
kexec_start_address:
.long 0x0
.globl kexec_indirection_page
kexec_indirection_page:
.long 0x0
.globl kexec_mach_type
kexec_mach_type:
.long 0x0
/* phy addr of the atags for the new kernel */
.globl kexec_boot_atags
kexec_boot_atags:
.long 0x0
ENDPROC(relocate_new_kernel) ENDPROC(relocate_new_kernel)
.align 3
relocate_new_kernel_end: relocate_new_kernel_end:
.globl relocate_new_kernel_size .globl relocate_new_kernel_size
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment