Last active
January 7, 2022 17:13
-
-
Save Tasssadar/4558647 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig | |
index 3c3b868..5903cec 100644 | |
--- a/arch/arm/Kconfig | |
+++ b/arch/arm/Kconfig | |
@@ -1993,6 +1993,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/Makefile b/arch/arm/boot/compressed/Makefile | |
index 0c74a6f..7643b37 100644 | |
--- a/arch/arm/boot/compressed/Makefile | |
+++ b/arch/arm/boot/compressed/Makefile | |
@@ -111,6 +111,9 @@ endif | |
ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) | |
LDFLAGS_vmlinux += --be8 | |
endif | |
+ifneq ($(PARAMS_PHYS),) | |
+LDFLAGS_vmlinux += --defsym params_phys=$(PARAMS_PHYS) | |
+endif | |
# ? | |
LDFLAGS_vmlinux += -p | |
# Report unresolved symbol references | |
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S | |
index 24701d6..7c3a581 100644 | |
--- a/arch/arm/boot/compressed/head.S | |
+++ b/arch/arm/boot/compressed/head.S | |
@@ -9,6 +9,11 @@ | |
* published by the Free Software Foundation. | |
*/ | |
#include <linux/linkage.h> | |
+#include <asm/memory.h> | |
+ | |
+#ifdef CONFIG_KEXEC_HARDBOOT | |
+ #include <asm/kexec.h> | |
+#endif | |
/* | |
* Debugging stuff | |
@@ -135,6 +140,98 @@ 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 the kernel tagged list (atags): | |
+ * | |
+ * The kernel requires atags to be located in a direct-mapped region, | |
+ * usually below the kernel in the first 16 kB of RAM. If they're above | |
+ * (the start of) the kernel, they need to be copied to a suitable | |
+ * location, e.g., the machine-defined params_phys. | |
+ * | |
+ * The assumption is that the tags will only be "out of place" if the | |
+ * decompressor code is also, so copying is implemented only in the "won't | |
+ * overwrite" case (which should be fixed). Still need to make sure that | |
+ * the copied tags don't overwrite either the kernel or decompressor code | |
+ * (or rather, the remainder of it since everything up to here has already | |
+ * been executed). | |
+ * | |
+ * Vojtech Bocek <[email protected]>: I've moved atags copying from guest kernel to | |
+ * the host and rewrote it from C to assembler in order to remove the need | |
+ * for guest kernel to be patched. I don't know assembler very well, so it | |
+ * doesn't look very good and I have no idea if I didn't accidentally break | |
+ * something, causing problems down the road. It's worked every time and I | |
+ * didn't notice any problem so far though. | |
+ * | |
+ * r4: zreladdr (kernel start) | |
+ * r8: kexec_boot_atags | |
+ * r2: boot_atags */ | |
+ ldr r8, [r3, #12] @ kexec_boot_atags (r2: boot_atags) | |
+ ldr r4, =zreladdr @ zreladdr | |
+ | |
+ /* No need to copy atags if they're already below kernel */ | |
+ cmp r8, r4 | |
+ blo no_atags_cpy | |
+ | |
+ /* r0: min(zreladdr, pc) */ | |
+ mov r0, pc | |
+ cmp r4, r0 | |
+ movlo r0, r4 | |
+ | |
+ /* Compute max space for atags, if max <= 0 don't copy. */ | |
+ subs r5, r0, r2 @ max = min(zreladdr, pc) - dest | |
+ bls no_atags_cpy | |
+ | |
+ /* Copy atags to params_phys. */ | |
+ /* r8 src, r2 dest, r5 max */ | |
+ | |
+ ldr r0, [r8] @ first tag size | |
+ cmp r0, #0 | |
+ moveq r4, #8 | |
+ beq catags_empty | |
+ mov r4, r8 | |
+ | |
+catags_foreach: | |
+ lsl r0, r0, #2 @ Multiply by 4 | |
+ ldr r0, [r4, r0]! @ Load next tag size to r0 and address to r4 | |
+ cmp r0, #0 | |
+ bne catags_foreach | |
+ | |
+ rsb r4, r8, r4 @ r4 -= r8 (get only size) | |
+ add r4, r4, #8 @ add size of the last tag | |
+catags_empty: | |
+ cmp r5, r4 @ if(max <= size) | |
+ bcc no_atags_cpy | |
+ | |
+ mov r5, #0 @ iterator | |
+catags_cpy: | |
+ ldr r0, [r8, r5] | |
+ str r0, [r2, r5] | |
+ add r5, r5, #4 | |
+ cmp r5, r4 | |
+ blo catags_cpy | |
+ | |
+no_atags_cpy: | |
+ /* 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 | |
@@ -348,6 +445,44 @@ not_relocated: mov r0, #0 | |
add r2, sp, #0x10000 @ 64k max | |
mov r3, r7 | |
bl decompress_kernel | |
+ | |
+/* Copy the kernel tagged list (atags): | |
+ * | |
+ * The kernel requires atags to be located in a direct-mapped region, | |
+ * usually below the kernel in the first 16 kB of RAM. If they're above | |
+ * (the start of) the kernel, they need to be copied to a suitable | |
+ * location, e.g., the machine-defined params_phys. | |
+ * | |
+ * The assumption is that the tags will only be "out of place" if the | |
+ * decompressor code is also, so copying is implemented only in the "won't | |
+ * overwrite" case (which should be fixed). Still need to make sure that | |
+ * the copied tags don't overwrite either the kernel or decompressor code | |
+ * (or rather, the remainder of it since everything up to here has already | |
+ * been executed). | |
+ * | |
+ * r4: zreladdr (kernel start) | |
+ * r8: atags */ | |
+ | |
+ /* Don't need to copy atags if they're already below the kernel. */ | |
+ cmp r8, r4 | |
+ blo call_kernel | |
+ | |
+ /* r1: min(zreladdr, pc) */ | |
+ mov r1, pc | |
+ cmp r4, r1 | |
+ movlo r1, r4 | |
+ | |
+ /* Compute max space for atags, if max <= 0 don't copy. */ | |
+ ldr r0, =params_phys @ dest | |
+ subs r2, r1, r0 @ max = min(zreladdr, pc) - dest | |
+ bls call_kernel | |
+ | |
+ /* Copy atags to params_phys. */ | |
+ mov r1, r8 @ src | |
+ bl copy_atags | |
+ mov r8, r0 | |
+ | |
+call_kernel: | |
bl cache_clean_flush | |
bl cache_off | |
mov r0, #0 @ must be zero | |
@@ -356,6 +491,8 @@ not_relocated: mov r0, #0 | |
ARM( mov pc, r4 ) @ call kernel | |
THUMB( bx r4 ) @ entry point is always ARM | |
+ .ltorg | |
+ | |
.align 2 | |
.type LC0, #object | |
LC0: .word LC0 @ r1 | |
@@ -467,9 +604,14 @@ __setup_mmu: sub r3, r4, #16384 @ Page directory size | |
* bits for the RAM area only. | |
*/ | |
mov r0, r3 | |
+#if defined(PLAT_PHYS_OFFSET) && defined(END_MEM) | |
+ mov r9, #PLAT_PHYS_OFFSET @ start of RAM | |
+ ldr r10, =END_MEM @ end of RAM | |
+#else | |
mov r9, r0, lsr #18 | |
mov r9, r9, lsl #18 @ start of RAM | |
add r10, r9, #0x10000000 @ a reasonable RAM size | |
+#endif | |
mov r1, #0x12 | |
orr r1, r1, #3 << 10 | |
add r2, r3, #16384 | |
diff --git a/arch/arm/boot/compressed/misc.c b/arch/arm/boot/compressed/misc.c | |
index 832d372..f1ce0ef 100644 | |
--- a/arch/arm/boot/compressed/misc.c | |
+++ b/arch/arm/boot/compressed/misc.c | |
@@ -25,6 +25,7 @@ unsigned int __machine_arch_type; | |
#include <linux/stddef.h> /* for NULL */ | |
#include <linux/linkage.h> | |
#include <asm/string.h> | |
+#include <asm/setup.h> | |
static void putstr(const char *ptr); | |
@@ -192,3 +193,25 @@ decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p, | |
else | |
putstr(" done, booting the kernel.\n"); | |
} | |
+ | |
+const struct tag *copy_atags(struct tag *dest, const struct tag *src, | |
+ size_t max) | |
+{ | |
+ struct tag *tag; | |
+ size_t size; | |
+ | |
+ /* Find the last tag (ATAG_NONE). */ | |
+ for_each_tag(tag, (struct tag *)src) | |
+ continue; | |
+ | |
+ /* Include the last tag in copy. */ | |
+ size = (char *)tag - (char *)src + sizeof(struct tag_header); | |
+ | |
+ /* If there's not enough room, just use original and hope it works. */ | |
+ if (size > max) | |
+ return src; | |
+ | |
+ memcpy(dest, src, size); | |
+ | |
+ return dest; | |
+} | |
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/atags.c b/arch/arm/kernel/atags.c | |
index 42a1a14..7872240 100644 | |
--- a/arch/arm/kernel/atags.c | |
+++ b/arch/arm/kernel/atags.c | |
@@ -4,29 +4,47 @@ | |
#include <asm/types.h> | |
#include <asm/page.h> | |
+/* | |
+ * [PATCH] Backport arch/arm/kernel/atags.c from 3.10 | |
+ * | |
+ * There is a bug in older kernels, causing kexec-tools binary to | |
+ * only read first 1024 bytes from /proc/atags. I guess the bug is | |
+ * somewhere in /fs/proc/, since I don't think the callback in atags.c | |
+ * does something wrong. It might affect all procfs files using that | |
+ * old read callback instead of fops. Doesn't matter though, since it | |
+ * was accidentally fixed when 3.10 removed it. | |
+ * | |
+ * This has no particular effect on grouper, because the atags are | |
+ * organized "just right" (most important tags are near beginning, | |
+ * the ones at the end are some useless nvidia-specific tags), but | |
+ * it might be very hard to track down on a device where it causes | |
+ * problems. | |
+ * | |
+ */ | |
+ | |
struct buffer { | |
size_t size; | |
char data[]; | |
}; | |
-static int | |
-read_buffer(char* page, char** start, off_t off, int count, | |
- int* eof, void* data) | |
-{ | |
- struct buffer *buffer = (struct buffer *)data; | |
- | |
- if (off >= buffer->size) { | |
- *eof = 1; | |
- return 0; | |
- } | |
- | |
- count = min((int) (buffer->size - off), count); | |
- | |
- memcpy(page, &buffer->data[off], count); | |
+static struct buffer* atags_buffer = NULL; | |
- return count; | |
+static ssize_t atags_read(struct file *file, char __user *buf, | |
+ size_t count, loff_t *ppos) | |
+{ | |
+ // These are introduced in kernel 3.10. I don't want to backport | |
+ // the whole chunk, and other things (ram_console) use static | |
+ // variable to keep data too, so I guess it's okay. | |
+ //struct buffer *b = PDE_DATA(file_inode(file)); | |
+ struct buffer *b = atags_buffer; | |
+ return simple_read_from_buffer(buf, count, ppos, b->data, b->size); | |
} | |
+static const struct file_operations atags_fops = { | |
+ .read = atags_read, | |
+ .llseek = default_llseek, | |
+}; | |
+ | |
#define BOOT_PARAMS_SIZE 1536 | |
static char __initdata atags_copy[BOOT_PARAMS_SIZE]; | |
@@ -66,12 +84,12 @@ static int __init init_atags_procfs(void) | |
b->size = size; | |
memcpy(b->data, atags_copy, size); | |
- tags_entry = create_proc_read_entry("atags", 0400, | |
- NULL, read_buffer, b); | |
- | |
+ tags_entry = proc_create_data("atags", 0400, NULL, &atags_fops, b); | |
if (!tags_entry) | |
goto nomem; | |
+ atags_buffer = b; | |
+ | |
return 0; | |
nomem: | |
diff --git a/arch/arm/kernel/machine_kexec.c b/arch/arm/kernel/machine_kexec.c | |
index e59bbd4..812c0cb 100644 | |
--- a/arch/arm/kernel/machine_kexec.c | |
+++ b/arch/arm/kernel/machine_kexec.c | |
@@ -22,6 +22,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; | |
+void (*kexec_hardboot_hook)(void); | |
+#endif | |
static atomic_t waiting_for_crash_ipi; | |
@@ -99,6 +103,9 @@ 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, | |
@@ -114,11 +121,23 @@ void machine_kexec(struct kimage *image) | |
local_irq_disable(); | |
local_fiq_disable(); | |
setup_mm_for_reboot(0); /* mode is not used, so just pass 0*/ | |
+ | |
+#ifdef CONFIG_KEXEC_HARDBOOT | |
+ /* Run any final machine-specific shutdown code. */ | |
+ if (image->hardboot && kexec_hardboot_hook) | |
+ kexec_hardboot_hook(); | |
+#endif | |
+ | |
flush_cache_all(); | |
outer_flush_all(); | |
outer_disable(); | |
cpu_proc_fin(); | |
- outer_inv_all(); | |
- flush_cache_all(); | |
- cpu_reset(reboot_code_buffer_phys); | |
+ | |
+ // Freezes the tegra 3 | |
+ //outer_inv_all(); | |
+ //flush_cache_all(); | |
+ | |
+ /* Must call cpu_reset via physical address since ARMv7 (& v6) stalls the | |
+ * pipeline after disabling the MMU. */ | |
+ ((typeof(cpu_reset) *)virt_to_phys(cpu_reset))(reboot_code_buffer_phys); | |
} | |
diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S | |
index d0cdedf..98e0a89 100644 | |
--- a/arch/arm/kernel/relocate_kernel.S | |
+++ b/arch/arm/kernel/relocate_kernel.S | |
@@ -4,6 +4,13 @@ | |
#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> | |
+#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,34 @@ 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] | |
+ | |
+#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 | |
+#else | |
+#error "No reboot method defined for hardboot." | |
+#endif | |
+ | |
+ .ltorg | |
+#endif | |
.align | |
.globl kexec_start_address | |
@@ -79,6 +120,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 | |
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig | |
index 9ac04dd..2899a01 100644 | |
--- a/arch/arm/mach-tegra/Kconfig | |
+++ b/arch/arm/mach-tegra/Kconfig | |
@@ -504,4 +504,13 @@ config TEGRA_PREINIT_CLOCKS | |
help | |
Preinitialize Tegra clocks to known states before actual full- | |
scale clock initialization starts. | |
+ | |
+config GROUPER_HARDBOOT_RECOVERY | |
+ bool "Reboot to recovery partition when using Kexec-hardboot" | |
+ depends on MACH_GROUPER | |
+ depends on KEXEC_HARDBOOT | |
+ default n | |
+ help | |
+ Reboot with the recovery kernel since the boot kernel decompressor may | |
+ not support the hardboot jump. | |
endif | |
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c | |
index d91ad83..89dede3 100755 | |
--- a/arch/arm/mach-tegra/common.c | |
+++ b/arch/arm/mach-tegra/common.c | |
@@ -961,13 +961,20 @@ void __init tegra_ram_console_debug_reserve(unsigned long ram_console_size) | |
{ | |
struct resource *res; | |
long ret; | |
+ unsigned long real_start, real_size; | |
res = platform_get_resource(&ram_console_device, IORESOURCE_MEM, 0); | |
if (!res) | |
goto fail; | |
+ | |
res->start = memblock_end_of_DRAM() - ram_console_size; | |
res->end = res->start + ram_console_size - 1; | |
- ret = memblock_remove(res->start, ram_console_size); | |
+ | |
+ // Register an extra 1M before ramconsole to store kexec stuff | |
+ real_start = res->start - SZ_1M; | |
+ real_size = ram_console_size + SZ_1M; | |
+ | |
+ ret = memblock_remove(real_start, real_size); | |
if (ret) | |
goto fail; | |
diff --git a/arch/arm/mach-tegra/include/mach/memory.h b/arch/arm/mach-tegra/include/mach/memory.h | |
index 5f51066..84dd44e 100644 | |
--- a/arch/arm/mach-tegra/include/mach/memory.h | |
+++ b/arch/arm/mach-tegra/include/mach/memory.h | |
@@ -29,6 +29,18 @@ | |
#define PLAT_PHYS_OFFSET UL(0x80000000) | |
#endif | |
+#if defined(CONFIG_MACH_GROUPER) | |
+#define END_MEM UL(0xBEA00000) | |
+#endif | |
+ | |
+#if defined(CONFIG_KEXEC_HARDBOOT) | |
+#if defined(CONFIG_MACH_GROUPER) | |
+#define KEXEC_HB_PAGE_ADDR UL(0xBEA00000) | |
+#else | |
+#error "Adress for kexec hardboot page not defined" | |
+#endif | |
+#endif | |
+ | |
/* | |
* Unaligned DMA causes tegra dma to place data on 4-byte boundary after | |
* expected address. Call to skb_reserve(skb, NET_IP_ALIGN) was causing skb | |
diff --git a/arch/arm/mach-tegra/reset.c b/arch/arm/mach-tegra/reset.c | |
index 3ab2c13..72ed17f 100644 | |
--- a/arch/arm/mach-tegra/reset.c | |
+++ b/arch/arm/mach-tegra/reset.c | |
@@ -27,6 +27,10 @@ | |
#include "sleep.h" | |
#include "pm.h" | |
+#ifdef CONFIG_KEXEC_HARDBOOT | |
+#include <asm/kexec.h> | |
+#endif | |
+ | |
static bool is_enabled; | |
static void tegra_cpu_reset_handler_enable(void) | |
@@ -88,6 +92,21 @@ void tegra_cpu_reset_handler_restore(void) | |
} | |
#endif | |
+#if defined(CONFIG_KEXEC_HARDBOOT) && defined(CONFIG_GROUPER_HARDBOOT_RECOVERY) | |
+#define RECOVERY_MODE BIT(31) | |
+void tegra_kexec_hardboot(void) | |
+{ | |
+ /* Reboot with the recovery kernel since the boot kernel decompressor may | |
+ * not support the hardboot jump. */ | |
+ | |
+ void __iomem *reset = IO_ADDRESS(TEGRA_PMC_BASE + 0x00); | |
+ | |
+ u32 reg = readl_relaxed(reset + PMC_SCRATCH0); | |
+ reg |= RECOVERY_MODE; | |
+ writel_relaxed(reg, reset + PMC_SCRATCH0); | |
+} | |
+#endif | |
+ | |
void __init tegra_cpu_reset_handler_init(void) | |
{ | |
#ifdef CONFIG_SMP | |
@@ -112,4 +131,8 @@ void __init tegra_cpu_reset_handler_init(void) | |
__pa(&__tegra_cpu_reset_handler_data[TEGRA_RESET_DATA_SIZE])); | |
tegra_cpu_reset_handler_enable(); | |
+ | |
+#if defined(CONFIG_KEXEC_HARDBOOT) && defined(CONFIG_GROUPER_HARDBOOT_RECOVERY) | |
+ kexec_hardboot_hook = tegra_kexec_hardboot; | |
+#endif | |
} | |
diff --git a/include/linux/kexec.h b/include/linux/kexec.h | |
index c2478a3..e0f1cee 100644 | |
--- a/include/linux/kexec.h | |
+++ b/include/linux/kexec.h | |
@@ -101,6 +101,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 | |
@@ -165,6 +169,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. | |
@@ -183,10 +192,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 296fbc8..2e2f1df 100644 | |
--- a/kernel/kexec.c | |
+++ b/kernel/kexec.c | |
@@ -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; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment