1
/* mm.c - generic EFI memory management */
3
* GRUB -- GRand Unified Bootloader
4
* Copyright (C) 2006,2007,2008,2009 Free Software Foundation, Inc.
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.
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.
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/>.
20
#include <grub/misc.h>
22
#include <grub/efi/api.h>
23
#include <grub/efi/efi.h>
25
#define NEXT_MEMORY_DESCRIPTOR(desc, size) \
26
((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
28
#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12)
29
#define PAGES_TO_BYTES(pages) ((pages) << 12)
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
35
/* The minimum and maximum heap size for GRUB itself. */
36
#define MIN_HEAP_SIZE 0x100000
37
#define MAX_HEAP_SIZE (1600 * 0x100000)
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;
46
/* Allocate pages. Return the pointer to the first of allocated pages. */
48
grub_efi_allocate_pages (grub_efi_physical_address_t address,
49
grub_efi_uintn_t pages)
51
grub_efi_allocate_type_t type;
52
grub_efi_status_t status;
53
grub_efi_boot_services_t *b;
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)
61
#if GRUB_TARGET_SIZEOF_VOID_P < 8 || defined (MCMODEL_SMALL)
64
type = GRUB_EFI_ALLOCATE_MAX_ADDRESS;
68
type = GRUB_EFI_ALLOCATE_ADDRESS;
71
type = GRUB_EFI_ALLOCATE_ANY_PAGES;
73
type = GRUB_EFI_ALLOCATE_ADDRESS;
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)
83
/* Uggh, the address 0 was allocated... This is too annoying,
84
so reallocate another one. */
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)
92
return (void *) ((grub_addr_t) address);
95
/* Free pages starting from ADDRESS. */
97
grub_efi_free_pages (grub_efi_physical_address_t address,
98
grub_efi_uintn_t pages)
100
grub_efi_boot_services_t *b;
102
b = grub_efi_system_table->boot_services;
103
efi_call_2 (b->free_pages, address, pages);
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)
112
grub_efi_boot_services_t *b;
113
grub_efi_status_t status;
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");
119
if (outbuf && *outbuf_size < finish_mmap_size)
120
return grub_error (GRUB_ERR_IO, "memory map buffer is too small");
122
finish_mmap_buf = grub_malloc (finish_mmap_size);
123
if (!finish_mmap_buf)
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");
130
b = grub_efi_system_table->boot_services;
131
status = efi_call_2 (b->exit_boot_services, grub_efi_image_handle,
133
if (status != GRUB_EFI_SUCCESS)
134
return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services");
136
grub_efi_is_finished = 1;
138
*outbuf_size = finish_mmap_size;
140
grub_memcpy (outbuf, finish_mmap_buf, finish_mmap_size);
142
*map_key = finish_key;
144
*efi_desc_size = finish_desc_size;
145
if (efi_desc_version)
146
*efi_desc_version = finish_desc_version;
148
return GRUB_ERR_NONE;
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. */
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)
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;
165
if (grub_efi_is_finished)
168
if (*memory_map_size < finish_mmap_size)
170
grub_memcpy (memory_map, finish_mmap_buf, *memory_map_size);
175
grub_memcpy (memory_map, finish_mmap_buf, finish_mmap_size);
178
*memory_map_size = finish_mmap_size;
180
*map_key = finish_key;
182
*descriptor_size = finish_desc_size;
183
if (descriptor_version)
184
*descriptor_version = finish_desc_version;
188
/* Allow some parameters to be missing. */
191
if (! descriptor_version)
192
descriptor_version = &version;
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)
199
else if (status == GRUB_EFI_BUFFER_TOO_SMALL)
205
/* Sort the memory map in place. */
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)
211
grub_efi_memory_descriptor_t *d1;
212
grub_efi_memory_descriptor_t *d2;
214
for (d1 = memory_map;
216
d1 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size))
218
grub_efi_memory_descriptor_t *max_desc = d1;
220
for (d2 = NEXT_MEMORY_DESCRIPTOR (d1, desc_size);
222
d2 = NEXT_MEMORY_DESCRIPTOR (d2, desc_size))
224
if (max_desc->num_pages < d2->num_pages)
230
grub_efi_memory_descriptor_t tmp;
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)
246
grub_efi_memory_descriptor_t *desc;
247
grub_efi_memory_descriptor_t *filtered_desc;
249
for (desc = memory_map, filtered_desc = filtered_memory_map;
250
desc < memory_map_end;
251
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
253
if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY
254
#if GRUB_TARGET_SIZEOF_VOID_P < 8 || defined (MCMODEL_SMALL)
255
&& desc->physical_start <= 0xffffffff
257
&& desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000
258
&& desc->num_pages != 0)
260
grub_memcpy (filtered_desc, desc, desc_size);
262
/* Avoid less than 1MB, because some loaders seem to be confused. */
263
if (desc->physical_start < 0x100000)
265
desc->num_pages -= BYTES_TO_PAGES (0x100000
266
- desc->physical_start);
267
desc->physical_start = 0x100000;
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));
279
if (filtered_desc->num_pages == 0)
282
filtered_desc = NEXT_MEMORY_DESCRIPTOR (filtered_desc, desc_size);
286
return filtered_desc;
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)
295
grub_efi_memory_descriptor_t *desc;
296
grub_efi_uint64_t total = 0;
298
for (desc = memory_map;
299
desc < memory_map_end;
300
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
301
total += desc->num_pages;
306
/* Add memory regions. */
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)
313
grub_efi_memory_descriptor_t *desc;
315
for (desc = memory_map;
316
desc < memory_map_end;
317
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
319
grub_efi_uint64_t pages;
320
grub_efi_physical_address_t start;
323
start = desc->physical_start;
324
pages = desc->num_pages;
325
if (pages > required_pages)
327
start += PAGES_TO_BYTES (pages - required_pages);
328
pages = required_pages;
331
addr = grub_efi_allocate_pages (start, pages);
333
grub_fatal ("cannot allocate conventional memory %p with %u pages",
334
(void *) ((grub_addr_t) start),
337
grub_mm_init_region (addr, PAGES_TO_BYTES (pages));
339
required_pages -= pages;
340
if (required_pages == 0)
344
if (required_pages > 0)
345
grub_fatal ("too little memory");
349
/* Print the memory map. */
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)
355
grub_efi_memory_descriptor_t *desc;
358
for (desc = memory_map, i = 0;
359
desc < memory_map_end;
360
desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size), i++)
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);
370
grub_efi_mm_init (void)
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;
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));
386
grub_fatal ("cannot allocate memory");
388
/* Obtain descriptors for available memory. */
389
map_size = MEMORY_MAP_SIZE;
391
mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0);
396
((grub_efi_physical_address_t) ((grub_addr_t) memory_map),
397
2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
399
/* Freeing/allocating operations may increase memory map size. */
400
map_size += desc_size * 32;
402
memory_map = grub_efi_allocate_pages (0, 2 * BYTES_TO_PAGES (map_size));
404
grub_fatal ("cannot allocate memory");
406
mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0,
411
grub_fatal ("cannot get memory map");
413
memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size);
415
filtered_memory_map = memory_map_end;
417
filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map,
418
desc_size, memory_map_end);
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);
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);
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);
439
map_size = MEMORY_MAP_SIZE;
441
if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0)
442
grub_fatal ("cannot get memory map");
444
grub_printf ("printing memory map\n");
445
print_memory_map (memory_map, desc_size,
446
NEXT_MEMORY_DESCRIPTOR (memory_map, map_size));
450
/* Release the memory maps. */
451
grub_efi_free_pages ((grub_addr_t) memory_map,
452
2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));