[Kindle Fire HDX 7" & 8.9" Original Android Develop] ggow:
- I have implemented a first cut of kexec hardboot patch for the Apollo and Thor Devices

- [MENTION=3418703]Tasssadar[/MENTION] created the excellent MultiROM

- I am working to provide support for our devices (Apollo and Thor) and as such this is first step

- The commit in my SlimLP kernel is here.

- This is the patch:

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 9468df5..a8b637a 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2249,6 +2249,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 64a6d6f..6c9d423 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -10,6 +10,13 @@
*/
#include <linux/linkage.h>

+	.arch	armv7-a
+
+#ifdef CONFIG_KEXEC_HARDBOOT
+ #include <asm/kexec.h>
+ #include <asm/memory.h>
+#endif
+
/*
* Debugging stuff
*
@@ -135,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
@@ -176,7 +241,19 @@ not_angel:
ldr	r4, =zreladdr
#endif

-	bl	cache_on
+	/*
+	* Set up a page table only if it won't overwrite ourself.
+	* That means r4 < pc && r4 - 16k page directory > &_end.
+	* Given that r4 > &_end is most unfrequent, we add a rough
+	* additional 1MB of room for a possible appended DTB.
+	*/
+	mov	r0, pc
+	cmp	r0, r4
+	ldrcc	r0, LC0+32
+	addcc	r0, r0, pc
+	cmpcc	r4, r0
+	orrcc	r4, r4, #1	@ remember we skipped cache_on
+	blcs	cache_on

restart:	adr	r0, LC0
ldmia	r0, {r1, r2, r3, r6, r10, r11, r12}
@@ -222,7 +299,7 @@ restart:	adr	r0, LC0
* r0 = delta
* r2 = BSS start
* r3 = BSS end
- * r4 = final kernel address
+ * r4 = final kernel address (possibly with LSB set)
* r5 = appended dtb size (still unknown)
* r6 = _edata
* r7 = architecture ID
@@ -270,6 +347,7 @@ restart:	adr	r0, LC0
*/
cmp	r0, #1
sub	r0, r4, #TEXT_OFFSET
+	bic	r0, r0, #1
add	r0, r0, #0x100
mov	r1, r6
sub	r2, sp, r6
@@ -316,12 +394,13 @@ dtb_check_done:

/*
* Check to see if we will overwrite ourselves.
- * r4 = final kernel address
+ * r4 = final kernel address (possibly with LSB set)
* r9 = size of decompressed image
* r10 = end of this image, including bss/stack/malloc space if non XIP
* We basically want:
* r4 - 16k page directory >= r10 -> OK
* r4 + image length <= address of wont_overwrite -> OK
+ * Note: the possible LSB in r4 is harmless here.
*/
add	r10, r10, #16384
cmp	r4, r10
@@ -369,7 +448,8 @@ dtb_check_done:
add	sp, sp, r6
#endif

-	bl	cache_clean_flush
+	tst	r4, #1
+	bleq	cache_clean_flush

adr	r0, BSYM(restart)
add	r0, r0, r6
@@ -381,7 +461,7 @@ wont_overwrite:
* r0 = delta
* r2 = BSS start
* r3 = BSS end
- * r4 = kernel execution address
+ * r4 = kernel execution address (possibly with LSB set)
* r5 = appended dtb size (0 if not present)
* r7 = architecture ID
* r8 = atags pointer
@@ -444,6 +524,15 @@ not_relocated:	mov	r0, #0
cmp	r2, r3
blo	1b

+	/*
+	* Did we skip the cache setup earlier?
+	* That is indicated by the LSB in r4.
+	* Do it now if so.
+	*/
+	tst	r4, #1
+	bic	r4, r4, #1
+	blne	cache_on
+
/*
* The C runtime environment should now be setup sufficiently.
* Set up some pointers, and start decompressing.
@@ -474,6 +563,7 @@ LC0:	.word	LC0	@ r1
.word	_got_start	@ r11
.word	_got_end	@ ip
.word	.L_user_stack_end	@ sp
+	.word	_end - restart + 16384 + 1024*1024
.size	LC0, . - LC0

#ifdef CONFIG_ARCH_RPC
@@ -510,6 +600,7 @@ cache_on:	mov	r3, #8	@ cache_on function
* to cover all 32bit address and cacheable and bufferable.
*/
__armv4_mpu_cache_on:
+	.arch armv4
mov	r0, #0x3f	@ 4G, the whole
mcr	p15, 0, r0, c6, c7, 0	@ PR7 Area Setting
mcr	p15, 0, r0, c6, c7, 1
@@ -614,13 +705,24 @@ __setup_mmu:	sub	r3, r4, #16384	@ Page directory size
mov	pc, lr
ENDPROC(__setup_mmu)

+@ Enable unaligned access on v6, to allow better code generation
+@ for the decompressor C code:
+__armv6_mmu_cache_on:
+	mrc	p15, 0, r0, c1, c0, 0	@ read SCTLR
+	bic	r0, r0, #2	@ A (no unaligned access fault)
+	orr	r0, r0, #1 << 22	@ U (v6 unaligned access model)
+	mcr	p15, 0, r0, c1, c0, 0	@ write SCTLR
+	b	__armv4_mmu_cache_on
+
__arm926ejs_mmu_cache_on:
#ifdef CONFIG_CPU_DCACHE_WRITETHROUGH
+	.arch armv5
mov	r0, #4	@ put dcache in WT mode
mcr	p15, 7, r0, c15, c0, 0
#endif

__armv4_mmu_cache_on:
+	.arch armv4
mov	r12, lr
#ifdef CONFIG_MMU
mov	r6, #CB_BITS | 0x12	@ U
@@ -641,6 +743,7 @@ __armv4_mmu_cache_on:
mov	pc, r12

__armv7_mmu_cache_on:
+	.arch armv7-a
mov	r12, lr
#ifdef CONFIG_MMU
mrc	p15, 0, r11, c0, c1, 4	@ read ID_MMFR0
@@ -653,8 +756,12 @@ __armv7_mmu_cache_on:
mcrne	p15, 0, r0, c8, c7, 0	@ flush I,D TLBs
#endif
mrc	p15, 0, r0, c1, c0, 0	@ read control reg
+	bic	r0, r0, #1 << 28	@ clear SCTLR.TRE
orr	r0, r0, #0x5000	@ I-cache enable, RR cache replacement
orr	r0, r0, #0x003c	@ write buffer
+	bic	r0, r0, #2	@ A (no unaligned access fault)
+	orr	r0, r0, #1 << 22	@ U (v6 unaligned access model)
+	@ (needed for ARM1176)
#ifdef CONFIG_MMU
#ifdef CONFIG_CPU_ENDIAN_BE8
orr	r0, r0, #1 << 25	@ big-endian page tables
@@ -687,6 +794,7 @@ __fa526_cache_on:
mov	pc, r12

__arm6_mmu_cache_on:
+	.arch armv6
mov	r12, lr
mov	r6, #CB_BITS | 0x12	@ U
bl	__setup_mmu
@@ -895,7 +1003,7 @@ proc_types:

.word	0x0007b000	@ ARMv6
.word	0x000ff000
-	W(b)	__armv4_mmu_cache_on
+	W(b)	__armv6_mmu_cache_on
W(b)	__armv4_mmu_cache_off
W(b)	__armv6_mmu_cache_flush

@@ -1015,7 +1123,10 @@ cache_clean_flush:
mov	r3, #16
b	call_cache_fn

+	.arch armv4
__armv4_mpu_cache_flush:
+	tst	r4, #1
+	movne	pc, lr
mov	r2, #1
mov	r3, #0
mcr	p15, 0, ip, c7, c6, 0	@ invalidate D cache
@@ -1033,6 +1144,8 @@ __armv4_mpu_cache_flush:
mov	pc, lr

__fa526_cache_flush:
+	tst	r4, #1
+	movne	pc, lr
mov	r1, #0
mcr	p15, 0, r1, c7, c14, 0	@ clean and invalidate D cache
mcr	p15, 0, r1, c7, c5, 0	@ flush I cache
@@ -1040,14 +1153,19 @@ __fa526_cache_flush:
mov	pc, lr

__armv6_mmu_cache_flush:
+	.arch armv6
mov	r1, #0
-	mcr	p15, 0, r1, c7, c14, 0	@ clean+invalidate D
+	tst	r4, #1
+	mcreq	p15, 0, r1, c7, c14, 0	@ clean+invalidate D
mcr	p15, 0, r1, c7, c5, 0	@ invalidate I+BTB
-	mcr	p15, 0, r1, c7, c15, 0	@ clean+invalidate unified
+	mcreq	p15, 0, r1, c7, c15, 0	@ clean+invalidate unified
mcr	p15, 0, r1, c7, c10, 4	@ drain WB
mov	pc, lr

+	.arch armv7-a
__armv7_mmu_cache_flush:
+	tst	r4, #1
+	bne	iflush
mrc	p15, 0, r10, c0, c1, 5	@ read ID_MMFR1
tst	r10, #0xf << 16	@ hierarchical cache (ARMv7)
mov	r10, #0
@@ -1107,14 +1225,20 @@ iflush:
mcr	p15, 0, r10, c7, c5, 4	@ ISB
mov	pc, lr

+	.arch armv5
__armv5tej_mmu_cache_flush:
+	tst	r4, #1
+	movne	pc, lr
1:	mrc	p15, 0, r15, c7, c14, 3	@ test,clean,invalidate D cache
bne	1b
mcr	p15, 0, r0, c7, c5, 0	@ flush I cache
mcr	p15, 0, r0, c7, c10, 4	@ drain WB
mov	pc, lr

+	.arch armv4
__armv4_mmu_cache_flush:
+	tst	r4, #1
+	movne	pc, lr
mov	r2, #64*1024	@ default: 32K dcache size (*2)
mov	r11, #32	@ default: 32 byte line size
mrc	p15, 0, r3, c0, c0, 1	@ read cache type
@@ -1148,10 +1272,14 @@ no_cache_id:

__armv3_mmu_cache_flush:
__armv3_mpu_cache_flush:
+	tst	r4, #1
+	movne	pc, lr
mov	r1, #0
mcr	p15, 0, r1, c7, c0, 0	@ invalidate whole cache v3
mov	pc, lr

+	.arch armv4
+
/*
* Various debugging routines for printing hex characters and
* memory, which again must be relocatable.
diff --git a/arch/arm/configs/apollo-android_defconfig b/arch/arm/configs/apollo-android_defconfig
index 4f143ea..1d498b6 100644
--- a/arch/arm/configs/apollo-android_defconfig
+++ b/arch/arm/configs/apollo-android_defconfig
@@ -674,7 +674,8 @@ 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_CRASH_DUMP is not set
# CONFIG_AUTO_ZRELADDR is not set

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 c355aeb..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;
}

@@ -110,7 +150,10 @@ void machine_kexec(struct kimage *image)
unsigned long reboot_code_buffer_phys;
void *reboot_code_buffer;

-	arch_kexec();
+	if (num_online_cpus() > 1) {
+	pr_err("kexec: error: multiple CPUs still online\n");
+	return;
+	}

page_list = image->head & PAGE_MASK;

@@ -120,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,
@@ -137,5 +184,18 @@ 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);
}
+
+void arch_crash_save_vmcoreinfo(void)
+{
+#ifdef CONFIG_ARM_LPAE
+	VMCOREINFO_CONFIG(ARM_LPAE);
+#endif
+}
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/board-8974.c b/arch/arm/mach-msm/board-8974.c
index 6d52ccc..21b40dd 100644
--- a/arch/arm/mach-msm/board-8974.c
+++ b/arch/arm/mach-msm/board-8974.c
@@ -52,6 +52,13 @@
#include "amzn_ram_console.h"
#endif

+#ifdef CONFIG_KEXEC_HARDBOOT
+#include <asm/setup.h>
+#include <asm/memory.h>
+#include <linux/memblock.h>
+#define HDX_PERSISTENT_RAM_SIZE	(SZ_1M)
+#endif
+
#if defined(CONFIG_ARCH_MSM8974_THOR) || defined(CONFIG_ARCH_MSM8974_APOLLO)
enum WLANBT_STATUS {
WLANOFF_BTOFF = 1,
@@ -84,12 +91,35 @@ static struct reserve_info msm8974_reserve_info __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;
+#endif
+
reserve_info = &msm8974_reserve_info;
of_scan_flat_dt(dt_scan_for_memory_reserve, msm8974_reserve_table);
msm_reserve();
#if defined(CONFIG_AMZN_RAM_CONSOLE) && (defined(CONFIG_ARCH_MSM8974_THOR) || defined(CONFIG_ARCH_MSM8974_APOLLO))
amzn_ram_console_init(AMZN_RAM_CONSOLE_START_DEFAULT, AMZN_RAM_CONSOLE_SIZE_DEFAULT);
#endif
+
+#ifdef CONFIG_KEXEC_HARDBOOT
+	if (meminfo.nr_banks < 2) {
+	pr_err("%s: not enough membank\n", __func__);
+	return;
+	}
+
+	bank = &meminfo.bank[1];
+	start = bank->start + SZ_1M + HDX_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
}

static void __init msm8974_early_memory(void)
diff --git a/arch/arm/mach-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
index 6119a3c..abe4882 100644
--- a/arch/arm/mach-msm/include/mach/memory.h
+++ b/arch/arm/mach-msm/include/mach/memory.h
@@ -20,6 +20,15 @@
/* physical offset of RAM */
#define PLAT_PHYS_OFFSET UL(CONFIG_PHYS_OFFSET)

+#if defined(CONFIG_KEXEC_HARDBOOT)
+#if defined(CONFIG_ARCH_MSM8974_THOR) || defined(CONFIG_ARCH_MSM8974_APOLLO)
+#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
+
#define MAX_PHYSMEM_BITS 32
#define SECTION_SIZE_BITS 28

diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
index a96b02f..da157a4 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
@@ -351,6 +355,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
@@ -367,6 +391,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;