SHA256
1
0
forked from pool/elilo
elilo/elilo-x86-64-kernel.diff

258 lines
7.7 KiB
Diff

---
alloc.c | 10 +++
ia32/bzimage.c | 2
x86_64/bzimage.c | 160 +++++++++++++++++++++++++++++++++++++++++++++----------
x86_64/sysdeps.h | 5 +
x86_64/system.c | 9 ++-
5 files changed, 152 insertions(+), 34 deletions(-)
--- a/alloc.c
+++ b/alloc.c
@@ -217,7 +217,15 @@ INTN
alloc_kmem_anywhere(VOID **start_addr, UINTN pgcnt)
{
void * tmp;
- if ((tmp = alloc_pages(pgcnt, EfiLoaderData, AllocateAnyPages, *start_addr)) == 0) return -1;
+ /*
+ * During "AllocateAnyPages" *start_addr will be ignored.
+ * Therefore we can safely subvert it to reuse this function with
+ * an alloc_kmem_anyhwere_below() semantic...
+ */
+ tmp = alloc_pages(pgcnt, EfiLoaderData,
+ (*start_addr) ? AllocateMaxAddress : AllocateAnyPages,
+ *start_addr);
+ if (tmp == NULL) return -1;
kmem_addr = tmp;
kmem_pgcnt = pgcnt;
--- a/ia32/bzimage.c
+++ b/ia32/bzimage.c
@@ -169,7 +169,7 @@ bzImage_probe(CHAR16 *kname)
kernel_start));
}
- kernel_load_address = kernel_start;
+ kernel_load_address = NULL; /* allocate anywhere! */
if (alloc_kmem(kernel_start, EFI_SIZE_TO_PAGES(kernel_size)) != 0) {
/*
--- a/x86_64/bzimage.c
+++ b/x86_64/bzimage.c
@@ -36,6 +36,129 @@ UINTN param_size = 0;
UINTN kernel_size = 0x800000; /* 8M (default x86_64 bzImage size limit) */
+static VOID *
+bzImage_alloc()
+{
+ UINTN pages = EFI_SIZE_TO_PAGES(kernel_size);
+ int reloc_kernel = 0;
+ VOID *kla, *kend = kernel_start + kernel_size;
+ UINT32 kalign, kmask;
+ boot_params_t *ps = param_start;
+
+ /*
+ * Get address for kernel from header, if applicable & available.
+ */
+ if ((ps->s.hdr_major < 2) ||
+ (ps->s.hdr_major == 2 && ps->s.hdr_minor < 5)) {
+ reloc_kernel = 0;
+ } else {
+ if (ps->s.kernel_start >= DEFAULT_KERNEL_START)
+ kernel_start = (void *)(UINT64)ps->s.kernel_start;
+ reloc_kernel = ps->s.relocatable_kernel;
+ kalign = ps->s.kernel_alignment;
+ kmask = kalign - 1;
+ VERB_PRT(3, Print(L"kernel header (%d.%d) suggests kernel "
+ "start at address "PTR_FMT" (%srelocatable!)\n",
+ ps->s.hdr_major, ps->s.hdr_minor, ps->s.kernel_start,
+ (reloc_kernel ? L"": L"not ")));
+ }
+
+ /*
+ * Best effort for old (< 2.6.20) and non-relocatable kernels
+ */
+ if (alloc_kmem(kernel_start, pages) == 0) {
+ VERB_PRT(3, Print(L"kernel_start: "PTR_FMT" kernel_size: %d\n",
+ kernel_start, kernel_size));
+ return kernel_start;
+ } else if ( ! reloc_kernel ) {
+ /*
+ * Couldn't get desired address--just load it anywhere and
+ * (try to) move it later. It's the only chance for non-
+ * relocatable kernels, but it breaks occassionally...
+ */
+ ERR_PRT((L"Kernel header (%d.%d) suggests kernel "
+ "start at address "PTR_FMT" (non relocatable!)\n"
+ "This address is not available, so an attempt"
+ "is made to copy the kernel there later on\n"
+ "BEWARE: this is unsupported and may not work. "
+ "Please update your kernel.\n",
+ ps->s.hdr_major, ps->s.hdr_minor, ps->s.kernel_start));
+ kla = (VOID *)(UINT32_MAX - kernel_size);
+ /* NULL would preserve the "anywhere" semantic, */
+ /* but it would not prevent allocation above 4GB! */
+
+ if (alloc_kmem_anywhere(&kla, pages) != 0) {
+ /* out of luck */
+ return NULL;
+ }
+ VERB_PRT(3, Print(L"kernel_start: "PTR_FMT
+ " kernel_size: %d loading at: "PTR_FMT"\n",
+ kernel_start, kernel_size, kla));
+ return kla;
+ }
+
+
+ /* Is 'ps->s.kernel_alignment' guaranteed to be sane? */
+ if (kalign < EFI_PAGE_SIZE) {
+ kalign = EFI_PAGE_SIZE;
+ kmask = EFI_PAGE_MASK;
+ }
+ DBG_PRT((L"alignment: kernel=0x%x efi_page=0x%x : 0x%x\n",
+ ps->s.kernel_alignment, EFI_PAGE_SIZE, kalign));
+
+ /*
+ * Couldn't get the preferred address, but luckily it's
+ * a relocatable kernel, so ...
+ *
+ * 1. use 'find_kernel_memory()' (like Itanium)
+ * 2. try out the 16 lowest possible aligned addresses (> 0)
+ * 3. get enough memory to align "creatively"
+ * 4. forget alignment (and start praying)...
+ */
+
+ /* 1. */
+ if ((find_kernel_memory(kernel_start, kend, kalign, &kla) != 0) ||
+ (alloc_kmem(kla, pages) != 0)) {
+ kla = NULL;
+ }
+
+ /* 2. */
+ if ( ! kla && (UINT64)kernel_start < kalign ) {
+ int i;
+ for ( i = 1; i < 16 && !kla; i++ ) {
+ VOID *tmp = (VOID *)((UINT64)kalign * i);
+ if (alloc_kmem(tmp, pages) == 0) {
+ kla = tmp;
+ }
+ }
+ }
+
+ /* 3. */
+ if ( ! kla ) {
+ UINTN apages = EFI_SIZE_TO_PAGES(kernel_size + kmask);
+ kla = (VOID *)(UINT32_MAX - kernel_size - kmask);
+
+ if (alloc_kmem_anywhere(&kla, apages) == 0) {
+ kla = (VOID *)(((UINT64)kla + kmask) & ~kmask);
+ } else {
+ kla = NULL;
+ }
+ }
+
+ /* 4. last resort */
+ if ( ! kla ) {
+ kla = (VOID *)(UINT32_MAX - kernel_size);
+ if (alloc_kmem_anywhere(&kla, pages) != 0) {
+ return NULL;
+ }
+ }
+
+ kernel_start = kla;
+ VERB_PRT(1, Print(L"relocating kernel_start: "PTR_FMT
+ " kernel_size: %d\n", kernel_start, kernel_size));
+ return kla;
+}
+
static INTN
bzImage_probe(CHAR16 *kname)
{
@@ -158,37 +281,16 @@ bzImage_probe(CHAR16 *kname)
* Allocate memory for kernel.
*/
- /*
- * Get correct address for kernel from header, if applicable & available.
- */
- if ((param_start->s.hdr_major == 2) &&
- (param_start->s.hdr_minor >= 6) &&
- (param_start->s.kernel_start >= DEFAULT_KERNEL_START)) {
- kernel_start = (void *)param_start->s.kernel_start;
- VERB_PRT(3, Print(L"kernel header suggests kernel start at address "PTR_FMT"\n",
- kernel_start));
- }
-
- kernel_load_address = kernel_start;
-
- if (alloc_kmem(kernel_start, EFI_SIZE_TO_PAGES(kernel_size)) != 0) {
- /*
- * Couldn't get desired address--just load it anywhere and move it later.
- * (Easier than relocating kernel, and also works with non-relocatable kernels.)
- */
- if (alloc_kmem_anywhere(&kernel_load_address, EFI_SIZE_TO_PAGES(kernel_size)) != 0) {
- ERR_PRT((L"Could not allocate memory for kernel."));
- free(param_start);
- param_start = NULL;
- param_size = 0;
- fops_close(fd);
- return -1;
- }
+ kernel_load_address = bzImage_alloc();
+ if ( ! kernel_load_address ) {
+ ERR_PRT((L"Could not allocate memory for kernel."));
+ free(param_start);
+ param_start = NULL;
+ param_size = 0;
+ fops_close(fd);
+ return -1;
}
- VERB_PRT(3, Print(L"kernel_start: "PTR_FMT" kernel_size: %d loading at: "PTR_FMT"\n",
- kernel_start, kernel_size, kernel_load_address));
-
/*
* Now read the rest of the kernel image into memory.
*/
--- a/x86_64/sysdeps.h
+++ b/x86_64/sysdeps.h
@@ -285,7 +285,10 @@ typedef union x86_64_boot_params {
/* 0x228 */ UINT32 cmdline_addr; /* LDR */
/* 0x22C */ UINT32 initrd_addr_max; /* BLD */
-/* 0x230 */ UINT32 pad_8[40];
+/* 0x230 */ UINT32 kernel_alignment; /* BLD */
+/* 0x234 */ UINT8 relocatable_kernel; /* BLD */
+/* 0x235 */ UINT8 pad_8[3];
+/* 0x238 */ UINT32 pad_9[38];
/* 0x2D0 */ UINT8 e820_map[2560];
} s;
} boot_params_t;
--- a/x86_64/system.c
+++ b/x86_64/system.c
@@ -105,10 +105,10 @@ UINTN high_base_mem = 0x90000;
UINTN high_ext_mem = 32 * 1024 * 1024;
/* This starting address will hold true for all of the loader types for now */
-VOID *kernel_start = (void *)DEFAULT_KERNEL_START;
+VOID *kernel_start = (VOID *)DEFAULT_KERNEL_START;
/* The kernel may load elsewhere if EFI firmware reserves kernel_start */
-VOID *kernel_load_address = DEFAULT_KERNEL_START;
+VOID *kernel_load_address = (VOID *)DEFAULT_KERNEL_START;
VOID *initrd_start = NULL;
UINTN initrd_size = 0;
@@ -631,6 +631,11 @@ sysdeps_create_boot_params(
/*
* Kernel entry point.
*/
+ if ( (UINT64)kernel_start != (UINT32)(UINT64)kernel_start ) {
+ ERR_PRT((L"Start of kernel (will be) out of reach (>4GB)."));
+ free_kmem();
+ return -1;
+ }
bp->s.kernel_start = (UINT32)(UINT64)kernel_start;
/*