File elilo-x86-64-kernel.diff of Package elilo

---
 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;
 
 	/*
openSUSE Build Service is sponsored by