Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- From a5c71c50da839c6932af1903bcc0036daa033f7e Mon Sep 17 00:00:00 2001
- From: Vojtech Bocek <vbocek@gmail.com>
- Date: Mon, 30 Sep 2013 18:02:43 +0200
- Subject: [PATCH] Implement kexec-hardboot
- "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."
- More info in /arch/arm/Kconfig.
- Original author: Mike Kasick <mike@kasick.org>
- Vojtech Bocek <vbocek@gmail.com>:
- I've ported it to mako and flo, it is based of my grouper port, which is
- based of Asus TF201 patche ported by Jens Andersen <jens.andersen@gmail.com>
- I've moved atags copying from guest to the host kernel, which means there
- is no need to patch the guest kernel, assuming the --mem-min in kexec call
- is within the first 256MB of System RAM, otherwise it will take a long time
- to load. I've also fixed /proc/atags entry, which would give the kexec-tools
- userspace binary only the first 1024 bytes of atags,
- see arch/arm/kernel/atags.c for more details.
- Other than that, memory-reservation code for the hardboot page and
- some assembler to do the watchdog reset on MSM chip are new for this device.
- ayysir <dresadd09691@gmail.com>:
- kexec: use mem_text_write_kernel_word to set reboot_code_buffer args
- in order to avoid protection faults (writes to read-only
- kernel memory) when CONFIG_STRICT_MEMORY_RWX is enabled.
- Signed-off-by: Vojtech Bocek <vbocek@gmail.com>
- ---
- arch/arm/Kconfig | 26 +++++++++
- arch/arm/boot/compressed/head.S | 96 +++++++++++++++++++++++++++++++++
- arch/arm/include/asm/kexec.h | 8 +++
- arch/arm/kernel/atags.c | 51 ++++++++++++------
- arch/arm/kernel/machine_kexec.c | 22 ++++++--
- arch/arm/kernel/relocate_kernel.S | 55 +++++++++++++++++++
- arch/arm/mach-msm/include/mach/memory.h | 10 ++++
- arch/arm/mach-msm/lge/devices_lge.c | 15 ++++++
- arch/arm/mach-msm/restart.c | 16 ++++++
- include/linux/kexec.h | 19 +++++--
- kernel/kexec.c | 4 ++
- 11 files changed, 298 insertions(+), 24 deletions(-)
- diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
- index c450e1d..a577ae7 100644
- --- a/arch/arm/Kconfig
- +++ b/arch/arm/Kconfig
- @@ -2180,6 +2180,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..c7f1e64 100644
- --- a/arch/arm/boot/compressed/head.S
- +++ b/arch/arm/boot/compressed/head.S
- @@ -10,6 +10,11 @@
- */
- #include <linux/linkage.h>
- +#ifdef CONFIG_KEXEC_HARDBOOT
- + #include <asm/kexec.h>
- + #include <asm/memory.h>
- +#endif
- +
- /*
- * Debugging stuff
- *
- @@ -135,6 +140,97 @@ 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 <vbocek@gmail.com>: 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 problems 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 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
- 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..0cfd7e4 100644
- --- a/arch/arm/kernel/atags.c
- +++ b/arch/arm/kernel/atags.c
- @@ -4,29 +4,45 @@
- #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 might have no particular effect on real devices, because the
- + * atags _might_ be organized "just right", 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);
- +static struct buffer* atags_buffer = NULL;
- - memcpy(page, &buffer->data[off], count);
- -
- - 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 +82,13 @@ 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 c355aeb..449394d 100644
- --- a/arch/arm/kernel/machine_kexec.c
- +++ b/arch/arm/kernel/machine_kexec.c
- @@ -14,6 +14,7 @@
- #include <asm/cacheflush.h>
- #include <asm/mach-types.h>
- #include <asm/system_misc.h>
- +#include <asm/mmu_writeable.h>
- extern const unsigned char relocate_new_kernel[];
- extern const unsigned int relocate_new_kernel_size;
- @@ -22,6 +23,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;
- @@ -120,10 +125,13 @@ 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);
- + 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 +145,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);
- }
- diff --git a/arch/arm/kernel/relocate_kernel.S b/arch/arm/kernel/relocate_kernel.S
- index d0cdedf..f534293 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)
- + #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,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 */
- + 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
- +#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
- +#else
- +#error "No reboot method defined for hardboot."
- +#endif
- +
- + .ltorg
- +#endif
- .align
- .globl kexec_start_address
- @@ -79,6 +128,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-msm/include/mach/memory.h b/arch/arm/mach-msm/include/mach/memory.h
- index 8329611..d9f6b78 100644
- --- a/arch/arm/mach-msm/include/mach/memory.h
- +++ b/arch/arm/mach-msm/include/mach/memory.h
- @@ -20,6 +20,16 @@
- /* 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)
- +#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/lge/devices_lge.c b/arch/arm/mach-msm/lge/devices_lge.c
- index 504cc1e..f74e4e7 100644
- --- a/arch/arm/mach-msm/lge/devices_lge.c
- +++ b/arch/arm/mach-msm/lge/devices_lge.c
- @@ -26,6 +26,10 @@
- #include <ram_console.h>
- +#ifdef CONFIG_KEXEC_HARDBOOT
- +#include <linux/memblock.h>
- +#endif
- +
- /* setting whether uart console is enalbed or disabled */
- static int uart_console_mode = 0;
- @@ -187,6 +191,17 @@ void __init lge_add_persistent_ram(void)
- void __init lge_reserve(void)
- {
- +#ifdef CONFIG_KEXEC_HARDBOOT
- + // Reserve space for hardboot page, just before the ram_console
- + struct membank* bank = &meminfo.bank[0];
- + phys_addr_t start = bank->start + bank->size - SZ_1M - LGE_PERSISTENT_RAM_SIZE;
- + int 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
- +
- lge_add_persistent_ram();
- }
- diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
- index 8fac40c..74ef77a 100644
- --- a/arch/arm/mach-msm/restart.c
- +++ b/arch/arm/mach-msm/restart.c
- @@ -35,6 +35,10 @@
- #include "msm_watchdog.h"
- #include "timer.h"
- +#ifdef CONFIG_KEXEC_HARDBOOT
- +#include <asm/kexec.h>
- +#endif
- +
- #define WDT0_RST 0x38
- #define WDT0_EN 0x40
- #define WDT0_BARK_TIME 0x4C
- @@ -322,6 +326,14 @@ 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 PMIC to restart-on-poweroff
- + pm8xxx_reset_pwr_off(1);
- +}
- +#endif
- +
- static int __init msm_restart_init(void)
- {
- #ifdef CONFIG_MSM_DLOAD_MODE
- @@ -337,6 +349,10 @@ static int __init msm_restart_init(void)
- restart_reason = MSM_IMEM_BASE + RESTART_REASON_ADDR;
- pm_power_off = msm_power_off;
- +#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;
- --
- 1.8.4.rc3
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement