forked from pool/elilo
226 lines
8.2 KiB
Diff
226 lines
8.2 KiB
Diff
From: Stuart_Hayes@Dell.com
|
|
Subject: Re: [elilo-discuss] Follow-Up to 1MB Allocation Problem
|
|
Message-ID: <DFEF91B22ED07447AB6AA4B237F913F90218586E@ausx3mpc125.aus.amer.dell.com>
|
|
|
|
Here's one solution to the problem, for bzImages in x86_64 (which could
|
|
easily be ported to the other image types and architecture). I tried to
|
|
make this minimally invasive. If bzImage_probe() can't load the kernel
|
|
where it should be, it will just load the kernel anywhere, and it will
|
|
be moved to the right address after elilo exits EFI boot services. (I
|
|
also made it read where the kernel should be from the kernel header.)
|
|
It looks like it was already loading the initrd just anywhere and then
|
|
moving it after exiting boot services.
|
|
|
|
(I also enhanced the "MEMCPY" macro in x86_64/sysdeps.h so it will copy
|
|
to an overlapping area correctly regardless of whether the "from"
|
|
address is higher or lower than the "to" address.)
|
|
|
|
I originally wrote code that would actually check if the kernel was
|
|
relocatable and then relocate it, like the ia64 code does. That worked
|
|
fine, except that the address that was found overlapped the area where
|
|
elilo copies the initrd in start_kernel() after EFI boot services are
|
|
exited! Instead of modifying the initrd addresses, too, I decided it
|
|
would be easier and less likely to break anyting else (and would work
|
|
with non-relocatable kernels, too) to just do it the other way.
|
|
|
|
Any chance we could get this patch into elilo? I can modify and
|
|
resubmit if there are any issues with this approach.
|
|
|
|
Thanks!
|
|
|
|
diff -purN '--exclude=*o' '--exclude=tags' elilo-3.8-3.35-orig1/alloc.c elilo-3.8-3.35-orig_c/alloc.c
|
|
--- elilo-3.8-3.35-orig1/alloc.c 2008-12-08 06:03:05.000000000 -0600
|
|
+++ elilo-3.8-3.35-orig_c/alloc.c 2008-12-08 04:51:28.000000000 -0600
|
|
@@ -213,6 +213,19 @@ free_all(VOID)
|
|
}
|
|
|
|
INTN
|
|
+alloc_kmem_anywhere(VOID **start_addr, UINTN pgcnt)
|
|
+{
|
|
+ void * tmp;
|
|
+ if ((tmp = alloc_pages(pgcnt, EfiLoaderData, AllocateAnyPages, *start_addr)) == 0) return -1;
|
|
+
|
|
+ kmem_addr = tmp;
|
|
+ kmem_pgcnt = pgcnt;
|
|
+ *start_addr = tmp;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+INTN
|
|
alloc_kmem(VOID *start_addr, UINTN pgcnt)
|
|
{
|
|
if (alloc_pages(pgcnt, EfiLoaderData, AllocateAddress, start_addr) == 0) return -1;
|
|
diff -purN '--exclude=*o' '--exclude=tags' elilo-3.8-3.35-orig1/elilo.c elilo-3.8-3.35-orig_c/elilo.c
|
|
--- elilo-3.8-3.35-orig1/elilo.c 2008-12-08 06:03:05.000000000 -0600
|
|
+++ elilo-3.8-3.35-orig_c/elilo.c 2008-12-08 05:01:33.000000000 -0600
|
|
@@ -132,6 +132,9 @@ kernel_load(EFI_HANDLE image, CHAR16 *kn
|
|
|
|
if (elilo_opt.initrd[0]) {
|
|
|
|
+ /*
|
|
+ * fix me? -- load_file() ignores address from sysdeps_initrd_get_addr()
|
|
+ */
|
|
if (sysdeps_initrd_get_addr(kd, imem) == -1) goto exit_error;
|
|
|
|
switch(load_file(elilo_opt.initrd, imem)) {
|
|
Files elilo-3.8-3.35-orig1/elilo.efi and elilo-3.8-3.35-orig_c/elilo.efi differ
|
|
Files elilo-3.8-3.35-orig1/tools/eliloalt and elilo-3.8-3.35-orig_c/tools/eliloalt differ
|
|
diff -purN '--exclude=*o' '--exclude=tags' elilo-3.8-3.35-orig1/x86_64/bzimage.c elilo-3.8-3.35-orig_c/x86_64/bzimage.c
|
|
--- elilo-3.8-3.35-orig1/x86_64/bzimage.c 2008-12-08 06:03:05.000000000 -0600
|
|
+++ elilo-3.8-3.35-orig_c/x86_64/bzimage.c 2008-12-08 05:04:52.000000000 -0600
|
|
@@ -158,13 +158,37 @@ 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 >= 0) &&
|
|
+ (param_start->s.kernel_start >= DEFAULT_KERNEL_START)) {
|
|
+ kernel_start = kernel_load_address = (VOID *)((UINTN)param_start->s.kernel_start);
|
|
+ VERB_PRT(3, Print(L"kernel header suggests kernel start at address 0x%x\n",
|
|
+ kernel_start));
|
|
+ }
|
|
+
|
|
+ kernel_load_address = kernel_start;
|
|
+
|
|
if (alloc_kmem(kernel_start, EFI_SIZE_TO_PAGES(kernel_size))) {
|
|
- ERR_PRT((L"Could not allocate kernel memory."));
|
|
- return -1;
|
|
- } else {
|
|
- VERB_PRT(3, Print(L"kernel_start: 0x%x kernel_size: %d\n",
|
|
- kernel_start, kernel_size));
|
|
+ /*
|
|
+ * 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))) {
|
|
+ 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: 0x%x kernel_size: %d loading at: 0x%x\n",
|
|
+ kernel_start, kernel_size, kernel_load_address));
|
|
+
|
|
/*
|
|
* Now read the rest of the kernel image into memory.
|
|
*/
|
|
@@ -172,7 +196,7 @@ bzImage_probe(CHAR16 *kname)
|
|
DBG_PRT((L"reading kernel image...\n"));
|
|
|
|
size = kernel_size;
|
|
- efi_status = fops_read(fd, kernel_start, &size);
|
|
+ efi_status = fops_read(fd, kernel_load_address, &size);
|
|
if (EFI_ERROR(efi_status) || size < 0x10000) {
|
|
ERR_PRT((L"Error reading kernel image %s.", kname));
|
|
free(param_start);
|
|
diff -purN '--exclude=*o' '--exclude=tags' elilo-3.8-3.35-orig1/x86_64/sysdeps.h elilo-3.8-3.35-orig_c/x86_64/sysdeps.h
|
|
--- elilo-3.8-3.35-orig1/x86_64/sysdeps.h 2008-12-08 06:03:05.000000000 -0600
|
|
+++ elilo-3.8-3.35-orig_c/x86_64/sysdeps.h 2008-12-08 06:01:52.000000000 -0600
|
|
@@ -48,6 +48,11 @@
|
|
#define INITRD_START (50*1024*1024)
|
|
|
|
/*
|
|
+ * Default start address for kernel.
|
|
+ */
|
|
+#define DEFAULT_KERNEL_START 0x100000
|
|
+
|
|
+/*
|
|
* This version must match the one in the kernel.
|
|
*
|
|
* This table was put together using information from the
|
|
@@ -307,10 +312,16 @@ typedef union x86_64_boot_params {
|
|
UINT8 *t = (UINT8 *)(to); \
|
|
UINT8 *f = (UINT8 *)(from); \
|
|
UINTN n = cnt; \
|
|
- if (t && f && n) { \
|
|
+ if (t && f && n && (t<f)) { \
|
|
while (n--) { \
|
|
*t++ = *f++; \
|
|
} \
|
|
+ } else if (t && f && n && (t>f)) { \
|
|
+ t += n; \
|
|
+ f += n; \
|
|
+ while (n--) { \
|
|
+ *t-- = *f--; \
|
|
+ } \
|
|
} \
|
|
}
|
|
|
|
@@ -343,6 +354,7 @@ extern UINTN param_size;
|
|
|
|
extern VOID *kernel_start;
|
|
extern UINTN kernel_size;
|
|
+extern VOID *kernel_load_address;
|
|
|
|
extern VOID *initrd_start;
|
|
extern UINTN initrd_size;
|
|
@@ -379,14 +391,24 @@ start_kernel(VOID *kentry, boot_params_t
|
|
asm volatile ( "cli" : : );
|
|
|
|
/*
|
|
- * Relocate initrd, if present.
|
|
+ * Relocate kernel (if needed) and initrd (if present).
|
|
+ * Copy kernel first, in case kernel was loaded overlapping where we're
|
|
+ * planning to copy the initrd. This assumes that the initrd didn't
|
|
+ * get loaded overlapping where we're planning to copy the kernel, but
|
|
+ * that's pretty unlikely since we couldn't alloc that space for the
|
|
+ * kernel (or the kernel would already be there).
|
|
*/
|
|
|
|
+ if (kernel_start != kernel_load_address) {
|
|
+ MEMCPY(kernel_start, kernel_load_address, kernel_size);
|
|
+ }
|
|
+
|
|
if (bp->s.initrd_start) {
|
|
temp = bp->s.initrd_start;
|
|
MEMCPY(INITRD_START, temp , bp->s.initrd_size);
|
|
bp->s.initrd_start = INITRD_START;
|
|
}
|
|
+
|
|
/*
|
|
* Copy boot sector, setup data and command line
|
|
* to final resting place. We need to copy
|
|
diff -purN '--exclude=*o' '--exclude=tags' elilo-3.8-3.35-orig1/x86_64/system.c elilo-3.8-3.35-orig_c/x86_64/system.c
|
|
--- elilo-3.8-3.35-orig1/x86_64/system.c 2008-12-08 06:03:25.000000000 -0600
|
|
+++ elilo-3.8-3.35-orig_c/x86_64/system.c 2008-12-08 04:51:28.000000000 -0600
|
|
@@ -105,7 +105,9 @@ 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 *)0x100000; /* 1M */
|
|
+VOID *kernel_start = (VOID *)DEFAULT_KERNEL_START;
|
|
+/* The kernel may load elsewhere if EFI firmware reserves kernel_start */
|
|
+VOID *kernel_load_address = (VOID *)DEFAULT_KERNEL_START;
|
|
|
|
VOID *initrd_start = NULL;
|
|
UINTN initrd_size = 0;
|
|
--- elilo/fs/localfs.c
|
|
+++ elilo/fs/localfs.c
|
|
@@ -98,7 +98,7 @@
|
|
|
|
DBG_PRT((L"localfs_open on %s\n", name));
|
|
|
|
- status = uefi_call_wrapper(lfs->volume->Open, 5, lfs->volume, &fh, name, EFI_FILE_MODE_READ, 0);
|
|
+ status = uefi_call_wrapper(lfs->volume->Open, 5, lfs->volume, &fh, name, EFI_FILE_MODE_READ, (UINT64)0);
|
|
if (status == EFI_SUCCESS) {
|
|
*fd = LOCALFS_F2FD(fh);
|
|
}
|
|
--- elilo/elilo.h
|
|
+++ elilo/elilo.h
|
|
@@ -151,6 +151,7 @@
|
|
extern VOID free_pages(VOID *);
|
|
extern VOID free_all(VOID);
|
|
extern INTN alloc_kmem(VOID *, UINTN);
|
|
+extern INTN alloc_kmem_anywhere(VOID **, UINTN);
|
|
extern VOID free_kmem(VOID);
|
|
extern VOID free_all_memory(VOID);
|
|
|