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>
24 #include <grub/cpu/efi/memory.h>
26 #if defined (__i386__) || defined (__x86_64__)
30 #define NEXT_MEMORY_DESCRIPTOR(desc, size) \
31 ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
33 #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12)
34 #define BYTES_TO_PAGES_DOWN(bytes) ((bytes) >> 12)
35 #define PAGES_TO_BYTES(pages) ((pages) << 12)
37 /* The size of a memory map obtained from the firmware. This must be
38 a multiplier of 4KB. */
39 #define MEMORY_MAP_SIZE 0x3000
41 /* The minimum and maximum heap size for GRUB itself. */
42 #define MIN_HEAP_SIZE 0x100000
43 #define MAX_HEAP_SIZE (1600 * 0x100000)
45 static void *finish_mmap_buf
= 0;
46 static grub_efi_uintn_t finish_mmap_size
= 0;
47 static grub_efi_uintn_t finish_key
= 0;
48 static grub_efi_uintn_t finish_desc_size
;
49 static grub_efi_uint32_t finish_desc_version
;
50 int grub_efi_is_finished
= 0;
52 /* Allocate pages below a specified address */
54 grub_efi_allocate_pages_max (grub_efi_physical_address_t max
,
55 grub_efi_uintn_t pages
)
57 grub_efi_status_t status
;
58 grub_efi_boot_services_t
*b
;
59 grub_efi_physical_address_t address
= max
;
64 b
= grub_efi_system_table
->boot_services
;
65 status
= efi_call_4 (b
->allocate_pages
, GRUB_EFI_ALLOCATE_MAX_ADDRESS
, GRUB_EFI_LOADER_DATA
, pages
, &address
);
67 if (status
!= GRUB_EFI_SUCCESS
)
72 /* Uggh, the address 0 was allocated... This is too annoying,
73 so reallocate another one. */
75 status
= efi_call_4 (b
->allocate_pages
, GRUB_EFI_ALLOCATE_MAX_ADDRESS
, GRUB_EFI_LOADER_DATA
, pages
, &address
);
76 grub_efi_free_pages (0, pages
);
77 if (status
!= GRUB_EFI_SUCCESS
)
81 return (void *) ((grub_addr_t
) address
);
84 /* Allocate pages. Return the pointer to the first of allocated pages. */
86 grub_efi_allocate_pages_real (grub_efi_physical_address_t address
,
87 grub_efi_uintn_t pages
,
88 grub_efi_allocate_type_t alloctype
,
89 grub_efi_memory_type_t memtype
)
91 grub_efi_status_t status
;
92 grub_efi_boot_services_t
*b
;
94 /* Limit the memory access to less than 4GB for 32-bit platforms. */
95 if (address
> GRUB_EFI_MAX_USABLE_ADDRESS
)
98 b
= grub_efi_system_table
->boot_services
;
99 status
= efi_call_4 (b
->allocate_pages
, alloctype
, memtype
, pages
, &address
);
100 if (status
!= GRUB_EFI_SUCCESS
)
105 /* Uggh, the address 0 was allocated... This is too annoying,
106 so reallocate another one. */
107 address
= GRUB_EFI_MAX_USABLE_ADDRESS
;
108 status
= efi_call_4 (b
->allocate_pages
, alloctype
, memtype
, pages
, &address
);
109 grub_efi_free_pages (0, pages
);
110 if (status
!= GRUB_EFI_SUCCESS
)
114 return (void *) ((grub_addr_t
) address
);
118 grub_efi_allocate_any_pages (grub_efi_uintn_t pages
)
120 return grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS
,
121 pages
, GRUB_EFI_ALLOCATE_MAX_ADDRESS
,
122 GRUB_EFI_LOADER_DATA
);
126 grub_efi_allocate_fixed (grub_efi_physical_address_t address
,
127 grub_efi_uintn_t pages
)
129 return grub_efi_allocate_pages_real (address
, pages
,
130 GRUB_EFI_ALLOCATE_ADDRESS
,
131 GRUB_EFI_LOADER_DATA
);
134 /* Free pages starting from ADDRESS. */
136 grub_efi_free_pages (grub_efi_physical_address_t address
,
137 grub_efi_uintn_t pages
)
139 grub_efi_boot_services_t
*b
;
141 b
= grub_efi_system_table
->boot_services
;
142 efi_call_2 (b
->free_pages
, address
, pages
);
145 #if defined (__i386__) || defined (__x86_64__)
147 /* Helper for stop_broadcom. */
149 find_card (grub_pci_device_t dev
, grub_pci_id_t pciid
,
150 void *data
__attribute__ ((unused
)))
152 grub_pci_address_t addr
;
154 grub_uint16_t pm_state
;
156 if ((pciid
& 0xffff) != GRUB_PCI_VENDOR_BROADCOM
)
159 addr
= grub_pci_make_address (dev
, GRUB_PCI_REG_CLASS
);
160 if (grub_pci_read (addr
) >> 24 != GRUB_PCI_CLASS_NETWORK
)
162 cap
= grub_pci_find_capability (dev
, GRUB_PCI_CAP_POWER_MANAGEMENT
);
165 addr
= grub_pci_make_address (dev
, cap
+ 4);
166 pm_state
= grub_pci_read_word (addr
);
167 pm_state
= pm_state
| 0x03;
168 grub_pci_write_word (addr
, pm_state
);
169 grub_pci_read_word (addr
);
176 grub_pci_iterate (find_card
, NULL
);
182 grub_efi_finish_boot_services (grub_efi_uintn_t
*outbuf_size
, void *outbuf
,
183 grub_efi_uintn_t
*map_key
,
184 grub_efi_uintn_t
*efi_desc_size
,
185 grub_efi_uint32_t
*efi_desc_version
)
187 grub_efi_boot_services_t
*b
;
188 grub_efi_status_t status
;
190 #if defined (__i386__) || defined (__x86_64__)
191 const grub_uint16_t apple
[] = { 'A', 'p', 'p', 'l', 'e' };
194 is_apple
= (grub_memcmp (grub_efi_system_table
->firmware_vendor
,
195 apple
, sizeof (apple
)) == 0);
200 if (grub_efi_get_memory_map (&finish_mmap_size
, finish_mmap_buf
, &finish_key
,
201 &finish_desc_size
, &finish_desc_version
) < 0)
202 return grub_error (GRUB_ERR_IO
, "couldn't retrieve memory map");
204 if (outbuf
&& *outbuf_size
< finish_mmap_size
)
205 return grub_error (GRUB_ERR_IO
, "memory map buffer is too small");
207 finish_mmap_buf
= grub_malloc (finish_mmap_size
);
208 if (!finish_mmap_buf
)
211 if (grub_efi_get_memory_map (&finish_mmap_size
, finish_mmap_buf
, &finish_key
,
212 &finish_desc_size
, &finish_desc_version
) <= 0)
214 grub_free (finish_mmap_buf
);
215 return grub_error (GRUB_ERR_IO
, "couldn't retrieve memory map");
218 b
= grub_efi_system_table
->boot_services
;
219 status
= efi_call_2 (b
->exit_boot_services
, grub_efi_image_handle
,
221 if (status
== GRUB_EFI_SUCCESS
)
224 if (status
!= GRUB_EFI_INVALID_PARAMETER
)
226 grub_free (finish_mmap_buf
);
227 return grub_error (GRUB_ERR_IO
, "couldn't terminate EFI services");
230 grub_free (finish_mmap_buf
);
231 grub_printf ("Trying to terminate EFI services again\n");
233 grub_efi_is_finished
= 1;
235 *outbuf_size
= finish_mmap_size
;
237 grub_memcpy (outbuf
, finish_mmap_buf
, finish_mmap_size
);
239 *map_key
= finish_key
;
241 *efi_desc_size
= finish_desc_size
;
242 if (efi_desc_version
)
243 *efi_desc_version
= finish_desc_version
;
245 #if defined (__i386__) || defined (__x86_64__)
250 return GRUB_ERR_NONE
;
254 * To obtain the UEFI memory map, we must pass a buffer of sufficient size
255 * to hold the entire map. This function returns a sane start value for
259 grub_efi_find_mmap_size (void)
261 grub_efi_uintn_t mmap_size
= 0;
262 grub_efi_uintn_t desc_size
;
264 if (grub_efi_get_memory_map (&mmap_size
, NULL
, NULL
, &desc_size
, 0) < 0)
266 grub_error (GRUB_ERR_IO
, "cannot get EFI memory map size");
271 * Add an extra page, since UEFI can alter the memory map itself on
272 * callbacks or explicit calls, including console output.
274 return ALIGN_UP (mmap_size
+ GRUB_EFI_PAGE_SIZE
, GRUB_EFI_PAGE_SIZE
);
277 /* Get the memory map as defined in the EFI spec. Return 1 if successful,
278 return 0 if partial, or return -1 if an error occurs. */
280 grub_efi_get_memory_map (grub_efi_uintn_t
*memory_map_size
,
281 grub_efi_memory_descriptor_t
*memory_map
,
282 grub_efi_uintn_t
*map_key
,
283 grub_efi_uintn_t
*descriptor_size
,
284 grub_efi_uint32_t
*descriptor_version
)
286 grub_efi_status_t status
;
287 grub_efi_boot_services_t
*b
;
288 grub_efi_uintn_t key
;
289 grub_efi_uint32_t version
;
290 grub_efi_uintn_t size
;
292 if (grub_efi_is_finished
)
295 if (*memory_map_size
< finish_mmap_size
)
297 grub_memcpy (memory_map
, finish_mmap_buf
, *memory_map_size
);
302 grub_memcpy (memory_map
, finish_mmap_buf
, finish_mmap_size
);
305 *memory_map_size
= finish_mmap_size
;
307 *map_key
= finish_key
;
309 *descriptor_size
= finish_desc_size
;
310 if (descriptor_version
)
311 *descriptor_version
= finish_desc_version
;
315 /* Allow some parameters to be missing. */
318 if (! descriptor_version
)
319 descriptor_version
= &version
;
320 if (! descriptor_size
)
321 descriptor_size
= &size
;
323 b
= grub_efi_system_table
->boot_services
;
324 status
= efi_call_5 (b
->get_memory_map
, memory_map_size
, memory_map
, map_key
,
325 descriptor_size
, descriptor_version
);
326 if (*descriptor_size
== 0)
327 *descriptor_size
= sizeof (grub_efi_memory_descriptor_t
);
328 if (status
== GRUB_EFI_SUCCESS
)
330 else if (status
== GRUB_EFI_BUFFER_TOO_SMALL
)
336 /* Sort the memory map in place. */
338 sort_memory_map (grub_efi_memory_descriptor_t
*memory_map
,
339 grub_efi_uintn_t desc_size
,
340 grub_efi_memory_descriptor_t
*memory_map_end
)
342 grub_efi_memory_descriptor_t
*d1
;
343 grub_efi_memory_descriptor_t
*d2
;
345 for (d1
= memory_map
;
347 d1
= NEXT_MEMORY_DESCRIPTOR (d1
, desc_size
))
349 grub_efi_memory_descriptor_t
*max_desc
= d1
;
351 for (d2
= NEXT_MEMORY_DESCRIPTOR (d1
, desc_size
);
353 d2
= NEXT_MEMORY_DESCRIPTOR (d2
, desc_size
))
355 if (max_desc
->num_pages
< d2
->num_pages
)
361 grub_efi_memory_descriptor_t tmp
;
370 /* Filter the descriptors. GRUB needs only available memory. */
371 static grub_efi_memory_descriptor_t
*
372 filter_memory_map (grub_efi_memory_descriptor_t
*memory_map
,
373 grub_efi_memory_descriptor_t
*filtered_memory_map
,
374 grub_efi_uintn_t desc_size
,
375 grub_efi_memory_descriptor_t
*memory_map_end
)
377 grub_efi_memory_descriptor_t
*desc
;
378 grub_efi_memory_descriptor_t
*filtered_desc
;
380 for (desc
= memory_map
, filtered_desc
= filtered_memory_map
;
381 desc
< memory_map_end
;
382 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
))
384 if (desc
->type
== GRUB_EFI_CONVENTIONAL_MEMORY
386 && desc
->physical_start
<= GRUB_EFI_MAX_USABLE_ADDRESS
388 && desc
->physical_start
+ PAGES_TO_BYTES (desc
->num_pages
) > 0x100000
389 && desc
->num_pages
!= 0)
391 grub_memcpy (filtered_desc
, desc
, desc_size
);
393 /* Avoid less than 1MB, because some loaders seem to be confused. */
394 if (desc
->physical_start
< 0x100000)
396 desc
->num_pages
-= BYTES_TO_PAGES (0x100000
397 - desc
->physical_start
);
398 desc
->physical_start
= 0x100000;
402 if (BYTES_TO_PAGES (filtered_desc
->physical_start
)
403 + filtered_desc
->num_pages
404 > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS
))
405 filtered_desc
->num_pages
406 = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS
)
407 - BYTES_TO_PAGES (filtered_desc
->physical_start
));
410 if (filtered_desc
->num_pages
== 0)
413 filtered_desc
= NEXT_MEMORY_DESCRIPTOR (filtered_desc
, desc_size
);
417 return filtered_desc
;
420 /* Return the total number of pages. */
421 static grub_efi_uint64_t
422 get_total_pages (grub_efi_memory_descriptor_t
*memory_map
,
423 grub_efi_uintn_t desc_size
,
424 grub_efi_memory_descriptor_t
*memory_map_end
)
426 grub_efi_memory_descriptor_t
*desc
;
427 grub_efi_uint64_t total
= 0;
429 for (desc
= memory_map
;
430 desc
< memory_map_end
;
431 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
))
432 total
+= desc
->num_pages
;
437 /* Add memory regions. */
439 add_memory_regions (grub_efi_memory_descriptor_t
*memory_map
,
440 grub_efi_uintn_t desc_size
,
441 grub_efi_memory_descriptor_t
*memory_map_end
,
442 grub_efi_uint64_t required_pages
)
444 grub_efi_memory_descriptor_t
*desc
;
446 for (desc
= memory_map
;
447 desc
< memory_map_end
;
448 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
))
450 grub_efi_uint64_t pages
;
451 grub_efi_physical_address_t start
;
454 start
= desc
->physical_start
;
455 pages
= desc
->num_pages
;
456 if (pages
> required_pages
)
458 start
+= PAGES_TO_BYTES (pages
- required_pages
);
459 pages
= required_pages
;
462 addr
= grub_efi_allocate_pages_real (start
, pages
,
463 GRUB_EFI_ALLOCATE_ADDRESS
,
464 GRUB_EFI_LOADER_CODE
);
466 grub_fatal ("cannot allocate conventional memory %p with %u pages",
467 (void *) ((grub_addr_t
) start
),
470 grub_mm_init_region (addr
, PAGES_TO_BYTES (pages
));
472 required_pages
-= pages
;
473 if (required_pages
== 0)
477 if (required_pages
> 0)
478 grub_fatal ("too little memory");
482 /* Print the memory map. */
484 print_memory_map (grub_efi_memory_descriptor_t
*memory_map
,
485 grub_efi_uintn_t desc_size
,
486 grub_efi_memory_descriptor_t
*memory_map_end
)
488 grub_efi_memory_descriptor_t
*desc
;
491 for (desc
= memory_map
, i
= 0;
492 desc
< memory_map_end
;
493 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
), i
++)
495 grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n",
496 desc
->type
, desc
->physical_start
, desc
->virtual_start
,
497 desc
->num_pages
, desc
->attribute
);
503 grub_efi_mm_init (void)
505 grub_efi_memory_descriptor_t
*memory_map
;
506 grub_efi_memory_descriptor_t
*memory_map_end
;
507 grub_efi_memory_descriptor_t
*filtered_memory_map
;
508 grub_efi_memory_descriptor_t
*filtered_memory_map_end
;
509 grub_efi_uintn_t map_size
;
510 grub_efi_uintn_t desc_size
;
511 grub_efi_uint64_t total_pages
;
512 grub_efi_uint64_t required_pages
;
515 /* Prepare a memory region to store two memory maps. */
516 memory_map
= grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE
));
518 grub_fatal ("cannot allocate memory");
520 /* Obtain descriptors for available memory. */
521 map_size
= MEMORY_MAP_SIZE
;
523 mm_status
= grub_efi_get_memory_map (&map_size
, memory_map
, 0, &desc_size
, 0);
528 ((grub_efi_physical_address_t
) ((grub_addr_t
) memory_map
),
529 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE
));
531 /* Freeing/allocating operations may increase memory map size. */
532 map_size
+= desc_size
* 32;
534 memory_map
= grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size
));
536 grub_fatal ("cannot allocate memory");
538 mm_status
= grub_efi_get_memory_map (&map_size
, memory_map
, 0,
543 grub_fatal ("cannot get memory map");
545 memory_map_end
= NEXT_MEMORY_DESCRIPTOR (memory_map
, map_size
);
547 filtered_memory_map
= memory_map_end
;
549 filtered_memory_map_end
= filter_memory_map (memory_map
, filtered_memory_map
,
550 desc_size
, memory_map_end
);
552 /* By default, request a quarter of the available memory. */
553 total_pages
= get_total_pages (filtered_memory_map
, desc_size
,
554 filtered_memory_map_end
);
555 required_pages
= (total_pages
>> 2);
556 if (required_pages
< BYTES_TO_PAGES (MIN_HEAP_SIZE
))
557 required_pages
= BYTES_TO_PAGES (MIN_HEAP_SIZE
);
558 else if (required_pages
> BYTES_TO_PAGES (MAX_HEAP_SIZE
))
559 required_pages
= BYTES_TO_PAGES (MAX_HEAP_SIZE
);
561 /* Sort the filtered descriptors, so that GRUB can allocate pages
562 from smaller regions. */
563 sort_memory_map (filtered_memory_map
, desc_size
, filtered_memory_map_end
);
565 /* Allocate memory regions for GRUB's memory management. */
566 add_memory_regions (filtered_memory_map
, desc_size
,
567 filtered_memory_map_end
, required_pages
);
571 map_size
= MEMORY_MAP_SIZE
;
573 if (grub_efi_get_memory_map (&map_size
, memory_map
, 0, &desc_size
, 0) < 0)
574 grub_fatal ("cannot get memory map");
576 grub_printf ("printing memory map\n");
577 print_memory_map (memory_map
, desc_size
,
578 NEXT_MEMORY_DESCRIPTOR (memory_map
, map_size
));
579 grub_fatal ("Debug. ");
582 /* Release the memory maps. */
583 grub_efi_free_pages ((grub_addr_t
) memory_map
,
584 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE
));