2002-12-27 08:53:07 +00:00
|
|
|
|
/* mm.c - functions for memory manager */
|
|
|
|
|
/*
|
2004-04-04 13:46:03 +00:00
|
|
|
|
* GRUB -- GRand Unified Bootloader
|
2010-01-03 22:05:07 +00:00
|
|
|
|
* Copyright (C) 2002,2005,2007,2008,2009 Free Software Foundation, Inc.
|
2002-12-27 08:53:07 +00:00
|
|
|
|
*
|
2007-07-21 23:32:33 +00:00
|
|
|
|
* GRUB is free software: you can redistribute it and/or modify
|
2002-12-27 08:53:07 +00:00
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
2007-07-21 23:32:33 +00:00
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
2002-12-27 08:53:07 +00:00
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
2007-07-21 23:32:33 +00:00
|
|
|
|
* GRUB is distributed in the hope that it will be useful,
|
2002-12-27 08:53:07 +00:00
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2007-07-21 23:32:33 +00:00
|
|
|
|
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
|
2002-12-27 08:53:07 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2005-07-12 19:33:32 +00:00
|
|
|
|
/*
|
|
|
|
|
The design of this memory manager.
|
|
|
|
|
|
|
|
|
|
This is a simple implementation of malloc with a few extensions. These are
|
|
|
|
|
the extensions:
|
|
|
|
|
|
|
|
|
|
- memalign is implemented efficiently.
|
|
|
|
|
|
|
|
|
|
- multiple regions may be used as free space. They may not be
|
|
|
|
|
contiguous.
|
|
|
|
|
|
|
|
|
|
Regions are managed by a singly linked list, and the meta information is
|
|
|
|
|
stored in the beginning of each region. Space after the meta information
|
|
|
|
|
is used to allocate memory.
|
|
|
|
|
|
|
|
|
|
The memory space is used as cells instead of bytes for simplicity. This
|
|
|
|
|
is important for some CPUs which may not access multiple bytes at a time
|
|
|
|
|
when the first byte is not aligned at a certain boundary (typically,
|
|
|
|
|
4-byte or 8-byte). The size of each cell is equal to the size of struct
|
|
|
|
|
grub_mm_header, so the header of each allocated/free block fits into one
|
|
|
|
|
cell precisely. One cell is 16 bytes on 32-bit platforms and 32 bytes
|
|
|
|
|
on 64-bit platforms.
|
|
|
|
|
|
|
|
|
|
There are two types of blocks: allocated blocks and free blocks.
|
|
|
|
|
|
|
|
|
|
In allocated blocks, the header of each block has only its size. Note that
|
|
|
|
|
this size is based on cells but not on bytes. The header is located right
|
|
|
|
|
before the returned pointer, that is, the header resides at the previous
|
|
|
|
|
cell.
|
|
|
|
|
|
|
|
|
|
Free blocks constitutes a ring, using a singly linked list. The first free
|
|
|
|
|
block is pointed to by the meta information of a region. The allocator
|
|
|
|
|
attempts to pick up the second block instead of the first one. This is
|
|
|
|
|
a typical optimization against defragmentation, and makes the
|
|
|
|
|
implementation a bit easier.
|
|
|
|
|
|
|
|
|
|
For safety, both allocated blocks and free ones are marked by magic
|
|
|
|
|
numbers. Whenever anything unexpected is detected, GRUB aborts the
|
|
|
|
|
operation.
|
|
|
|
|
*/
|
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
#include <config.h>
|
2004-04-04 13:46:03 +00:00
|
|
|
|
#include <grub/mm.h>
|
|
|
|
|
#include <grub/misc.h>
|
|
|
|
|
#include <grub/err.h>
|
|
|
|
|
#include <grub/types.h>
|
|
|
|
|
#include <grub/disk.h>
|
|
|
|
|
#include <grub/dl.h>
|
2010-01-09 23:30:33 +00:00
|
|
|
|
#include <grub/mm_private.h>
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
2006-04-25 20:08:31 +00:00
|
|
|
|
#ifdef MM_DEBUG
|
|
|
|
|
# undef grub_malloc
|
2009-07-16 22:14:09 +00:00
|
|
|
|
# undef grub_zalloc
|
2006-04-25 20:08:31 +00:00
|
|
|
|
# undef grub_realloc
|
|
|
|
|
# undef grub_free
|
|
|
|
|
# undef grub_memalign
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
|
2010-01-09 23:30:33 +00:00
|
|
|
|
grub_mm_region_t grub_mm_base;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
/* Get a header from the pointer PTR, and set *P and *R to a pointer
|
|
|
|
|
to the header and a pointer to its region, respectively. PTR must
|
|
|
|
|
be allocated. */
|
|
|
|
|
static void
|
2004-04-04 13:46:03 +00:00
|
|
|
|
get_header_from_pointer (void *ptr, grub_mm_header_t *p, grub_mm_region_t *r)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2005-01-20 17:25:39 +00:00
|
|
|
|
if ((grub_addr_t) ptr & (GRUB_MM_ALIGN - 1))
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_fatal ("unaligned pointer %p", ptr);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
2010-01-09 23:30:33 +00:00
|
|
|
|
for (*r = grub_mm_base; *r; *r = (*r)->next)
|
|
|
|
|
if ((grub_addr_t) ptr > (grub_addr_t) ((*r) + 1)
|
|
|
|
|
&& (grub_addr_t) ptr <= (grub_addr_t) ((*r) + 1) + (*r)->size)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (! *r)
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_fatal ("out of range pointer %p", ptr);
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
*p = (grub_mm_header_t) ptr - 1;
|
|
|
|
|
if ((*p)->magic != GRUB_MM_ALLOC_MAGIC)
|
|
|
|
|
grub_fatal ("alloc magic is broken at %p", *p);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initialize a region starting from ADDR and whose size is SIZE,
|
|
|
|
|
to use it as free space. */
|
|
|
|
|
void
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_init_region (void *addr, grub_size_t size)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_header_t h;
|
|
|
|
|
grub_mm_region_t r, *p, q;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
2008-01-23 14:57:40 +00:00
|
|
|
|
#if 0
|
|
|
|
|
grub_printf ("Using memory for heap: start=%p, end=%p\n", addr, addr + (unsigned int) size);
|
|
|
|
|
#endif
|
2005-07-12 22:36:43 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
/* Allocate a region from the head. */
|
2010-01-09 23:30:33 +00:00
|
|
|
|
r = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) addr, GRUB_MM_ALIGN);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
size -= (char *) r - (char *) addr + sizeof (*r);
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2010-05-03 11:07:31 +00:00
|
|
|
|
/* If this region is too small, ignore it. */
|
|
|
|
|
if (size < GRUB_MM_ALIGN)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-01-09 23:30:33 +00:00
|
|
|
|
h = (grub_mm_header_t) (r + 1);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
h->next = h;
|
2004-04-04 13:46:03 +00:00
|
|
|
|
h->magic = GRUB_MM_FREE_MAGIC;
|
|
|
|
|
h->size = (size >> GRUB_MM_ALIGN_LOG2);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
r->first = h;
|
2010-01-09 23:30:33 +00:00
|
|
|
|
r->pre_size = (grub_addr_t) r - (grub_addr_t) addr;
|
2004-04-04 13:46:03 +00:00
|
|
|
|
r->size = (h->size << GRUB_MM_ALIGN_LOG2);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
/* Find where to insert this region. Put a smaller one before bigger ones,
|
2007-12-30 08:52:06 +00:00
|
|
|
|
to prevent fragmentation. */
|
2010-01-09 23:30:33 +00:00
|
|
|
|
for (p = &grub_mm_base, q = *p; q; p = &(q->next), q = *p)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (q->size > r->size)
|
|
|
|
|
break;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
*p = r;
|
|
|
|
|
r->next = q;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate the number of units N with the alignment ALIGN from the ring
|
2009-02-08 10:52:03 +00:00
|
|
|
|
buffer starting from *FIRST. ALIGN must be a power of two. Both N and
|
|
|
|
|
ALIGN are in units of GRUB_MM_ALIGN. Return a non-NULL if successful,
|
|
|
|
|
otherwise return NULL. */
|
2002-12-27 08:53:07 +00:00
|
|
|
|
static void *
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_header_t p, q;
|
2003-01-31 03:26:56 +00:00
|
|
|
|
|
2009-02-08 10:52:03 +00:00
|
|
|
|
/* When everything is allocated side effect is that *first will have alloc
|
|
|
|
|
magic marked, meaning that there is no room in this region. */
|
2004-04-04 13:46:03 +00:00
|
|
|
|
if ((*first)->magic == GRUB_MM_ALLOC_MAGIC)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2009-02-08 10:52:03 +00:00
|
|
|
|
/* Try to search free slot for allocation in this memory region. */
|
2002-12-27 08:53:07 +00:00
|
|
|
|
for (q = *first, p = q->next; ; q = p, p = p->next)
|
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_off_t extra;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
extra = ((grub_addr_t) (p + 1) >> GRUB_MM_ALIGN_LOG2) % align;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (extra)
|
|
|
|
|
extra = align - extra;
|
|
|
|
|
|
|
|
|
|
if (! p)
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_fatal ("null in the ring");
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
if (p->magic != GRUB_MM_FREE_MAGIC)
|
|
|
|
|
grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
if (p->size >= n + extra)
|
|
|
|
|
{
|
2010-08-28 22:19:13 +00:00
|
|
|
|
extra += (p->size - extra - n) & (~(align - 1));
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (extra == 0 && p->size == n)
|
|
|
|
|
{
|
2009-02-08 10:52:03 +00:00
|
|
|
|
/* There is no special alignment requirement and memory block
|
|
|
|
|
is complete match.
|
|
|
|
|
|
|
|
|
|
1. Just mark memory block as allocated and remove it from
|
|
|
|
|
free list.
|
|
|
|
|
|
|
|
|
|
Result:
|
|
|
|
|
+---------------+ previous block's next
|
|
|
|
|
| alloc, size=n | |
|
|
|
|
|
+---------------+ v
|
|
|
|
|
*/
|
2002-12-27 08:53:07 +00:00
|
|
|
|
q->next = p->next;
|
|
|
|
|
}
|
2010-03-14 22:56:13 +00:00
|
|
|
|
else if (align == 1 || p->size == n + extra)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2009-02-08 10:52:03 +00:00
|
|
|
|
/* There might be alignment requirement, when taking it into
|
|
|
|
|
account memory block fits in.
|
|
|
|
|
|
|
|
|
|
1. Allocate new area at end of memory block.
|
|
|
|
|
2. Reduce size of available blocks from original node.
|
|
|
|
|
3. Mark new area as allocated and "remove" it from free
|
|
|
|
|
list.
|
|
|
|
|
|
|
|
|
|
Result:
|
|
|
|
|
+---------------+
|
|
|
|
|
| free, size-=n | next --+
|
|
|
|
|
+---------------+ |
|
|
|
|
|
| alloc, size=n | |
|
|
|
|
|
+---------------+ v
|
|
|
|
|
*/
|
2010-03-14 22:56:13 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
p->size -= n;
|
|
|
|
|
p += p->size;
|
|
|
|
|
}
|
2010-03-14 22:56:13 +00:00
|
|
|
|
else if (extra == 0)
|
|
|
|
|
{
|
|
|
|
|
grub_mm_header_t r;
|
|
|
|
|
|
|
|
|
|
r = p + extra + n;
|
|
|
|
|
r->magic = GRUB_MM_FREE_MAGIC;
|
|
|
|
|
r->size = p->size - extra - n;
|
|
|
|
|
r->next = p->next;
|
|
|
|
|
q->next = r;
|
2010-05-03 10:53:32 +00:00
|
|
|
|
|
|
|
|
|
if (q == p)
|
|
|
|
|
{
|
|
|
|
|
q = r;
|
|
|
|
|
r->next = r;
|
|
|
|
|
}
|
2002-12-27 08:53:07 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2009-02-08 10:52:03 +00:00
|
|
|
|
/* There is alignment requirement and there is room in memory
|
|
|
|
|
block. Split memory block to three pieces.
|
|
|
|
|
|
|
|
|
|
1. Create new memory block right after section being
|
|
|
|
|
allocated. Mark it as free.
|
|
|
|
|
2. Add new memory block to free chain.
|
|
|
|
|
3. Mark current memory block having only extra blocks.
|
|
|
|
|
4. Advance to aligned block and mark that as allocated and
|
|
|
|
|
"remove" it from free list.
|
|
|
|
|
|
|
|
|
|
Result:
|
|
|
|
|
+------------------------------+
|
|
|
|
|
| free, size=extra | next --+
|
|
|
|
|
+------------------------------+ |
|
|
|
|
|
| alloc, size=n | |
|
|
|
|
|
+------------------------------+ |
|
|
|
|
|
| free, size=orig.size-extra-n | <------+, next --+
|
|
|
|
|
+------------------------------+ v
|
|
|
|
|
*/
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_header_t r;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
r = p + extra + n;
|
2004-04-04 13:46:03 +00:00
|
|
|
|
r->magic = GRUB_MM_FREE_MAGIC;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
r->size = p->size - extra - n;
|
2010-01-09 23:30:33 +00:00
|
|
|
|
r->next = p;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
p->size = extra;
|
2010-01-09 23:30:33 +00:00
|
|
|
|
q->next = r;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
p += extra;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-03 11:12:55 +00:00
|
|
|
|
p->magic = GRUB_MM_ALLOC_MAGIC;
|
|
|
|
|
p->size = n;
|
|
|
|
|
|
2009-02-08 10:52:03 +00:00
|
|
|
|
/* Mark find as a start marker for next allocation to fasten it.
|
|
|
|
|
This will have side effect of fragmenting memory as small
|
|
|
|
|
pieces before this will be un-used. */
|
2002-12-27 08:53:07 +00:00
|
|
|
|
*first = q;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
return p + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-08 10:52:03 +00:00
|
|
|
|
/* Search was completed without result. */
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (p == *first)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate SIZE bytes with the alignment ALIGN and return the pointer. */
|
|
|
|
|
void *
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_memalign (grub_size_t align, grub_size_t size)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_region_t r;
|
|
|
|
|
grub_size_t n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
|
2003-01-06 00:01:35 +00:00
|
|
|
|
int count = 0;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2010-08-28 09:32:49 +00:00
|
|
|
|
if (!grub_mm_base)
|
|
|
|
|
goto fail;
|
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
align = (align >> GRUB_MM_ALIGN_LOG2);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (align == 0)
|
|
|
|
|
align = 1;
|
|
|
|
|
|
|
|
|
|
again:
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2010-01-09 23:30:33 +00:00
|
|
|
|
for (r = grub_mm_base; r; r = r->next)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
|
|
|
|
void *p;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
p = grub_real_malloc (&(r->first), n, align);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (p)
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-06 00:01:35 +00:00
|
|
|
|
/* If failed, increase free memory somehow. */
|
|
|
|
|
switch (count)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2003-01-06 00:01:35 +00:00
|
|
|
|
case 0:
|
|
|
|
|
/* Invalidate disk caches. */
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_disk_cache_invalidate_all ();
|
2003-01-06 00:01:35 +00:00
|
|
|
|
count++;
|
|
|
|
|
goto again;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2003-01-06 00:01:35 +00:00
|
|
|
|
case 1:
|
|
|
|
|
/* Unload unneeded modules. */
|
2010-01-18 17:39:35 +00:00
|
|
|
|
// grub_dl_unload_unneeded ();
|
2003-01-06 00:01:35 +00:00
|
|
|
|
count++;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
goto again;
|
|
|
|
|
|
2003-01-06 00:01:35 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2010-08-28 09:32:49 +00:00
|
|
|
|
fail:
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
|
2002-12-27 08:53:07 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate SIZE bytes and return the pointer. */
|
|
|
|
|
void *
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_malloc (grub_size_t size)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
return grub_memalign (0, size);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-07-16 22:14:09 +00:00
|
|
|
|
/* Allocate SIZE bytes, clear them and return the pointer. */
|
|
|
|
|
void *
|
|
|
|
|
grub_zalloc (grub_size_t size)
|
|
|
|
|
{
|
|
|
|
|
void *ret;
|
|
|
|
|
|
|
|
|
|
ret = grub_memalign (0, size);
|
|
|
|
|
if (ret)
|
|
|
|
|
grub_memset (ret, 0, size);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
/* Deallocate the pointer PTR. */
|
|
|
|
|
void
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_free (void *ptr)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_header_t p;
|
|
|
|
|
grub_mm_region_t r;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
if (! ptr)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
get_header_from_pointer (ptr, &p, &r);
|
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
if (r->first->magic == GRUB_MM_ALLOC_MAGIC)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
p->magic = GRUB_MM_FREE_MAGIC;
|
2003-01-20 04:13:46 +00:00
|
|
|
|
r->first = p->next = p;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_header_t q;
|
2003-01-20 04:13:46 +00:00
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
q = r->first;
|
|
|
|
|
do
|
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n",
|
2010-02-09 14:32:42 +00:00
|
|
|
|
GRUB_FILE, __LINE__, q, q->size, q->magic);
|
2003-01-20 04:13:46 +00:00
|
|
|
|
q = q->next;
|
|
|
|
|
}
|
|
|
|
|
while (q != r->first);
|
|
|
|
|
#endif
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
for (q = r->first; q >= p || q->next <= p; q = q->next)
|
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
if (q->magic != GRUB_MM_FREE_MAGIC)
|
|
|
|
|
grub_fatal ("free magic is broken at %p: 0x%x", q, q->magic);
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (q >= q->next && (q < p || q->next > p))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
p->magic = GRUB_MM_FREE_MAGIC;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
p->next = q->next;
|
|
|
|
|
q->next = p;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (p + p->size == p->next)
|
|
|
|
|
{
|
2005-06-23 08:12:19 +00:00
|
|
|
|
if (p->next == q)
|
|
|
|
|
q = p;
|
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
p->next->magic = 0;
|
|
|
|
|
p->size += p->next->size;
|
|
|
|
|
p->next = p->next->next;
|
|
|
|
|
}
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (q + q->size == p)
|
|
|
|
|
{
|
|
|
|
|
p->magic = 0;
|
|
|
|
|
q->size += p->size;
|
|
|
|
|
q->next = p->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
r->first = q;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Reallocate SIZE bytes and return the pointer. The contents will be
|
|
|
|
|
the same as that of PTR. */
|
|
|
|
|
void *
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_realloc (void *ptr, grub_size_t size)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_header_t p;
|
|
|
|
|
grub_mm_region_t r;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
void *q;
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_size_t n;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (! ptr)
|
2004-04-04 13:46:03 +00:00
|
|
|
|
return grub_malloc (size);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
|
|
|
|
if (! size)
|
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_free (ptr);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FIXME: Not optimal. */
|
2004-04-04 13:46:03 +00:00
|
|
|
|
n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
get_header_from_pointer (ptr, &p, &r);
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (p->size >= n)
|
2004-07-12 17:53:07 +00:00
|
|
|
|
return ptr;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
q = grub_malloc (size);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
if (! q)
|
|
|
|
|
return q;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_memcpy (q, ptr, size);
|
|
|
|
|
grub_free (ptr);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
return q;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-25 20:08:31 +00:00
|
|
|
|
#ifdef MM_DEBUG
|
2007-02-20 22:39:57 +00:00
|
|
|
|
int grub_mm_debug = 0;
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
grub_mm_dump_free (void)
|
|
|
|
|
{
|
|
|
|
|
grub_mm_region_t r;
|
|
|
|
|
|
2010-01-09 23:30:33 +00:00
|
|
|
|
for (r = grub_mm_base; r; r = r->next)
|
2007-02-20 22:39:57 +00:00
|
|
|
|
{
|
|
|
|
|
grub_mm_header_t p;
|
|
|
|
|
|
|
|
|
|
/* Follow the free list. */
|
|
|
|
|
p = r->first;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (p->magic != GRUB_MM_FREE_MAGIC)
|
|
|
|
|
grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic);
|
|
|
|
|
|
|
|
|
|
grub_printf ("F:%p:%u:%p\n",
|
|
|
|
|
p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2, p->next);
|
|
|
|
|
p = p->next;
|
|
|
|
|
}
|
|
|
|
|
while (p != r->first);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
grub_printf ("\n");
|
|
|
|
|
}
|
2006-04-25 20:08:31 +00:00
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
void
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_dump (unsigned lineno)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_region_t r;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_printf ("called at line %u\n", lineno);
|
2010-01-09 23:30:33 +00:00
|
|
|
|
for (r = grub_mm_base; r; r = r->next)
|
2002-12-27 08:53:07 +00:00
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_mm_header_t p;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2010-01-09 23:30:33 +00:00
|
|
|
|
for (p = (grub_mm_header_t) ALIGN_UP ((grub_addr_t) (r + 1),
|
|
|
|
|
GRUB_MM_ALIGN);
|
|
|
|
|
(grub_addr_t) p < (grub_addr_t) (r+1) + r->size;
|
2002-12-27 08:53:07 +00:00
|
|
|
|
p++)
|
|
|
|
|
{
|
|
|
|
|
switch (p->magic)
|
|
|
|
|
{
|
2004-04-04 13:46:03 +00:00
|
|
|
|
case GRUB_MM_FREE_MAGIC:
|
|
|
|
|
grub_printf ("F:%p:%u:%p\n",
|
2005-08-21 18:42:55 +00:00
|
|
|
|
p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2, p->next);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
break;
|
2004-04-04 13:46:03 +00:00
|
|
|
|
case GRUB_MM_ALLOC_MAGIC:
|
2005-08-21 18:42:55 +00:00
|
|
|
|
grub_printf ("A:%p:%u\n", p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2);
|
2002-12-27 08:53:07 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-04 13:46:03 +00:00
|
|
|
|
grub_printf ("\n");
|
2002-12-27 08:53:07 +00:00
|
|
|
|
}
|
2006-04-25 20:08:31 +00:00
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
grub_debug_malloc (const char *file, int line, grub_size_t size)
|
|
|
|
|
{
|
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
|
|
if (grub_mm_debug)
|
2009-07-01 15:39:21 +00:00
|
|
|
|
grub_printf ("%s:%d: malloc (0x%zx) = ", file, line, size);
|
2006-04-25 20:08:31 +00:00
|
|
|
|
ptr = grub_malloc (size);
|
|
|
|
|
if (grub_mm_debug)
|
|
|
|
|
grub_printf ("%p\n", ptr);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-16 22:14:09 +00:00
|
|
|
|
void *
|
|
|
|
|
grub_debug_zalloc (const char *file, int line, grub_size_t size)
|
|
|
|
|
{
|
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
|
|
if (grub_mm_debug)
|
|
|
|
|
grub_printf ("%s:%d: zalloc (0x%zx) = ", file, line, size);
|
|
|
|
|
ptr = grub_zalloc (size);
|
|
|
|
|
if (grub_mm_debug)
|
|
|
|
|
grub_printf ("%p\n", ptr);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-25 20:08:31 +00:00
|
|
|
|
void
|
|
|
|
|
grub_debug_free (const char *file, int line, void *ptr)
|
|
|
|
|
{
|
|
|
|
|
if (grub_mm_debug)
|
|
|
|
|
grub_printf ("%s:%d: free (%p)\n", file, line, ptr);
|
2007-02-20 22:39:57 +00:00
|
|
|
|
grub_free (ptr);
|
2006-04-25 20:08:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
grub_debug_realloc (const char *file, int line, void *ptr, grub_size_t size)
|
|
|
|
|
{
|
|
|
|
|
if (grub_mm_debug)
|
2009-07-01 15:39:21 +00:00
|
|
|
|
grub_printf ("%s:%d: realloc (%p, 0x%zx) = ", file, line, ptr, size);
|
2006-04-25 20:08:31 +00:00
|
|
|
|
ptr = grub_realloc (ptr, size);
|
|
|
|
|
if (grub_mm_debug)
|
|
|
|
|
grub_printf ("%p\n", ptr);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void *
|
|
|
|
|
grub_debug_memalign (const char *file, int line, grub_size_t align,
|
|
|
|
|
grub_size_t size)
|
|
|
|
|
{
|
|
|
|
|
void *ptr;
|
2009-02-08 10:52:03 +00:00
|
|
|
|
|
2006-04-25 20:08:31 +00:00
|
|
|
|
if (grub_mm_debug)
|
2009-07-01 15:39:21 +00:00
|
|
|
|
grub_printf ("%s:%d: memalign (0x%zx, 0x%zx) = ",
|
2006-04-25 20:08:31 +00:00
|
|
|
|
file, line, align, size);
|
|
|
|
|
ptr = grub_memalign (align, size);
|
|
|
|
|
if (grub_mm_debug)
|
|
|
|
|
grub_printf ("%p\n", ptr);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-27 08:53:07 +00:00
|
|
|
|
#endif /* MM_DEBUG */
|