1 /* Memory management for efiemu */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 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 To keep efiemu runtime contiguous this mm is special.
21 It uses deferred allocation.
22 In the first stage you may request memory with grub_efiemu_request_memalign
23 It will give you a handle with which in the second phase you can access your
24 memory with grub_efiemu_mm_obtain_request (handle). It's guaranteed that
25 subsequent calls with the same handle return the same result. You can't request any additional memory once you're in the second phase
29 #include <grub/normal.h>
31 #include <grub/misc.h>
32 #include <grub/efiemu/efiemu.h>
33 #include <grub/memory.h>
35 struct grub_efiemu_memrequest
37 struct grub_efiemu_memrequest
*next
;
38 grub_efi_memory_type_t type
;
40 grub_size_t align_overhead
;
44 /* Linked list of requested memory. */
45 static struct grub_efiemu_memrequest
*memrequests
= 0;
47 static grub_efi_memory_descriptor_t
*efiemu_mmap
= 0;
48 /* Pointer to allocated memory */
49 static void *resident_memory
= 0;
50 /* Size of requested memory per type */
51 static grub_size_t requested_memory
[GRUB_EFI_MAX_MEMORY_TYPE
];
52 /* How many slots is allocated for memory_map and how many are already used */
53 static int mmap_reserved_size
= 0, mmap_num
= 0;
55 /* Add a memory region to map*/
57 grub_efiemu_add_to_mmap (grub_uint64_t start
, grub_uint64_t size
,
58 grub_efi_memory_type_t type
)
60 grub_uint64_t page_start
, npages
;
62 /* Extend map if necessary*/
63 if (mmap_num
>= mmap_reserved_size
)
66 mmap_reserved_size
= 2 * (mmap_reserved_size
+ 1);
68 efiemu_mmap
= (grub_efi_memory_descriptor_t
*)
69 grub_realloc (efiemu_mmap
, mmap_reserved_size
70 * sizeof (grub_efi_memory_descriptor_t
));
79 page_start
= start
- (start
% GRUB_EFIEMU_PAGESIZE
);
80 npages
= (size
+ (start
% GRUB_EFIEMU_PAGESIZE
) + GRUB_EFIEMU_PAGESIZE
- 1)
81 / GRUB_EFIEMU_PAGESIZE
;
82 efiemu_mmap
[mmap_num
].physical_start
= page_start
;
83 efiemu_mmap
[mmap_num
].virtual_start
= page_start
;
84 efiemu_mmap
[mmap_num
].num_pages
= npages
;
85 efiemu_mmap
[mmap_num
].type
= type
;
91 /* Request a resident memory of type TYPE of size SIZE aligned at ALIGN
92 ALIGN must be a divisor of page size (if it's a divisor of 4096
93 it should be ok on all platforms)
96 grub_efiemu_request_memalign (grub_size_t align
, grub_size_t size
,
97 grub_efi_memory_type_t type
)
99 grub_size_t align_overhead
;
100 struct grub_efiemu_memrequest
*ret
, *cur
, *prev
;
101 /* Check that the request is correct */
102 if (type
<= GRUB_EFI_LOADER_CODE
|| type
== GRUB_EFI_PERSISTENT_MEMORY
||
103 type
>= GRUB_EFI_MAX_MEMORY_TYPE
)
106 /* Add new size to requested size */
107 align_overhead
= align
- (requested_memory
[type
]%align
);
108 if (align_overhead
== align
)
110 requested_memory
[type
] += align_overhead
+ size
;
112 /* Remember the request */
113 ret
= grub_zalloc (sizeof (*ret
));
118 ret
->align_overhead
= align_overhead
;
121 /* Add request to the end of the chain.
122 It should be at the end because otherwise alignment isn't guaranteed */
123 for (cur
= memrequests
; cur
; prev
= cur
, cur
= cur
->next
);
126 ret
->handle
= prev
->handle
+ 1;
131 ret
->handle
= 1; /* Avoid 0 handle*/
137 /* Really allocate the memory */
139 efiemu_alloc_requests (void)
141 grub_size_t align_overhead
= 0;
142 grub_uint8_t
*curptr
, *typestart
;
143 struct grub_efiemu_memrequest
*cur
;
144 grub_size_t total_alloc
= 0;
146 /* Order of memory regions */
147 grub_efi_memory_type_t reqorder
[] =
149 /* First come regions usable by OS*/
150 GRUB_EFI_LOADER_CODE
,
151 GRUB_EFI_LOADER_DATA
,
152 GRUB_EFI_BOOT_SERVICES_CODE
,
153 GRUB_EFI_BOOT_SERVICES_DATA
,
154 GRUB_EFI_CONVENTIONAL_MEMORY
,
155 GRUB_EFI_ACPI_RECLAIM_MEMORY
,
157 /* Then memory used by runtime */
158 /* This way all our regions are in a single block */
159 GRUB_EFI_RUNTIME_SERVICES_CODE
,
160 GRUB_EFI_RUNTIME_SERVICES_DATA
,
161 GRUB_EFI_ACPI_MEMORY_NVS
,
163 /* And then unavailable memory types. This is more for a completeness.
164 You should double think before allocating memory of any of these types
166 GRUB_EFI_UNUSABLE_MEMORY
,
167 GRUB_EFI_MEMORY_MAPPED_IO
,
168 GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE
,
172 * These are not allocatable:
173 * GRUB_EFI_RESERVED_MEMORY_TYPE
174 * GRUB_EFI_PERSISTENT_MEMORY
175 * >= GRUB_EFI_MAX_MEMORY_TYPE
179 /* Compute total memory needed */
180 for (i
= 0; i
< sizeof (reqorder
) / sizeof (reqorder
[0]); i
++)
182 align_overhead
= GRUB_EFIEMU_PAGESIZE
183 - (requested_memory
[reqorder
[i
]] % GRUB_EFIEMU_PAGESIZE
);
184 if (align_overhead
== GRUB_EFIEMU_PAGESIZE
)
186 total_alloc
+= requested_memory
[reqorder
[i
]] + align_overhead
;
189 /* Allocate the whole memory in one block */
190 resident_memory
= grub_memalign (GRUB_EFIEMU_PAGESIZE
, total_alloc
);
191 if (!resident_memory
)
194 /* Split the memory into blocks by type */
195 curptr
= resident_memory
;
196 for (i
= 0; i
< sizeof (reqorder
) / sizeof (reqorder
[0]); i
++)
198 if (!requested_memory
[reqorder
[i
]])
202 /* Write pointers to requests */
203 for (cur
= memrequests
; cur
; cur
= cur
->next
)
204 if (cur
->type
== reqorder
[i
])
206 curptr
= ((grub_uint8_t
*)curptr
) + cur
->align_overhead
;
208 curptr
= ((grub_uint8_t
*)curptr
) + cur
->size
;
211 /* Ensure that the regions are page-aligned */
212 align_overhead
= GRUB_EFIEMU_PAGESIZE
213 - (requested_memory
[reqorder
[i
]] % GRUB_EFIEMU_PAGESIZE
);
214 if (align_overhead
== GRUB_EFIEMU_PAGESIZE
)
216 curptr
= ((grub_uint8_t
*) curptr
) + align_overhead
;
218 /* Add the region to memory map */
219 grub_efiemu_add_to_mmap ((grub_addr_t
) typestart
,
220 curptr
- typestart
, reqorder
[i
]);
223 return GRUB_ERR_NONE
;
226 /* Get a pointer to requested memory from handle */
228 grub_efiemu_mm_obtain_request (int handle
)
230 struct grub_efiemu_memrequest
*cur
;
231 for (cur
= memrequests
; cur
; cur
= cur
->next
)
232 if (cur
->handle
== handle
)
237 /* Get type of requested memory by handle */
238 grub_efi_memory_type_t
239 grub_efiemu_mm_get_type (int handle
)
241 struct grub_efiemu_memrequest
*cur
;
242 for (cur
= memrequests
; cur
; cur
= cur
->next
)
243 if (cur
->handle
== handle
)
250 grub_efiemu_mm_return_request (int handle
)
252 struct grub_efiemu_memrequest
*cur
, *prev
;
254 /* Remove head if necessary */
255 while (memrequests
&& memrequests
->handle
== handle
)
257 cur
= memrequests
->next
;
258 grub_free (memrequests
);
264 /* Remove request from a middle of chain*/
265 for (prev
= memrequests
, cur
= prev
->next
; cur
;)
266 if (cur
->handle
== handle
)
268 prev
->next
= cur
->next
;
279 /* Helper for grub_efiemu_mmap_init. */
281 bounds_hook (grub_uint64_t addr
__attribute__ ((unused
)),
282 grub_uint64_t size
__attribute__ ((unused
)),
283 grub_memory_type_t type
__attribute__ ((unused
)),
284 void *data
__attribute__ ((unused
)))
286 mmap_reserved_size
++;
290 /* Reserve space for memory map */
292 grub_efiemu_mmap_init (void)
294 // the place for memory used by efiemu itself
295 mmap_reserved_size
= GRUB_EFI_MAX_MEMORY_TYPE
+ 1;
297 #ifndef GRUB_MACHINE_EMU
298 grub_machine_mmap_iterate (bounds_hook
, NULL
);
301 return GRUB_ERR_NONE
;
304 /* This is a drop-in replacement of grub_efi_get_memory_map */
305 /* Get the memory map as defined in the EFI spec. Return 1 if successful,
306 return 0 if partial, or return -1 if an error occurs. */
308 grub_efiemu_get_memory_map (grub_efi_uintn_t
*memory_map_size
,
309 grub_efi_memory_descriptor_t
*memory_map
,
310 grub_efi_uintn_t
*map_key
,
311 grub_efi_uintn_t
*descriptor_size
,
312 grub_efi_uint32_t
*descriptor_version
)
316 grub_error (GRUB_ERR_INVALID_COMMAND
,
317 "you need to first launch efiemu_prepare");
321 if (*memory_map_size
< mmap_num
* sizeof (grub_efi_memory_descriptor_t
))
323 *memory_map_size
= mmap_num
* sizeof (grub_efi_memory_descriptor_t
);
327 *memory_map_size
= mmap_num
* sizeof (grub_efi_memory_descriptor_t
);
328 grub_memcpy (memory_map
, efiemu_mmap
, *memory_map_size
);
330 *descriptor_size
= sizeof (grub_efi_memory_descriptor_t
);
331 if (descriptor_version
)
332 *descriptor_version
= 1;
340 grub_efiemu_finish_boot_services (grub_efi_uintn_t
*memory_map_size
,
341 grub_efi_memory_descriptor_t
*memory_map
,
342 grub_efi_uintn_t
*map_key
,
343 grub_efi_uintn_t
*descriptor_size
,
344 grub_efi_uint32_t
*descriptor_version
)
346 int val
= grub_efiemu_get_memory_map (memory_map_size
,
351 return GRUB_ERR_NONE
;
354 return grub_error (GRUB_ERR_IO
, "memory map buffer is too small");
358 /* Free everything */
360 grub_efiemu_mm_unload (void)
362 struct grub_efiemu_memrequest
*cur
, *d
;
363 for (cur
= memrequests
; cur
;)
370 grub_memset (&requested_memory
, 0, sizeof (requested_memory
));
371 grub_free (resident_memory
);
373 grub_free (efiemu_mmap
);
375 mmap_reserved_size
= mmap_num
= 0;
376 return GRUB_ERR_NONE
;
379 /* This function should be called before doing any requests */
381 grub_efiemu_mm_init (void)
385 err
= grub_efiemu_mm_unload ();
389 grub_efiemu_mmap_init ();
391 return GRUB_ERR_NONE
;
394 /* Helper for grub_efiemu_mmap_fill. */
396 fill_hook (grub_uint64_t addr
, grub_uint64_t size
, grub_memory_type_t type
,
397 void *data
__attribute__ ((unused
)))
401 case GRUB_MEMORY_AVAILABLE
:
402 return grub_efiemu_add_to_mmap (addr
, size
,
403 GRUB_EFI_CONVENTIONAL_MEMORY
);
405 case GRUB_MEMORY_ACPI
:
406 return grub_efiemu_add_to_mmap (addr
, size
,
407 GRUB_EFI_ACPI_RECLAIM_MEMORY
);
409 case GRUB_MEMORY_NVS
:
410 return grub_efiemu_add_to_mmap (addr
, size
,
411 GRUB_EFI_ACPI_MEMORY_NVS
);
413 case GRUB_MEMORY_PERSISTENT
:
414 case GRUB_MEMORY_PERSISTENT_LEGACY
:
415 return grub_efiemu_add_to_mmap (addr
, size
,
416 GRUB_EFI_PERSISTENT_MEMORY
);
418 grub_dprintf ("efiemu",
419 "Unknown memory type %d. Assuming unusable\n", type
);
421 case GRUB_MEMORY_RESERVED
:
422 return grub_efiemu_add_to_mmap (addr
, size
,
423 GRUB_EFI_UNUSABLE_MEMORY
);
427 /* Copy host memory map */
429 grub_efiemu_mmap_fill (void)
431 #ifndef GRUB_MACHINE_EMU
432 grub_machine_mmap_iterate (fill_hook
, NULL
);
435 return GRUB_ERR_NONE
;
439 grub_efiemu_mmap_iterate (grub_memory_hook_t hook
, void *hook_data
)
443 for (i
= 0; i
< (unsigned) mmap_num
; i
++)
444 switch (efiemu_mmap
[i
].type
)
446 case GRUB_EFI_RUNTIME_SERVICES_CODE
:
447 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
448 GRUB_MEMORY_CODE
, hook_data
);
451 case GRUB_EFI_UNUSABLE_MEMORY
:
452 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
453 GRUB_MEMORY_BADRAM
, hook_data
);
456 case GRUB_EFI_RESERVED_MEMORY_TYPE
:
457 case GRUB_EFI_RUNTIME_SERVICES_DATA
:
458 case GRUB_EFI_MEMORY_MAPPED_IO
:
459 case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE
:
460 case GRUB_EFI_PAL_CODE
:
462 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
463 GRUB_MEMORY_RESERVED
, hook_data
);
466 case GRUB_EFI_LOADER_CODE
:
467 case GRUB_EFI_LOADER_DATA
:
468 case GRUB_EFI_BOOT_SERVICES_CODE
:
469 case GRUB_EFI_BOOT_SERVICES_DATA
:
470 case GRUB_EFI_CONVENTIONAL_MEMORY
:
471 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
472 GRUB_MEMORY_AVAILABLE
, hook_data
);
475 case GRUB_EFI_ACPI_RECLAIM_MEMORY
:
476 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
477 GRUB_MEMORY_ACPI
, hook_data
);
480 case GRUB_EFI_ACPI_MEMORY_NVS
:
481 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
482 GRUB_MEMORY_NVS
, hook_data
);
485 case GRUB_EFI_PERSISTENT_MEMORY
:
486 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
487 GRUB_MEMORY_PERSISTENT
, hook_data
);
496 /* This function resolves overlapping regions and sorts the memory map
497 It uses scanline (sweeping) algorithm
500 grub_efiemu_mmap_sort_and_uniq (void)
502 /* If same page is used by multiple types it's resolved
503 according to priority
505 1 - memory immediately usable after ExitBootServices
506 2 - memory usable after loading ACPI tables
510 int priority
[GRUB_EFI_MAX_MEMORY_TYPE
] =
512 [GRUB_EFI_RESERVED_MEMORY_TYPE
] = 4,
513 [GRUB_EFI_LOADER_CODE
] = 1,
514 [GRUB_EFI_LOADER_DATA
] = 1,
515 [GRUB_EFI_BOOT_SERVICES_CODE
] = 1,
516 [GRUB_EFI_BOOT_SERVICES_DATA
] = 1,
517 [GRUB_EFI_RUNTIME_SERVICES_CODE
] = 3,
518 [GRUB_EFI_RUNTIME_SERVICES_DATA
] = 3,
519 [GRUB_EFI_CONVENTIONAL_MEMORY
] = 0,
520 [GRUB_EFI_UNUSABLE_MEMORY
] = 4,
521 [GRUB_EFI_ACPI_RECLAIM_MEMORY
] = 2,
522 [GRUB_EFI_ACPI_MEMORY_NVS
] = 3,
523 [GRUB_EFI_MEMORY_MAPPED_IO
] = 4,
524 [GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE
] = 4,
525 [GRUB_EFI_PAL_CODE
] = 4,
526 [GRUB_EFI_PERSISTENT_MEMORY
] = 4
531 /* Scanline events */
532 struct grub_efiemu_mmap_scan
534 /* At which memory address*/
536 /* 0 = region starts, 1 = region ends */
538 /* Which type of memory region */
539 grub_efi_memory_type_t memtype
;
541 struct grub_efiemu_mmap_scan
*scanline_events
;
542 struct grub_efiemu_mmap_scan t
;
544 /* Previous scanline event */
545 grub_uint64_t lastaddr
;
547 /* Current scanline event */
549 /* how many regions of given type overlap at current location */
550 int present
[GRUB_EFI_MAX_MEMORY_TYPE
];
551 /* Here is stored the resulting memory map*/
552 grub_efi_memory_descriptor_t
*result
;
554 /* Initialize variables*/
555 grub_memset (present
, 0, sizeof (int) * GRUB_EFI_MAX_MEMORY_TYPE
);
556 scanline_events
= (struct grub_efiemu_mmap_scan
*)
557 grub_malloc (sizeof (struct grub_efiemu_mmap_scan
) * 2 * mmap_num
);
559 /* Number of chunks can't increase more than by factor of 2 */
560 result
= (grub_efi_memory_descriptor_t
*)
561 grub_malloc (sizeof (grub_efi_memory_descriptor_t
) * 2 * mmap_num
);
562 if (!result
|| !scanline_events
)
565 grub_free (scanline_events
);
569 /* Register scanline events */
570 for (i
= 0; i
< mmap_num
; i
++)
572 scanline_events
[2 * i
].pos
= efiemu_mmap
[i
].physical_start
;
573 scanline_events
[2 * i
].type
= 0;
574 scanline_events
[2 * i
].memtype
= efiemu_mmap
[i
].type
;
575 scanline_events
[2 * i
+ 1].pos
= efiemu_mmap
[i
].physical_start
576 + efiemu_mmap
[i
].num_pages
* GRUB_EFIEMU_PAGESIZE
;
577 scanline_events
[2 * i
+ 1].type
= 1;
578 scanline_events
[2 * i
+ 1].memtype
= efiemu_mmap
[i
].type
;
581 /* Primitive bubble sort. It has complexity O(n^2) but since we're
582 unlikely to have more than 100 chunks it's probably one of the
583 fastest for one purpose */
588 for (i
= 0; i
< 2 * mmap_num
- 1; i
++)
589 if (scanline_events
[i
+ 1].pos
< scanline_events
[i
].pos
)
591 t
= scanline_events
[i
+ 1];
592 scanline_events
[i
+ 1] = scanline_events
[i
];
593 scanline_events
[i
] = t
;
598 /* Pointer in resulting memory map */
600 lastaddr
= scanline_events
[0].pos
;
601 lasttype
= scanline_events
[0].memtype
;
602 for (i
= 0; i
< 2 * mmap_num
; i
++)
605 if (scanline_events
[i
].type
)
606 present
[scanline_events
[i
].memtype
]--;
608 present
[scanline_events
[i
].memtype
]++;
610 /* Determine current region type */
612 for (k
= 0; k
< GRUB_EFI_MAX_MEMORY_TYPE
; k
++)
613 if (present
[k
] && (curtype
== -1 || priority
[k
] > priority
[curtype
]))
616 /* Add memory region to resulting map if necessary */
617 if ((curtype
== -1 || curtype
!= lasttype
)
618 && lastaddr
!= scanline_events
[i
].pos
621 result
[j
].virtual_start
= result
[j
].physical_start
= lastaddr
;
622 result
[j
].num_pages
= (scanline_events
[i
].pos
- lastaddr
)
623 / GRUB_EFIEMU_PAGESIZE
;
624 result
[j
].type
= lasttype
;
626 /* We set runtime attribute on pages we need to be mapped */
628 = (lasttype
== GRUB_EFI_RUNTIME_SERVICES_CODE
629 || lasttype
== GRUB_EFI_RUNTIME_SERVICES_DATA
)
630 ? GRUB_EFI_MEMORY_RUNTIME
: 0;
631 grub_dprintf ("efiemu",
632 "mmap entry: type %d start 0x%llx 0x%llx pages\n",
634 result
[j
].physical_start
, result
[j
].num_pages
);
638 /* Update last values if necessary */
639 if (curtype
== -1 || curtype
!= lasttype
)
642 lastaddr
= scanline_events
[i
].pos
;
646 grub_free (scanline_events
);
648 /* Shrink resulting memory map to really used size and replace efiemu_mmap
650 grub_free (efiemu_mmap
);
651 efiemu_mmap
= grub_realloc (result
, j
* sizeof (*result
));
652 return GRUB_ERR_NONE
;
655 /* This function is called to switch from first to second phase */
657 grub_efiemu_mm_do_alloc (void)
661 /* Preallocate mmap */
662 efiemu_mmap
= (grub_efi_memory_descriptor_t
*)
663 grub_malloc (mmap_reserved_size
* sizeof (grub_efi_memory_descriptor_t
));
666 grub_efiemu_unload ();
670 err
= efiemu_alloc_requests ();
673 err
= grub_efiemu_mmap_fill ();
676 return grub_efiemu_mmap_sort_and_uniq ();