From: H. Peter Anvin Date: Fri, 3 Apr 2009 23:09:29 +0000 (-0700) Subject: linux.c32: make load_linux() support relocation X-Git-Tag: syslinux-3.74-pre16~2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ddad35c72c939a87ee840cde7522cd7ef907b6b5;p=profile%2Fivi%2Fsyslinux.git linux.c32: make load_linux() support relocation In the case where the preferred kernel locations are not available, make load_linux() smart enough to be able to relocate the kernel if at all possible. Signed-off-by: H. Peter Anvin --- diff --git a/com32/include/syslinux/align.h b/com32/include/syslinux/align.h new file mode 100644 index 0000000..5b01bf2 --- /dev/null +++ b/com32/include/syslinux/align.h @@ -0,0 +1,47 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Intel Corporation; author H. Peter Anvin + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- */ + +#ifndef _SYSLINUX_ALIGN_H +#define _SYSLINUX_ALIGN_H + +#include + +static inline uintptr_t __align_down(uintptr_t __p, uintptr_t __a) +{ + return __p & ~(__a - 1); +} +static inline uintptr_t __align_up(uintptr_t __p, uintptr_t __a) +{ + return (__p + __a - 1) & ~(__a - 1); +} + +#define ALIGN_UP(p,a) ((__typeof__(p))__align_up((uintptr_t)(p), (a))) +#define ALIGN_DOWN(p,a) ((__typeof__(p))__align_down((uintptr_t)(p), (a))) +#define ALIGN_UP_FOR(p,t) ALIGN_UP(p,sizeof(t)) +#define ALIGN_DOWN_FOR(p,t) ALIGN_DOWN(p,sizeof(t)) + +#endif /* _SYSLINUX_ALIGN_H */ diff --git a/com32/lib/sys/argv.c b/com32/lib/sys/argv.c index 0d16bfe..8b55d94 100644 --- a/com32/lib/sys/argv.c +++ b/com32/lib/sys/argv.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation @@ -35,8 +35,7 @@ #include #include #include - -#define ALIGN_UP(p,t) ((t *)(((uintptr_t)(p) + (sizeof(t)-1)) & ~(sizeof(t)-1))) +#include extern char _end[]; /* Symbol created by linker */ void *__mem_end = &_end; /* Global variable for use by malloc() */ @@ -75,7 +74,7 @@ int __parse_argv(char ***argv, const char *str) } /* Now create argv */ - arg = ALIGN_UP(q,char *); + arg = ALIGN_UP_FOR(q, char *); *argv = arg; *arg++ = argv0; /* argv[0] */ diff --git a/com32/lib/syslinux/load_linux.c b/com32/lib/syslinux/load_linux.c index cca2efd..4f583cd 100644 --- a/com32/lib/syslinux/load_linux.c +++ b/com32/lib/syslinux/load_linux.c @@ -31,9 +31,11 @@ * Load a Linux kernel (Image/zImage/bzImage). */ +#include #include #include #include +#include #include #include #include @@ -159,6 +161,7 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, struct syslinux_movelist *fraglist = NULL; struct syslinux_memmap *mmap = NULL; struct syslinux_memmap *amap = NULL; + bool ok; cmdline_size = strlen(cmdline)+1; @@ -189,6 +192,9 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, if (!memlimit || memlimit-1 > hdr.initrd_addr_max) memlimit = hdr.initrd_addr_max+1; /* Zero for no limit */ + if (hdr.version < 0x0205) + hdr.relocatable_kernel = 0; + if (hdr.version < 0x0206) hdr.cmdline_max_len = 256; @@ -250,7 +256,79 @@ int syslinux_boot_linux(void *kernel_buf, size_t kernel_size, /* Place the kernel in memory */ + /* First, find a suitable place for the protected-mode code */ + if (syslinux_memmap_type(amap, prot_mode_base, prot_mode_size) + != SMT_FREE) { + const struct syslinux_memmap *mp; + if (!hdr.relocatable_kernel) + goto bail; /* Can't relocate - no hope */ + + ok = false; + for (mp = amap; mp; mp = mp->next) { + addr_t start, end; + start = mp->start; + end = mp->next->start; + + if (mp->type != SMT_FREE) + continue; + + if (end <= prot_mode_base) + continue; /* Only relocate upwards */ + + if (start <= prot_mode_base) + start = prot_mode_base; + + start = ALIGN_UP(start, hdr.kernel_alignment); + if (start >= end) + continue; + + /* The 3* here is a total fudge factor... it's supposed to + account for the fact that the kernel needs to be decompressed, + and then followed by the BSS and BRK regions. This doesn't, + however, account for the fact that the kernel is decompressed + into a whole other place, either. */ + if (end - start >= 3*prot_mode_size) { + prot_mode_base = start; + ok = true; + break; + } + } + + if (!ok) + goto bail; + } + /* Real mode code */ + if (syslinux_memmap_type(amap, real_mode_base, + cmdline_offset+cmdline_size) != SMT_FREE) { + const struct syslinux_memmap *mp; + + ok = false; + for (mp = amap; mp; mp = mp->next) { + addr_t start, end; + start = mp->start; + end = mp->next->start; + + if (mp->type != SMT_FREE) + continue; + + if (start < real_mode_base) + start = real_mode_base; /* Lowest address we'll use */ + if (end > 640*1024) + end = 640*1024; + + start = ALIGN_UP(start, 16); + if (start >= end) + continue; + + if (end - start >= cmdline_offset+cmdline_size) { + real_mode_base = start; + ok = true; + break; + } + } + } + if (syslinux_add_movelist(&fraglist, real_mode_base, (addr_t)kernel_buf, real_mode_size)) goto bail;