Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tasssadar/757c939f2d028c00d089 to your computer and use it in GitHub Desktop.
Save Tasssadar/757c939f2d028c00d089 to your computer and use it in GitHub Desktop.
Kexec-hardboot patch for Shamu
From d12d6a5300dafba36474cb308a99339d1096842e Mon Sep 17 00:00:00 2001
From: Vojtech Bocek <vbocek@gmail.com>
Date: Thu, 5 Mar 2015 16:51:25 +0100
Subject: [PATCH] Implement kexec-hardboot
"Allows hard booting (i.e., with a full hardware reboot) to a kernel
previously loaded in memory by kexec. This works around the problem of
soft-booted kernel hangs due to improper device shutdown and/or
reinitialization."
More info in /arch/arm/Kconfig.
Original author: Mike Kasick <mike@kasick.org>
Vojtech Bocek <vbocek@gmail.com>:
I've ported it to shamu, it is based of my hammerhead, mako, flo
and grouper ports, which are based of Asus TF201 patch ported
by Jens Andersen <jens.andersen@gmail.com>
Shamu is rather similar to hammerhead - it uses device tree binary instead
of atags. It is also rather picky - its bootloader erases first 128MB of RAM
on reboot, so following hacks had to be applied:
* Copy dtb to location passed in r2 before jumping to kexecd' kernel,
the kernel would just freeze when I left dtb above 128MB of RAM.
* Copy kexecd' kernel to location in the first 128MB before jumping to it,
the decompressor operates very slowly if the kernel is above the cached
area, copying it lower and decompressing it afterwards is much faster.
The guest kernel does not require any modifications, kexec-hardboot
is equivalent to standard kexec in this aspect.
ayysir <dresadd09691@gmail.com>:
kexec: use mem_text_write_kernel_word to set reboot_code_buffer args
in order to avoid protection faults (writes to read-only
kernel memory) when CONFIG_STRICT_MEMORY_RWX is enabled.
Change-Id: Ia701eeaff292df5e344c9312edb61e43209c4b1b
Signed-off-by: Vojtech Bocek <vbocek@gmail.com>
---
arch/arm/Kconfig | 27 +++++++++
arch/arm/boot/compressed/head.S | 64 ++++++++++++++++++++
.../dts/apq8084-shamu/apq8084-moto-common.dtsi | 5 ++
arch/arm/include/asm/kexec.h | 8 +++
arch/arm/kernel/machine_kexec.c | 39 +++++++++++--
arch/arm/kernel/relocate_kernel.S | 68 ++++++++++++++++++++++
arch/arm/mach-msm/include/mach/memory.h | 12 ++++
arch/arm/mach-msm/restart.c | 28 +++++++++
include/linux/kexec.h | 14 ++++-
include/uapi/linux/kexec.h | 4 ++
kernel/kexec.c | 4 ++
11 files changed, 264 insertions(+), 9 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 12ecf9b..4a24b48 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2177,6 +2177,33 @@ config ATAGS_PROC
Should the atags used to boot the kernel be exported in an "atags"
file in procfs. Useful with kexec.
+config KEXEC_HARDBOOT
+ bool "Support hard booting to a kexec kernel"
+ depends on KEXEC
+ help
+ Allows hard booting (i.e., with a full hardware reboot) to a kernel
+ previously loaded in memory by kexec. This works around the problem of
+ soft-booted kernel hangs due to improper device shutdown and/or
+ reinitialization. Support is comprised of two components:
+
+ First, a "hardboot" flag is added to the kexec syscall to force a hard
+ reboot in relocate_new_kernel() (which requires machine-specific assembly
+ code). This also requires the kexec userspace tool to load the kexec'd
+ kernel in memory region left untouched by the bootloader (i.e., not
+ explicitly cleared and not overwritten by the boot kernel). Just prior
+ to reboot, the kexec kernel arguments are stashed in a machine-specific
+ memory page that must also be preserved. Note that this hardboot page
+ need not be reserved during regular kernel execution.
+
+ Second, the zImage decompresor of the boot (bootloader-loaded) kernel is
+ modified to check the hardboot page for fresh kexec arguments, and if
+ present, attempts to jump to the kexec'd kernel preserved in memory.
+
+ Note that hardboot support is only required in the boot kernel and any
+ kernel capable of performing a hardboot kexec. It is _not_ required by a
+ kexec'd kernel.
+
+
config CRASH_DUMP
bool "Build kdump crash kernel (EXPERIMENTAL)"
help
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index a7cd673..abd508b 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -11,6 +11,11 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#ifdef CONFIG_KEXEC_HARDBOOT
+ #include <asm/kexec.h>
+ #include <asm/memory.h>
+#endif
+
.arch armv7-a
/*
* Debugging stuff
@@ -142,6 +147,65 @@ start:
mov r7, r1 @ save architecture ID
mov r8, r2 @ save atags pointer
+#ifdef CONFIG_KEXEC_HARDBOOT
+ /* Check hardboot page for a kexec kernel. */
+ ldr r3, =KEXEC_HB_PAGE_ADDR
+ ldr r0, [r3]
+ ldr r1, =KEXEC_HB_PAGE_MAGIC
+ teq r0, r1
+ bne not_booting_other
+
+ /* Clear hardboot page magic to avoid boot loop. */
+ mov r0, #0
+ str r0, [r3]
+
+ /*
+ * Copy dtb from location up high in memory to default location.
+ * Kernel freezes if this is not done.
+ */
+ ldr r1, [r3, #12] @ kexec_boot_atags
+ ldr r2, [r3, #16] @ kexec_boot_atags_len
+ mov r5, #0 @ iterator
+catags_cpy:
+ ldr r0, [r1, r5] @ from kexec_boot_atags
+ str r0, [r8, r5] @ to atags_pointer
+ add r5, r5, #4
+ cmp r5, r2
+ blo catags_cpy
+
+#ifdef KEXEC_HB_KERNEL_LOC
+ /*
+ * Copy kernel from location up high in memory to location in first 128MB.
+ * Bootloader erases first 128MB of ram on reboot, so it cannot be in there
+ * before reboot, but decompressing in location above 128MB takes
+ * a long time. This memcpy is much quicker, for some reason.
+ */
+ ldr r2, [r3, #4] @ kexec_start_address
+ ldr r4, [r3, #20] @ kexec_kernel_len
+ ldr r6, =KEXEC_HB_KERNEL_LOC @ target
+ mov r5, #0 @ iterator
+kernel_cpy:
+ ldr r0, [r2, r5] @ from kexec_start_address
+ str r0, [r6, r5] @ to KEXEC_HB_KERNEL_LOC
+ add r5, r5, #4
+ cmp r5, r4
+ blo kernel_cpy
+#else
+ ldr r6, [r3, #4] @ kexec_start_address
+#endif
+
+ /* set registers and boot kexecd' kernel */
+ mov r0, #0
+ ldr r1, [r3, #8] @ kexec_mach_type
+ mov r2, r8 @ atags pointer
+ mov pc, r6
+
+ .ltorg
+
+not_booting_other:
+#endif
+
+
#ifndef __ARM_ARCH_2__
/*
* Booting from Angel - need to enter SVC mode and disable
diff --git a/arch/arm/boot/dts/apq8084-shamu/apq8084-moto-common.dtsi b/arch/arm/boot/dts/apq8084-shamu/apq8084-moto-common.dtsi
index b0469e2..32f4266 100644
--- a/arch/arm/boot/dts/apq8084-shamu/apq8084-moto-common.dtsi
+++ b/arch/arm/boot/dts/apq8084-shamu/apq8084-moto-common.dtsi
@@ -62,6 +62,11 @@
};
};
+ kexec_hardboot-hole {
+ compatible = "qcom,msm-contig-mem";
+ qcom,memblock-reserve = <0x3e9df000 0x1000>;
+ };
+
sound-9330 {
status = "disabled";
};
diff --git a/arch/arm/include/asm/kexec.h b/arch/arm/include/asm/kexec.h
index c2b9b4b..564c55b 100644
--- a/arch/arm/include/asm/kexec.h
+++ b/arch/arm/include/asm/kexec.h
@@ -17,6 +17,10 @@
#define KEXEC_ARM_ATAGS_OFFSET 0x1000
#define KEXEC_ARM_ZIMAGE_OFFSET 0x8000
+#ifdef CONFIG_KEXEC_HARDBOOT
+ #define KEXEC_HB_PAGE_MAGIC 0x4a5db007
+#endif
+
#ifndef __ASSEMBLY__
/**
@@ -53,6 +57,10 @@ static inline void crash_setup_regs(struct pt_regs *newregs,
/* Function pointer to optional machine-specific reinitialization */
extern void (*kexec_reinit)(void);
+#ifdef CONFIG_KEXEC_HARDBOOT
+extern void (*kexec_hardboot_hook)(void);
+#endif
+
#endif /* __ASSEMBLY__ */
#endif /* CONFIG_KEXEC */
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c
index c3ef920..08eeb9a 100644
--- a/arch/arm/kernel/machine_kexec.c
+++ b/arch/arm/kernel/machine_kexec.c
@@ -16,6 +16,9 @@
#include <asm/cacheflush.h>
#include <asm/mach-types.h>
#include <asm/system_misc.h>
+#include <linux/memblock.h>
+#include <linux/of_fdt.h>
+#include <asm/mmu_writeable.h>
extern const unsigned char relocate_new_kernel[];
extern const unsigned int relocate_new_kernel_size;
@@ -24,6 +27,12 @@ extern unsigned long kexec_start_address;
extern unsigned long kexec_indirection_page;
extern unsigned long kexec_mach_type;
extern unsigned long kexec_boot_atags;
+#ifdef CONFIG_KEXEC_HARDBOOT
+extern unsigned long kexec_hardboot;
+extern unsigned long kexec_boot_atags_len;
+extern unsigned long kexec_kernel_len;
+void (*kexec_hardboot_hook)(void);
+#endif
static atomic_t waiting_for_crash_ipi;
@@ -49,12 +58,22 @@ int machine_kexec_prepare(struct kimage *image)
current_segment->memsz))
return -EINVAL;
+#ifdef CONFIG_KEXEC_HARDBOOT
+ if(current_segment->mem == image->start)
+ mem_text_write_kernel_word(&kexec_kernel_len, current_segment->memsz);
+#endif
+
err = get_user(header, (__be32*)current_segment->buf);
if (err)
return err;
if (be32_to_cpu(header) == OF_DT_HEADER)
- kexec_boot_atags = current_segment->mem;
+ {
+ mem_text_write_kernel_word(&kexec_boot_atags, current_segment->mem);
+#ifdef CONFIG_KEXEC_HARDBOOT
+ mem_text_write_kernel_word(&kexec_boot_atags_len, current_segment->memsz);
+#endif
+ }
}
return 0;
}
@@ -148,12 +167,14 @@ void machine_kexec(struct kimage *image)
reboot_code_buffer = page_address(image->control_code_page);
/* Prepare parameters for reboot_code_buffer*/
- kexec_start_address = image->start;
- kexec_indirection_page = page_list;
- kexec_mach_type = machine_arch_type;
+ mem_text_write_kernel_word(&kexec_start_address, image->start);
+ mem_text_write_kernel_word(&kexec_indirection_page, page_list);
+ mem_text_write_kernel_word(&kexec_mach_type, machine_arch_type);
if (!kexec_boot_atags)
- kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;
-
+ mem_text_write_kernel_word(&kexec_boot_atags, image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET);
+#ifdef CONFIG_KEXEC_HARDBOOT
+ mem_text_write_kernel_word(&kexec_hardboot, image->hardboot);
+#endif
/* copy our kernel relocation code to the control code page */
memcpy(reboot_code_buffer,
@@ -167,6 +188,12 @@ void machine_kexec(struct kimage *image)
if (kexec_reinit)
kexec_reinit();
+#ifdef CONFIG_KEXEC_HARDBOOT
+ /* Run any final machine-specific shutdown code. */
+ if (image->hardboot && kexec_hardboot_hook)
+ kexec_hardboot_hook();
+#endif
+
soft_restart(reboot_code_buffer_phys);
}
diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S
index d0cdedf..3411691 100644
--- a/arch/arm/kernel/relocate_kernel.S
+++ b/arch/arm/kernel/relocate_kernel.S
@@ -3,6 +3,14 @@
*/
#include <asm/kexec.h>
+#ifdef CONFIG_KEXEC_HARDBOOT
+#include <asm/memory.h>
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ #include <mach/iomap.h>
+#elif defined(CONFIG_ARCH_APQ8064) || defined(CONFIG_ARCH_MSM8974) || defined(CONFIG_ARCH_APQ8084)
+ #include <mach/msm_iomap.h>
+#endif
+#endif
.globl relocate_new_kernel
relocate_new_kernel:
@@ -52,6 +60,12 @@ relocate_new_kernel:
b 0b
2:
+#ifdef CONFIG_KEXEC_HARDBOOT
+ ldr r0, kexec_hardboot
+ teq r0, #0
+ bne hardboot
+#endif
+
/* Jump to relocated kernel */
mov lr,r1
mov r0,#0
@@ -60,6 +74,46 @@ relocate_new_kernel:
ARM( mov pc, lr )
THUMB( bx lr )
+#ifdef CONFIG_KEXEC_HARDBOOT
+hardboot:
+ /* Stash boot arguments in hardboot page:
+ * 0: KEXEC_HB_PAGE_MAGIC
+ * 4: kexec_start_address
+ * 8: kexec_mach_type
+ * 12: kexec_boot_atags
+ * 16: kexec_boot_atags_len
+ * 20: kexec_kernel_len */
+ ldr r0, =KEXEC_HB_PAGE_ADDR
+ str r1, [r0, #4]
+ ldr r1, kexec_mach_type
+ str r1, [r0, #8]
+ ldr r1, kexec_boot_atags
+ str r1, [r0, #12]
+ ldr r1, kexec_boot_atags_len
+ str r1, [r0, #16]
+ ldr r1, kexec_kernel_len
+ str r1, [r0, #20]
+ ldr r1, =KEXEC_HB_PAGE_MAGIC
+ str r1, [r0]
+
+#if defined(CONFIG_ARCH_MSM8974)
+ /* Restart using the PMIC chip, see mach-msm/restart.c */
+ ldr r0, =MSM8974_MPM2_PSHOLD_PHYS
+ mov r1, #0
+ str r1, [r0, #0]
+loop: b loop
+#elif defined(CONFIG_ARCH_APQ8084)
+ /* Restart using the PMIC chip, see mach-msm/restart.c */
+ ldr r0, =APQ8084_MPM2_PSHOLD_PHYS
+ mov r1, #0
+ str r1, [r0, #0]
+loop: b loop
+#else
+#error "No reboot method defined for hardboot."
+#endif
+
+ .ltorg
+#endif
.align
.globl kexec_start_address
@@ -79,6 +133,20 @@ kexec_mach_type:
kexec_boot_atags:
.long 0x0
+#ifdef CONFIG_KEXEC_HARDBOOT
+ .globl kexec_boot_atags_len
+kexec_boot_atags_len:
+ .long 0x0
+
+ .globl kexec_kernel_len
+kexec_kernel_len:
+ .long 0x0
+
+ .globl kexec_hardboot
+kexec_hardboot:
+ .long 0x0
+#endif
+
relocate_new_kernel_end:
.globl relocate_new_kernel_size
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index 9225230..c66e7ae 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -20,6 +20,18 @@
/* physical offset of RAM */
#define PLAT_PHYS_OFFSET UL(CONFIG_PHYS_OFFSET)
+#if defined(CONFIG_KEXEC_HARDBOOT)
+#if defined(CONFIG_MACH_MSM8974_HAMMERHEAD)
+#define KEXEC_HB_PAGE_ADDR UL(0x10100000)
+#define KEXEC_HB_KERNEL_LOC UL(0x3208000)
+#elif defined(CONFIG_ARCH_APQ8084) /* shamu */
+#define KEXEC_HB_PAGE_ADDR UL(0x3e9df000)
+#define KEXEC_HB_KERNEL_LOC UL(0x3208000)
+#else
+#error "Adress for kexec hardboot page not defined"
+#endif
+#endif
+
#ifndef __ASSEMBLY__
void clean_and_invalidate_caches(unsigned long, unsigned long, unsigned long);
void clean_caches(unsigned long, unsigned long, unsigned long);
diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
index f14f7e8..829b1be 100644
--- a/arch/arm/mach-msm/restart.c
+++ b/arch/arm/mach-msm/restart.c
@@ -38,6 +38,10 @@
#include "timer.h"
#include "wdog_debug.h"
+#ifdef CONFIG_KEXEC_HARDBOOT
+#include <asm/kexec.h>
+#endif
+
#define WDT0_RST 0x38
#define WDT0_EN 0x40
#define WDT0_BARK_TIME 0x4C
@@ -277,6 +281,26 @@ void msm_restart(char mode, const char *cmd)
printk(KERN_ERR "Restarting has failed\n");
}
+#ifdef CONFIG_KEXEC_HARDBOOT
+static void msm_kexec_hardboot_hook(void)
+{
+ set_dload_mode(0);
+
+ // Set PMIC to restart-on-poweroff
+ pm8xxx_reset_pwr_off(1);
+
+ // These are executed on normal reboot, but with kexec-hardboot,
+ // they reboot/panic the system immediately.
+#if 0
+ qpnp_pon_system_pwr_off(PON_POWER_OFF_WARM_RESET);
+
+ /* Needed to bypass debug image on some chips */
+ msm_disable_wdog_debug();
+ halt_spmi_pmic_arbiter();
+#endif
+}
+#endif
+
static int __init msm_restart_init(void)
{
struct device_node *np;
@@ -330,6 +354,10 @@ static int __init msm_restart_init(void)
if (scm_is_call_available(SCM_SVC_PWR, SCM_IO_DISABLE_PMIC_ARBITER) > 0)
scm_pmic_arbiter_disable_supported = true;
+#ifdef CONFIG_KEXEC_HARDBOOT
+ kexec_hardboot_hook = msm_kexec_hardboot_hook;
+#endif
+
return 0;
err_restart_reason:
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index 5fd33dc..ba5de8f 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -111,6 +111,10 @@ struct kimage {
#define KEXEC_TYPE_CRASH 1
unsigned int preserve_context : 1;
+#ifdef CONFIG_KEXEC_HARDBOOT
+ unsigned int hardboot : 1;
+#endif
+
#ifdef ARCH_HAS_KIMAGE_ARCH
struct kimage_arch arch;
#endif
@@ -176,10 +180,14 @@ extern struct kimage *kexec_crash_image;
#endif
/* List of defined/legal kexec flags */
-#ifndef CONFIG_KEXEC_JUMP
-#define KEXEC_FLAGS KEXEC_ON_CRASH
-#else
+#if defined(CONFIG_KEXEC_JUMP) && defined(CONFIG_KEXEC_HARDBOOT)
+#define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT | KEXEC_HARDBOOT)
+#elif defined(CONFIG_KEXEC_JUMP)
#define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_PRESERVE_CONTEXT)
+#elif defined(CONFIG_KEXEC_HARDBOOT)
+#define KEXEC_FLAGS (KEXEC_ON_CRASH | KEXEC_HARDBOOT)
+#else
+#define KEXEC_FLAGS (KEXEC_ON_CRASH)
#endif
#define VMCOREINFO_BYTES (4096)
diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h
index 104838f..a94f254 100644
--- a/include/uapi/linux/kexec.h
+++ b/include/uapi/linux/kexec.h
@@ -13,6 +13,10 @@
#define KEXEC_PRESERVE_CONTEXT 0x00000002
#define KEXEC_ARCH_MASK 0xffff0000
+#ifdef CONFIG_KEXEC_HARDBOOT
+#define KEXEC_HARDBOOT 0x00000004
+#endif
+
/* These values match the ELF architecture values.
* Unless there is a good reason that should continue to be the case.
*/
diff --git a/kernel/kexec.c b/kernel/kexec.c
index 1f8d938..fb44c10 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -1002,6 +1002,10 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments,
if (flags & KEXEC_PRESERVE_CONTEXT)
image->preserve_context = 1;
+#ifdef CONFIG_KEXEC_HARDBOOT
+ if (flags & KEXEC_HARDBOOT)
+ image->hardboot = 1;
+#endif
result = machine_kexec_prepare(image);
if (result)
goto out;
--
2.1.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment