Merge tag 'upstream/3.14'
[debian/elilo] / x86_64 / bzimage.c
index e3c7c6bb31a00e12066454538bd1b83b3730e3d4..9be4bd662f1c7c321cbb3b761c496676783055d2 100644 (file)
@@ -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,53 +281,34 @@ 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.
         */
 
-       DBG_PRT((L"reading kernel image...\n"));
+       Print(L"Loading kernel %s... ", kname);
 
        size = kernel_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));
+               ERR_PRT((L"Error reading kernel image (0x%x).", efi_status));
                free(param_start);
                param_start = NULL;
                param_size = 0;
                fops_close(fd);
                free_kmem();
                return -1;
+       } else {
+               Print(L" done\n");
        }
 
        DBG_PRT((L"kernel image read:  %d bytes, %d Kbytes\n", size, size / 1024));