~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to grub-core/kern/efi/mm.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* mm.c - generic EFI memory management */
 
2
/*
 
3
 *  GRUB  --  GRand Unified Bootloader
 
4
 *  Copyright (C) 2006,2007,2008,2009  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 3 of the License, or
 
9
 *  (at your option) any later version.
 
10
 *
 
11
 *  GRUB 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, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include <grub/misc.h>
 
21
#include <grub/mm.h>
 
22
#include <grub/efi/api.h>
 
23
#include <grub/efi/efi.h>
 
24
 
 
25
#define NEXT_MEMORY_DESCRIPTOR(desc, size)      \
 
26
  ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
 
27
 
 
28
#define BYTES_TO_PAGES(bytes)   (((bytes) + 0xfff) >> 12)
 
29
#define PAGES_TO_BYTES(pages)   ((pages) << 12)
 
30
 
 
31
/* The size of a memory map obtained from the firmware. This must be
 
32
   a multiplier of 4KB.  */
 
33
#define MEMORY_MAP_SIZE 0x3000
 
34
 
 
35
/* The minimum and maximum heap size for GRUB itself.  */
 
36
#define MIN_HEAP_SIZE   0x100000
 
37
#define MAX_HEAP_SIZE   (1600 * 0x100000)
 
38
 
 
39
static void *finish_mmap_buf = 0;
 
40
static grub_efi_uintn_t finish_mmap_size = 0;
 
41
static grub_efi_uintn_t finish_key = 0;
 
42
static grub_efi_uintn_t finish_desc_size;
 
43
static grub_efi_uint32_t finish_desc_version;
 
44
int grub_efi_is_finished = 0;
 
45
 
 
46
/* Allocate pages. Return the pointer to the first of allocated pages.  */
 
47
void *
 
48
grub_efi_allocate_pages (grub_efi_physical_address_t address,
 
49
                         grub_efi_uintn_t pages)
 
50
{
 
51
  grub_efi_allocate_type_t type;
 
52
  grub_efi_status_t status;
 
53
  grub_efi_boot_services_t *b;
 
54
 
 
55
#if GRUB_TARGET_SIZEOF_VOID_P < 8
 
56
  /* Limit the memory access to less than 4GB for 32-bit platforms.  */
 
57
  if (address > 0xffffffff)
 
58
    return 0;
 
59
#endif
 
60
 
 
61
#if GRUB_TARGET_SIZEOF_VOID_P < 8 || defined (MCMODEL_SMALL)
 
62
  if (address == 0)
 
63
    {
 
64
      type = GRUB_EFI_ALLOCATE_MAX_ADDRESS;
 
65
      address = 0xffffffff;
 
66
    }
 
67
  else
 
68
    type = GRUB_EFI_ALLOCATE_ADDRESS;
 
69
#else
 
70
  if (address == 0)
 
71
    type = GRUB_EFI_ALLOCATE_ANY_PAGES;
 
72
  else
 
73
    type = GRUB_EFI_ALLOCATE_ADDRESS;
 
74
#endif
 
75
 
 
76
  b = grub_efi_system_table->boot_services;
 
77
  status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address);
 
78
  if (status != GRUB_EFI_SUCCESS)
 
79
    return 0;
 
80
 
 
81
  if (address == 0)
 
82
    {
 
83
      /* Uggh, the address 0 was allocated... This is too annoying,
 
84
         so reallocate another one.  */
 
85
      address = 0xffffffff;
 
86
      status = efi_call_4 (b->allocate_pages, type, GRUB_EFI_LOADER_DATA, pages, &address);
 
87
      grub_efi_free_pages (0, pages);
 
88
      if (status != GRUB_EFI_SUCCESS)
 
89
        return 0;
 
90
    }
 
91
 
 
92
  return (void *) ((grub_addr_t) address);
 
93
}
 
94
 
 
95
/* Free pages starting from ADDRESS.  */
 
96
void
 
97
grub_efi_free_pages (grub_efi_physical_address_t address,
 
98
                     grub_efi_uintn_t pages)
 
99
{
 
100
  grub_efi_boot_services_t *b;
 
101
 
 
102
  b = grub_efi_system_table->boot_services;
 
103
  efi_call_2 (b->free_pages, address, pages);
 
104
}
 
105
 
 
106
grub_err_t
 
107
grub_efi_finish_boot_services (grub_efi_uintn_t *outbuf_size, void *outbuf,
 
108
                               grub_efi_uintn_t *map_key,
 
109
                               grub_efi_uintn_t *efi_desc_size,
 
110
                               grub_efi_uint32_t *efi_desc_version)
 
111
{
 
112
  grub_efi_boot_services_t *b;
 
113
  grub_efi_status_t status;
 
114
 
 
115
  if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
 
116
                               &finish_desc_size, &finish_desc_version) < 0)
 
117
    return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
 
118
 
 
119
  if (outbuf && *outbuf_size < finish_mmap_size)
 
120
    return grub_error (GRUB_ERR_IO, "memory map buffer is too small");
 
121
 
 
122
  finish_mmap_buf = grub_malloc (finish_mmap_size);
 
123
  if (!finish_mmap_buf)
 
124
    return grub_errno;
 
125
 
 
126
  if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
 
127
                               &finish_desc_size, &finish_desc_version) <= 0)
 
128
    return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
 
129
 
 
130
  b = grub_efi_system_table->boot_services;
 
131
  status = efi_call_2 (b->exit_boot_services, grub_efi_image_handle,
 
132
                       finish_key);
 
133
  if (status != GRUB_EFI_SUCCESS)
 
134
    return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services");
 
135
 
 
136
  grub_efi_is_finished = 1;
 
137
  if (outbuf_size)
 
138
    *outbuf_size = finish_mmap_size;
 
139
  if (outbuf)
 
140
    grub_memcpy (outbuf, finish_mmap_buf, finish_mmap_size);
 
141
  if (map_key)
 
142
    *map_key = finish_key;
 
143
  if (efi_desc_size)
 
144
    *efi_desc_size = finish_desc_size;
 
145
  if (efi_desc_version)
 
146
    *efi_desc_version = finish_desc_version;
 
147
 
 
148
  return GRUB_ERR_NONE;
 
149
}
 
150
 
 
151
/* Get the memory map as defined in the EFI spec. Return 1 if successful,
 
152
   return 0 if partial, or return -1 if an error occurs.  */
 
153
int
 
154
grub_efi_get_memory_map (grub_efi_uintn_t *memory_map_size,
 
155
                         grub_efi_memory_descriptor_t *memory_map,
 
156
                         grub_efi_uintn_t *map_key,
 
157
                         grub_efi_uintn_t *descriptor_size,
 
158
                         grub_efi_uint32_t *descriptor_version)
 
159
{
 
160
  grub_efi_status_t status;
 
161
  grub_efi_boot_services_t *b;
 
162
  grub_efi_uintn_t key;
 
163
  grub_efi_uint32_t version;
 
164
 
 
165
  if (grub_efi_is_finished)
 
166
    {
 
167
      int ret = 1;
 
168
      if (*memory_map_size < finish_mmap_size)
 
169
        {
 
170
          grub_memcpy (memory_map, finish_mmap_buf, *memory_map_size);
 
171
          ret = 0;
 
172
        }
 
173
      else
 
174
        {
 
175
          grub_memcpy (memory_map, finish_mmap_buf, finish_mmap_size);
 
176
          ret = 1;
 
177
        }
 
178
      *memory_map_size = finish_mmap_size;
 
179
      if (map_key)
 
180
        *map_key = finish_key;
 
181
      if (descriptor_size)
 
182
        *descriptor_size = finish_desc_size;
 
183
      if (descriptor_version)
 
184
        *descriptor_version = finish_desc_version;
 
185
      return ret;
 
186
    }
 
187
 
 
188
  /* Allow some parameters to be missing.  */
 
189
  if (! map_key)
 
190
    map_key = &key;
 
191
  if (! descriptor_version)
 
192
    descriptor_version = &version;
 
193
 
 
194
  b = grub_efi_system_table->boot_services;
 
195
  status = efi_call_5 (b->get_memory_map, memory_map_size, memory_map, map_key,
 
196
                              descriptor_size, descriptor_version);
 
197
  if (status == GRUB_EFI_SUCCESS)
 
198
    return 1;
 
199
  else if (status == GRUB_EFI_BUFFER_TOO_SMALL)
 
200
    return 0;
 
201
  else
 
202
    return -1;
 
203
}
 
204
 
 
205
/* Sort the memory map in place.  */
 
206
static void
 
207
sort_memory_map (grub_efi_memory_descriptor_t *memory_map,
 
208
                 grub_efi_uintn_t desc_size,
 
209
                 grub_efi_memory_descriptor_t *memory_map_end)
 
210
{
 
211
  grub_efi_memory_descriptor_t *d1;
 
212
  grub_efi_memory_descriptor_t *d2;
 
213
 
 
214
  for (d1 = memory_map;
 
215
       d1 < memory_map_end;
 
216
       d1 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size))
 
217
    {
 
218
      grub_efi_memory_descriptor_t *max_desc = d1;
 
219
 
 
220
      for (d2 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size);
 
221
           d2 < memory_map_end;
 
222
           d2 = NEXT_MEMORY_DESCRIPTOR (d2, desc_size))
 
223
        {
 
224
          if (max_desc->num_pages < d2->num_pages)
 
225
            max_desc = d2;
 
226
        }
 
227
 
 
228
      if (max_desc != d1)
 
229
        {
 
230
          grub_efi_memory_descriptor_t tmp;
 
231
 
 
232
          tmp = *d1;
 
233
          *d1 = *max_desc;
 
234
          *max_desc = tmp;
 
235
        }
 
236
    }
 
237
}
 
238
 
 
239
/* Filter the descriptors. GRUB needs only available memory.  */
 
240
static grub_efi_memory_descriptor_t *
 
241
filter_memory_map (grub_efi_memory_descriptor_t *memory_map,
 
242
                   grub_efi_memory_descriptor_t *filtered_memory_map,
 
243
                   grub_efi_uintn_t desc_size,
 
244
                   grub_efi_memory_descriptor_t *memory_map_end)
 
245
{
 
246
  grub_efi_memory_descriptor_t *desc;
 
247
  grub_efi_memory_descriptor_t *filtered_desc;
 
248
 
 
249
  for (desc = memory_map, filtered_desc = filtered_memory_map;
 
250
       desc < memory_map_end;
 
251
       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
 
252
    {
 
253
      if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
 
254
#if GRUB_TARGET_SIZEOF_VOID_P < 8 || defined (MCMODEL_SMALL)
 
255
          && desc->physical_start <= 0xffffffff
 
256
#endif
 
257
          && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000
 
258
          && desc->num_pages != 0)
 
259
        {
 
260
          grub_memcpy (filtered_desc, desc, desc_size);
 
261
 
 
262
          /* Avoid less than 1MB, because some loaders seem to be confused.  */
 
263
          if (desc->physical_start < 0x100000)
 
264
            {
 
265
              desc->num_pages -= BYTES_TO_PAGES (0x100000
 
266
                                                 - desc->physical_start);
 
267
              desc->physical_start = 0x100000;
 
268
            }
 
269
 
 
270
#if GRUB_TARGET_SIZEOF_VOID_P < 8 || defined (MCMODEL_SMALL)
 
271
          if (BYTES_TO_PAGES (filtered_desc->physical_start)
 
272
              + filtered_desc->num_pages
 
273
              > BYTES_TO_PAGES (0x100000000LL))
 
274
            filtered_desc->num_pages
 
275
              = (BYTES_TO_PAGES (0x100000000LL)
 
276
                 - BYTES_TO_PAGES (filtered_desc->physical_start));
 
277
#endif
 
278
 
 
279
          if (filtered_desc->num_pages == 0)
 
280
            continue;
 
281
 
 
282
          filtered_desc = NEXT_MEMORY_DESCRIPTOR (filtered_desc, desc_size);
 
283
        }
 
284
    }
 
285
 
 
286
  return filtered_desc;
 
287
}
 
288
 
 
289
/* Return the total number of pages.  */
 
290
static grub_efi_uint64_t
 
291
get_total_pages (grub_efi_memory_descriptor_t *memory_map,
 
292
                 grub_efi_uintn_t desc_size,
 
293
                 grub_efi_memory_descriptor_t *memory_map_end)
 
294
{
 
295
  grub_efi_memory_descriptor_t *desc;
 
296
  grub_efi_uint64_t total = 0;
 
297
 
 
298
  for (desc = memory_map;
 
299
       desc < memory_map_end;
 
300
       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
 
301
    total += desc->num_pages;
 
302
 
 
303
  return total;
 
304
}
 
305
 
 
306
/* Add memory regions.  */
 
307
static void
 
308
add_memory_regions (grub_efi_memory_descriptor_t *memory_map,
 
309
                    grub_efi_uintn_t desc_size,
 
310
                    grub_efi_memory_descriptor_t *memory_map_end,
 
311
                    grub_efi_uint64_t required_pages)
 
312
{
 
313
  grub_efi_memory_descriptor_t *desc;
 
314
 
 
315
  for (desc = memory_map;
 
316
       desc < memory_map_end;
 
317
       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
 
318
    {
 
319
      grub_efi_uint64_t pages;
 
320
      grub_efi_physical_address_t start;
 
321
      void *addr;
 
322
 
 
323
      start = desc->physical_start;
 
324
      pages = desc->num_pages;
 
325
      if (pages > required_pages)
 
326
        {
 
327
          start += PAGES_TO_BYTES (pages - required_pages);
 
328
          pages = required_pages;
 
329
        }
 
330
 
 
331
      addr = grub_efi_allocate_pages (start, pages);
 
332
      if (! addr)
 
333
        grub_fatal ("cannot allocate conventional memory %p with %u pages",
 
334
                    (void *) ((grub_addr_t) start),
 
335
                    (unsigned) pages);
 
336
 
 
337
      grub_mm_init_region (addr, PAGES_TO_BYTES (pages));
 
338
 
 
339
      required_pages -= pages;
 
340
      if (required_pages == 0)
 
341
        break;
 
342
    }
 
343
 
 
344
  if (required_pages > 0)
 
345
    grub_fatal ("too little memory");
 
346
}
 
347
 
 
348
#if 0
 
349
/* Print the memory map.  */
 
350
static void
 
351
print_memory_map (grub_efi_memory_descriptor_t *memory_map,
 
352
                  grub_efi_uintn_t desc_size,
 
353
                  grub_efi_memory_descriptor_t *memory_map_end)
 
354
{
 
355
  grub_efi_memory_descriptor_t *desc;
 
356
  int i;
 
357
 
 
358
  for (desc = memory_map, i = 0;
 
359
       desc < memory_map_end;
 
360
       desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size), i++)
 
361
    {
 
362
      grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n",
 
363
                   desc->type, desc->physical_start, desc->virtual_start,
 
364
                   desc->num_pages, desc->attribute);
 
365
    }
 
366
}
 
367
#endif
 
368
 
 
369
void
 
370
grub_efi_mm_init (void)
 
371
{
 
372
  grub_efi_memory_descriptor_t *memory_map;
 
373
  grub_efi_memory_descriptor_t *memory_map_end;
 
374
  grub_efi_memory_descriptor_t *filtered_memory_map;
 
375
  grub_efi_memory_descriptor_t *filtered_memory_map_end;
 
376
  grub_efi_uintn_t map_size;
 
377
  grub_efi_uintn_t desc_size;
 
378
  grub_efi_uint64_t total_pages;
 
379
  grub_efi_uint64_t required_pages;
 
380
  int mm_status;
 
381
 
 
382
  /* Prepare a memory region to store two memory maps.  */
 
383
  memory_map = grub_efi_allocate_pages (0,
 
384
                                        2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
 
385
  if (! memory_map)
 
386
    grub_fatal ("cannot allocate memory");
 
387
 
 
388
  /* Obtain descriptors for available memory.  */
 
389
  map_size = MEMORY_MAP_SIZE;
 
390
 
 
391
  mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0);
 
392
 
 
393
  if (mm_status == 0)
 
394
    {
 
395
      grub_efi_free_pages
 
396
        ((grub_efi_physical_address_t) ((grub_addr_t) memory_map),
 
397
         2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
 
398
 
 
399
      /* Freeing/allocating operations may increase memory map size.  */
 
400
      map_size += desc_size * 32;
 
401
 
 
402
      memory_map = grub_efi_allocate_pages (0, 2 * BYTES_TO_PAGES (map_size));
 
403
      if (! memory_map)
 
404
        grub_fatal ("cannot allocate memory");
 
405
 
 
406
      mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0,
 
407
                                           &desc_size, 0);
 
408
    }
 
409
 
 
410
  if (mm_status < 0)
 
411
    grub_fatal ("cannot get memory map");
 
412
 
 
413
  memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size);
 
414
 
 
415
  filtered_memory_map = memory_map_end;
 
416
 
 
417
  filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map,
 
418
                                               desc_size, memory_map_end);
 
419
 
 
420
  /* By default, request a quarter of the available memory.  */
 
421
  total_pages = get_total_pages (filtered_memory_map, desc_size,
 
422
                                 filtered_memory_map_end);
 
423
  required_pages = (total_pages >> 2);
 
424
  if (required_pages < BYTES_TO_PAGES (MIN_HEAP_SIZE))
 
425
    required_pages = BYTES_TO_PAGES (MIN_HEAP_SIZE);
 
426
  else if (required_pages > BYTES_TO_PAGES (MAX_HEAP_SIZE))
 
427
    required_pages = BYTES_TO_PAGES (MAX_HEAP_SIZE);
 
428
 
 
429
  /* Sort the filtered descriptors, so that GRUB can allocate pages
 
430
     from smaller regions.  */
 
431
  sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);
 
432
 
 
433
  /* Allocate memory regions for GRUB's memory management.  */
 
434
  add_memory_regions (filtered_memory_map, desc_size,
 
435
                      filtered_memory_map_end, required_pages);
 
436
 
 
437
#if 0
 
438
  /* For debug.  */
 
439
  map_size = MEMORY_MAP_SIZE;
 
440
 
 
441
  if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0)
 
442
    grub_fatal ("cannot get memory map");
 
443
 
 
444
  grub_printf ("printing memory map\n");
 
445
  print_memory_map (memory_map, desc_size,
 
446
                    NEXT_MEMORY_DESCRIPTOR (memory_map, map_size));
 
447
  grub_abort ();
 
448
#endif
 
449
 
 
450
  /* Release the memory maps.  */
 
451
  grub_efi_free_pages ((grub_addr_t) memory_map,
 
452
                       2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
 
453
}