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/machine/memory.h>
33 #include <grub/efiemu/efiemu.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
)
65 efiemu_mmap
= (grub_efi_memory_descriptor_t
*)
66 grub_realloc (efiemu_mmap
, (++mmap_reserved_size
)
67 * sizeof (grub_efi_memory_descriptor_t
));
69 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
70 "Not enough space for memory map");
74 page_start
= start
- (start
% GRUB_EFIEMU_PAGESIZE
);
75 npages
= (size
+ (start
% GRUB_EFIEMU_PAGESIZE
) + GRUB_EFIEMU_PAGESIZE
- 1)
76 / GRUB_EFIEMU_PAGESIZE
;
77 efiemu_mmap
[mmap_num
].physical_start
= page_start
;
78 efiemu_mmap
[mmap_num
].virtual_start
= page_start
;
79 efiemu_mmap
[mmap_num
].num_pages
= npages
;
80 efiemu_mmap
[mmap_num
].type
= type
;
86 /* Request a resident memory of type TYPE of size SIZE aligned at ALIGN
87 ALIGN must be a divisor of page size (if it's a divisor of 4096
88 it should be ok on all platforms)
91 grub_efiemu_request_memalign (grub_size_t align
, grub_size_t size
,
92 grub_efi_memory_type_t type
)
94 grub_size_t align_overhead
;
95 struct grub_efiemu_memrequest
*ret
, *cur
, *prev
;
96 /* Check that the request is correct */
97 if (type
>= GRUB_EFI_MAX_MEMORY_TYPE
|| type
<= GRUB_EFI_LOADER_CODE
)
100 /* Add new size to requested size */
101 align_overhead
= align
- (requested_memory
[type
]%align
);
102 if (align_overhead
== align
)
104 requested_memory
[type
] += align_overhead
+ size
;
106 /* Remember the request */
107 ret
= grub_malloc (sizeof (*ret
));
112 ret
->align_overhead
= align_overhead
;
117 /* Add request to the end of the chain.
118 It should be at the end because otherwise alignment isn't guaranteed */
119 for (cur
= memrequests
; cur
; prev
= cur
, cur
= cur
->next
);
122 ret
->handle
= prev
->handle
+ 1;
127 ret
->handle
= 1; /* Avoid 0 handle*/
133 /* Really allocate the memory */
135 efiemu_alloc_requests (void)
137 grub_size_t align_overhead
= 0;
138 grub_uint8_t
*curptr
, *typestart
;
139 struct grub_efiemu_memrequest
*cur
;
140 grub_size_t total_alloc
= 0;
142 /* Order of memory regions */
143 grub_efi_memory_type_t reqorder
[] =
145 /* First come regions usable by OS*/
146 GRUB_EFI_LOADER_CODE
,
147 GRUB_EFI_LOADER_DATA
,
148 GRUB_EFI_BOOT_SERVICES_CODE
,
149 GRUB_EFI_BOOT_SERVICES_DATA
,
150 GRUB_EFI_CONVENTIONAL_MEMORY
,
151 GRUB_EFI_ACPI_RECLAIM_MEMORY
,
153 /* Then memory used by runtime */
154 /* This way all our regions are in a single block */
155 GRUB_EFI_RUNTIME_SERVICES_CODE
,
156 GRUB_EFI_RUNTIME_SERVICES_DATA
,
157 GRUB_EFI_ACPI_MEMORY_NVS
,
159 /* And then unavailable memory types. This is more for a completeness.
160 You should double think before allocating memory of any of these types
162 GRUB_EFI_UNUSABLE_MEMORY
,
163 GRUB_EFI_MEMORY_MAPPED_IO
,
164 GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE
,
168 /* Compute total memory needed */
169 for (i
= 0; i
< sizeof (reqorder
) / sizeof (reqorder
[0]); i
++)
171 align_overhead
= GRUB_EFIEMU_PAGESIZE
172 - (requested_memory
[reqorder
[i
]] % GRUB_EFIEMU_PAGESIZE
);
173 if (align_overhead
== GRUB_EFIEMU_PAGESIZE
)
175 total_alloc
+= requested_memory
[reqorder
[i
]] + align_overhead
;
178 /* Allocate the whole memory in one block */
179 resident_memory
= grub_memalign (GRUB_EFIEMU_PAGESIZE
, total_alloc
);
180 if (!resident_memory
)
181 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
182 "couldn't allocate resident memory");
184 /* Split the memory into blocks by type */
185 curptr
= resident_memory
;
186 for (i
= 0; i
< sizeof (reqorder
) / sizeof (reqorder
[0]); i
++)
188 if (!requested_memory
[reqorder
[i
]])
192 /* Write pointers to requests */
193 for (cur
= memrequests
; cur
; cur
= cur
->next
)
194 if (cur
->type
== reqorder
[i
])
196 curptr
= ((grub_uint8_t
*)curptr
) + cur
->align_overhead
;
198 curptr
= ((grub_uint8_t
*)curptr
) + cur
->size
;
201 /* Ensure that the regions are page-aligned */
202 align_overhead
= GRUB_EFIEMU_PAGESIZE
203 - (requested_memory
[reqorder
[i
]] % GRUB_EFIEMU_PAGESIZE
);
204 if (align_overhead
== GRUB_EFIEMU_PAGESIZE
)
206 curptr
= ((grub_uint8_t
*)curptr
) + align_overhead
;
208 /* Add the region to memory map */
209 grub_efiemu_add_to_mmap (PTR_TO_UINT64 (typestart
),
210 curptr
- typestart
, reqorder
[i
]);
213 return GRUB_ERR_NONE
;
216 /* Get a pointer to requested memory from handle */
218 grub_efiemu_mm_obtain_request (int handle
)
220 struct grub_efiemu_memrequest
*cur
;
221 for (cur
= memrequests
; cur
; cur
= cur
->next
)
222 if (cur
->handle
== handle
)
227 /* Get type of requested memory by handle */
228 grub_efi_memory_type_t
229 grub_efiemu_mm_get_type (int handle
)
231 struct grub_efiemu_memrequest
*cur
;
232 for (cur
= memrequests
; cur
; cur
= cur
->next
)
233 if (cur
->handle
== handle
)
240 grub_efiemu_mm_return_request (int handle
)
242 struct grub_efiemu_memrequest
*cur
, *prev
;
244 /* Remove head if necessary */
245 while (memrequests
&& memrequests
->handle
== handle
)
247 cur
= memrequests
->next
;
248 grub_free (memrequests
);
254 /* Remove request from a middle of chain*/
255 for (prev
= memrequests
, cur
= prev
->next
; cur
;)
256 if (cur
->handle
== handle
)
258 prev
->next
= cur
->next
;
269 /* Reserve space for memory map */
271 grub_efiemu_mmap_init (void)
273 auto int NESTED_FUNC_ATTR
bounds_hook (grub_uint64_t
, grub_uint64_t
,
275 int NESTED_FUNC_ATTR
bounds_hook (grub_uint64_t addr
__attribute__ ((unused
)),
276 grub_uint64_t size
__attribute__ ((unused
)),
277 grub_uint32_t type
__attribute__ ((unused
)))
279 mmap_reserved_size
++;
283 // the place for memory used by efiemu itself
284 mmap_reserved_size
= GRUB_EFI_MAX_MEMORY_TYPE
+ 1;
287 grub_machine_mmap_iterate (bounds_hook
);
290 return GRUB_ERR_NONE
;
293 /* This is a drop-in replacement of grub_efi_get_memory_map */
294 /* Get the memory map as defined in the EFI spec. Return 1 if successful,
295 return 0 if partial, or return -1 if an error occurs. */
297 grub_efiemu_get_memory_map (grub_efi_uintn_t
*memory_map_size
,
298 grub_efi_memory_descriptor_t
*memory_map
,
299 grub_efi_uintn_t
*map_key
,
300 grub_efi_uintn_t
*descriptor_size
,
301 grub_efi_uint32_t
*descriptor_version
)
305 grub_error (GRUB_ERR_INVALID_COMMAND
,
306 "you need to first launch efiemu_prepare");
310 if (*memory_map_size
< mmap_num
* sizeof (grub_efi_memory_descriptor_t
))
312 *memory_map_size
= mmap_num
* sizeof (grub_efi_memory_descriptor_t
);
316 *memory_map_size
= mmap_num
* sizeof (grub_efi_memory_descriptor_t
);
317 grub_memcpy (memory_map
, efiemu_mmap
, *memory_map_size
);
319 *descriptor_size
= sizeof (grub_efi_memory_descriptor_t
);
320 if (descriptor_version
)
321 *descriptor_version
= 1;
328 /* Free everything */
330 grub_efiemu_mm_unload (void)
332 struct grub_efiemu_memrequest
*cur
, *d
;
333 for (cur
= memrequests
; cur
;)
340 grub_memset (&requested_memory
, 0, sizeof (requested_memory
));
341 grub_free (resident_memory
);
343 grub_free (efiemu_mmap
);
345 mmap_reserved_size
= mmap_num
= 0;
346 return GRUB_ERR_NONE
;
349 /* This function should be called before doing any requests */
351 grub_efiemu_mm_init (void)
355 err
= grub_efiemu_mm_unload ();
359 grub_efiemu_mmap_init ();
361 return GRUB_ERR_NONE
;
364 /* Copy host memory map */
366 grub_efiemu_mmap_fill (void)
368 auto int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t
, grub_uint64_t
, grub_uint32_t
);
369 int NESTED_FUNC_ATTR
fill_hook (grub_uint64_t addr
,
375 case GRUB_MACHINE_MEMORY_AVAILABLE
:
376 return grub_efiemu_add_to_mmap (addr
, size
,
377 GRUB_EFI_CONVENTIONAL_MEMORY
);
379 #ifdef GRUB_MACHINE_MEMORY_ACPI
380 case GRUB_MACHINE_MEMORY_ACPI
:
381 return grub_efiemu_add_to_mmap (addr
, size
,
382 GRUB_EFI_ACPI_RECLAIM_MEMORY
);
385 #ifdef GRUB_MACHINE_MEMORY_NVS
386 case GRUB_MACHINE_MEMORY_NVS
:
387 return grub_efiemu_add_to_mmap (addr
, size
,
388 GRUB_EFI_ACPI_MEMORY_NVS
);
392 grub_printf ("Unknown memory type %d. Marking as unusable\n", type
);
393 case GRUB_MACHINE_MEMORY_RESERVED
:
394 return grub_efiemu_add_to_mmap (addr
, size
,
395 GRUB_EFI_UNUSABLE_MEMORY
);
400 grub_machine_mmap_iterate (fill_hook
);
403 return GRUB_ERR_NONE
;
407 grub_efiemu_mmap_iterate (int NESTED_FUNC_ATTR (*hook
) (grub_uint64_t
,
413 for (i
= 0; i
< (unsigned) mmap_num
; i
++)
414 switch (efiemu_mmap
[i
].type
)
416 case GRUB_EFI_RUNTIME_SERVICES_CODE
:
417 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
418 GRUB_EFIEMU_MEMORY_CODE
);
421 case GRUB_EFI_RESERVED_MEMORY_TYPE
:
422 case GRUB_EFI_RUNTIME_SERVICES_DATA
:
423 case GRUB_EFI_UNUSABLE_MEMORY
:
424 case GRUB_EFI_MEMORY_MAPPED_IO
:
425 case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE
:
426 case GRUB_EFI_PAL_CODE
:
427 case GRUB_EFI_MAX_MEMORY_TYPE
:
428 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
429 GRUB_EFIEMU_MEMORY_RESERVED
);
432 case GRUB_EFI_LOADER_CODE
:
433 case GRUB_EFI_LOADER_DATA
:
434 case GRUB_EFI_BOOT_SERVICES_CODE
:
435 case GRUB_EFI_BOOT_SERVICES_DATA
:
436 case GRUB_EFI_CONVENTIONAL_MEMORY
:
437 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
438 GRUB_EFIEMU_MEMORY_AVAILABLE
);
441 case GRUB_EFI_ACPI_RECLAIM_MEMORY
:
442 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
443 GRUB_EFIEMU_MEMORY_ACPI
);
446 case GRUB_EFI_ACPI_MEMORY_NVS
:
447 hook (efiemu_mmap
[i
].physical_start
, efiemu_mmap
[i
].num_pages
* 4096,
448 GRUB_EFIEMU_MEMORY_NVS
);
456 /* This function resolves overlapping regions and sorts the memory map
457 It uses scanline (sweeping) algorithm
460 grub_efiemu_mmap_sort_and_uniq (void)
462 /* If same page is used by multiple types it's resolved
463 according to priority
465 1 - memory immediately usable after ExitBootServices
466 2 - memory usable after loading ACPI tables
470 int priority
[GRUB_EFI_MAX_MEMORY_TYPE
] =
472 [GRUB_EFI_RESERVED_MEMORY_TYPE
] = 4,
473 [GRUB_EFI_LOADER_CODE
] = 1,
474 [GRUB_EFI_LOADER_DATA
] = 1,
475 [GRUB_EFI_BOOT_SERVICES_CODE
] = 1,
476 [GRUB_EFI_BOOT_SERVICES_DATA
] = 1,
477 [GRUB_EFI_RUNTIME_SERVICES_CODE
] = 3,
478 [GRUB_EFI_RUNTIME_SERVICES_DATA
] = 3,
479 [GRUB_EFI_CONVENTIONAL_MEMORY
] = 0,
480 [GRUB_EFI_UNUSABLE_MEMORY
] = 4,
481 [GRUB_EFI_ACPI_RECLAIM_MEMORY
] = 2,
482 [GRUB_EFI_ACPI_MEMORY_NVS
] = 3,
483 [GRUB_EFI_MEMORY_MAPPED_IO
] = 4,
484 [GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE
] = 4,
485 [GRUB_EFI_PAL_CODE
] = 4
490 /* Scanline events */
491 struct grub_efiemu_mmap_scan
493 /* At which memory address*/
495 /* 0 = region starts, 1 = region ends */
497 /* Which type of memory region */
498 grub_efi_memory_type_t memtype
;
500 struct grub_efiemu_mmap_scan
*scanline_events
;
501 struct grub_efiemu_mmap_scan t
;
503 /* Previous scanline event */
504 grub_uint64_t lastaddr
;
506 /* Current scanline event */
508 /* how many regions of given type overlap at current location */
509 int present
[GRUB_EFI_MAX_MEMORY_TYPE
];
510 /* Here is stored the resulting memory map*/
511 grub_efi_memory_descriptor_t
*result
;
513 /* Initialize variables*/
514 grub_memset (present
, 0, sizeof (int) * GRUB_EFI_MAX_MEMORY_TYPE
);
515 scanline_events
= (struct grub_efiemu_mmap_scan
*)
516 grub_malloc (sizeof (struct grub_efiemu_mmap_scan
) * 2 * mmap_num
);
518 /* Number of chunks can't increase more than by factor of 2 */
519 result
= (grub_efi_memory_descriptor_t
*)
520 grub_malloc (sizeof (grub_efi_memory_descriptor_t
) * 2 * mmap_num
);
521 if (!result
|| !scanline_events
)
524 grub_free (scanline_events
);
525 return grub_error (GRUB_ERR_OUT_OF_MEMORY
,
526 "couldn't allocate space for new memory map");
529 /* Register scanline events */
530 for (i
= 0; i
< mmap_num
; i
++)
532 scanline_events
[2 * i
].pos
= efiemu_mmap
[i
].physical_start
;
533 scanline_events
[2 * i
].type
= 0;
534 scanline_events
[2 * i
].memtype
= efiemu_mmap
[i
].type
;
535 scanline_events
[2 * i
+ 1].pos
= efiemu_mmap
[i
].physical_start
536 + efiemu_mmap
[i
].num_pages
* GRUB_EFIEMU_PAGESIZE
;
537 scanline_events
[2 * i
+ 1].type
= 1;
538 scanline_events
[2 * i
+ 1].memtype
= efiemu_mmap
[i
].type
;
541 /* Primitive bubble sort. It has complexity O(n^2) but since we're
542 unlikely to have more than 100 chunks it's probably one of the
543 fastest for one purpose */
548 for (i
= 0; i
< 2 * mmap_num
- 1; i
++)
549 if (scanline_events
[i
+ 1].pos
< scanline_events
[i
].pos
)
551 t
= scanline_events
[i
+ 1];
552 scanline_events
[i
+ 1] = scanline_events
[i
];
553 scanline_events
[i
] = t
;
558 /* Pointer in resulting memory map */
560 lastaddr
= scanline_events
[0].pos
;
561 lasttype
= scanline_events
[0].memtype
;
562 for (i
= 0; i
< 2 * mmap_num
; i
++)
565 if (scanline_events
[i
].type
)
566 present
[scanline_events
[i
].memtype
]--;
568 present
[scanline_events
[i
].memtype
]++;
570 /* Determine current region type */
572 for (k
= 0; k
< GRUB_EFI_MAX_MEMORY_TYPE
; k
++)
573 if (present
[k
] && (curtype
== -1 || priority
[k
] > priority
[curtype
]))
576 /* Add memory region to resulting map if necessary */
577 if ((curtype
== -1 || curtype
!= lasttype
)
578 && lastaddr
!= scanline_events
[i
].pos
581 result
[j
].virtual_start
= result
[j
].physical_start
= lastaddr
;
582 result
[j
].num_pages
= (scanline_events
[i
].pos
- lastaddr
)
583 / GRUB_EFIEMU_PAGESIZE
;
584 result
[j
].type
= lasttype
;
586 /* We set runtime attribute on pages we need to be mapped */
588 = (lasttype
== GRUB_EFI_RUNTIME_SERVICES_CODE
589 || lasttype
== GRUB_EFI_RUNTIME_SERVICES_DATA
)
590 ? GRUB_EFI_MEMORY_RUNTIME
: 0;
591 grub_dprintf ("efiemu",
592 "mmap entry: type %d start 0x%llx 0x%llx pages\n",
594 result
[j
].physical_start
, result
[j
].num_pages
);
598 /* Update last values if necessary */
599 if (curtype
== -1 || curtype
!= lasttype
)
602 lastaddr
= scanline_events
[i
].pos
;
606 grub_free (scanline_events
);
608 /* Shrink resulting memory map to really used size and replace efiemu_mmap
610 grub_free (efiemu_mmap
);
611 efiemu_mmap
= grub_realloc (result
, j
* sizeof (*result
));
612 return GRUB_ERR_NONE
;
615 /* This function is called to switch from first to second phase */
617 grub_efiemu_mm_do_alloc (void)
621 /* Preallocate mmap */
622 efiemu_mmap
= (grub_efi_memory_descriptor_t
*)
623 grub_malloc (mmap_reserved_size
* sizeof (grub_efi_memory_descriptor_t
));
626 grub_efiemu_unload ();
627 return grub_error (GRUB_ERR_OUT_OF_MEMORY
, "Couldn't initialize mmap");
630 if ((err
= efiemu_alloc_requests ()))
632 if ((err
= grub_efiemu_mmap_fill ()))
634 return grub_efiemu_mmap_sort_and_uniq ();