relocator unloading support
This commit is contained in:
parent
7d8c9ec63d
commit
ddf23b9d81
1 changed files with 86 additions and 42 deletions
128
lib/relocator.c
128
lib/relocator.c
|
@ -23,7 +23,6 @@
|
||||||
#include <grub/cache.h>
|
#include <grub/cache.h>
|
||||||
|
|
||||||
/* TODO: use more efficient data structures if necessary. */
|
/* TODO: use more efficient data structures if necessary. */
|
||||||
/* FIXME: implement unload. */
|
|
||||||
/* FIXME: check memory map. */
|
/* FIXME: check memory map. */
|
||||||
/* FIXME: try to request memory from firmware. */
|
/* FIXME: try to request memory from firmware. */
|
||||||
|
|
||||||
|
@ -42,6 +41,8 @@ struct grub_relocator_chunk
|
||||||
grub_addr_t src;
|
grub_addr_t src;
|
||||||
grub_addr_t target;
|
grub_addr_t target;
|
||||||
grub_size_t size;
|
grub_size_t size;
|
||||||
|
enum {CHUNK_TYPE_IN_REGION, CHUNK_TYPE_REGION_START} type;
|
||||||
|
grub_addr_t host_start;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct grub_relocator *
|
struct grub_relocator *
|
||||||
|
@ -204,8 +205,8 @@ get_best_header (struct grub_relocator *rel,
|
||||||
static int
|
static int
|
||||||
malloc_in_range (struct grub_relocator *rel,
|
malloc_in_range (struct grub_relocator *rel,
|
||||||
grub_addr_t start, grub_addr_t end, grub_addr_t align,
|
grub_addr_t start, grub_addr_t end, grub_addr_t align,
|
||||||
grub_size_t size, grub_addr_t *res, int from_low_priv,
|
grub_size_t size, struct grub_relocator_chunk *res,
|
||||||
int collisioncheck)
|
int from_low_priv, int collisioncheck)
|
||||||
{
|
{
|
||||||
grub_mm_region_t rb, rbp;
|
grub_mm_region_t rb, rbp;
|
||||||
grub_mm_header_t hb = NULL, hbp = NULL;
|
grub_mm_header_t hb = NULL, hbp = NULL;
|
||||||
|
@ -262,6 +263,11 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
grub_addr_t newreg_start, newreg_raw_start = best_addr + size;
|
grub_addr_t newreg_start, newreg_raw_start = best_addr + size;
|
||||||
grub_addr_t newreg_size, newreg_presize;
|
grub_addr_t newreg_size, newreg_presize;
|
||||||
grub_mm_header_t new_header;
|
grub_mm_header_t new_header;
|
||||||
|
|
||||||
|
res->src = best_addr;
|
||||||
|
res->type = CHUNK_TYPE_REGION_START;
|
||||||
|
res->host_start = (grub_addr_t) rb - rb->pre_size;
|
||||||
|
|
||||||
newreg_start = ALIGN_UP (newreg_raw_start, GRUB_MM_ALIGN);
|
newreg_start = ALIGN_UP (newreg_raw_start, GRUB_MM_ALIGN);
|
||||||
newreg_presize = newreg_start - newreg_raw_start;
|
newreg_presize = newreg_start - newreg_raw_start;
|
||||||
newreg_size = rb->size - (newreg_start - (grub_addr_t) rb);
|
newreg_size = rb->size - (newreg_start - (grub_addr_t) rb);
|
||||||
|
@ -313,12 +319,14 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
while (h != newreg->first);
|
while (h != newreg->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*res = best_addr;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
struct grub_mm_header *foll = NULL;
|
struct grub_mm_header *foll = NULL;
|
||||||
|
|
||||||
|
res->src = best_addr;
|
||||||
|
res->type = CHUNK_TYPE_IN_REGION;
|
||||||
|
|
||||||
if (ALIGN_UP (best_addr + size, GRUB_MM_ALIGN) + GRUB_MM_ALIGN
|
if (ALIGN_UP (best_addr + size, GRUB_MM_ALIGN) + GRUB_MM_ALIGN
|
||||||
<= (grub_addr_t) (hb + hb->size))
|
<= (grub_addr_t) (hb + hb->size))
|
||||||
{
|
{
|
||||||
|
@ -351,7 +359,6 @@ malloc_in_range (struct grub_relocator *rel,
|
||||||
if (rb->first == hb)
|
if (rb->first == hb)
|
||||||
rb->first = (void *) (rb + 1);
|
rb->first = (void *) (rb + 1);
|
||||||
}
|
}
|
||||||
*res = best_addr;
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -384,7 +391,6 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
|
||||||
grub_addr_t target, grub_size_t size)
|
grub_addr_t target, grub_size_t size)
|
||||||
{
|
{
|
||||||
struct grub_relocator_chunk *chunk;
|
struct grub_relocator_chunk *chunk;
|
||||||
grub_addr_t start;
|
|
||||||
grub_addr_t min_addr = 0, max_addr;
|
grub_addr_t min_addr = 0, max_addr;
|
||||||
|
|
||||||
if (target > ~size)
|
if (target > ~size)
|
||||||
|
@ -413,24 +419,24 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
|
||||||
#if defined (__i386__) || defined (__x86_64__)
|
#if defined (__i386__) || defined (__x86_64__)
|
||||||
if (target < 0x100000)
|
if (target < 0x100000)
|
||||||
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
|
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
|
||||||
size, &start, 0, 1))
|
size, chunk, 0, 1))
|
||||||
{
|
{
|
||||||
if (rel->postchunks > start)
|
if (rel->postchunks > chunk->src)
|
||||||
rel->postchunks = start;
|
rel->postchunks = chunk->src;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (malloc_in_range (rel, target, max_addr, 1, size, &start, 1, 0))
|
if (malloc_in_range (rel, target, max_addr, 1, size, chunk, 1, 0))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (malloc_in_range (rel, min_addr, target, 1, size, &start, 0, 0))
|
if (malloc_in_range (rel, min_addr, target, 1, size, chunk, 0, 0))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
|
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
|
||||||
size, &start, 0, 1))
|
size, chunk, 0, 1))
|
||||||
{
|
{
|
||||||
if (rel->postchunks > start)
|
if (rel->postchunks > chunk->src)
|
||||||
rel->postchunks = start;
|
rel->postchunks = chunk->src;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,35 +447,34 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
|
||||||
while (0);
|
while (0);
|
||||||
|
|
||||||
grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
|
grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
|
||||||
(unsigned long long) start, (unsigned long long) target);
|
(unsigned long long) chunk->src, (unsigned long long) target);
|
||||||
|
|
||||||
if (rel->highestaddr < target + size)
|
if (rel->highestaddr < target + size)
|
||||||
rel->highestaddr = target + size;
|
rel->highestaddr = target + size;
|
||||||
|
|
||||||
if (rel->highestaddr < start + size)
|
if (rel->highestaddr < chunk->src + size)
|
||||||
rel->highestaddr = start + size;
|
rel->highestaddr = chunk->src + size;
|
||||||
|
|
||||||
if (start < rel->postchunks)
|
if (chunk->src < rel->postchunks)
|
||||||
{
|
{
|
||||||
if (rel->highestnonpostaddr < target + size)
|
if (rel->highestnonpostaddr < target + size)
|
||||||
rel->highestnonpostaddr = target + size;
|
rel->highestnonpostaddr = target + size;
|
||||||
|
|
||||||
if (rel->highestnonpostaddr < start + size)
|
if (rel->highestnonpostaddr < chunk->src + size)
|
||||||
rel->highestnonpostaddr = start + size;
|
rel->highestnonpostaddr = chunk->src + size;
|
||||||
}
|
}
|
||||||
|
|
||||||
grub_dprintf ("relocator", "relocators_size=%ld\n",
|
grub_dprintf ("relocator", "relocators_size=%ld\n",
|
||||||
(unsigned long) rel->relocators_size);
|
(unsigned long) rel->relocators_size);
|
||||||
|
|
||||||
if (start < target)
|
if (chunk->src < target)
|
||||||
rel->relocators_size += grub_relocator_backward_size;
|
rel->relocators_size += grub_relocator_backward_size;
|
||||||
if (start > target)
|
if (chunk->src > target)
|
||||||
rel->relocators_size += grub_relocator_forward_size;
|
rel->relocators_size += grub_relocator_forward_size;
|
||||||
|
|
||||||
grub_dprintf ("relocator", "relocators_size=%ld\n",
|
grub_dprintf ("relocator", "relocators_size=%ld\n",
|
||||||
(unsigned long) rel->relocators_size);
|
(unsigned long) rel->relocators_size);
|
||||||
|
|
||||||
chunk->src = start;
|
|
||||||
chunk->target = target;
|
chunk->target = target;
|
||||||
chunk->size = size;
|
chunk->size = size;
|
||||||
chunk->next = rel->chunks;
|
chunk->next = rel->chunks;
|
||||||
|
@ -477,7 +482,7 @@ grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
|
||||||
grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
|
grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
|
||||||
rel->chunks->next);
|
rel->chunks->next);
|
||||||
|
|
||||||
*src = (void *) start;
|
*src = (void *) chunk->src;
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,7 +495,6 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
|
||||||
{
|
{
|
||||||
grub_addr_t min_addr2 = 0, max_addr2;
|
grub_addr_t min_addr2 = 0, max_addr2;
|
||||||
struct grub_relocator_chunk *chunk;
|
struct grub_relocator_chunk *chunk;
|
||||||
grub_addr_t start;
|
|
||||||
|
|
||||||
if (max_addr > ~size)
|
if (max_addr > ~size)
|
||||||
max_addr = ~size;
|
max_addr = ~size;
|
||||||
|
@ -507,19 +511,19 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
|
||||||
return grub_errno;
|
return grub_errno;
|
||||||
|
|
||||||
if (malloc_in_range (rel, min_addr, max_addr, align,
|
if (malloc_in_range (rel, min_addr, max_addr, align,
|
||||||
size, &start,
|
size, chunk,
|
||||||
preference != GRUB_RELOCATOR_PREFERENCE_HIGH, 1))
|
preference != GRUB_RELOCATOR_PREFERENCE_HIGH, 1))
|
||||||
{
|
{
|
||||||
grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
|
grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
|
||||||
(unsigned long long) start, (unsigned long long) start);
|
(unsigned long long) chunk->src,
|
||||||
|
(unsigned long long) chunk->src);
|
||||||
grub_dprintf ("relocator", "chunks = %p\n", rel->chunks);
|
grub_dprintf ("relocator", "chunks = %p\n", rel->chunks);
|
||||||
chunk->src = start;
|
chunk->target = chunk->src;
|
||||||
chunk->target = start;
|
|
||||||
chunk->size = size;
|
chunk->size = size;
|
||||||
chunk->next = rel->chunks;
|
chunk->next = rel->chunks;
|
||||||
rel->chunks = chunk;
|
rel->chunks = chunk;
|
||||||
*src = (void *) start;
|
*src = (void *) chunk->src;
|
||||||
*target = start;
|
*target = chunk->target;
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,14 +535,14 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if (malloc_in_range (rel, min_addr2, max_addr2, align,
|
if (malloc_in_range (rel, min_addr2, max_addr2, align,
|
||||||
size, &start, 1, 1))
|
size, chunk, 1, 1))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
|
if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
|
||||||
size, &start, 0, 1))
|
size, chunk, 0, 1))
|
||||||
{
|
{
|
||||||
if (rel->postchunks > start)
|
if (rel->postchunks > chunk->src)
|
||||||
rel->postchunks = start;
|
rel->postchunks = chunk->src;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -574,18 +578,17 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (start < chunk->target)
|
if (chunk->src < chunk->target)
|
||||||
rel->relocators_size += grub_relocator_backward_size;
|
rel->relocators_size += grub_relocator_backward_size;
|
||||||
if (start > chunk->target)
|
if (chunk->src > chunk->target)
|
||||||
rel->relocators_size += grub_relocator_forward_size;
|
rel->relocators_size += grub_relocator_forward_size;
|
||||||
|
|
||||||
chunk->src = start;
|
|
||||||
chunk->size = size;
|
chunk->size = size;
|
||||||
chunk->next = rel->chunks;
|
chunk->next = rel->chunks;
|
||||||
rel->chunks = chunk;
|
rel->chunks = chunk;
|
||||||
grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
|
grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
|
||||||
rel->chunks->next);
|
rel->chunks->next);
|
||||||
*src = (void *) start;
|
*src = (void *) chunk->src;
|
||||||
*target = chunk->target;
|
*target = chunk->target;
|
||||||
return GRUB_ERR_NONE;
|
return GRUB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
@ -598,7 +601,47 @@ grub_relocator_unload (struct grub_relocator *rel)
|
||||||
return;
|
return;
|
||||||
for (chunk = rel->chunks; chunk; chunk = next)
|
for (chunk = rel->chunks; chunk; chunk = next)
|
||||||
{
|
{
|
||||||
grub_fatal ("Relocator unloading isn't implemented yet");
|
switch (chunk->type)
|
||||||
|
{
|
||||||
|
case CHUNK_TYPE_REGION_START:
|
||||||
|
{
|
||||||
|
grub_mm_region_t r1, r2, *rp;
|
||||||
|
grub_mm_header_t h;
|
||||||
|
grub_size_t pre_size;
|
||||||
|
r1 = (grub_mm_region_t) ALIGN_UP (chunk->src + chunk->size,
|
||||||
|
GRUB_MM_ALIGN);
|
||||||
|
r2 = (grub_mm_region_t) ALIGN_UP (chunk->host_start, GRUB_MM_ALIGN);
|
||||||
|
for (rp = &grub_mm_base; *rp && *rp != r2; rp = &((*rp)->next));
|
||||||
|
if (!*rp)
|
||||||
|
grub_fatal ("Anomaly in region alocations detected. "
|
||||||
|
"Simultaneous relocators?");
|
||||||
|
pre_size = ALIGN_UP (chunk->host_start, GRUB_MM_ALIGN)
|
||||||
|
- chunk->host_start;
|
||||||
|
r2->first = r1->first;
|
||||||
|
r2->next = r1->next;
|
||||||
|
r2->pre_size = pre_size;
|
||||||
|
r2->size = r1->size + (r2 - r1) * sizeof (*r2);
|
||||||
|
*rp = r1;
|
||||||
|
h = (grub_mm_header_t) (r1 + 1);
|
||||||
|
h->next = h;
|
||||||
|
h->magic = GRUB_MM_ALLOC_MAGIC;
|
||||||
|
h->size = (r2 - r1);
|
||||||
|
grub_free (h + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CHUNK_TYPE_IN_REGION:
|
||||||
|
{
|
||||||
|
grub_mm_header_t h = (grub_mm_header_t) ALIGN_DOWN (chunk->src,
|
||||||
|
GRUB_MM_ALIGN);
|
||||||
|
h->size = (chunk->src / GRUB_MM_ALIGN)
|
||||||
|
- ((chunk->src + chunk->size + GRUB_MM_ALIGN - 1)
|
||||||
|
/ GRUB_MM_ALIGN);
|
||||||
|
h->next = h;
|
||||||
|
h->magic = GRUB_MM_ALLOC_MAGIC;
|
||||||
|
grub_free (h + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
next = chunk->next;
|
next = chunk->next;
|
||||||
grub_free (chunk);
|
grub_free (chunk);
|
||||||
}
|
}
|
||||||
|
@ -613,15 +656,16 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
|
||||||
struct grub_relocator_chunk *sorted;
|
struct grub_relocator_chunk *sorted;
|
||||||
grub_size_t nchunks = 0;
|
grub_size_t nchunks = 0;
|
||||||
unsigned j;
|
unsigned j;
|
||||||
|
struct grub_relocator_chunk movers_chunk;
|
||||||
|
|
||||||
grub_dprintf ("relocator", "Preparing relocs (size=%ld)\n",
|
grub_dprintf ("relocator", "Preparing relocs (size=%ld)\n",
|
||||||
(unsigned long) rel->relocators_size);
|
(unsigned long) rel->relocators_size);
|
||||||
|
|
||||||
if (!malloc_in_range (rel, 0, ~(grub_addr_t)0 - rel->relocators_size + 1,
|
if (!malloc_in_range (rel, 0, ~(grub_addr_t)0 - rel->relocators_size + 1,
|
||||||
grub_relocator_align,
|
grub_relocator_align,
|
||||||
rel->relocators_size, &rels0, 1, 1))
|
rel->relocators_size, &movers_chunk, 1, 1))
|
||||||
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
|
return grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
|
||||||
rels = rels0;
|
rels = rels0 = movers_chunk.src;
|
||||||
|
|
||||||
if (relsize)
|
if (relsize)
|
||||||
*relsize = rel->relocators_size;
|
*relsize = rel->relocators_size;
|
||||||
|
|
Loading…
Add table
Reference in a new issue