]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Ported linux to relocator framework
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 11 Jan 2010 13:59:01 +0000 (14:59 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 11 Jan 2010 13:59:01 +0000 (14:59 +0100)
include/grub/i386/relocator.h
lib/i386/relocator.c
lib/i386/relocator32.S
lib/relocator.c
loader/i386/linux.c

index 2027a275cc89977af0a0d9309a78a4b7419a9298..e4c9e7ca7349084626296dbcd01e991b41e179ed 100644 (file)
@@ -31,6 +31,7 @@ struct grub_relocator32_state
   grub_uint32_t ecx;
   grub_uint32_t edx;
   grub_uint32_t eip;
+  grub_uint32_t esi;
 };
 
 grub_err_t grub_relocator32_boot (struct grub_relocator *rel,
index 2396999bb7b98f582bf3c9cd724ca4b007af10a6..11e673cf766babf63f4bb99629c5e1151c47e909 100644 (file)
@@ -47,6 +47,7 @@ extern grub_uint32_t grub_relocator32_ecx;
 extern grub_uint32_t grub_relocator32_edx;
 extern grub_uint32_t grub_relocator32_eip;
 extern grub_uint32_t grub_relocator32_esp;
+extern grub_uint32_t grub_relocator32_esi;
 
 #define RELOCATOR_SIZEOF(x)    (&grub_relocator##x##_end - &grub_relocator##x##_start)
 
@@ -126,6 +127,7 @@ grub_relocator32_boot (struct grub_relocator *rel,
   grub_relocator32_edx = state.edx;
   grub_relocator32_eip = state.eip;
   grub_relocator32_esp = state.esp;
+  grub_relocator32_esi = state.esi;
 
   grub_memmove (src, &grub_relocator32_start, RELOCATOR_SIZEOF (32));
 
index f69e0bdc80b4dfbdbe188c7600ac95849c68cfb7..23bf6530206eddfaa8968e35574baeba3e59b195 100644 (file)
@@ -93,6 +93,13 @@ VARIABLE(grub_relocator32_esp)
 
        movl    %eax, %esp
 
+       /* mov imm32, %eax */
+       .byte   0xb8
+VARIABLE(grub_relocator32_esi)
+       .long   0
+
+       movl    %eax, %esi
+       
        /* mov imm32, %eax */
        .byte   0xb8
 VARIABLE(grub_relocator32_eax)
index 4b638cd66a684c6095b5f3894f4e1059508224d0..71517d94bf3d2094a3ed777f25ae3272734d5ba3 100644 (file)
@@ -99,6 +99,7 @@ get_best_header (struct grub_relocator *rel,
            hb = h;
            hbp = hp;
            *best_addr = addr;
+           grub_dprintf ("relocator", "picked %p/%x\n", hb, addr);
          }
       }
     else
@@ -141,6 +142,7 @@ get_best_header (struct grub_relocator *rel,
            hb = h;
            hbp = hp;
            *best_addr = addr;
+           grub_dprintf ("relocator", "picked %p/%x\n", hb, addr);
          }
       }
   }
@@ -188,8 +190,12 @@ malloc_in_range (struct grub_relocator *rel,
     grub_mm_region_t r, rp;
     for (rp = NULL, r = grub_mm_base; r; rp = r, r = r->next)
       {
-       if ((grub_addr_t) r + r->size + sizeof (*r) > start
-           && (grub_addr_t) r <= end && r->size + sizeof (*r) >= size
+       grub_dprintf ("relocator", "region %p. %d %d %d\n", r,
+                     (grub_addr_t) r + r->size + sizeof (*r) >= start,
+                     (grub_addr_t) r < end && r->size + sizeof (*r) >= size,
+                     (rb == NULL || (from_low_priv ? rb > r : rb < r)));
+       if ((grub_addr_t) r + r->size + sizeof (*r) >= start
+           && (grub_addr_t) r < end && r->size + sizeof (*r) >= size
            && (rb == NULL || (from_low_priv ? rb > r : rb < r)))
          {
            rb = r;
@@ -221,7 +227,7 @@ malloc_in_range (struct grub_relocator *rel,
     }
 
   /* Special case: relocating region start.  */
-  if (best_addr < (grub_addr_t) hbp)
+  if (best_addr < (grub_addr_t) hb)
     {
       grub_addr_t newreg_start, newreg_raw_start = best_addr + size;
       grub_addr_t newreg_size, newreg_presize;
@@ -320,7 +326,8 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
   /* Keep chunks in memory in the same order as they'll be after relocation.  */
   for (chunk = rel->chunks; chunk; chunk = chunk->next)
     {
-      if (chunk->target > target && chunk->src > max_addr)
+      if (chunk->target > target && chunk->src < max_addr
+         && chunk->src < rel->postchunks)
        max_addr = chunk->src;
       if (chunk->target + chunk->size <= target
          && chunk->src + chunk->size > min_addr
@@ -345,10 +352,10 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
       /* A trick to improve Linux allocation.  */
 #if defined (__i386__) || defined (__x86_64__)
       if (target < 0x100000)
-       if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 0,
-                            size, &start, 1, 0))
+       if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
+                            size, &start, 0, 1))
          {
-           if (rel->postchunks < start)
+           if (rel->postchunks > start)
              rel->postchunks = start;
            break;
          }
@@ -356,7 +363,7 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
       if (malloc_in_range (rel, target, max_addr, 1, size, &start, 1, 0))
        break;
 
-      if (malloc_in_range (rel, min_addr, target, 0, size, &start, 1, 0))
+      if (malloc_in_range (rel, min_addr, target, 1, size, &start, 0, 0))
        break;
 
       grub_dprintf ("relocator", "not allocated\n");
@@ -436,7 +443,8 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
      relocation.  */
   for (chunk = rel->chunks; chunk; chunk = chunk->next)
     {
-      if (chunk->target > max_addr && chunk->src > max_addr2)
+      if (chunk->target > max_addr && chunk->src > max_addr2
+         && chunk->src < rel->postchunks)
        max_addr2 = chunk->src;
       if (chunk->target + chunk->size <= min_addr
          && chunk->src + chunk->size < min_addr2
index 43c455cd66b4a2eab6fcf0a31f8ab686707600f5..e8d06b0e7866d116758031c41235189d5fe8755a 100644 (file)
@@ -34,6 +34,7 @@
 #include <grub/command.h>
 #include <grub/i386/pc/vbe.h>
 #include <grub/i386/pc/console.h>
+#include <grub/i386/relocator.h>
 #include <grub/i18n.h>
 
 #define GRUB_LINUX_CL_OFFSET           0x1000
@@ -44,36 +45,18 @@ static grub_dl_t my_mod;
 static grub_size_t linux_mem_size;
 static int loaded;
 static void *real_mode_mem;
+static grub_addr_t real_mode_target;
 static void *prot_mode_mem;
+static grub_addr_t prot_mode_target;
 static void *initrd_mem;
+static grub_addr_t initrd_mem_target;
 static grub_uint32_t real_mode_pages;
 static grub_uint32_t prot_mode_pages;
 static grub_uint32_t initrd_pages;
+static struct grub_relocator *relocator = NULL;
 
-static grub_uint8_t gdt[] __attribute__ ((aligned(16))) =
-  {
-    /* NULL.  */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    /* Reserved.  */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    /* Code segment.  */
-    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x9A, 0xCF, 0x00,
-    /* Data segment.  */
-    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x92, 0xCF, 0x00
-  };
-
-struct gdt_descriptor
-{
-  grub_uint16_t limit;
-  void *base;
-} __attribute__ ((packed));
-
-static struct gdt_descriptor gdt_desc =
-  {
-    sizeof (gdt) - 1,
-    gdt
-  };
-
+/* FIXME */
+#if 0
 struct idt_descriptor
 {
   grub_uint16_t limit;
@@ -85,6 +68,7 @@ static struct idt_descriptor idt_desc =
     0,
     0
   };
+#endif
 
 #ifdef GRUB_MACHINE_PCBIOS
 struct linux_vesafb_res
@@ -290,15 +274,19 @@ find_mmap_size (void)
 static void
 free_pages (void)
 {
+  grub_relocator_unload (relocator);
+  relocator = NULL;
   real_mode_mem = prot_mode_mem = initrd_mem = 0;
+  real_mode_target = prot_mode_target = initrd_mem_target = 0;
 }
 
 /* Allocate pages for the real mode code and the protected mode code
    for linux as well as a memory map buffer.  */
-static int
+static grub_err_t
 allocate_pages (grub_size_t prot_size)
 {
   grub_size_t real_size, mmap_size;
+  grub_err_t err;
 
   /* Make sure that each size is aligned to a page boundary.  */
   real_size = GRUB_LINUX_CL_END_OFFSET;
@@ -316,6 +304,13 @@ allocate_pages (grub_size_t prot_size)
   /* Initialize the memory pointers with NULL for convenience.  */
   free_pages ();
 
+  relocator = grub_relocator_new ();
+  if (!relocator)
+    {
+      err = grub_errno;
+      goto fail;
+    }
+
   /* FIXME: Should request low memory from the heap when this feature is
      implemented.  */
 
@@ -339,32 +334,42 @@ allocate_pages (grub_size_t prot_size)
          if (real_size + mmap_size > size)
            return 0;
 
-         real_mode_mem =
-           (void *) (grub_size_t) ((addr + size) - (real_size + mmap_size));
+         real_mode_target = ((addr + size) - (real_size + mmap_size));
          return 1;
        }
 
       return 0;
     }
   grub_mmap_iterate (hook);
-  if (! real_mode_mem)
+  if (! real_mode_target)
     {
-      grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
+      err = grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
       goto fail;
     }
 
-  prot_mode_mem = (void *) 0x100000;
+  err = grub_relocator_alloc_chunk_addr (relocator, &real_mode_mem,
+                                        real_mode_target,
+                                        (real_size + mmap_size));
+  if (err)
+    goto fail;
+
+  prot_mode_target = 0x100000;
+
+  err = grub_relocator_alloc_chunk_addr (relocator, &prot_mode_mem,
+                                        prot_mode_target, prot_size);
+  if (err)
+    goto fail;
 
   grub_dprintf ("linux", "real_mode_mem = %lx, real_mode_pages = %x, "
                 "prot_mode_mem = %lx, prot_mode_pages = %x\n",
                 (unsigned long) real_mode_mem, (unsigned) real_mode_pages,
                 (unsigned long) prot_mode_mem, (unsigned) prot_mode_pages);
 
-  return 1;
+  return GRUB_ERR_NONE;
 
  fail:
   free_pages ();
-  return 0;
+  return err;
 }
 
 static void
@@ -460,16 +465,12 @@ grub_linux_boot (void)
   int e820_num;
   grub_err_t err = 0;
   char *modevar, *tmp;
+  struct grub_relocator32_state state;
 
   params = real_mode_mem;
 
-  grub_dprintf ("linux", "code32_start = %x, idt_desc = %lx, gdt_desc = %lx\n",
-               (unsigned) params->code32_start,
-                (unsigned long) &(idt_desc.limit),
-               (unsigned long) &(gdt_desc.limit));
-  grub_dprintf ("linux", "idt = %x:%lx, gdt = %x:%lx\n",
-               (unsigned) idt_desc.limit, (unsigned long) idt_desc.base,
-               (unsigned) gdt_desc.limit, (unsigned long) gdt_desc.base);
+  grub_dprintf ("linux", "code32_start = %x\n",
+               (unsigned) params->code32_start);
 
   auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t, grub_uint32_t);
   int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type)
@@ -568,31 +569,12 @@ grub_linux_boot (void)
        }
     }
 
-#ifdef __x86_64__
-
-  grub_memcpy ((char *) prot_mode_mem + (prot_mode_pages << 12),
-              grub_linux_trampoline_start,
-              grub_linux_trampoline_end - grub_linux_trampoline_start);
-
-  ((void (*) (unsigned long, void *)) ((char *) prot_mode_mem
-                                      + (prot_mode_pages << 12)))
-    (params->code32_start, real_mode_mem);
-#else
-
-  /* Hardware interrupts are not safe any longer.  */
-  asm volatile ("cli" : : );
-
-  /* Load the IDT and the GDT for the bootstrap.  */
-  asm volatile ("lidt %0" : : "m" (idt_desc));
-  asm volatile ("lgdt %0" : : "m" (gdt_desc));
-
-  /* Enter Linux.  */
-  asm volatile ("jmp *%2" : : "b" (0), "S" (real_mode_mem), "g" (params->code32_start));
-
-#endif
-
-  /* Never reach here.  */
-  return GRUB_ERR_NONE;
+  /* FIXME.  */
+  /*  asm volatile ("lidt %0" : : "m" (idt_desc)); */
+  state.ebx = 0;
+  state.esi = real_mode_target;
+  state.eip = params->code32_start;
+  return grub_relocator32_boot (relocator, state);
 }
 
 static grub_err_t
@@ -678,7 +660,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
   real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
   prot_size = grub_file_size (file) - real_size - GRUB_DISK_SECTOR_SIZE;
 
-  if (allocate_pages (prot_size))
+  if (allocate_pages (prot_size))
     goto fail;
 
   params = (struct linux_kernel_params *) real_mode_mem;
@@ -701,7 +683,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
   params->cl_magic = GRUB_LINUX_CL_MAGIC;
   params->cl_offset = 0x1000;
 
-  params->cmd_line_ptr = (unsigned long) real_mode_mem + 0x1000;
+  params->cmd_line_ptr = real_mode_target + 0x1000;
   params->ramdisk_image = 0;
   params->ramdisk_size = 0;
 
@@ -910,6 +892,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
   grub_ssize_t size;
   grub_addr_t addr_min, addr_max;
   grub_addr_t addr;
+  grub_err_t err;
   struct linux_kernel_header *lh;
 
   if (argc == 0)
@@ -957,7 +940,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
   addr_max -= 0x10000;
 
   /* Usually, the compression ratio is about 50%.  */
-  addr_min = (grub_addr_t) prot_mode_mem + ((prot_mode_pages * 3) << 12)
+  addr_min = (grub_addr_t) prot_mode_target + ((prot_mode_pages * 3) << 12)
              + page_align (size);
 
   if (addr_max > grub_os_area_addr + grub_os_area_size)
@@ -972,7 +955,11 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
       goto fail;
     }
 
-  initrd_mem = (void *) addr;
+  err = grub_relocator_alloc_chunk_align (relocator, &initrd_mem,
+                                         &initrd_mem_target,
+                                         addr_min, addr, size, 0x1000);
+  if (err)
+    return err;
 
   if (grub_file_read (file, initrd_mem, size) != size)
     {
@@ -983,7 +970,7 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
   grub_dprintf ("linux", "Initrd, addr=0x%x, size=0x%x\n",
                (unsigned) addr, (unsigned) size);
 
-  lh->ramdisk_image = addr;
+  lh->ramdisk_image = initrd_mem_target;
   lh->ramdisk_size = size;
   lh->root_dev = 0x0100; /* XXX */