~ubuntu-branches/ubuntu/trusty/grub2/trusty-updates

« back to all changes in this revision

Viewing changes to kern/mm.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mto: (17.3.1 squeeze) (1.9.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060105152040-b72i5pq1a82z22yi
Tags: upstream-1.92
ImportĀ upstreamĀ versionĀ 1.92

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* mm.c - functions for memory manager */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2002,2005  Free Software Foundation, Inc.
 
5
 *
 
6
 *  GRUB is free software; you can redistribute it and/or modify
 
7
 *  it under the terms of the GNU General Public License as published by
 
8
 *  the Free Software Foundation; either version 2 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  This program is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *  GNU General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU General Public License
 
17
 *  along with GRUB; if not, write to the Free Software
 
18
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
19
 */
 
20
 
 
21
/*
 
22
  The design of this memory manager.
 
23
 
 
24
  This is a simple implementation of malloc with a few extensions. These are
 
25
  the extensions:
 
26
 
 
27
  - memalign is implemented efficiently.
 
28
 
 
29
  - multiple regions may be used as free space. They may not be
 
30
  contiguous.
 
31
 
 
32
  Regions are managed by a singly linked list, and the meta information is
 
33
  stored in the beginning of each region. Space after the meta information
 
34
  is used to allocate memory.
 
35
 
 
36
  The memory space is used as cells instead of bytes for simplicity. This
 
37
  is important for some CPUs which may not access multiple bytes at a time
 
38
  when the first byte is not aligned at a certain boundary (typically,
 
39
  4-byte or 8-byte). The size of each cell is equal to the size of struct
 
40
  grub_mm_header, so the header of each allocated/free block fits into one
 
41
  cell precisely. One cell is 16 bytes on 32-bit platforms and 32 bytes
 
42
  on 64-bit platforms.
 
43
 
 
44
  There are two types of blocks: allocated blocks and free blocks.
 
45
 
 
46
  In allocated blocks, the header of each block has only its size. Note that
 
47
  this size is based on cells but not on bytes. The header is located right
 
48
  before the returned pointer, that is, the header resides at the previous
 
49
  cell.
 
50
 
 
51
  Free blocks constitutes a ring, using a singly linked list. The first free
 
52
  block is pointed to by the meta information of a region. The allocator
 
53
  attempts to pick up the second block instead of the first one. This is
 
54
  a typical optimization against defragmentation, and makes the
 
55
  implementation a bit easier.
 
56
 
 
57
  For safety, both allocated blocks and free ones are marked by magic
 
58
  numbers. Whenever anything unexpected is detected, GRUB aborts the
 
59
  operation.
 
60
 */
 
61
 
 
62
#include <config.h>
 
63
#include <grub/mm.h>
 
64
#include <grub/misc.h>
 
65
#include <grub/err.h>
 
66
#include <grub/types.h>
 
67
#include <grub/disk.h>
 
68
#include <grub/dl.h>
 
69
 
 
70
/* Magic words.  */
 
71
#define GRUB_MM_FREE_MAGIC      0x2d3c2808
 
72
#define GRUB_MM_ALLOC_MAGIC     0x6db08fa4
 
73
 
 
74
typedef struct grub_mm_header
 
75
{
 
76
  struct grub_mm_header *next;
 
77
  grub_size_t size;
 
78
  grub_size_t magic;
 
79
#if GRUB_CPU_SIZEOF_VOID_P == 4
 
80
  char padding[4];
 
81
#elif GRUB_CPU_SIZEOF_VOID_P == 8
 
82
  char padding[8];
 
83
#else
 
84
# error "unknown word size"
 
85
#endif
 
86
}
 
87
*grub_mm_header_t;
 
88
 
 
89
#if GRUB_CPU_SIZEOF_VOID_P == 4
 
90
# define GRUB_MM_ALIGN_LOG2     4
 
91
#elif GRUB_CPU_SIZEOF_VOID_P == 8
 
92
# define GRUB_MM_ALIGN_LOG2     5
 
93
#endif
 
94
 
 
95
#define GRUB_MM_ALIGN   (1 << GRUB_MM_ALIGN_LOG2)
 
96
 
 
97
typedef struct grub_mm_region
 
98
{
 
99
  struct grub_mm_header *first;
 
100
  struct grub_mm_region *next;
 
101
  grub_addr_t addr;
 
102
  grub_size_t size;
 
103
}
 
104
*grub_mm_region_t;
 
105
 
 
106
 
 
107
 
 
108
static grub_mm_region_t base;
 
109
 
 
110
/* Get a header from the pointer PTR, and set *P and *R to a pointer
 
111
   to the header and a pointer to its region, respectively. PTR must
 
112
   be allocated.  */
 
113
static void
 
114
get_header_from_pointer (void *ptr, grub_mm_header_t *p, grub_mm_region_t *r)
 
115
{
 
116
  if ((grub_addr_t) ptr & (GRUB_MM_ALIGN - 1))
 
117
    grub_fatal ("unaligned pointer %p", ptr);
 
118
 
 
119
  for (*r = base; *r; *r = (*r)->next)
 
120
    if ((grub_addr_t) ptr > (*r)->addr
 
121
        && (grub_addr_t) ptr <= (*r)->addr + (*r)->size)
 
122
      break;
 
123
 
 
124
  if (! *r)
 
125
    grub_fatal ("out of range pointer %p", ptr);
 
126
  
 
127
  *p = (grub_mm_header_t) ptr - 1;
 
128
  if ((*p)->magic != GRUB_MM_ALLOC_MAGIC)
 
129
    grub_fatal ("alloc magic is broken at %p", *p);
 
130
}
 
131
 
 
132
/* Initialize a region starting from ADDR and whose size is SIZE,
 
133
   to use it as free space.  */
 
134
void
 
135
grub_mm_init_region (void *addr, grub_size_t size)
 
136
{
 
137
  grub_mm_header_t h;
 
138
  grub_mm_region_t r, *p, q;
 
139
 
 
140
  grub_dprintf ("mem", "Using memory for heap: addr=%p, size=%u\n", addr, (unsigned int) size);
 
141
 
 
142
  /* If this region is too small, ignore it.  */
 
143
  if (size < GRUB_MM_ALIGN * 2)
 
144
    return;
 
145
 
 
146
  /* Allocate a region from the head.  */
 
147
  r = (grub_mm_region_t) (((grub_addr_t) addr + GRUB_MM_ALIGN - 1)
 
148
                          & (~(GRUB_MM_ALIGN - 1)));
 
149
  size -= (char *) r - (char *) addr + sizeof (*r);
 
150
  
 
151
  h = (grub_mm_header_t) ((char *) r + GRUB_MM_ALIGN);
 
152
  h->next = h;
 
153
  h->magic = GRUB_MM_FREE_MAGIC;
 
154
  h->size = (size >> GRUB_MM_ALIGN_LOG2);
 
155
 
 
156
  r->first = h;
 
157
  r->addr = (grub_addr_t) h;
 
158
  r->size = (h->size << GRUB_MM_ALIGN_LOG2);
 
159
 
 
160
  /* Find where to insert this region. Put a smaller one before bigger ones,
 
161
     to prevent fragmentations.  */
 
162
  for (p = &base, q = *p; q; p = &(q->next), q = *p)
 
163
    if (q->size > r->size)
 
164
      break;
 
165
  
 
166
  *p = r;
 
167
  r->next = q;
 
168
}
 
169
 
 
170
/* Allocate the number of units N with the alignment ALIGN from the ring
 
171
   buffer starting from *FIRST. ALIGN must be a power of two. Return a
 
172
   non-NULL if successful, otherwise return NULL.  */
 
173
static void *
 
174
grub_real_malloc (grub_mm_header_t *first, grub_size_t n, grub_size_t align)
 
175
{
 
176
  grub_mm_header_t p, q;
 
177
 
 
178
  if ((*first)->magic == GRUB_MM_ALLOC_MAGIC)
 
179
    return 0;
 
180
 
 
181
  for (q = *first, p = q->next; ; q = p, p = p->next)
 
182
    {
 
183
      grub_off_t extra;
 
184
 
 
185
      extra = ((grub_addr_t) (p + 1) >> GRUB_MM_ALIGN_LOG2) % align;
 
186
      if (extra)
 
187
        extra = align - extra;
 
188
 
 
189
      if (! p)
 
190
        grub_fatal ("null in the ring");
 
191
 
 
192
      if (p->magic != GRUB_MM_FREE_MAGIC)
 
193
        grub_fatal ("free magic is broken at %p: 0x%x", p, p->magic);
 
194
 
 
195
      if (p->size >= n + extra)
 
196
        {
 
197
          if (extra == 0 && p->size == n)
 
198
            {
 
199
              q->next = p->next;
 
200
              p->magic = GRUB_MM_ALLOC_MAGIC;
 
201
            }
 
202
          else if (extra == 0 || p->size == n + extra)
 
203
            {
 
204
              p->size -= n;
 
205
              p += p->size;
 
206
              p->size = n;
 
207
              p->magic = GRUB_MM_ALLOC_MAGIC;
 
208
            }
 
209
          else
 
210
            {
 
211
              grub_mm_header_t r;
 
212
 
 
213
              r = p + extra + n;
 
214
              r->magic = GRUB_MM_FREE_MAGIC;
 
215
              r->size = p->size - extra - n;
 
216
              r->next = p->next;
 
217
              
 
218
              p->size = extra;
 
219
              p->next = r;
 
220
              p += extra;
 
221
              p->size = n;
 
222
              p->magic = GRUB_MM_ALLOC_MAGIC;
 
223
            }
 
224
 
 
225
          *first = q;
 
226
          
 
227
          return p + 1;
 
228
        }
 
229
 
 
230
      if (p == *first)
 
231
        break;
 
232
    }
 
233
 
 
234
  return 0;
 
235
}
 
236
 
 
237
/* Allocate SIZE bytes with the alignment ALIGN and return the pointer.  */
 
238
void *
 
239
grub_memalign (grub_size_t align, grub_size_t size)
 
240
{
 
241
  grub_mm_region_t r;
 
242
  grub_size_t n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
 
243
  int count = 0;
 
244
  
 
245
  align = (align >> GRUB_MM_ALIGN_LOG2);
 
246
  if (align == 0)
 
247
    align = 1;
 
248
 
 
249
 again:
 
250
  
 
251
  for (r = base; r; r = r->next)
 
252
    {
 
253
      void *p;
 
254
      
 
255
      p = grub_real_malloc (&(r->first), n, align);
 
256
      if (p)
 
257
        return p;
 
258
    }
 
259
 
 
260
  /* If failed, increase free memory somehow.  */
 
261
  switch (count)
 
262
    {
 
263
    case 0:
 
264
      /* Invalidate disk caches.  */
 
265
      grub_disk_cache_invalidate_all ();
 
266
      count++;
 
267
      goto again;
 
268
      
 
269
    case 1:
 
270
      /* Unload unneeded modules.  */
 
271
      grub_dl_unload_unneeded ();
 
272
      count++;
 
273
      goto again;
 
274
 
 
275
    default:
 
276
      break;
 
277
    }
 
278
  
 
279
  grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of memory");
 
280
  return 0;
 
281
}
 
282
 
 
283
/* Allocate SIZE bytes and return the pointer.  */
 
284
void *
 
285
grub_malloc (grub_size_t size)
 
286
{
 
287
  return grub_memalign (0, size);
 
288
}
 
289
 
 
290
/* Deallocate the pointer PTR.  */
 
291
void
 
292
grub_free (void *ptr)
 
293
{
 
294
  grub_mm_header_t p;
 
295
  grub_mm_region_t r;
 
296
 
 
297
  if (! ptr)
 
298
    return;
 
299
 
 
300
  get_header_from_pointer (ptr, &p, &r);
 
301
 
 
302
  if (r->first->magic == GRUB_MM_ALLOC_MAGIC)
 
303
    {
 
304
      p->magic = GRUB_MM_FREE_MAGIC;
 
305
      r->first = p->next = p;
 
306
    }
 
307
  else
 
308
    {
 
309
      grub_mm_header_t q;
 
310
 
 
311
#if 0
 
312
      q = r->first;
 
313
      do
 
314
        {
 
315
          grub_printf ("%s:%d: q=%p, q->size=0x%x, q->magic=0x%x\n",
 
316
                       __FILE__, __LINE__, q, q->size, q->magic);
 
317
          q = q->next;
 
318
        }
 
319
      while (q != r->first);
 
320
#endif
 
321
      
 
322
      for (q = r->first; q >= p || q->next <= p; q = q->next)
 
323
        {
 
324
          if (q->magic != GRUB_MM_FREE_MAGIC)
 
325
            grub_fatal ("free magic is broken at %p: 0x%x", q, q->magic);
 
326
          
 
327
          if (q >= q->next && (q < p || q->next > p))
 
328
            break;
 
329
        }
 
330
 
 
331
      p->magic = GRUB_MM_FREE_MAGIC;
 
332
      p->next = q->next;
 
333
      q->next = p;
 
334
      
 
335
      if (p + p->size == p->next)
 
336
        {
 
337
          if (p->next == q)
 
338
            q = p;
 
339
 
 
340
          p->next->magic = 0;
 
341
          p->size += p->next->size;
 
342
          p->next = p->next->next;
 
343
        }
 
344
      
 
345
      if (q + q->size == p)
 
346
        {
 
347
          p->magic = 0;
 
348
          q->size += p->size;
 
349
          q->next = p->next;
 
350
        }
 
351
 
 
352
      r->first = q;
 
353
    }
 
354
}
 
355
 
 
356
/* Reallocate SIZE bytes and return the pointer. The contents will be
 
357
   the same as that of PTR.  */
 
358
void *
 
359
grub_realloc (void *ptr, grub_size_t size)
 
360
{
 
361
  grub_mm_header_t p;
 
362
  grub_mm_region_t r;
 
363
  void *q;
 
364
  grub_size_t n;
 
365
  
 
366
  if (! ptr)
 
367
    return grub_malloc (size);
 
368
 
 
369
  if (! size)
 
370
    {
 
371
      grub_free (ptr);
 
372
      return 0;
 
373
    }
 
374
 
 
375
  /* FIXME: Not optimal.  */
 
376
  n = ((size + GRUB_MM_ALIGN - 1) >> GRUB_MM_ALIGN_LOG2) + 1;
 
377
  get_header_from_pointer (ptr, &p, &r);
 
378
  
 
379
  if (p->size >= n)
 
380
    return ptr;
 
381
  
 
382
  q = grub_malloc (size);
 
383
  if (! q)
 
384
    return q;
 
385
  
 
386
  grub_memcpy (q, ptr, size);
 
387
  grub_free (ptr);
 
388
  return q;
 
389
}
 
390
 
 
391
#if MM_DEBUG
 
392
void
 
393
grub_mm_dump (unsigned lineno)
 
394
{
 
395
  grub_mm_region_t r;
 
396
 
 
397
  grub_printf ("called at line %u\n", lineno);
 
398
  for (r = base; r; r = r->next)
 
399
    {
 
400
      grub_mm_header_t p;
 
401
      
 
402
      for (p = (grub_mm_header_t) ((r->addr + GRUB_MM_ALIGN - 1)
 
403
                                   & (~(GRUB_MM_ALIGN - 1)));
 
404
           (grub_addr_t) p < r->addr + r->size;
 
405
           p++)
 
406
        {
 
407
          switch (p->magic)
 
408
            {
 
409
            case GRUB_MM_FREE_MAGIC:
 
410
              grub_printf ("F:%p:%u:%p\n",
 
411
                           p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2, p->next);
 
412
              break;
 
413
            case GRUB_MM_ALLOC_MAGIC:
 
414
              grub_printf ("A:%p:%u\n", p, (unsigned int) p->size << GRUB_MM_ALIGN_LOG2);
 
415
              break;
 
416
            }
 
417
        }
 
418
    }
 
419
 
 
420
  grub_printf ("\n");
 
421
}
 
422
#endif /* MM_DEBUG */