Skip to content

Commit

Permalink
Add kexec-hardboot support for MultiROM
Browse files Browse the repository at this point in the history
Change-Id: I1183ff51ef583b8f8b1f96ebfb668c67e26f6b8e

Conflicts:
	arch/arm/configs/cm_m8_defconfig
	arch/arm/include/asm/kexec.h
	arch/arm/kernel/machine_kexec.c
	arch/arm/mach-msm/restart.c
	include/linux/kexec.h

Conflicts:
	arch/arm/mach-msm/restart.c
  • Loading branch information
CaptainThrowback authored and fizbanrapper committed Aug 30, 2015
1 parent 3c8982e commit fd346e9
Show file tree
Hide file tree
Showing 11 changed files with 304 additions and 4 deletions.
26 changes: 26 additions & 0 deletions arch/arm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -2252,6 +2252,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
Expand Down
63 changes: 63 additions & 0 deletions arch/arm/boot/compressed/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
*/
#include <linux/linkage.h>

#ifdef CONFIG_KEXEC_HARDBOOT
#include <asm/kexec.h>
#include <asm/memory.h>
#endif

.arch armv7-a
/*
* Debugging stuff
Expand Down Expand Up @@ -136,6 +141,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
Expand Down
5 changes: 5 additions & 0 deletions arch/arm/configs/cm_m8_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3740,3 +3740,8 @@ CONFIG_NLATTR=y
# CONFIG_CORDIC is not set
CONFIG_QMI_ENCDEC=y
# CONFIG_QMI_ENCDEC_DEBUG is not set

# kexec-hardboot for MultiROM support
CONFIG_KEXEC=y
CONFIG_KEXEC_HARDBOOT=y
CONFIG_PROC_DEVICETREE=y
12 changes: 11 additions & 1 deletion arch/arm/include/asm/kexec.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__

/**
Expand Down Expand Up @@ -53,8 +57,14 @@ static inline void crash_setup_regs(struct pt_regs *newregs,
/* Function pointer to optional machine-specific reinitialization */
extern void (*kexec_reinit)(void);

#endif /* __ASSEMBLY__ */
#ifdef CONFIG_KEXEC_HARDBOOT
extern void (*kexec_hardboot_hook)(void);
#endif

#endif /* CONFIG_KEXEC */


#endif /* _ARM_KEXEC_H */

#endif

57 changes: 57 additions & 0 deletions arch/arm/kernel/machine_kexec.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
#include <asm/cacheflush.h>
#include <asm/mach-types.h>
#include <asm/system_misc.h>
#ifdef CONFIG_KEXEC_HARDBOOT
#include <linux/memblock.h>
#include <linux/of_fdt.h>
#include <asm/mmu_writeable.h>
#endif

extern const unsigned char relocate_new_kernel[];
extern const unsigned int relocate_new_kernel_size;
Expand All @@ -23,6 +28,13 @@ 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;

/*
Expand All @@ -32,6 +44,35 @@ static atomic_t waiting_for_crash_ipi;

int machine_kexec_prepare(struct kimage *image)
{
#ifdef CONFIG_KEXEC_HARDBOOT
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;

if(current_segment->mem == image->start)
mem_text_write_kernel_word(&kexec_kernel_len, current_segment->memsz);

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);
mem_text_write_kernel_word(&kexec_boot_atags_len, current_segment->memsz);
}
}
#endif
return 0;
}

Expand Down Expand Up @@ -120,11 +161,21 @@ void machine_kexec(struct kimage *image)
reboot_code_buffer = page_address(image->control_code_page);

/* Prepare parameters for reboot_code_buffer*/
#ifdef CONFIG_KEXEC_HARDBOOT
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);
mem_text_write_kernel_word(&kexec_hardboot, image->hardboot);
#else

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;

#endif
/* copy our kernel relocation code to the control code page */
memcpy(reboot_code_buffer,
relocate_new_kernel, relocate_new_kernel_size);
Expand All @@ -137,5 +188,11 @@ 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);
}
59 changes: 59 additions & 0 deletions arch/arm/kernel/relocate_kernel.S
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@

#include <asm/kexec.h>

#ifdef CONFIG_KEXEC_HARDBOOT
#include <asm/memory.h>
#include <mach/msm_iomap.h>
#endif

.globl relocate_new_kernel
relocate_new_kernel:

Expand Down Expand Up @@ -52,6 +57,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
Expand All @@ -60,6 +71,40 @@ 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
#else
#error "No reboot method defined for hardboot."
#endif

.ltorg
#endif
.align

.globl kexec_start_address
Expand All @@ -79,6 +124,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
Expand Down
25 changes: 25 additions & 0 deletions arch/arm/mach-msm/board-htc-8974.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@
#include <linux/android_ediagpmem.h>
#endif

#ifdef CONFIG_KEXEC_HARDBOOT
#include <linux/memblock.h>
#include <asm/setup.h>
#endif

#if defined(CONFIG_FB_MSM_MDSS_HDMI_MHL_SII8240_SII8558) && defined(CONFIG_HTC_MHL_DETECTION)
#include "../../../drivers/video/msm/mdss/sii8240_8558/mhl_platform.h"
#endif
Expand Down Expand Up @@ -760,7 +765,27 @@ static void __init htc_8974_map_io(void)

void __init htc_8974_init_early(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 + HTC_8974_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(&htc_8974_persistent_ram);

}
Expand Down
9 changes: 9 additions & 0 deletions arch/arm/mach-msm/include/mach/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@
/* physical offset of RAM */
#define PLAT_PHYS_OFFSET UL(CONFIG_PHYS_OFFSET)

#if defined(CONFIG_KEXEC_HARDBOOT)
#define KEXEC_HB_PAGE_ADDR UL(0x5ED0000)
/*
#define KEXEC_HB_KERNEL_LOC UL(0x3208000)
*/
#else
#error "Adress for kexec hardboot page not defined"
#endif

#define MAX_PHYSMEM_BITS 32
#define SECTION_SIZE_BITS 28

Expand Down
Loading

0 comments on commit fd346e9

Please sign in to comment.