Permalink
Browse files

Support hard booting to a kexec kernel.

See KEXEC_HARDBOOT config option help for details.

Conflicts:

	arch/arm/configs/crespo_defconfig
  • Loading branch information...
1 parent 6d50f06 commit e443f15cd73232b78827dc108c1d2ffbd5bc1fb1 @mkasick mkasick committed with Apr 7, 2012
View
@@ -1941,6 +1941,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
@@ -11,6 +11,10 @@
#include <linux/linkage.h>
#include <asm/memory.h>
+#ifdef CONFIG_KEXEC_HARDBOOT
+#include <asm/kexec.h>
+#endif
+
/*
* Debugging stuff
*
@@ -138,6 +142,28 @@ start:
movne r8, r0 @ Save kexec_boot_tags.
moveq 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]
+
+ /* Load boot arguments and jump to kexec kernel. */
+ ldr r0, [r3, #12] @ kexec_boot_atags (r2: boot_atags)
+ ldr r1, [r3, #8] @ kexec_mach_type
+ ldr pc, [r3, #4] @ kexec_start_address
+
+ .ltorg
+
+not_booting_other:
+#endif
+
#ifndef __ARM_ARCH_2__
/*
* Booting from Angel - need to enter SVC mode and disable
@@ -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__
/**
@@ -13,6 +13,10 @@
#include <asm/cacheflush.h>
#include <asm/mach-types.h>
+#if defined(CONFIG_KEXEC_HARDBOOT) && defined(CONFIG_ARCH_S5PV210)
+#include <mach/regs-clock.h>
+#endif
+
extern const unsigned char relocate_new_kernel[];
extern const unsigned int relocate_new_kernel_size;
@@ -22,6 +26,10 @@ 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;
+#endif
+
static atomic_t waiting_for_crash_ipi;
@@ -99,6 +107,10 @@ void machine_kexec(struct kimage *image)
kexec_indirection_page = page_list;
kexec_mach_type = machine_arch_type;
kexec_boot_atags = image->start - KEXEC_ARM_ZIMAGE_OFFSET + KEXEC_ARM_ATAGS_OFFSET;
+#ifdef CONFIG_KEXEC_HARDBOOT
+ kexec_hardboot = image->hardboot;
+#endif
+
/* copy our kernel relocation code to the control code page */
memcpy(reboot_code_buffer,
@@ -4,6 +4,13 @@
#include <asm/kexec.h>
+#ifdef CONFIG_KEXEC_HARDBOOT
+#include <asm/memory.h>
+#ifdef CONFIG_ARCH_S5PV210
+#include <mach/map.h>
+#endif
+#endif
+
.globl relocate_new_kernel
relocate_new_kernel:
@@ -52,6 +59,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 +73,41 @@ 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 */
+ 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_HB_PAGE_MAGIC
+ str r1, [r0]
+
+#ifdef CONFIG_ARCH_S5PV210
+ /* Hard reset via watchdog, decompressor jumps to kernel. */
+ ldr r0, =S3C_PA_WDT
+ mov r1, #0x8000
+ str r1, [r0, #4]
+ mov r1, #1
+ str r1, [r0, #4]
+ mov r1, #8
+ str r1, [r0, #8]
+ movw r1, #0x8021
+ str r1, [r0]
+loop: b loop
+#else
+#error "No reboot method defined for hardboot."
+#endif
+
+ .ltorg
+#endif
+
.align
.globl kexec_start_address
@@ -79,6 +127,12 @@ kexec_mach_type:
kexec_boot_atags:
.long 0x0
+#ifdef CONFIG_KEXEC_HARDBOOT
+ .globl kexec_hardboot
+kexec_hardboot:
+ .long 0x0
+#endif
+
relocate_new_kernel_end:
.globl relocate_new_kernel_size
@@ -807,15 +807,21 @@ static void __init tf201_ramconsole_reserve(unsigned long size)
{
struct resource *res;
long ret;
+ unsigned long real_start, real_size;
res = platform_get_resource(&ram_console_device, IORESOURCE_MEM, 0);
if (!res) {
pr_err("Failed to find memory resource for ram console\n");
return;
}
+
res->start = memblock_end_of_DRAM() - size;
res->end = res->start + size - 1;
- ret = memblock_remove(res->start, size);
+ // Register an extra 1M before ramconsole to store kexec stuff
+ real_start = res->start - SZ_1M;
+ real_size = size + SZ_1M;
+
+ ret = memblock_remove(real_start, real_size);
if (ret) {
ram_console_device.resource = NULL;
ram_console_device.num_resources = 0;
@@ -26,9 +26,15 @@
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
#define PLAT_PHYS_OFFSET UL(0)
#define END_MEM UL(0x40000000)
+#if defined(CONFIG_KEXEC_HARDBOOT)
+#define KEXEC_HB_PAGE_ADDR UL(0x3FE00000)
+#endif
#else
#define PLAT_PHYS_OFFSET UL(0x80000000)
#define END_MEM UL(0xC0000000)
+#if defined(CONFIG_KEXEC_HARDBOOT)
+#define KEXEC_HB_PAGE_ADDR UL(0xBFE00000)
+#endif
#endif
/*
@@ -41,5 +47,7 @@
#define CONSISTENT_DMA_SIZE (14 * SZ_1M)
+
+
#endif
View
@@ -100,6 +100,9 @@ struct kimage {
#define KEXEC_TYPE_DEFAULT 0
#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;
@@ -165,6 +168,9 @@ 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.
@@ -183,10 +189,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)
View
@@ -1005,6 +1005,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;
@@ -1537,7 +1541,15 @@ int kernel_kexec(void)
} else
#endif
{
- kernel_restart_prepare(NULL);
+#ifdef CONFIG_KEXEC_HARDBOOT
+ if (kexec_image->hardboot)
+ /* Reboot with the recovery kernel since the boot kernel decompressor may
+ * not support the hardboot jump. */
+ kernel_restart_prepare("recovery");
+ else
+#endif
+ kernel_restart_prepare(NULL);
+
printk(KERN_EMERG "Starting new kernel\n");
machine_shutdown();
}

0 comments on commit e443f15

Please sign in to comment.