diff options
author | Wang Nan <wangnan0@huawei.com> | 2014-05-06 00:38:29 +0000 |
---|---|---|
committer | Simon Horman <horms@verge.net.au> | 2014-05-11 08:47:36 +0900 |
commit | f22cf559f949ae0a78d1acd4a2b3884e5bc54f52 (patch) | |
tree | 22331dba79d894bb19599143c8e1747975e0f14d | |
parent | 981a689f09535a53fb4e179a3dd8305770656245 (diff) | |
download | kexec-tools-f22cf559f949ae0a78d1acd4a2b3884e5bc54f52.tar.gz |
zImage-arm: bugfix: kernel offset must be 0x8000
This patch fixs a problem introduced by commit e5d6a55 which make ARM
kexec fails.
Due to that commit, kernel is loaded at a dynamically offset: it computes
extra_size using size of dtb, and load zImage at base + extra_size. When
dtb size small (for example, 0x3000 bytes), kernel will be loaded at
address like 0x60003000. For ARM zImage such address is incorrect.
In kernel code arch/arm/boot/compressed/head.S, zImage builds a
temporary page table at (pc & 0xf8000000) + TEXT_OFFSET - 0x4000. The
related instructions sequence is:
mov r4, pc
and r4, r4, #0xf8000000
add r4, r4, #TEXT_OFFSET @ (TEXT_OFFSET == 0x8000 on most platforms)
call cache_on
...
call __armv7_mmu_cache_on
...
call __setup_mmu
sub r3, r4, #16384 @ Page directory size
r3 becomes page table pointer.
When kernel is loaded at 0x60003000, page table is still built at
0x60004000, which destroys kernel.
This patch make extra_size a fix value (0x8000) to avoid the failure.
For the problem commit e5d6a55 tries solve, this patch uses
locate_holes() to find a place can hold initrd and dtb.
Change from v1:
- Coding style enhancements.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Daniel Mack <zonque@gmail.com>
Signed-off-by: Simon Horman <horms@verge.net.au>
-rw-r--r-- | kexec/arch/arm/kexec-zImage-arm.c | 37 |
1 files changed, 29 insertions, 8 deletions
diff --git a/kexec/arch/arm/kexec-zImage-arm.c b/kexec/arch/arm/kexec-zImage-arm.c index 220826e..6aa98b3 100644 --- a/kexec/arch/arm/kexec-zImage-arm.c +++ b/kexec/arch/arm/kexec-zImage-arm.c @@ -282,7 +282,7 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, { unsigned long base; unsigned int atag_offset = 0x1000; /* 4k offset from memory start */ - unsigned int extra_size = 0; + unsigned int extra_size = 0x8000; /* TEXT_OFFSET */ const char *command_line; char *modified_cmdline = NULL; off_t command_line_len; @@ -356,16 +356,11 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, if (command_line_len > COMMAND_LINE_SIZE) command_line_len = COMMAND_LINE_SIZE; } - if (ramdisk) { + if (ramdisk) ramdisk_buf = slurp_file(ramdisk, &initrd_size); - } - if (dtb_file) { + if (dtb_file) dtb_buf = slurp_file(dtb_file, &dtb_length); - extra_size = _ALIGN(dtb_length, getpagesize()); - } else if (use_atags) { - extra_size = 0x8000; /* 32k should be plenty for ATAGs */ - } /* * If we are loading a dump capture kernel, we need to update kernel @@ -458,6 +453,32 @@ int zImage_arm_load(int argc, char **argv, const char *buf, off_t len, create_flatten_tree(&dtb_buf, &dtb_length, command_line); } + /* + * Search in memory to make sure there is enough memory + * to hold initrd and dtb. + * + * Even if no initrd is used, this check is still + * required for dtb. + * + * Crash kernel use fixed address, no check is ok. + */ + if ((info->kexec_flags & KEXEC_ON_CRASH) == 0) { + unsigned long page_size = getpagesize(); + /* + * DTB size may be increase a little + * when setup initrd size. Add a full page + * for it is enough. + */ + unsigned long hole_size = _ALIGN_UP(initrd_size, page_size) + + _ALIGN(dtb_length + page_size, page_size); + unsigned long initrd_base_new = locate_hole(info, + hole_size, page_size, + initrd_base, ULONG_MAX, INT_MAX); + if (base == ULONG_MAX) + return -1; + initrd_base = initrd_base_new; + } + if (ramdisk) { add_segment(info, ramdisk_buf, initrd_size, initrd_base, initrd_size); |