]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
Sort chunks
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 17 Jan 2010 11:42:28 +0000 (12:42 +0100)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 17 Jan 2010 11:42:28 +0000 (12:42 +0100)
include/grub/relocator_private.h
lib/relocator.c

index cc68305c8a8eaa5119b73089218304be7d25a272..7a3ef2bd77c8d1e19ddf93c687e3ac2a5c6e8e62 100644 (file)
@@ -27,23 +27,6 @@ extern grub_size_t grub_relocator_forward_size;
 extern grub_size_t grub_relocator_backward_size;
 extern grub_size_t grub_relocator_jumper_size;
 
-struct grub_relocator
-{
-  struct grub_relocator_chunk *chunks;
-  grub_addr_t postchunks;
-  grub_addr_t highestaddr;
-  grub_addr_t highestnonpostaddr;
-  grub_size_t relocators_size;
-};
-
-struct grub_relocator_chunk
-{
-  struct grub_relocator_chunk *next;
-  grub_addr_t src;
-  grub_addr_t target;
-  grub_size_t size;
-};
-
 void
 grub_cpu_relocator_init (void);
 grub_err_t
index 0064824a456c10ba24ecfcf6ee54604c7ace32bc..a02bca332acacfa2e74d3d8b434f28ea8b66d814 100644 (file)
 /* FIXME: implement unload. */
 /* FIXME: check memory map.  */ 
 /* FIXME: try to request memory from firmware.  */
-/* FIXME: sort chunk when programming relocators.  */
+
+struct grub_relocator
+{
+  struct grub_relocator_chunk *chunks;
+  grub_addr_t postchunks;
+  grub_addr_t highestaddr;
+  grub_addr_t highestnonpostaddr;
+  grub_size_t relocators_size;
+};
+
+struct grub_relocator_chunk
+{
+  struct grub_relocator_chunk *next;
+  grub_addr_t src;
+  grub_addr_t target;
+  grub_size_t size;
+};
 
 struct grub_relocator *
 grub_relocator_new (void)
@@ -591,9 +607,11 @@ grub_err_t
 grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
                               grub_addr_t *relstart)
 {
-  struct grub_relocator_chunk *chunk;
   grub_addr_t rels;
   grub_addr_t rels0;
+  struct grub_relocator_chunk *sorted;
+  grub_size_t nchunks = 0;
+  unsigned j;
 
   grub_dprintf ("relocator", "Preparing relocs (size=%ld)\n",
                (unsigned long) rel->relocators_size);
@@ -605,29 +623,84 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
   rels = rels0;
 
   grub_dprintf ("relocator", "Relocs allocated\n");
+  
+  {
+    unsigned i;
+    grub_size_t count[257];
+    struct grub_relocator_chunk *from, *to, *tmp;
 
-  for (chunk = rel->chunks; chunk; chunk = chunk->next)
+    grub_memset (count, 0, sizeof (count));
+
+    {
+        struct grub_relocator_chunk *chunk;
+       for (chunk = rel->chunks; chunk; chunk = chunk->next)
+         {
+           grub_dprintf ("relocator", "chunk %p->%p, 0x%x\n", 
+                         (void *) chunk->src, (void *) chunk->target,
+                         chunk->size);
+           nchunks++;
+           count[(chunk->src & 0xff) + 1]++;
+         }
+    }
+    from = grub_malloc (nchunks * sizeof (sorted[0]));
+    to = grub_malloc (nchunks * sizeof (sorted[0]));
+    if (!from || !to)
+      {
+       grub_free (from);
+       grub_free (to);
+       return grub_errno;
+      }
+
+    for (j = 0; j < 256; j++)
+      count[j+1] += count[j];
+
+    {
+      struct grub_relocator_chunk *chunk;
+      for (chunk = rel->chunks; chunk; chunk = chunk->next)
+       from[count[chunk->src & 0xff]++] = *chunk;
+    }
+
+    for (i = 1; i < GRUB_CPU_SIZEOF_VOID_P; i++)
+      {
+       grub_memset (count, 0, sizeof (count));
+       for (j = 0; j < nchunks; j++)
+         count[((from[j].src >> (8 * i)) & 0xff) + 1]++;
+       for (j = 0; j < 256; j++)
+         count[j+1] += count[j];
+       for (j = 0; j < nchunks; j++)
+         to[count[(from[j].src >> (8 * i)) & 0xff]++] = from[j];
+       tmp = to;
+       to = from;
+       from = tmp;
+      }
+    sorted = from;
+    grub_free (to);
+  }
+
+  for (j = 0; j < nchunks; j++)
     {
-      grub_dprintf ("relocator", "chunk %p->%p\n", 
-                   (void *) chunk->src, (void *) chunk->target);
-      if (chunk->src < chunk->target)
+      grub_dprintf ("relocator", "sorted chunk %p->%p, 0x%x\n", 
+                   (void *) sorted[j].src, (void *) sorted[j].target,
+                   sorted[j].size);
+      if (sorted[j].src < sorted[j].target)
        {
          grub_cpu_relocator_backward ((void *) rels,
-                                      (void *) chunk->src,
-                                      (void *) chunk->target,
-                                      chunk->size);
+                                      (void *) sorted[j].src,
+                                      (void *) sorted[j].target,
+                                      sorted[j].size);
          rels += grub_relocator_backward_size;
        }
-      if (chunk->src > chunk->target)
+      if (sorted[j].src > sorted[j].target)
        {
          grub_cpu_relocator_forward ((void *) rels,
-                                     (void *) chunk->src,
-                                     (void *) chunk->target,
-                                     chunk->size);
+                                     (void *) sorted[j].src,
+                                     (void *) sorted[j].target,
+                                     sorted[j].size);
          rels += grub_relocator_forward_size;
        }
     }
   grub_cpu_relocator_jumper ((void *) rels, addr);
   *relstart = rels0;
+  grub_free (sorted);
   return GRUB_ERR_NONE;
 }