mmap.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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/misc.h>
  23. #include <grub/mm.h>
  24. #include <grub/command.h>
  25. #include <grub/dl.h>
  26. #include <grub/i18n.h>
  27. GRUB_EXPORT(grub_mmap_register);
  28. GRUB_EXPORT(grub_mmap_unregister);
  29. GRUB_EXPORT(grub_mmap_iterate);
  30. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  31. struct grub_mmap_region *grub_mmap_overlays = 0;
  32. static int curhandle = 1;
  33. #endif
  34. struct grub_mmap_iterate_closure
  35. {
  36. int i;
  37. struct grub_mmap_scan *scanline_events;
  38. int *priority;
  39. };
  40. static int
  41. count_hook (grub_uint64_t addr __attribute__ ((unused)),
  42. grub_uint64_t size __attribute__ ((unused)),
  43. grub_uint32_t type __attribute__ ((unused)), void *closure)
  44. {
  45. int *mmap_num = closure;
  46. (*mmap_num)++;
  47. return 0;
  48. }
  49. /* Scanline events. */
  50. struct grub_mmap_scan
  51. {
  52. /* At which memory address. */
  53. grub_uint64_t pos;
  54. /* 0 = region starts, 1 = region ends. */
  55. int type;
  56. /* Which type of memory region? */
  57. int memtype;
  58. };
  59. static int
  60. fill_hook (grub_uint64_t addr, grub_uint64_t size, grub_uint32_t type,
  61. void *closure)
  62. {
  63. struct grub_mmap_iterate_closure *c = closure;
  64. struct grub_mmap_scan *scanline_events = c->scanline_events;
  65. scanline_events[c->i].pos = addr;
  66. scanline_events[c->i].type = 0;
  67. if (type <= GRUB_MACHINE_MEMORY_MAX_TYPE && c->priority[type])
  68. scanline_events[c->i].memtype = type;
  69. else
  70. {
  71. grub_dprintf ("mmap", "Unknown memory type %d. Assuming unusable\n",
  72. type);
  73. scanline_events[c->i].memtype = GRUB_MACHINE_MEMORY_RESERVED;
  74. }
  75. c->i++;
  76. scanline_events[c->i].pos = addr + size;
  77. scanline_events[c->i].type = 1;
  78. scanline_events[c->i].memtype = scanline_events[c->i - 1].memtype;
  79. c->i++;
  80. return 0;
  81. }
  82. grub_err_t
  83. grub_mmap_iterate (int (*hook) (grub_uint64_t, grub_uint64_t,
  84. grub_uint32_t, void *), void *closure)
  85. {
  86. /* This function resolves overlapping regions and sorts the memory map.
  87. It uses scanline (sweeping) algorithm.
  88. */
  89. /* If same page is used by multiple types it's resolved
  90. according to priority:
  91. 1 - free memory
  92. 2 - memory usable by firmware-aware code
  93. 3 - unusable memory
  94. 4 - a range deliberately empty
  95. */
  96. int priority[GRUB_MACHINE_MEMORY_MAX_TYPE + 2] =
  97. {
  98. #ifdef GRUB_MACHINE_MEMORY_AVAILABLE
  99. [GRUB_MACHINE_MEMORY_AVAILABLE] = 1,
  100. #endif
  101. #if defined (GRUB_MACHINE_MEMORY_RESERVED) && GRUB_MACHINE_MEMORY_RESERVED != GRUB_MACHINE_MEMORY_HOLE
  102. [GRUB_MACHINE_MEMORY_RESERVED] = 3,
  103. #endif
  104. #ifdef GRUB_MACHINE_MEMORY_ACPI
  105. [GRUB_MACHINE_MEMORY_ACPI] = 2,
  106. #endif
  107. #ifdef GRUB_MACHINE_MEMORY_CODE
  108. [GRUB_MACHINE_MEMORY_CODE] = 3,
  109. #endif
  110. #ifdef GRUB_MACHINE_MEMORY_NVS
  111. [GRUB_MACHINE_MEMORY_NVS] = 3,
  112. #endif
  113. [GRUB_MACHINE_MEMORY_HOLE] = 4,
  114. };
  115. int i, k, done;
  116. struct grub_mmap_scan *scanline_events;
  117. struct grub_mmap_scan t;
  118. /* Previous scanline event. */
  119. grub_uint64_t lastaddr;
  120. int lasttype;
  121. /* Current scanline event. */
  122. int curtype;
  123. /* How many regions of given type overlap at current location? */
  124. int present[GRUB_MACHINE_MEMORY_MAX_TYPE + 2];
  125. /* Number of mmap chunks. */
  126. int mmap_num;
  127. struct grub_mmap_iterate_closure c;
  128. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  129. struct grub_mmap_region *cur;
  130. #endif
  131. mmap_num = 0;
  132. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  133. for (cur = grub_mmap_overlays; cur; cur = cur->next)
  134. mmap_num++;
  135. #endif
  136. grub_machine_mmap_iterate (count_hook, &mmap_num);
  137. /* Initialize variables. */
  138. grub_memset (present, 0, sizeof (present));
  139. scanline_events = (struct grub_mmap_scan *)
  140. grub_malloc (sizeof (struct grub_mmap_scan) * 2 * mmap_num);
  141. if (! scanline_events)
  142. {
  143. return grub_error (GRUB_ERR_OUT_OF_MEMORY,
  144. "couldn't allocate space for new memory map");
  145. }
  146. i = 0;
  147. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  148. /* Register scanline events. */
  149. for (cur = grub_mmap_overlays; cur; cur = cur->next)
  150. {
  151. scanline_events[i].pos = cur->start;
  152. scanline_events[i].type = 0;
  153. if (cur->type == GRUB_MACHINE_MEMORY_HOLE
  154. || (cur->type >= 0 && cur->type <= GRUB_MACHINE_MEMORY_MAX_TYPE
  155. && priority[cur->type]))
  156. scanline_events[i].memtype = cur->type;
  157. else
  158. scanline_events[i].memtype = GRUB_MACHINE_MEMORY_RESERVED;
  159. i++;
  160. scanline_events[i].pos = cur->end;
  161. scanline_events[i].type = 1;
  162. scanline_events[i].memtype = scanline_events[i - 1].memtype;
  163. i++;
  164. }
  165. #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
  166. c.i = i;
  167. c.scanline_events = scanline_events;
  168. c.priority = priority;
  169. grub_machine_mmap_iterate (fill_hook, &c);
  170. /* Primitive bubble sort. It has complexity O(n^2) but since we're
  171. unlikely to have more than 100 chunks it's probably one of the
  172. fastest for one purpose. */
  173. done = 1;
  174. while (done)
  175. {
  176. done = 0;
  177. for (i = 0; i < 2 * mmap_num - 1; i++)
  178. if (scanline_events[i + 1].pos < scanline_events[i].pos
  179. || (scanline_events[i + 1].pos == scanline_events[i].pos
  180. && scanline_events[i + 1].type == 0
  181. && scanline_events[i].type == 1))
  182. {
  183. t = scanline_events[i + 1];
  184. scanline_events[i + 1] = scanline_events[i];
  185. scanline_events[i] = t;
  186. done = 1;
  187. }
  188. }
  189. lastaddr = scanline_events[0].pos;
  190. lasttype = scanline_events[0].memtype;
  191. for (i = 0; i < 2 * mmap_num; i++)
  192. {
  193. /* Process event. */
  194. if (scanline_events[i].type)
  195. present[scanline_events[i].memtype]--;
  196. else
  197. present[scanline_events[i].memtype]++;
  198. /* Determine current region type. */
  199. curtype = -1;
  200. for (k = 0; k <= GRUB_MACHINE_MEMORY_MAX_TYPE + 1; k++)
  201. if (present[k] && (curtype == -1 || priority[k] > priority[curtype]))
  202. curtype = k;
  203. /* Announce region to the hook if necessary. */
  204. if ((curtype == -1 || curtype != lasttype)
  205. && lastaddr != scanline_events[i].pos
  206. && lasttype != -1
  207. && lasttype != GRUB_MACHINE_MEMORY_HOLE
  208. && hook (lastaddr, scanline_events[i].pos - lastaddr, lasttype,
  209. closure))
  210. {
  211. grub_free (scanline_events);
  212. return GRUB_ERR_NONE;
  213. }
  214. /* Update last values if necessary. */
  215. if (curtype == -1 || curtype != lasttype)
  216. {
  217. lasttype = curtype;
  218. lastaddr = scanline_events[i].pos;
  219. }
  220. }
  221. grub_free (scanline_events);
  222. return GRUB_ERR_NONE;
  223. }
  224. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  225. int
  226. grub_mmap_register (grub_uint64_t start, grub_uint64_t size, int type)
  227. {
  228. struct grub_mmap_region *cur;
  229. grub_dprintf ("mmap", "registering\n");
  230. cur = (struct grub_mmap_region *)
  231. grub_malloc (sizeof (struct grub_mmap_region));
  232. if (! cur)
  233. {
  234. grub_error (GRUB_ERR_OUT_OF_MEMORY,
  235. "couldn't allocate memory map overlay");
  236. return 0;
  237. }
  238. cur->next = grub_mmap_overlays;
  239. cur->start = start;
  240. cur->end = start + size;
  241. cur->type = type;
  242. cur->handle = curhandle++;
  243. grub_mmap_overlays = cur;
  244. if (grub_machine_mmap_register (start, size, type, curhandle))
  245. {
  246. grub_mmap_overlays = cur->next;
  247. grub_free (cur);
  248. return 0;
  249. }
  250. return cur->handle;
  251. }
  252. grub_err_t
  253. grub_mmap_unregister (int handle)
  254. {
  255. struct grub_mmap_region *cur, *prev;
  256. for (cur = grub_mmap_overlays, prev = 0; cur; prev= cur, cur = cur->next)
  257. if (handle == cur->handle)
  258. {
  259. grub_err_t err;
  260. if ((err = grub_machine_mmap_unregister (handle)))
  261. return err;
  262. if (prev)
  263. prev->next = cur->next;
  264. else
  265. grub_mmap_overlays = cur->next;
  266. grub_free (cur);
  267. return GRUB_ERR_NONE;
  268. }
  269. return grub_error (GRUB_ERR_BAD_ARGUMENT, "mmap overlay not found");
  270. }
  271. #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
  272. #define CHUNK_SIZE 0x400
  273. static inline grub_uint64_t
  274. fill_mask (grub_uint64_t addr, grub_uint64_t mask, grub_uint64_t iterator)
  275. {
  276. int i, j;
  277. grub_uint64_t ret = (addr & mask);
  278. /* Find first fixed bit. */
  279. for (i = 0; i < 64; i++)
  280. if ((mask & (1ULL << i)) != 0)
  281. break;
  282. j = 0;
  283. for (; i < 64; i++)
  284. if ((mask & (1ULL << i)) == 0)
  285. {
  286. if ((iterator & (1ULL << j)) != 0)
  287. ret |= 1ULL << i;
  288. j++;
  289. }
  290. return ret;
  291. }
  292. struct grub_cmd_badram_closure
  293. {
  294. grub_uint64_t badaddr, badmask;
  295. };
  296. static int
  297. grub_cmd_badram_hook (grub_uint64_t addr, grub_uint64_t size,
  298. grub_uint32_t type __attribute__ ((unused)),
  299. void *closure)
  300. {
  301. struct grub_cmd_badram_closure *c = closure;
  302. grub_uint64_t iterator, low, high, cur;
  303. int tail, var;
  304. int i;
  305. grub_dprintf ("badram", "hook %llx+%llx\n", (unsigned long long) addr,
  306. (unsigned long long) size);
  307. /* How many trailing zeros? */
  308. for (tail = 0; ! (c->badmask & (1ULL << tail)); tail++);
  309. /* How many zeros in mask? */
  310. var = 0;
  311. for (i = 0; i < 64; i++)
  312. if (! (c->badmask & (1ULL << i)))
  313. var++;
  314. if (fill_mask (c->badaddr, c->badmask, 0) >= addr)
  315. iterator = 0;
  316. else
  317. {
  318. low = 0;
  319. high = ~0ULL;
  320. /* Find starting value. Keep low and high such that
  321. fill_mask (low) < addr and fill_mask (high) >= addr;
  322. */
  323. while (high - low > 1)
  324. {
  325. cur = (low + high) / 2;
  326. if (fill_mask (c->badaddr, c->badmask, cur) >= addr)
  327. high = cur;
  328. else
  329. low = cur;
  330. }
  331. iterator = high;
  332. }
  333. for (; iterator < (1ULL << (var - tail))
  334. && (cur = fill_mask (c->badaddr, c->badmask, iterator)) < addr + size;
  335. iterator++)
  336. {
  337. grub_dprintf ("badram", "%llx (size %llx) is a badram range\n",
  338. (unsigned long long) cur, (1ULL << tail));
  339. grub_mmap_register (cur, (1ULL << tail), GRUB_MACHINE_MEMORY_HOLE);
  340. }
  341. return 0;
  342. }
  343. static grub_err_t
  344. grub_cmd_badram (grub_command_t cmd __attribute__ ((unused)),
  345. int argc, char **args)
  346. {
  347. char * str;
  348. if (argc != 1)
  349. return grub_error (GRUB_ERR_BAD_ARGUMENT, "badram string required");
  350. grub_dprintf ("badram", "executing badram\n");
  351. str = args[0];
  352. while (1)
  353. {
  354. struct grub_cmd_badram_closure c;
  355. /* Parse address and mask. */
  356. c.badaddr = grub_strtoull (str, &str, 16);
  357. if (*str == ',')
  358. str++;
  359. c.badmask = grub_strtoull (str, &str, 16);
  360. if (*str == ',')
  361. str++;
  362. if (grub_errno == GRUB_ERR_BAD_NUMBER)
  363. {
  364. grub_errno = 0;
  365. return GRUB_ERR_NONE;
  366. }
  367. /* When part of a page is tainted, we discard the whole of it. There's
  368. no point in providing sub-page chunks. */
  369. c.badmask &= ~(CHUNK_SIZE - 1);
  370. grub_dprintf ("badram", "badram %llx:%llx\n",
  371. (unsigned long long) c.badaddr,
  372. (unsigned long long) c.badmask);
  373. grub_mmap_iterate (grub_cmd_badram_hook, &c);
  374. }
  375. }
  376. static grub_command_t cmd;
  377. GRUB_MOD_INIT(mmap)
  378. {
  379. cmd = grub_register_command ("badram", grub_cmd_badram,
  380. N_("ADDR1,MASK1[,ADDR2,MASK2[,...]]"),
  381. N_("Declare memory regions as badram."));
  382. }
  383. GRUB_MOD_FINI(mmap)
  384. {
  385. grub_unregister_command (cmd);
  386. }