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 #if defined (__i386__) || defined (__x86_64__)
29 #define NEXT_MEMORY_DESCRIPTOR(desc, size) \
30 ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
32 #define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12)
33 #define PAGES_TO_BYTES(pages) ((pages) << 12)
35 /* The size of a memory map obtained from the firmware. This must be
36 a multiplier of 4KB. */
37 #define MEMORY_MAP_SIZE 0x3000
39 /* The minimum and maximum heap size for GRUB itself. */
40 #define MIN_HEAP_SIZE 0x100000
41 #define MAX_HEAP_SIZE (1600 * 0x100000)
43 static void *finish_mmap_buf
= 0;
44 static grub_efi_uintn_t finish_mmap_size
= 0;
45 static grub_efi_uintn_t finish_key
= 0;
46 static grub_efi_uintn_t finish_desc_size
;
47 static grub_efi_uint32_t finish_desc_version
;
48 int grub_efi_is_finished
= 0;
50 /* Allocate pages. Return the pointer to the first of allocated pages. */
52 grub_efi_allocate_pages (grub_efi_physical_address_t address
,
53 grub_efi_uintn_t pages
)
55 grub_efi_allocate_type_t type
;
56 grub_efi_status_t status
;
57 grub_efi_boot_services_t
*b
;
60 /* Limit the memory access to less than 4GB for 32-bit platforms. */
61 if (address
> 0xffffffff)
68 type
= GRUB_EFI_ALLOCATE_MAX_ADDRESS
;
72 type
= GRUB_EFI_ALLOCATE_ADDRESS
;
75 type
= GRUB_EFI_ALLOCATE_ANY_PAGES
;
77 type
= GRUB_EFI_ALLOCATE_ADDRESS
;
80 b
= grub_efi_system_table
->boot_services
;
81 status
= efi_call_4 (b
->allocate_pages
, type
, GRUB_EFI_LOADER_DATA
, pages
, &address
);
82 if (status
!= GRUB_EFI_SUCCESS
)
87 /* Uggh, the address 0 was allocated... This is too annoying,
88 so reallocate another one. */
90 status
= efi_call_4 (b
->allocate_pages
, type
, GRUB_EFI_LOADER_DATA
, pages
, &address
);
91 grub_efi_free_pages (0, pages
);
92 if (status
!= GRUB_EFI_SUCCESS
)
96 return (void *) ((grub_addr_t
) address
);
99 /* Free pages starting from ADDRESS. */
101 grub_efi_free_pages (grub_efi_physical_address_t address
,
102 grub_efi_uintn_t pages
)
104 grub_efi_boot_services_t
*b
;
106 b
= grub_efi_system_table
->boot_services
;
107 efi_call_2 (b
->free_pages
, address
, pages
);
110 #if defined (__i386__) || defined (__x86_64__)
115 auto int NESTED_FUNC_ATTR
find_card (grub_pci_device_t dev
,
116 grub_pci_id_t pciid
);
118 int NESTED_FUNC_ATTR
find_card (grub_pci_device_t dev
,
121 grub_pci_address_t addr
;
123 grub_uint16_t pm_state
;
125 if ((pciid
& 0xffff) != GRUB_PCI_VENDOR_BROADCOM
)
128 addr
= grub_pci_make_address (dev
, GRUB_PCI_REG_CLASS
);
129 if (grub_pci_read (addr
) >> 24 != GRUB_PCI_CLASS_NETWORK
)
131 cap
= grub_pci_find_capability (dev
, GRUB_PCI_CAP_POWER_MANAGEMENT
);
134 addr
= grub_pci_make_address (dev
, cap
+ 4);
135 pm_state
= grub_pci_read_word (addr
);
136 pm_state
= pm_state
| 0x03;
137 grub_pci_write_word (addr
, pm_state
);
138 grub_pci_read_word (addr
);
142 grub_pci_iterate (find_card
);
148 grub_efi_finish_boot_services (grub_efi_uintn_t
*outbuf_size
, void *outbuf
,
149 grub_efi_uintn_t
*map_key
,
150 grub_efi_uintn_t
*efi_desc_size
,
151 grub_efi_uint32_t
*efi_desc_version
)
153 grub_efi_boot_services_t
*b
;
154 grub_efi_status_t status
;
156 #if defined (__i386__) || defined (__x86_64__)
157 const grub_uint16_t apple
[] = { 'A', 'p', 'p', 'l', 'e' };
160 is_apple
= (grub_memcmp (grub_efi_system_table
->firmware_vendor
,
161 apple
, sizeof (apple
)) == 0);
164 if (grub_efi_get_memory_map (&finish_mmap_size
, finish_mmap_buf
, &finish_key
,
165 &finish_desc_size
, &finish_desc_version
) < 0)
166 return grub_error (GRUB_ERR_IO
, "couldn't retrieve memory map");
168 if (outbuf
&& *outbuf_size
< finish_mmap_size
)
169 return grub_error (GRUB_ERR_IO
, "memory map buffer is too small");
171 finish_mmap_buf
= grub_malloc (finish_mmap_size
);
172 if (!finish_mmap_buf
)
175 if (grub_efi_get_memory_map (&finish_mmap_size
, finish_mmap_buf
, &finish_key
,
176 &finish_desc_size
, &finish_desc_version
) <= 0)
177 return grub_error (GRUB_ERR_IO
, "couldn't retrieve memory map");
179 b
= grub_efi_system_table
->boot_services
;
180 status
= efi_call_2 (b
->exit_boot_services
, grub_efi_image_handle
,
182 if (status
!= GRUB_EFI_SUCCESS
)
183 return grub_error (GRUB_ERR_IO
, "couldn't terminate EFI services");
185 grub_efi_is_finished
= 1;
187 *outbuf_size
= finish_mmap_size
;
189 grub_memcpy (outbuf
, finish_mmap_buf
, finish_mmap_size
);
191 *map_key
= finish_key
;
193 *efi_desc_size
= finish_desc_size
;
194 if (efi_desc_version
)
195 *efi_desc_version
= finish_desc_version
;
197 #if defined (__i386__) || defined (__x86_64__)
202 return GRUB_ERR_NONE
;
205 /* Get the memory map as defined in the EFI spec. Return 1 if successful,
206 return 0 if partial, or return -1 if an error occurs. */
208 grub_efi_get_memory_map (grub_efi_uintn_t
*memory_map_size
,
209 grub_efi_memory_descriptor_t
*memory_map
,
210 grub_efi_uintn_t
*map_key
,
211 grub_efi_uintn_t
*descriptor_size
,
212 grub_efi_uint32_t
*descriptor_version
)
214 grub_efi_status_t status
;
215 grub_efi_boot_services_t
*b
;
216 grub_efi_uintn_t key
;
217 grub_efi_uint32_t version
;
219 if (grub_efi_is_finished
)
222 if (*memory_map_size
< finish_mmap_size
)
224 grub_memcpy (memory_map
, finish_mmap_buf
, *memory_map_size
);
229 grub_memcpy (memory_map
, finish_mmap_buf
, finish_mmap_size
);
232 *memory_map_size
= finish_mmap_size
;
234 *map_key
= finish_key
;
236 *descriptor_size
= finish_desc_size
;
237 if (descriptor_version
)
238 *descriptor_version
= finish_desc_version
;
242 /* Allow some parameters to be missing. */
245 if (! descriptor_version
)
246 descriptor_version
= &version
;
248 b
= grub_efi_system_table
->boot_services
;
249 status
= efi_call_5 (b
->get_memory_map
, memory_map_size
, memory_map
, map_key
,
250 descriptor_size
, descriptor_version
);
251 if (status
== GRUB_EFI_SUCCESS
)
253 else if (status
== GRUB_EFI_BUFFER_TOO_SMALL
)
259 /* Sort the memory map in place. */
261 sort_memory_map (grub_efi_memory_descriptor_t
*memory_map
,
262 grub_efi_uintn_t desc_size
,
263 grub_efi_memory_descriptor_t
*memory_map_end
)
265 grub_efi_memory_descriptor_t
*d1
;
266 grub_efi_memory_descriptor_t
*d2
;
268 for (d1
= memory_map
;
270 d1
= NEXT_MEMORY_DESCRIPTOR (d1
, desc_size
))
272 grub_efi_memory_descriptor_t
*max_desc
= d1
;
274 for (d2
= NEXT_MEMORY_DESCRIPTOR (d1
, desc_size
);
276 d2
= NEXT_MEMORY_DESCRIPTOR (d2
, desc_size
))
278 if (max_desc
->num_pages
< d2
->num_pages
)
284 grub_efi_memory_descriptor_t tmp
;
293 /* Filter the descriptors. GRUB needs only available memory. */
294 static grub_efi_memory_descriptor_t
*
295 filter_memory_map (grub_efi_memory_descriptor_t
*memory_map
,
296 grub_efi_memory_descriptor_t
*filtered_memory_map
,
297 grub_efi_uintn_t desc_size
,
298 grub_efi_memory_descriptor_t
*memory_map_end
)
300 grub_efi_memory_descriptor_t
*desc
;
301 grub_efi_memory_descriptor_t
*filtered_desc
;
303 for (desc
= memory_map
, filtered_desc
= filtered_memory_map
;
304 desc
< memory_map_end
;
305 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
))
307 if (desc
->type
== GRUB_EFI_CONVENTIONAL_MEMORY
309 && desc
->physical_start
<= 0xffffffff
311 && desc
->physical_start
+ PAGES_TO_BYTES (desc
->num_pages
) > 0x100000
312 && desc
->num_pages
!= 0)
314 grub_memcpy (filtered_desc
, desc
, desc_size
);
316 /* Avoid less than 1MB, because some loaders seem to be confused. */
317 if (desc
->physical_start
< 0x100000)
319 desc
->num_pages
-= BYTES_TO_PAGES (0x100000
320 - desc
->physical_start
);
321 desc
->physical_start
= 0x100000;
325 if (BYTES_TO_PAGES (filtered_desc
->physical_start
)
326 + filtered_desc
->num_pages
327 > BYTES_TO_PAGES (0x100000000LL
))
328 filtered_desc
->num_pages
329 = (BYTES_TO_PAGES (0x100000000LL
)
330 - BYTES_TO_PAGES (filtered_desc
->physical_start
));
333 if (filtered_desc
->num_pages
== 0)
336 filtered_desc
= NEXT_MEMORY_DESCRIPTOR (filtered_desc
, desc_size
);
340 return filtered_desc
;
343 /* Return the total number of pages. */
344 static grub_efi_uint64_t
345 get_total_pages (grub_efi_memory_descriptor_t
*memory_map
,
346 grub_efi_uintn_t desc_size
,
347 grub_efi_memory_descriptor_t
*memory_map_end
)
349 grub_efi_memory_descriptor_t
*desc
;
350 grub_efi_uint64_t total
= 0;
352 for (desc
= memory_map
;
353 desc
< memory_map_end
;
354 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
))
355 total
+= desc
->num_pages
;
360 /* Add memory regions. */
362 add_memory_regions (grub_efi_memory_descriptor_t
*memory_map
,
363 grub_efi_uintn_t desc_size
,
364 grub_efi_memory_descriptor_t
*memory_map_end
,
365 grub_efi_uint64_t required_pages
)
367 grub_efi_memory_descriptor_t
*desc
;
369 for (desc
= memory_map
;
370 desc
< memory_map_end
;
371 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
))
373 grub_efi_uint64_t pages
;
374 grub_efi_physical_address_t start
;
377 start
= desc
->physical_start
;
378 pages
= desc
->num_pages
;
379 if (pages
> required_pages
)
381 start
+= PAGES_TO_BYTES (pages
- required_pages
);
382 pages
= required_pages
;
385 addr
= grub_efi_allocate_pages (start
, pages
);
387 grub_fatal ("cannot allocate conventional memory %p with %u pages",
388 (void *) ((grub_addr_t
) start
),
391 grub_mm_init_region (addr
, PAGES_TO_BYTES (pages
));
393 required_pages
-= pages
;
394 if (required_pages
== 0)
398 if (required_pages
> 0)
399 grub_fatal ("too little memory");
403 /* Print the memory map. */
405 print_memory_map (grub_efi_memory_descriptor_t
*memory_map
,
406 grub_efi_uintn_t desc_size
,
407 grub_efi_memory_descriptor_t
*memory_map_end
)
409 grub_efi_memory_descriptor_t
*desc
;
412 for (desc
= memory_map
, i
= 0;
413 desc
< memory_map_end
;
414 desc
= NEXT_MEMORY_DESCRIPTOR (desc
, desc_size
), i
++)
416 grub_printf ("MD: t=%x, p=%llx, v=%llx, n=%llx, a=%llx\n",
417 desc
->type
, desc
->physical_start
, desc
->virtual_start
,
418 desc
->num_pages
, desc
->attribute
);
424 grub_efi_mm_init (void)
426 grub_efi_memory_descriptor_t
*memory_map
;
427 grub_efi_memory_descriptor_t
*memory_map_end
;
428 grub_efi_memory_descriptor_t
*filtered_memory_map
;
429 grub_efi_memory_descriptor_t
*filtered_memory_map_end
;
430 grub_efi_uintn_t map_size
;
431 grub_efi_uintn_t desc_size
;
432 grub_efi_uint64_t total_pages
;
433 grub_efi_uint64_t required_pages
;
436 /* Prepare a memory region to store two memory maps. */
437 memory_map
= grub_efi_allocate_pages (0,
438 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE
));
440 grub_fatal ("cannot allocate memory");
442 /* Obtain descriptors for available memory. */
443 map_size
= MEMORY_MAP_SIZE
;
445 mm_status
= grub_efi_get_memory_map (&map_size
, memory_map
, 0, &desc_size
, 0);
450 ((grub_efi_physical_address_t
) ((grub_addr_t
) memory_map
),
451 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE
));
453 /* Freeing/allocating operations may increase memory map size. */
454 map_size
+= desc_size
* 32;
456 memory_map
= grub_efi_allocate_pages (0, 2 * BYTES_TO_PAGES (map_size
));
458 grub_fatal ("cannot allocate memory");
460 mm_status
= grub_efi_get_memory_map (&map_size
, memory_map
, 0,
465 grub_fatal ("cannot get memory map");
467 memory_map_end
= NEXT_MEMORY_DESCRIPTOR (memory_map
, map_size
);
469 filtered_memory_map
= memory_map_end
;
471 filtered_memory_map_end
= filter_memory_map (memory_map
, filtered_memory_map
,
472 desc_size
, memory_map_end
);
474 /* By default, request a quarter of the available memory. */
475 total_pages
= get_total_pages (filtered_memory_map
, desc_size
,
476 filtered_memory_map_end
);
477 required_pages
= (total_pages
>> 2);
478 if (required_pages
< BYTES_TO_PAGES (MIN_HEAP_SIZE
))
479 required_pages
= BYTES_TO_PAGES (MIN_HEAP_SIZE
);
480 else if (required_pages
> BYTES_TO_PAGES (MAX_HEAP_SIZE
))
481 required_pages
= BYTES_TO_PAGES (MAX_HEAP_SIZE
);
483 /* Sort the filtered descriptors, so that GRUB can allocate pages
484 from smaller regions. */
485 sort_memory_map (filtered_memory_map
, desc_size
, filtered_memory_map_end
);
487 /* Allocate memory regions for GRUB's memory management. */
488 add_memory_regions (filtered_memory_map
, desc_size
,
489 filtered_memory_map_end
, required_pages
);
493 map_size
= MEMORY_MAP_SIZE
;
495 if (grub_efi_get_memory_map (&map_size
, memory_map
, 0, &desc_size
, 0) < 0)
496 grub_fatal ("cannot get memory map");
498 grub_printf ("printing memory map\n");
499 print_memory_map (memory_map
, desc_size
,
500 NEXT_MEMORY_DESCRIPTOR (memory_map
, map_size
));
504 /* Release the memory maps. */
505 grub_efi_free_pages ((grub_addr_t
) memory_map
,
506 2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE
));