ads

mercredi 23 septembre 2015

[DEV] Kexec Hardboot Patch



- I have implemented a first cut of kexec hardboot patch for the Apollo and Thor Devices
- @Tasssadar 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:




Code:


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;







Aucun commentaire:

Enregistrer un commentaire