First part of virtual addr support in relocator

This commit is contained in:
Vladimir 'phcoder' Serbinenko 2010-04-21 15:25:49 +02:00
parent ba2f141cb5
commit 368c17f85d
4 changed files with 110 additions and 75 deletions

View file

@ -28,4 +28,28 @@
#define GRUB_MEMORY_CPU_AMD64_MSR 0xc0000080 #define GRUB_MEMORY_CPU_AMD64_MSR 0xc0000080
#define GRUB_MEMORY_CPU_AMD64_MSR_ON 0x00000100 #define GRUB_MEMORY_CPU_AMD64_MSR_ON 0x00000100
#ifndef ASM_FILE
typedef grub_addr_t grub_phys_addr_t;
static inline grub_phys_addr_t
grub_vtop (void *a)
{
return (grub_phys_addr_t) a;
}
static inline void *
grub_map_memory (grub_phys_addr_t a, grub_size_t size __attribute__ ((unused)))
{
return (void *) a;
}
static inline void
grub_unmap_memory (void *a __attribute__ ((unused)),
grub_size_t size __attribute__ ((unused)))
{
}
#endif
#endif /* ! GRUB_MEMORY_CPU_HEADER */ #endif /* ! GRUB_MEMORY_CPU_HEADER */

View file

@ -31,8 +31,9 @@ extern grub_size_t grub_relocator_jumper_size;
void void
grub_cpu_relocator_init (void); grub_cpu_relocator_init (void);
grub_err_t grub_err_t
grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, grub_relocator_prepare_relocs (struct grub_relocator *rel,
grub_addr_t *relstart, grub_size_t *relsize); void *addr,
void **relstart, grub_size_t *relsize);
void grub_cpu_relocator_forward (void *rels, void *src, void *tgt, void grub_cpu_relocator_forward (void *rels, void *src, void *tgt,
grub_size_t size); grub_size_t size);
void grub_cpu_relocator_backward (void *rels, void *src, void *tgt, void grub_cpu_relocator_backward (void *rels, void *src, void *tgt,
@ -72,7 +73,7 @@ struct grub_relocator_mmap_event
COLLISION_START = 10, COLLISION_START = 10,
COLLISION_END = COLLISION_START | 1 COLLISION_END = COLLISION_START | 1
} type; } type;
grub_addr_t pos; grub_phys_addr_t pos;
union union
{ {
struct struct
@ -91,10 +92,12 @@ struct grub_relocator_mmap_event
/* Return 0 on failure, 1 on success. The failure here /* Return 0 on failure, 1 on success. The failure here
can be very time-expensive, so please make sure fill events is accurate. */ can be very time-expensive, so please make sure fill events is accurate. */
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
int grub_relocator_firmware_alloc_region (grub_addr_t start, grub_size_t size); int grub_relocator_firmware_alloc_region (grub_phys_addr_t start,
grub_size_t size);
unsigned grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events); unsigned grub_relocator_firmware_fill_events (struct grub_relocator_mmap_event *events);
unsigned grub_relocator_firmware_get_max_events (void); unsigned grub_relocator_firmware_get_max_events (void);
void grub_relocator_firmware_free_region (grub_addr_t start, grub_size_t size); void grub_relocator_firmware_free_region (grub_phys_addr_t start,
grub_size_t size);
#endif #endif
#endif #endif

View file

@ -84,6 +84,12 @@ grub_size_t grub_relocator_jumper_size = 12;
grub_size_t grub_relocator_jumper_size = 7; grub_size_t grub_relocator_jumper_size = 7;
#endif #endif
static inline void *
ptov (grub_addr_t a)
{
return (void *) a;
}
void void
grub_cpu_relocator_init (void) grub_cpu_relocator_init (void)
{ {
@ -148,10 +154,10 @@ grub_err_t
grub_relocator32_boot (struct grub_relocator *rel, grub_relocator32_boot (struct grub_relocator *rel,
struct grub_relocator32_state state) struct grub_relocator32_state state)
{ {
grub_addr_t target; grub_phys_addr_t target;
void *src; void *src;
grub_err_t err; grub_err_t err;
grub_addr_t relst; void *relst;
err = grub_relocator_alloc_chunk_align (rel, &src, &target, 0, err = grub_relocator_alloc_chunk_align (rel, &src, &target, 0,
(0xffffffff - RELOCATOR_SIZEOF (32)) (0xffffffff - RELOCATOR_SIZEOF (32))
@ -170,7 +176,7 @@ grub_relocator32_boot (struct grub_relocator *rel,
grub_memmove (src, &grub_relocator32_start, RELOCATOR_SIZEOF (32)); grub_memmove (src, &grub_relocator32_start, RELOCATOR_SIZEOF (32));
err = grub_relocator_prepare_relocs (rel, target, &relst, NULL); err = grub_relocator_prepare_relocs (rel, ptov (target), &relst, NULL);
if (err) if (err)
return err; return err;
@ -185,10 +191,10 @@ grub_err_t
grub_relocator16_boot (struct grub_relocator *rel, grub_relocator16_boot (struct grub_relocator *rel,
struct grub_relocator16_state state) struct grub_relocator16_state state)
{ {
grub_addr_t target; grub_phys_addr_t target;
void *src; void *src;
grub_err_t err; grub_err_t err;
grub_addr_t relst; void *relst;
err = grub_relocator_alloc_chunk_align (rel, &src, &target, 0, err = grub_relocator_alloc_chunk_align (rel, &src, &target, 0,
0xa0000 - RELOCATOR_SIZEOF (16), 0xa0000 - RELOCATOR_SIZEOF (16),
@ -212,7 +218,7 @@ grub_relocator16_boot (struct grub_relocator *rel,
grub_memmove (src, &grub_relocator16_start, RELOCATOR_SIZEOF (16)); grub_memmove (src, &grub_relocator16_start, RELOCATOR_SIZEOF (16));
err = grub_relocator_prepare_relocs (rel, target, &relst, NULL); err = grub_relocator_prepare_relocs (rel, ptov (target), &relst, NULL);
if (err) if (err)
return err; return err;
@ -228,10 +234,10 @@ grub_relocator64_boot (struct grub_relocator *rel,
struct grub_relocator64_state state, struct grub_relocator64_state state,
grub_addr_t min_addr, grub_addr_t max_addr) grub_addr_t min_addr, grub_addr_t max_addr)
{ {
grub_addr_t target; grub_phys_addr_t target;
void *src; void *src;
grub_err_t err; grub_err_t err;
grub_addr_t relst; void *relst;
err = grub_relocator_alloc_chunk_align (rel, &src, &target, min_addr, err = grub_relocator_alloc_chunk_align (rel, &src, &target, min_addr,
max_addr - RELOCATOR_SIZEOF (64), max_addr - RELOCATOR_SIZEOF (64),
@ -251,7 +257,7 @@ grub_relocator64_boot (struct grub_relocator *rel,
grub_memmove (src, &grub_relocator64_start, RELOCATOR_SIZEOF (64)); grub_memmove (src, &grub_relocator64_start, RELOCATOR_SIZEOF (64));
err = grub_relocator_prepare_relocs (rel, target, &relst, NULL); err = grub_relocator_prepare_relocs (rel, ptov (target), &relst, NULL);
if (err) if (err)
return err; return err;

View file

@ -26,9 +26,9 @@
struct grub_relocator struct grub_relocator
{ {
struct grub_relocator_chunk *chunks; struct grub_relocator_chunk *chunks;
grub_addr_t postchunks; grub_phys_addr_t postchunks;
grub_addr_t highestaddr; grub_phys_addr_t highestaddr;
grub_addr_t highestnonpostaddr; grub_phys_addr_t highestnonpostaddr;
grub_size_t relocators_size; grub_size_t relocators_size;
}; };
@ -39,9 +39,11 @@ struct grub_relocator_subchunk
CHUNK_TYPE_FIRMWARE, CHUNK_TYPE_LEFTOVER CHUNK_TYPE_FIRMWARE, CHUNK_TYPE_LEFTOVER
#endif #endif
} type; } type;
grub_addr_t host_start; grub_mm_region_t reg;
grub_addr_t start; grub_mm_header_t head;
grub_phys_addr_t start;
grub_size_t size; grub_size_t size;
grub_size_t pre_size;
struct grub_relocator_extra_block *extra; struct grub_relocator_extra_block *extra;
struct grub_relocator_fw_leftover *pre, *post; struct grub_relocator_fw_leftover *pre, *post;
}; };
@ -49,8 +51,9 @@ struct grub_relocator_subchunk
struct grub_relocator_chunk struct grub_relocator_chunk
{ {
struct grub_relocator_chunk *next; struct grub_relocator_chunk *next;
grub_addr_t src; grub_phys_addr_t src;
grub_addr_t target; void *srcv;
grub_phys_addr_t target;
grub_size_t size; grub_size_t size;
struct grub_relocator_subchunk *subchunks; struct grub_relocator_subchunk *subchunks;
unsigned nsubchunks; unsigned nsubchunks;
@ -60,8 +63,8 @@ struct grub_relocator_extra_block
{ {
struct grub_relocator_extra_block *next; struct grub_relocator_extra_block *next;
struct grub_relocator_extra_block **prev; struct grub_relocator_extra_block **prev;
grub_addr_t start; grub_phys_addr_t start;
grub_addr_t end; grub_phys_addr_t end;
}; };
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
@ -69,7 +72,7 @@ struct grub_relocator_fw_leftover
{ {
struct grub_relocator_fw_leftover *next; struct grub_relocator_fw_leftover *next;
struct grub_relocator_fw_leftover **prev; struct grub_relocator_fw_leftover **prev;
grub_addr_t quantstart; grub_phys_addr_t quantstart;
grub_uint8_t freebytes[GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT / 8]; grub_uint8_t freebytes[GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT / 8];
}; };
@ -89,7 +92,7 @@ grub_relocator_new (void)
if (!ret) if (!ret)
return NULL; return NULL;
ret->postchunks = ~(grub_addr_t) 0; ret->postchunks = ~(grub_phys_addr_t) 0;
ret->relocators_size = grub_relocator_jumper_size; ret->relocators_size = grub_relocator_jumper_size;
grub_dprintf ("relocator", "relocators_size=%lu\n", grub_dprintf ("relocator", "relocators_size=%lu\n",
(unsigned long) ret->relocators_size); (unsigned long) ret->relocators_size);
@ -110,10 +113,11 @@ is_start (int type)
} }
static void static void
allocate_regstart (grub_addr_t addr, grub_size_t size, grub_mm_region_t rb, allocate_regstart (grub_phys_addr_t addr, grub_size_t size, grub_mm_region_t rb,
grub_mm_region_t *regancestor, grub_mm_header_t hancestor) grub_mm_region_t *regancestor, grub_mm_header_t hancestor)
{ {
grub_addr_t newreg_start, newreg_raw_start = addr + size; grub_addr_t newreg_start, newreg_raw_start
= (grub_addr_t) rb + (addr - grub_vtop (rb)) + 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;
grub_mm_header_t hb = (grub_mm_header_t) (rb + 1); grub_mm_header_t hb = (grub_mm_header_t) (rb + 1);
@ -176,23 +180,24 @@ allocate_regstart (grub_addr_t addr, grub_size_t size, grub_mm_region_t rb,
} }
static void static void
allocate_inreg (grub_addr_t addr, grub_size_t size, allocate_inreg (grub_phys_addr_t paddr, grub_size_t size,
grub_mm_header_t hb, grub_mm_header_t hbp, grub_mm_header_t hb, grub_mm_header_t hbp,
grub_mm_region_t rb) grub_mm_region_t rb)
{ {
struct grub_mm_header *foll = NULL; struct grub_mm_header *foll = NULL;
grub_addr_t vaddr = (grub_addr_t) hb + (paddr - grub_vtop (hb));
if (ALIGN_UP (addr + size, GRUB_MM_ALIGN) + GRUB_MM_ALIGN if (ALIGN_UP (vaddr + size, GRUB_MM_ALIGN) + GRUB_MM_ALIGN
<= (grub_addr_t) (hb + hb->size)) <= (grub_addr_t) (hb + hb->size))
{ {
foll = (void *) ALIGN_UP (addr + size, GRUB_MM_ALIGN); foll = (void *) ALIGN_UP (vaddr + size, GRUB_MM_ALIGN);
foll->magic = GRUB_MM_FREE_MAGIC; foll->magic = GRUB_MM_FREE_MAGIC;
foll->size = hb->size - (foll - hb); foll->size = hb->size - (foll - hb);
} }
if (addr - (grub_addr_t) hb >= sizeof (*hb)) if (vaddr - (grub_addr_t) hb >= sizeof (*hb))
{ {
hb->size = ((addr - (grub_addr_t) hb) >> GRUB_MM_ALIGN_LOG2); hb->size = ((vaddr - (grub_addr_t) hb) >> GRUB_MM_ALIGN_LOG2);
if (foll) if (foll)
{ {
foll->next = hb; foll->next = hb;
@ -250,12 +255,13 @@ free_subchunk (const struct grub_relocator_subchunk *subchu)
grub_mm_region_t r1, r2, *rp; grub_mm_region_t r1, r2, *rp;
grub_mm_header_t h; grub_mm_header_t h;
grub_size_t pre_size; grub_size_t pre_size;
r1 = (grub_mm_region_t) ALIGN_UP (subchu->host_start, GRUB_MM_ALIGN); r1 = subchu->reg;
r2 = (grub_mm_region_t) ALIGN_UP (subchu->start + subchu->size, r2 = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) subchu->reg
+ (grub_vtop (subchu->reg)
- subchu->start) + subchu->size,
GRUB_MM_ALIGN); GRUB_MM_ALIGN);
for (rp = &grub_mm_base; *rp && *rp != r2; rp = &((*rp)->next)); for (rp = &grub_mm_base; *rp && *rp != r2; rp = &((*rp)->next));
pre_size = ALIGN_UP (subchu->host_start, GRUB_MM_ALIGN) pre_size = subchu->pre_size;
- subchu->host_start;
if (*rp) if (*rp)
{ {
@ -328,7 +334,7 @@ free_subchunk (const struct grub_relocator_subchunk *subchu)
} }
case CHUNK_TYPE_IN_REGION: case CHUNK_TYPE_IN_REGION:
{ {
grub_mm_header_t h = (grub_mm_header_t) ALIGN_DOWN (subchu->start, grub_mm_header_t h = (grub_mm_header_t) ALIGN_DOWN ((grub_addr_t) subchu->head,
GRUB_MM_ALIGN); GRUB_MM_ALIGN);
h->size h->size
= ((subchu->start + subchu->size + GRUB_MM_ALIGN - 1) / GRUB_MM_ALIGN) = ((subchu->start + subchu->size + GRUB_MM_ALIGN - 1) / GRUB_MM_ALIGN)
@ -549,26 +555,26 @@ malloc_in_range (struct grub_relocator *rel,
{ {
pre_added = 1; pre_added = 1;
events[N].type = REG_BEG_START; events[N].type = REG_BEG_START;
events[N].pos = (grub_addr_t) r - r->pre_size; events[N].pos = grub_vtop (r) - r->pre_size;
events[N].reg = r; events[N].reg = r;
events[N].regancestor = ra; events[N].regancestor = ra;
events[N].head = p; events[N].head = p;
events[N].hancestor = pa; events[N].hancestor = pa;
N++; N++;
events[N].type = REG_BEG_END; events[N].type = REG_BEG_END;
events[N].pos = (grub_addr_t) (p + p->size) - sizeof (*r); events[N].pos = grub_vtop (p + p->size) - sizeof (*r);
N++; N++;
} }
else else
{ {
events[N].type = IN_REG_START; events[N].type = IN_REG_START;
events[N].pos = (grub_addr_t) p; events[N].pos = grub_vtop (p);
events[N].head = p; events[N].head = p;
events[N].hancestor = pa; events[N].hancestor = pa;
events[N].reg = r; events[N].reg = r;
N++; N++;
events[N].type = IN_REG_END; events[N].type = IN_REG_END;
events[N].pos = (grub_addr_t) (p + p->size); events[N].pos = grub_vtop (p + p->size);
N++; N++;
} }
pa = p; pa = p;
@ -862,20 +868,6 @@ malloc_in_range (struct grub_relocator *rel,
} }
} }
grub_memset ((void *) target, 0, size);
grub_dprintf ("relocator", "baseptr = %p\n", &base_saved);
for (r = base_saved; r; r = r->next)
{
p = r->first;
do
{
if (!p)
grub_fatal ("null in the ring %p\n", r);
p = p->next;
}
while (p != r->first);
}
/* Malloc is available again. */ /* Malloc is available again. */
grub_mm_base = base_saved; grub_mm_base = base_saved;
@ -927,7 +919,13 @@ malloc_in_range (struct grub_relocator *rel,
curschu->type = typepre; curschu->type = typepre;
curschu->start = alloc_start; curschu->start = alloc_start;
curschu->size = alloc_end - alloc_start; curschu->size = alloc_end - alloc_start;
if (typepre == CHUNK_TYPE_REGION_START) if (typepre == CHUNK_TYPE_REGION_START
|| typepre == CHUNK_TYPE_IN_REGION)
{
curschu->reg = events[last_start].reg;
curschu->head = events[last_start].head;
curschu->pre_size = alloc_start - events[j - 1].pos;
}
if (!oom && (typepre == CHUNK_TYPE_REGION_START if (!oom && (typepre == CHUNK_TYPE_REGION_START
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
|| typepre == CHUNK_TYPE_FIRMWARE || typepre == CHUNK_TYPE_FIRMWARE
@ -1106,8 +1104,8 @@ malloc_in_range (struct grub_relocator *rel,
static void static void
adjust_limits (struct grub_relocator *rel, adjust_limits (struct grub_relocator *rel,
grub_addr_t *min_addr, grub_addr_t *max_addr, grub_phys_addr_t *min_addr, grub_phys_addr_t *max_addr,
grub_addr_t in_min, grub_addr_t in_max) grub_phys_addr_t in_min, grub_phys_addr_t in_max)
{ {
struct grub_relocator_chunk *chunk; struct grub_relocator_chunk *chunk;
@ -1129,10 +1127,10 @@ adjust_limits (struct grub_relocator *rel,
grub_err_t grub_err_t
grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src, grub_relocator_alloc_chunk_addr (struct grub_relocator *rel, void **src,
grub_addr_t target, grub_size_t size) grub_phys_addr_t target, grub_size_t size)
{ {
struct grub_relocator_chunk *chunk; struct grub_relocator_chunk *chunk;
grub_addr_t min_addr = 0, max_addr; grub_phys_addr_t min_addr = 0, max_addr;
if (target > ~size) if (target > ~size)
return grub_error (GRUB_ERR_OUT_OF_RANGE, "address is out of range"); return grub_error (GRUB_ERR_OUT_OF_RANGE, "address is out of range");
@ -1223,14 +1221,15 @@ 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 *) chunk->src; *src = chunk->srcv = grub_map_memory (chunk->src, chunk->size);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
grub_err_t grub_err_t
grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src, grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
grub_addr_t *target, grub_phys_addr_t *target,
grub_addr_t min_addr, grub_addr_t max_addr, grub_phys_addr_t min_addr,
grub_phys_addr_t max_addr,
grub_size_t size, grub_size_t align, grub_size_t size, grub_size_t align,
int preference) int preference)
{ {
@ -1354,7 +1353,7 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel, void **src,
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 *) chunk->src; *src = chunk->srcv = grub_map_memory (chunk->src, chunk->size);
*target = chunk->target; *target = chunk->target;
return GRUB_ERR_NONE; return GRUB_ERR_NONE;
} }
@ -1370,6 +1369,7 @@ grub_relocator_unload (struct grub_relocator *rel)
unsigned i; unsigned i;
for (i = 0; i < chunk->nsubchunks; i++) for (i = 0; i < chunk->nsubchunks; i++)
free_subchunk (&chunk->subchunks[i]); free_subchunk (&chunk->subchunks[i]);
grub_unmap_memory (chunk->srcv, chunk->size);
next = chunk->next; next = chunk->next;
grub_free (chunk->subchunks); grub_free (chunk->subchunks);
grub_free (chunk); grub_free (chunk);
@ -1378,11 +1378,11 @@ grub_relocator_unload (struct grub_relocator *rel)
} }
grub_err_t grub_err_t
grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr, grub_relocator_prepare_relocs (struct grub_relocator *rel, void *addr,
grub_addr_t *relstart, grub_size_t *relsize) void **relstart, grub_size_t *relsize)
{ {
grub_addr_t rels; grub_uint8_t *rels;
grub_addr_t rels0; grub_uint8_t *rels0;
struct grub_relocator_chunk *sorted; struct grub_relocator_chunk *sorted;
grub_size_t nchunks = 0; grub_size_t nchunks = 0;
unsigned j; unsigned j;
@ -1395,7 +1395,7 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
grub_relocator_align, grub_relocator_align,
rel->relocators_size, &movers_chunk, 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 = movers_chunk.src; rels = rels0 = movers_chunk.srcv;
if (relsize) if (relsize)
*relsize = rel->relocators_size; *relsize = rel->relocators_size;
@ -1463,23 +1463,25 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
if (sorted[j].src < sorted[j].target) if (sorted[j].src < sorted[j].target)
{ {
grub_cpu_relocator_backward ((void *) rels, grub_cpu_relocator_backward ((void *) rels,
(void *) sorted[j].src, sorted[j].srcv,
(void *) sorted[j].target, grub_map_memory (sorted[j].target,
sorted[j].size),
sorted[j].size); sorted[j].size);
rels += grub_relocator_backward_size; rels += grub_relocator_backward_size;
} }
if (sorted[j].src > sorted[j].target) if (sorted[j].src > sorted[j].target)
{ {
grub_cpu_relocator_forward ((void *) rels, grub_cpu_relocator_forward ((void *) rels,
(void *) sorted[j].src, sorted[j].srcv,
(void *) sorted[j].target, grub_map_memory (sorted[j].target,
sorted[j].size),
sorted[j].size); sorted[j].size);
rels += grub_relocator_forward_size; rels += grub_relocator_forward_size;
} }
if (sorted[j].src == sorted[j].target) if (sorted[j].src == sorted[j].target)
grub_arch_sync_caches ((void *) sorted[j].src, sorted[j].size); grub_arch_sync_caches (sorted[j].srcv, sorted[j].size);
} }
grub_cpu_relocator_jumper ((void *) rels, addr); grub_cpu_relocator_jumper ((void *) rels, (grub_addr_t) addr);
*relstart = rels0; *relstart = rels0;
grub_free (sorted); grub_free (sorted);
return GRUB_ERR_NONE; return GRUB_ERR_NONE;