mmap.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /* Mmap management. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2009 Free Software Foundation, Inc.
  5. *
  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.
  10. *
  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.
  15. *
  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/>.
  18. */
  19. #include <grub/machine/memory.h>
  20. #include <grub/memory.h>
  21. #include <grub/err.h>
  22. #include <grub/efi/api.h>
  23. #include <grub/efi/efi.h>
  24. #include <grub/mm.h>
  25. #include <grub/misc.h>
  26. #define NEXT_MEMORY_DESCRIPTOR(desc, size) \
  27. ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
  28. grub_err_t
  29. grub_efi_mmap_iterate (grub_memory_hook_t hook, void *hook_data,
  30. int avoid_efi_boot_services)
  31. {
  32. grub_efi_uintn_t mmap_size = 0;
  33. grub_efi_memory_descriptor_t *map_buf = 0;
  34. grub_efi_uintn_t map_key = 0;
  35. grub_efi_uintn_t desc_size = 0;
  36. grub_efi_uint32_t desc_version = 0;
  37. grub_efi_memory_descriptor_t *desc;
  38. if (grub_efi_get_memory_map (&mmap_size, map_buf,
  39. &map_key, &desc_size,
  40. &desc_version) < 0)
  41. return grub_errno;
  42. map_buf = grub_malloc (mmap_size);
  43. if (! map_buf)
  44. return grub_errno;
  45. if (grub_efi_get_memory_map (&mmap_size, map_buf,
  46. &map_key, &desc_size,
  47. &desc_version) <= 0)
  48. {
  49. grub_free (map_buf);
  50. return grub_errno;
  51. }
  52. for (desc = map_buf;
  53. desc < NEXT_MEMORY_DESCRIPTOR (map_buf, mmap_size);
  54. desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size))
  55. {
  56. grub_dprintf ("mmap", "EFI memory region 0x%llx-0x%llx: %d\n",
  57. (unsigned long long) desc->physical_start,
  58. (unsigned long long) desc->physical_start
  59. + desc->num_pages * 4096, desc->type);
  60. switch (desc->type)
  61. {
  62. case GRUB_EFI_BOOT_SERVICES_CODE:
  63. if (!avoid_efi_boot_services)
  64. {
  65. hook (desc->physical_start, desc->num_pages * 4096,
  66. GRUB_MEMORY_AVAILABLE, hook_data);
  67. break;
  68. }
  69. /* FALLTHROUGH */
  70. case GRUB_EFI_RUNTIME_SERVICES_CODE:
  71. hook (desc->physical_start, desc->num_pages * 4096,
  72. GRUB_MEMORY_CODE, hook_data);
  73. break;
  74. case GRUB_EFI_UNUSABLE_MEMORY:
  75. hook (desc->physical_start, desc->num_pages * 4096,
  76. GRUB_MEMORY_BADRAM, hook_data);
  77. break;
  78. case GRUB_EFI_BOOT_SERVICES_DATA:
  79. if (!avoid_efi_boot_services)
  80. {
  81. hook (desc->physical_start, desc->num_pages * 4096,
  82. GRUB_MEMORY_AVAILABLE, hook_data);
  83. break;
  84. }
  85. /* FALLTHROUGH */
  86. case GRUB_EFI_RESERVED_MEMORY_TYPE:
  87. case GRUB_EFI_RUNTIME_SERVICES_DATA:
  88. case GRUB_EFI_MEMORY_MAPPED_IO:
  89. case GRUB_EFI_MEMORY_MAPPED_IO_PORT_SPACE:
  90. case GRUB_EFI_PAL_CODE:
  91. hook (desc->physical_start, desc->num_pages * 4096,
  92. GRUB_MEMORY_RESERVED, hook_data);
  93. break;
  94. case GRUB_EFI_LOADER_CODE:
  95. case GRUB_EFI_LOADER_DATA:
  96. case GRUB_EFI_CONVENTIONAL_MEMORY:
  97. hook (desc->physical_start, desc->num_pages * 4096,
  98. GRUB_MEMORY_AVAILABLE, hook_data);
  99. break;
  100. case GRUB_EFI_ACPI_RECLAIM_MEMORY:
  101. hook (desc->physical_start, desc->num_pages * 4096,
  102. GRUB_MEMORY_ACPI, hook_data);
  103. break;
  104. case GRUB_EFI_ACPI_MEMORY_NVS:
  105. hook (desc->physical_start, desc->num_pages * 4096,
  106. GRUB_MEMORY_NVS, hook_data);
  107. break;
  108. case GRUB_EFI_PERSISTENT_MEMORY:
  109. hook (desc->physical_start, desc->num_pages * 4096,
  110. GRUB_MEMORY_PERSISTENT, hook_data);
  111. break;
  112. default:
  113. grub_printf ("Unknown memory type %d, considering reserved\n",
  114. desc->type);
  115. hook (desc->physical_start, desc->num_pages * 4096,
  116. GRUB_MEMORY_RESERVED, hook_data);
  117. break;
  118. }
  119. }
  120. return GRUB_ERR_NONE;
  121. }
  122. grub_err_t
  123. grub_machine_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
  124. {
  125. return grub_efi_mmap_iterate (hook, hook_data, 0);
  126. }
  127. static inline grub_efi_memory_type_t
  128. make_efi_memtype (int type)
  129. {
  130. switch (type)
  131. {
  132. case GRUB_MEMORY_CODE:
  133. return GRUB_EFI_RUNTIME_SERVICES_CODE;
  134. /* No way to remove a chunk of memory from EFI mmap.
  135. So mark it as unusable. */
  136. case GRUB_MEMORY_HOLE:
  137. /*
  138. * AllocatePages() does not support GRUB_EFI_PERSISTENT_MEMORY,
  139. * so no translation for GRUB_MEMORY_PERSISTENT or
  140. * GRUB_MEMORY_PERSISTENT_LEGACY.
  141. */
  142. case GRUB_MEMORY_PERSISTENT:
  143. case GRUB_MEMORY_PERSISTENT_LEGACY:
  144. case GRUB_MEMORY_RESERVED:
  145. return GRUB_EFI_UNUSABLE_MEMORY;
  146. case GRUB_MEMORY_AVAILABLE:
  147. return GRUB_EFI_CONVENTIONAL_MEMORY;
  148. case GRUB_MEMORY_ACPI:
  149. return GRUB_EFI_ACPI_RECLAIM_MEMORY;
  150. case GRUB_MEMORY_NVS:
  151. return GRUB_EFI_ACPI_MEMORY_NVS;
  152. }
  153. return GRUB_EFI_UNUSABLE_MEMORY;
  154. }
  155. struct overlay
  156. {
  157. struct overlay *next;
  158. grub_efi_physical_address_t address;
  159. grub_efi_uintn_t pages;
  160. int handle;
  161. };
  162. static struct overlay *overlays = 0;
  163. static int curhandle = 1;
  164. int
  165. grub_mmap_register (grub_uint64_t start, grub_uint64_t size, int type)
  166. {
  167. grub_uint64_t end = start + size;
  168. grub_efi_physical_address_t address;
  169. grub_efi_boot_services_t *b;
  170. grub_efi_uintn_t pages;
  171. grub_efi_status_t status;
  172. struct overlay *curover;
  173. curover = (struct overlay *) grub_malloc (sizeof (struct overlay));
  174. if (! curover)
  175. return 0;
  176. b = grub_efi_system_table->boot_services;
  177. address = start & (~0xfffULL);
  178. pages = (end - address + 0xfff) >> 12;
  179. status = efi_call_2 (b->free_pages, address, pages);
  180. if (status != GRUB_EFI_SUCCESS && status != GRUB_EFI_NOT_FOUND)
  181. {
  182. grub_free (curover);
  183. return 0;
  184. }
  185. status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ADDRESS,
  186. make_efi_memtype (type), pages, &address);
  187. if (status != GRUB_EFI_SUCCESS)
  188. {
  189. grub_free (curover);
  190. return 0;
  191. }
  192. curover->next = overlays;
  193. curover->handle = curhandle++;
  194. curover->address = address;
  195. curover->pages = pages;
  196. overlays = curover;
  197. return curover->handle;
  198. }
  199. grub_err_t
  200. grub_mmap_unregister (int handle)
  201. {
  202. struct overlay *curover, *prevover;
  203. grub_efi_boot_services_t *b;
  204. b = grub_efi_system_table->boot_services;
  205. for (curover = overlays, prevover = 0; curover;
  206. prevover = curover, curover = curover->next)
  207. {
  208. if (curover->handle == handle)
  209. {
  210. efi_call_2 (b->free_pages, curover->address, curover->pages);
  211. if (prevover != 0)
  212. prevover->next = curover->next;
  213. else
  214. overlays = curover->next;
  215. grub_free (curover);
  216. return GRUB_ERR_NONE;
  217. }
  218. }
  219. return grub_error (GRUB_ERR_BUG, "handle %d not found", handle);
  220. }
  221. /* Result is always page-aligned. */
  222. void *
  223. grub_mmap_malign_and_register (grub_uint64_t align __attribute__ ((unused)),
  224. grub_uint64_t size,
  225. int *handle, int type,
  226. int flags __attribute__ ((unused)))
  227. {
  228. grub_efi_physical_address_t address;
  229. grub_efi_boot_services_t *b;
  230. grub_efi_uintn_t pages;
  231. grub_efi_status_t status;
  232. struct overlay *curover;
  233. grub_efi_allocate_type_t atype;
  234. curover = (struct overlay *) grub_malloc (sizeof (struct overlay));
  235. if (! curover)
  236. return 0;
  237. b = grub_efi_system_table->boot_services;
  238. address = 0xffffffff;
  239. #if GRUB_TARGET_SIZEOF_VOID_P < 8
  240. /* Limit the memory access to less than 4GB for 32-bit platforms. */
  241. atype = GRUB_EFI_ALLOCATE_MAX_ADDRESS;
  242. #else
  243. atype = GRUB_EFI_ALLOCATE_ANY_PAGES;
  244. #endif
  245. pages = (size + 0xfff) >> 12;
  246. status = efi_call_4 (b->allocate_pages, atype,
  247. make_efi_memtype (type), pages, &address);
  248. if (status != GRUB_EFI_SUCCESS)
  249. {
  250. grub_free (curover);
  251. return 0;
  252. }
  253. if (address == 0)
  254. {
  255. /* Uggh, the address 0 was allocated... This is too annoying,
  256. so reallocate another one. */
  257. address = 0xffffffff;
  258. status = efi_call_4 (b->allocate_pages, atype,
  259. make_efi_memtype (type), pages, &address);
  260. grub_efi_free_pages (0, pages);
  261. if (status != GRUB_EFI_SUCCESS)
  262. return 0;
  263. }
  264. curover->next = overlays;
  265. curover->handle = curhandle++;
  266. curover->address = address;
  267. curover->pages = pages;
  268. overlays = curover;
  269. *handle = curover->handle;
  270. return (void *) (grub_addr_t) curover->address;
  271. }
  272. void
  273. grub_mmap_free_and_unregister (int handle)
  274. {
  275. grub_mmap_unregister (handle);
  276. }