* [PATCH RFC 0/1] ARM: boot: make atags area relocatable @ 2013-10-14 13:44 ` Daniel Mack 0 siblings, 0 replies; 4+ messages in thread From: Daniel Mack @ 2013-10-14 13:44 UTC (permalink / raw) To: linux; +Cc: kexec, neumann, linux-arm-kernel, Daniel Mack I've been hunting an effect of failed ARM kernel boots when kexec is used, and here's a RFC for a solution of what I figured out. kexec currently assumes that the kernel image compression rate is 1:4, and stuffs the dtb image after what is assumed the end of the kernel image. However, it does not read in the kernel's symbol table or anything alike and hence can't know about the size of the bss section. In my case, it turns out that upon __vet_atags is executed, the dtb image lies inside the bss area and is hence overridden with zeros later on. This patch makes an attempt to fix this problem by detecting the described condition and relocating the atags/bss section to where the '_end' symbol is stored. Disclaimer: I'm really not sure whether my virt_to_phys() approach and other asm details are fully correct. I'm open to suggestions :) Successfully tested on a AM335x platform that fails to kexec without the patch. Thanks, Daniel Daniel Mack (1): ARM: head-common.S: relocate atags area if necessary arch/arm/kernel/head-common.S | 58 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) -- 1.8.3.1 _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH RFC 0/1] ARM: boot: make atags area relocatable @ 2013-10-14 13:44 ` Daniel Mack 0 siblings, 0 replies; 4+ messages in thread From: Daniel Mack @ 2013-10-14 13:44 UTC (permalink / raw) To: linux-arm-kernel I've been hunting an effect of failed ARM kernel boots when kexec is used, and here's a RFC for a solution of what I figured out. kexec currently assumes that the kernel image compression rate is 1:4, and stuffs the dtb image after what is assumed the end of the kernel image. However, it does not read in the kernel's symbol table or anything alike and hence can't know about the size of the bss section. In my case, it turns out that upon __vet_atags is executed, the dtb image lies inside the bss area and is hence overridden with zeros later on. This patch makes an attempt to fix this problem by detecting the described condition and relocating the atags/bss section to where the '_end' symbol is stored. Disclaimer: I'm really not sure whether my virt_to_phys() approach and other asm details are fully correct. I'm open to suggestions :) Successfully tested on a AM335x platform that fails to kexec without the patch. Thanks, Daniel Daniel Mack (1): ARM: head-common.S: relocate atags area if necessary arch/arm/kernel/head-common.S | 58 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) -- 1.8.3.1 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH RFC 1/1] ARM: head-common.S: relocate atags area if necessary 2013-10-14 13:44 ` Daniel Mack @ 2013-10-14 13:44 ` Daniel Mack -1 siblings, 0 replies; 4+ messages in thread From: Daniel Mack @ 2013-10-14 13:44 UTC (permalink / raw) To: linux; +Cc: kexec, neumann, linux-arm-kernel, Daniel Mack If the supplied atags/dtb pointer is located at memory inside the bss section, it will be erased by __mmap_switched. The problem is that the code that sets up the pointer can't know about a safe value unless it examines the kernel's symbol tables, so we should care about that case and relocate the area if necessary. This patch does that from inside __vet_atags. In order to determine the size of the section in dtb cases, it reads the next word after the dtb binary magic, and also has to convert that value from big to CPU endianess. For the atags case, a total size of up to 4k is assumed for now. Signed-off-by: Daniel Mack <zonque@gmail.com> --- arch/arm/kernel/head-common.S | 58 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S index 47cd974..36f0e7e 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -36,12 +36,18 @@ * that the pointer be aligned, in the first 16k of physical RAM and * that the ATAG_CORE marker is first and present. If CONFIG_OF_FLATTREE * is selected, then it will also accept a dtb pointer. Future revisions - * of this function may be more lenient with the physical address and - * may also be able to move the ATAGS block if necessary. + * of this function may be more lenient with the physical address. + * + * It is also checked whether the atags/dtb area is located before the + * end of the kernel's bss section and would hence be overridden by zeros + * later. In that case, the atags area is relocated to the '_end' symbol. + * + * r2 = atags or dtb + * r8 = phys_offset * * Returns: * r2 either valid atags pointer, valid dtb pointer, or zero - * r5, r6 corrupted + * r3, r5 - r7 corrupted */ __vet_atags: tst r2, #0x3 @ aligned? @@ -51,21 +57,61 @@ __vet_atags: #ifdef CONFIG_OF_FLATTREE ldr r6, =OF_DT_MAGIC @ is it a DTB? cmp r5, r6 - beq 2f -#endif - cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE? + bne 5f + + ldreq r5, [r2, #4] @ fdt total size is at offset 4 ... +#ifndef CONFIG_CPU_BIG_ENDIAN + eor r6, r5, r5, ror #16 @ ... and stored in be32 order + mov r6, r6, lsr #8 + bic r6, r6, #0xff00 + eor r5, r6, r5, ror #8 +#endif /* !CONFIG_CPU_BIG_ENDIAN */ + + add r5, r5, #4 @ align the size to 32bit + bic r5, r5, #3 + b 4f +#endif /* CONFIG_OF_FLATTREE */ + +5: cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE? cmpne r5, #ATAG_CORE_SIZE_EMPTY bne 1f ldr r5, [r2, #4] ldr r6, =ATAG_CORE cmp r5, r6 + movne r5, #4096 @ FIXME: we should walk the atags and + @ determine the real size. bne 1f +4: adr r3, 6f + ldmia r3!, {r6, r7} + + @ The kernel end address is stored in virtual address space, but we're + @ still in flat mapping. Hence, we have to do virt_to_phys() manually. + subs r6, r6, r7 @ r7 = PAGE_OFFSET + add r6, r6, r8 @ r8 = PHYS_OFFET + + cmp r2, r6 @ is the atags pointer inside the + @ kernel area? + bgt 2f + + mov r3, r6 @ relocate start + add r6, r6, r5 @ relocate end +3: cmp r3, r6 + ldrne fp, [r2], #4 + strne fp, [r3], #4 + bne 3b + + subs r2, r6, r5 @ rewind back to the new + @ atags/dtb image start + 2: mov pc, lr @ atag/dtb pointer is ok 1: mov r2, #0 mov pc, lr ENDPROC(__vet_atags) + .align +6: .long _end @ r6 + .long PAGE_OFFSET @ r7 /* * The following fragment of code is executed with the MMU on in MMU mode, -- 1.8.3.1 _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH RFC 1/1] ARM: head-common.S: relocate atags area if necessary @ 2013-10-14 13:44 ` Daniel Mack 0 siblings, 0 replies; 4+ messages in thread From: Daniel Mack @ 2013-10-14 13:44 UTC (permalink / raw) To: linux-arm-kernel If the supplied atags/dtb pointer is located at memory inside the bss section, it will be erased by __mmap_switched. The problem is that the code that sets up the pointer can't know about a safe value unless it examines the kernel's symbol tables, so we should care about that case and relocate the area if necessary. This patch does that from inside __vet_atags. In order to determine the size of the section in dtb cases, it reads the next word after the dtb binary magic, and also has to convert that value from big to CPU endianess. For the atags case, a total size of up to 4k is assumed for now. Signed-off-by: Daniel Mack <zonque@gmail.com> --- arch/arm/kernel/head-common.S | 58 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S index 47cd974..36f0e7e 100644 --- a/arch/arm/kernel/head-common.S +++ b/arch/arm/kernel/head-common.S @@ -36,12 +36,18 @@ * that the pointer be aligned, in the first 16k of physical RAM and * that the ATAG_CORE marker is first and present. If CONFIG_OF_FLATTREE * is selected, then it will also accept a dtb pointer. Future revisions - * of this function may be more lenient with the physical address and - * may also be able to move the ATAGS block if necessary. + * of this function may be more lenient with the physical address. + * + * It is also checked whether the atags/dtb area is located before the + * end of the kernel's bss section and would hence be overridden by zeros + * later. In that case, the atags area is relocated to the '_end' symbol. + * + * r2 = atags or dtb + * r8 = phys_offset * * Returns: * r2 either valid atags pointer, valid dtb pointer, or zero - * r5, r6 corrupted + * r3, r5 - r7 corrupted */ __vet_atags: tst r2, #0x3 @ aligned? @@ -51,21 +57,61 @@ __vet_atags: #ifdef CONFIG_OF_FLATTREE ldr r6, =OF_DT_MAGIC @ is it a DTB? cmp r5, r6 - beq 2f -#endif - cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE? + bne 5f + + ldreq r5, [r2, #4] @ fdt total size is at offset 4 ... +#ifndef CONFIG_CPU_BIG_ENDIAN + eor r6, r5, r5, ror #16 @ ... and stored in be32 order + mov r6, r6, lsr #8 + bic r6, r6, #0xff00 + eor r5, r6, r5, ror #8 +#endif /* !CONFIG_CPU_BIG_ENDIAN */ + + add r5, r5, #4 @ align the size to 32bit + bic r5, r5, #3 + b 4f +#endif /* CONFIG_OF_FLATTREE */ + +5: cmp r5, #ATAG_CORE_SIZE @ is first tag ATAG_CORE? cmpne r5, #ATAG_CORE_SIZE_EMPTY bne 1f ldr r5, [r2, #4] ldr r6, =ATAG_CORE cmp r5, r6 + movne r5, #4096 @ FIXME: we should walk the atags and + @ determine the real size. bne 1f +4: adr r3, 6f + ldmia r3!, {r6, r7} + + @ The kernel end address is stored in virtual address space, but we're + @ still in flat mapping. Hence, we have to do virt_to_phys() manually. + subs r6, r6, r7 @ r7 = PAGE_OFFSET + add r6, r6, r8 @ r8 = PHYS_OFFET + + cmp r2, r6 @ is the atags pointer inside the + @ kernel area? + bgt 2f + + mov r3, r6 @ relocate start + add r6, r6, r5 @ relocate end +3: cmp r3, r6 + ldrne fp, [r2], #4 + strne fp, [r3], #4 + bne 3b + + subs r2, r6, r5 @ rewind back to the new + @ atags/dtb image start + 2: mov pc, lr @ atag/dtb pointer is ok 1: mov r2, #0 mov pc, lr ENDPROC(__vet_atags) + .align +6: .long _end @ r6 + .long PAGE_OFFSET @ r7 /* * The following fragment of code is executed with the MMU on in MMU mode, -- 1.8.3.1 ^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2013-10-14 13:44 UTC | newest] Thread overview: 4+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2013-10-14 13:44 [PATCH RFC 0/1] ARM: boot: make atags area relocatable Daniel Mack 2013-10-14 13:44 ` Daniel Mack 2013-10-14 13:44 ` [PATCH RFC 1/1] ARM: head-common.S: relocate atags area if necessary Daniel Mack 2013-10-14 13:44 ` Daniel Mack
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.