grub_addr_t host_start;
grub_addr_t start;
grub_size_t size;
+ struct grub_relocator_extra_block *extra;
};
struct grub_relocator_chunk
struct grub_relocator_extra_block
{
struct grub_relocator_extra_block *next;
+ struct grub_relocator_extra_block **prev;
grub_addr_t start;
grub_addr_t end;
};
}
}
-/* FIXME: remove extra blocks. */
static void
free_subchunk (const struct grub_relocator_subchunk *subchu)
{
grub_mm_region_t r1, r2, *rp;
grub_mm_header_t h;
grub_size_t pre_size;
- r1 = (grub_mm_region_t) ALIGN_UP (subchu->start + subchu->size,
+ r1 = (grub_mm_region_t) ALIGN_UP (subchu->host_start, GRUB_MM_ALIGN);
+ r2 = (grub_mm_region_t) ALIGN_UP (subchu->start + subchu->size,
GRUB_MM_ALIGN);
- r2 = (grub_mm_region_t) ALIGN_UP (subchu->host_start, GRUB_MM_ALIGN);
for (rp = &grub_mm_base; *rp && *rp != r2; rp = &((*rp)->next));
- /* FIXME */
- if (!*rp)
- grub_fatal ("Anomaly in region alocations detected. "
- "Simultaneous relocators?");
pre_size = ALIGN_UP (subchu->host_start, GRUB_MM_ALIGN)
- subchu->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);
+
+ if (*rp)
+ {
+ grub_mm_header_t h2, *hp;
+ r1->first = r2->first;
+ r1->next = r2->next;
+ r1->pre_size = pre_size;
+ r1->size = r2->size + (r2 - r1) * sizeof (*r2);
+ *rp = r1;
+ h = (grub_mm_header_t) (r1 + 1);
+ h->next = r2->first;
+ h->magic = GRUB_MM_FREE_MAGIC;
+ h->size = (r2 - r1 - 1);
+ for (hp = &r2->first, h2 = *hp; h2->next != r2->first;
+ hp = &(h2->next), h2 = *hp)
+ if (h2 == (grub_mm_header_t) (r2 + 1))
+ break;
+ if (h2 == (grub_mm_header_t) (r2 + 1))
+ {
+ h->size = h2->size + (h2 - h);
+ h->next = h2->next;
+ *hp = h;
+ if (hp == &r2->first)
+ {
+ for (h2 = r2->first; h2->next != r2->first; h2 = h2->next);
+ h2->next = h;
+ }
+ }
+ else
+ {
+ h2->next = h;
+ }
+ }
+ else
+ {
+ r1->pre_size = pre_size;
+ r1->size = (r2 - r1) * sizeof (*r2);
+ /* Find where to insert this region.
+ Put a smaller one before bigger ones,
+ to prevent fragmentation. */
+ for (rp = &grub_mm_base; *rp; rp = &((*rp)->next))
+ if ((*rp)->size > r1->size)
+ break;
+ r1->next = *rp;
+ *rp = r1->next;
+ h = (grub_mm_header_t) (r1 + 1);
+ r1->first = h;
+ h->next = h;
+ h->magic = GRUB_MM_FREE_MAGIC;
+ h->size = (r2 - r1 - 1);
+ }
+ for (r2 = grub_mm_base; r2; r2 = r2->next)
+ if ((grub_addr_t) r2 + r2->size == (grub_addr_t) r1)
+ break;
+ if (r2)
+ {
+ grub_mm_header_t hl2, hl, g;
+ g = (grub_mm_header_t) ((grub_addr_t) r2 + r2->size);
+ g->size = (grub_mm_header_t) r1 - g;
+ r2->size += r1->size;
+ for (hl = r2->first; hl->next != r2->first; hl = hl->next);
+ for (hl2 = r1->first; hl2->next != r1->first; hl2 = hl2->next);
+ hl2->next = r2->first;
+ r2->first = r1->first;
+ hl->next = r2->first;
+ *rp = (*rp)->next;
+ grub_free (g + 1);
+ }
break;
}
case CHUNK_TYPE_IN_REGION:
{
grub_mm_header_t h = (grub_mm_header_t) ALIGN_DOWN (subchu->start,
GRUB_MM_ALIGN);
- h->size = (subchu->start / GRUB_MM_ALIGN)
- - ((subchu->start + subchu->size + GRUB_MM_ALIGN - 1) / GRUB_MM_ALIGN);
+ h->size
+ = ((subchu->start + subchu->size + GRUB_MM_ALIGN - 1) / GRUB_MM_ALIGN)
+ - (subchu->start / GRUB_MM_ALIGN);
h->next = h;
h->magic = GRUB_MM_ALLOC_MAGIC;
grub_free (h + 1);
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
case CHUNK_TYPE_FIRMWARE:
grub_relocator_firmware_free_region (subchu->start, subchu->size);
+ *curschu->extra->prev = curschu->extra->next;
+ grub_free (curschu->extra);
break;
#endif
- }
+ }
}
static int
(unsigned long) alloc_start,
(unsigned long) alloc_end, typepre);
curschu->type = typepre;
+ curschu->start = alloc_start;
+ curschu->size = alloc_end - alloc_start;
if (typepre == CHUNK_TYPE_REGION_START)
curschu->host_start = (grub_addr_t) events[last_start].reg;
#if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
ne->start = alloc_start;
ne->end = alloc_end;
ne->next = extra_blocks;
+ ne->prev = &extra_blocks;
+ extra_blocks->prev = &(ne->next);
extra_blocks = ne;
+ curschu->extra = ne;
}
}
#endif
grub_free (chunk->subchunks);
grub_free (chunk);
}
+ grub_free (rel);
}
grub_err_t