mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-12-20 05:14:32 -05:00
578 lines
17 KiB
Diff
578 lines
17 KiB
Diff
|
From 7fd9acebf8d5973f3d345cac4713691cff517e5c Mon Sep 17 00:00:00 2001
|
||
|
From: Tad <tad@spotco.us>
|
||
|
Date: Sat, 17 Oct 2015 20:49:21 -0400
|
||
|
Subject: [PATCH] Implement KEXEC
|
||
|
|
||
|
---
|
||
|
arch/arm/Kconfig | 26 ++++++++++
|
||
|
arch/arm/boot/compressed/head.S | 64 ++++++++++++++++++++++++
|
||
|
arch/arm/configs/cyanogenmod_bacon_defconfig | 6 ++-
|
||
|
arch/arm/include/asm/kexec.h | 8 +++
|
||
|
arch/arm/kernel/machine_kexec.c | 58 +++++++++++++++++++--
|
||
|
arch/arm/kernel/relocate_kernel.S | 75 ++++++++++++++++++++++++++++
|
||
|
arch/arm/mach-msm/include/mach/memory.h | 16 ++++++
|
||
|
arch/arm/mach-msm/oppo/board-8974-oppo.c | 27 ++++++++++
|
||
|
arch/arm/mach-msm/restart.c | 28 +++++++++++
|
||
|
include/linux/kexec.h | 19 +++++--
|
||
|
kernel/kexec.c | 4 ++
|
||
|
11 files changed, 322 insertions(+), 9 deletions(-)
|
||
|
|
||
|
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
|
||
|
index 37d3c6d..f801a19 100644
|
||
|
--- a/arch/arm/Kconfig
|
||
|
+++ b/arch/arm/Kconfig
|
||
|
@@ -2299,6 +2299,32 @@ 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)"
|
||
|
depends on EXPERIMENTAL
|
||
|
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
|
||
|
index d3892ef..be05aa1 100644
|
||
|
--- a/arch/arm/boot/compressed/head.S
|
||
|
+++ b/arch/arm/boot/compressed/head.S
|
||
|
@@ -11,6 +11,12 @@
|
||
|
#include <linux/linkage.h>
|
||
|
|
||
|
.arch armv7-a
|
||
|
+
|
||
|
+#ifdef CONFIG_KEXEC_HARDBOOT
|
||
|
+ #include <asm/kexec.h>
|
||
|
+ #include <asm/memory.h>
|
||
|
+#endif
|
||
|
+
|
||
|
/*
|
||
|
* Debugging stuff
|
||
|
*
|
||
|
@@ -136,6 +142,64 @@ start:
|
||
|
1: 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 on hammerhead erases first 128MB of ram on reboot, so it can't
|
||
|
+ * 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/configs/cyanogenmod_bacon_defconfig b/arch/arm/configs/cyanogenmod_bacon_defconfig
|
||
|
index 0a8ac5f..009a34d 100644
|
||
|
--- a/arch/arm/configs/cyanogenmod_bacon_defconfig
|
||
|
+++ b/arch/arm/configs/cyanogenmod_bacon_defconfig
|
||
|
@@ -695,7 +695,9 @@ CONFIG_ZBOOT_ROM_BSS=0
|
||
|
# CONFIG_ARM_APPENDED_DTB is not set
|
||
|
CONFIG_CMDLINE=""
|
||
|
# CONFIG_XIP_KERNEL is not set
|
||
|
-# CONFIG_KEXEC is not set
|
||
|
+CONFIG_KEXEC=y
|
||
|
+CONFIG_KEXEC_HARDBOOT=y
|
||
|
+CONFIG_ATAGS_PROC=n
|
||
|
# CONFIG_CRASH_DUMP is not set
|
||
|
# CONFIG_AUTO_ZRELADDR is not set
|
||
|
|
||
|
@@ -1285,7 +1287,7 @@ CONFIG_OF=y
|
||
|
#
|
||
|
# Device Tree and Open Firmware support
|
||
|
#
|
||
|
-# CONFIG_PROC_DEVICETREE is not set
|
||
|
+CONFIG_PROC_DEVICETREE=y
|
||
|
# CONFIG_OF_SELFTEST is not set
|
||
|
CONFIG_OF_FLATTREE=y
|
||
|
CONFIG_OF_EARLY_FLATTREE=y
|
||
|
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 357b651..29cdd2f 100644
|
||
|
--- a/arch/arm/kernel/machine_kexec.c
|
||
|
+++ b/arch/arm/kernel/machine_kexec.c
|
||
|
@@ -14,6 +14,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;
|
||
|
@@ -22,6 +25,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;
|
||
|
|
||
|
@@ -32,6 +41,37 @@ static atomic_t waiting_for_crash_ipi;
|
||
|
|
||
|
int machine_kexec_prepare(struct kimage *image)
|
||
|
{
|
||
|
+ struct kexec_segment *current_segment;
|
||
|
+ __be32 header;
|
||
|
+ int i, err;
|
||
|
+
|
||
|
+ /* No segment at default ATAGs address. try to locate
|
||
|
+ * a dtb using magic */
|
||
|
+ for (i = 0; i < image->nr_segments; i++) {
|
||
|
+ current_segment = &image->segment[i];
|
||
|
+
|
||
|
+ err = memblock_is_region_memory(current_segment->mem,
|
||
|
+ current_segment->memsz);
|
||
|
+ if (!err)
|
||
|
+ 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)
|
||
|
+ {
|
||
|
+ 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;
|
||
|
}
|
||
|
|
||
|
@@ -123,10 +163,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;
|
||
|
- kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;
|
||
|
+ 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)
|
||
|
+ 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,
|
||
|
@@ -140,6 +184,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..0e45ffc 100644
|
||
|
--- a/arch/arm/kernel/relocate_kernel.S
|
||
|
+++ b/arch/arm/kernel/relocate_kernel.S
|
||
|
@@ -4,6 +4,15 @@
|
||
|
|
||
|
#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)
|
||
|
+ #include <mach/msm_iomap.h>
|
||
|
+#endif
|
||
|
+#endif
|
||
|
+
|
||
|
.globl relocate_new_kernel
|
||
|
relocate_new_kernel:
|
||
|
|
||
|
@@ -52,6 +61,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 +75,52 @@ 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_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
|
||
|
+ ldr r0, =TEGRA_PMC_BASE
|
||
|
+ ldr r1, [r0]
|
||
|
+ orr r1, r1, #0x10
|
||
|
+ str r1, [r0]
|
||
|
+loop: b loop
|
||
|
+#elif defined(CONFIG_ARCH_APQ8064)
|
||
|
+ /* Restart using the PMIC chip, see mach-msm/restart.c */
|
||
|
+ ldr r0, =APQ8064_TLMM_PHYS
|
||
|
+ mov r1, #0
|
||
|
+ str r1, [r0, #0x820] @ PSHOLD_CTL_SU
|
||
|
+loop: b loop
|
||
|
+#elif 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
|
||
|
+#else
|
||
|
+#error "No reboot method defined for hardboot."
|
||
|
+#endif
|
||
|
+
|
||
|
+ .ltorg
|
||
|
+#endif
|
||
|
.align
|
||
|
|
||
|
.globl kexec_start_address
|
||
|
@@ -79,6 +140,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..1c87b96 100644
|
||
|
--- a/arch/arm/mach-msm/include/mach/memory.h
|
||
|
+++ b/arch/arm/mach-msm/include/mach/memory.h
|
||
|
@@ -20,6 +20,22 @@
|
||
|
/* physical offset of RAM */
|
||
|
#define PLAT_PHYS_OFFSET UL(CONFIG_PHYS_OFFSET)
|
||
|
|
||
|
+#if defined(CONFIG_KEXEC_HARDBOOT)
|
||
|
+#if defined(CONFIG_MACH_APQ8064_FLO)
|
||
|
+#define KEXEC_HB_PAGE_ADDR UL(0x88C00000)
|
||
|
+#elif defined(CONFIG_MACH_APQ8064_MAKO)
|
||
|
+#define KEXEC_HB_PAGE_ADDR UL(0x88600000)
|
||
|
+#elif defined(CONFIG_MACH_MSM8974_HAMMERHEAD)
|
||
|
+#define KEXEC_HB_PAGE_ADDR UL(0x10100000)
|
||
|
+#define KEXEC_HB_KERNEL_LOC UL(0x3208000)
|
||
|
+#elif defined(CONFIG_MACH_OPPO_MSM8974)
|
||
|
+#define KEXEC_HB_PAGE_ADDR UL(0x2F600000)
|
||
|
+#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/oppo/board-8974-oppo.c b/arch/arm/mach-msm/oppo/board-8974-oppo.c
|
||
|
index eb24545..10bbbda 100644
|
||
|
--- a/arch/arm/mach-msm/oppo/board-8974-oppo.c
|
||
|
+++ b/arch/arm/mach-msm/oppo/board-8974-oppo.c
|
||
|
@@ -54,6 +54,13 @@
|
||
|
|
||
|
#include <linux/pcb_version.h>
|
||
|
|
||
|
+#ifdef CONFIG_KEXEC_HARDBOOT
|
||
|
+#include <asm/setup.h>
|
||
|
+#include <asm/memory.h>
|
||
|
+#include <linux/memblock.h>
|
||
|
+#define OPPO_PERSISTENT_RAM_SIZE (SZ_1M)
|
||
|
+#endif
|
||
|
+
|
||
|
static struct platform_device *ram_console_dev;
|
||
|
|
||
|
static struct persistent_ram_descriptor msm_prd[] __initdata = {
|
||
|
@@ -72,6 +79,26 @@ static struct persistent_ram msm_pr __initdata = {
|
||
|
|
||
|
void __init msm_8974_reserve(void)
|
||
|
{
|
||
|
+#ifdef CONFIG_KEXEC_HARDBOOT
|
||
|
+ // Reserve space for hardboot page - just after ram_console,
|
||
|
+ // at the start of second memory bank
|
||
|
+ int ret;
|
||
|
+ phys_addr_t start;
|
||
|
+ struct membank* bank;
|
||
|
+
|
||
|
+ if (meminfo.nr_banks < 2) {
|
||
|
+ pr_err("%s: not enough membank\n", __func__);
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ bank = &meminfo.bank[1];
|
||
|
+ start = bank->start + SZ_1M + OPPO_PERSISTENT_RAM_SIZE;
|
||
|
+ ret = memblock_remove(start, SZ_1M);
|
||
|
+ if(!ret)
|
||
|
+ pr_info("Hardboot page reserved at 0x%X\n", start);
|
||
|
+ else
|
||
|
+ pr_err("Failed to reserve space for hardboot page at 0x%X!\n", start);
|
||
|
+#endif
|
||
|
persistent_ram_early_init(&msm_pr);
|
||
|
of_scan_flat_dt(dt_scan_for_memory_reserve, NULL);
|
||
|
}
|
||
|
diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
|
||
|
index a04ab8d..fe89976 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
|
||
|
@@ -373,6 +377,26 @@ static int __init msm_pmic_restart_init(void)
|
||
|
|
||
|
late_initcall(msm_pmic_restart_init);
|
||
|
|
||
|
+#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)
|
||
|
{
|
||
|
#ifdef CONFIG_MSM_DLOAD_MODE
|
||
|
@@ -392,6 +416,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;
|
||
|
}
|
||
|
early_initcall(msm_restart_init);
|
||
|
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
|
||
|
index af84a25..a4509ad 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
|
||
|
@@ -178,6 +182,11 @@ extern struct kimage *kexec_crash_image;
|
||
|
|
||
|
#define KEXEC_ON_CRASH 0x00000001
|
||
|
#define KEXEC_PRESERVE_CONTEXT 0x00000002
|
||
|
+
|
||
|
+#ifdef CONFIG_KEXEC_HARDBOOT
|
||
|
+#define KEXEC_HARDBOOT 0x00000004
|
||
|
+#endif
|
||
|
+
|
||
|
#define KEXEC_ARCH_MASK 0xffff0000
|
||
|
|
||
|
/* These values match the ELF architecture values.
|
||
|
@@ -196,10 +205,14 @@ extern struct kimage *kexec_crash_image;
|
||
|
#define KEXEC_ARCH_MIPS ( 8 << 16)
|
||
|
|
||
|
/* 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/kernel/kexec.c b/kernel/kexec.c
|
||
|
index 4e2e472..aef7893 100644
|
||
|
--- a/kernel/kexec.c
|
||
|
+++ b/kernel/kexec.c
|
||
|
@@ -1004,6 +1004,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;
|