mmap.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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/memory.h>
  20. #include <grub/machine/memory.h>
  21. #include <grub/err.h>
  22. #include <grub/lockdown.h>
  23. #include <grub/misc.h>
  24. #include <grub/mm.h>
  25. #include <grub/command.h>
  26. #include <grub/dl.h>
  27. #include <grub/i18n.h>
  28. GRUB_MOD_LICENSE ("GPLv3+");
  29. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  30. struct grub_mmap_region *grub_mmap_overlays = 0;
  31. static int curhandle = 1;
  32. #endif
  33. static int current_priority = 1;
  34. /* Scanline events. */
  35. struct grub_mmap_scan
  36. {
  37. /* At which memory address. */
  38. grub_uint64_t pos;
  39. /* 0 = region starts, 1 = region ends. */
  40. int type;
  41. /* Which type of memory region? */
  42. grub_memory_type_t memtype;
  43. /* Priority. 0 means coming from firmware. */
  44. int priority;
  45. };
  46. /* Context for grub_mmap_iterate. */
  47. struct grub_mmap_iterate_ctx
  48. {
  49. struct grub_mmap_scan *scanline_events;
  50. int i;
  51. };
  52. /* Helper for grub_mmap_iterate. */
  53. static int
  54. count_hook (grub_uint64_t addr __attribute__ ((unused)),
  55. grub_uint64_t size __attribute__ ((unused)),
  56. grub_memory_type_t type __attribute__ ((unused)), void *data)
  57. {
  58. int *mmap_num = data;
  59. (*mmap_num)++;
  60. return 0;
  61. }
  62. /* Helper for grub_mmap_iterate. */
  63. static int
  64. fill_hook (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type,
  65. void *data)
  66. {
  67. struct grub_mmap_iterate_ctx *ctx = data;
  68. if (type == GRUB_MEMORY_HOLE)
  69. {
  70. grub_dprintf ("mmap", "Unknown memory type %d. Assuming unusable\n",
  71. type);
  72. type = GRUB_MEMORY_RESERVED;
  73. }
  74. ctx->scanline_events[ctx->i].pos = addr;
  75. ctx->scanline_events[ctx->i].type = 0;
  76. ctx->scanline_events[ctx->i].memtype = type;
  77. ctx->scanline_events[ctx->i].priority = 0;
  78. ctx->i++;
  79. ctx->scanline_events[ctx->i].pos = addr + size;
  80. ctx->scanline_events[ctx->i].type = 1;
  81. ctx->scanline_events[ctx->i].memtype = type;
  82. ctx->scanline_events[ctx->i].priority = 0;
  83. ctx->i++;
  84. return 0;
  85. }
  86. struct mm_list
  87. {
  88. struct mm_list *next;
  89. grub_memory_type_t val;
  90. int present;
  91. };
  92. grub_err_t
  93. grub_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
  94. {
  95. /* This function resolves overlapping regions and sorts the memory map.
  96. It uses scanline (sweeping) algorithm.
  97. */
  98. struct grub_mmap_iterate_ctx ctx;
  99. int i, done;
  100. struct grub_mmap_scan t;
  101. /* Previous scanline event. */
  102. grub_uint64_t lastaddr;
  103. int lasttype;
  104. /* Current scanline event. */
  105. int curtype;
  106. /* How many regions of given type/priority overlap at current location? */
  107. /* Normally there shouldn't be more than one region per priority but be robust. */
  108. struct mm_list *present;
  109. /* Number of mmap chunks. */
  110. int mmap_num;
  111. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  112. struct grub_mmap_region *cur;
  113. #endif
  114. mmap_num = 0;
  115. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  116. for (cur = grub_mmap_overlays; cur; cur = cur->next)
  117. mmap_num++;
  118. #endif
  119. grub_machine_mmap_iterate (count_hook, &mmap_num);
  120. /* Initialize variables. */
  121. ctx.scanline_events = (struct grub_mmap_scan *)
  122. grub_calloc (mmap_num, sizeof (struct grub_mmap_scan) * 2);
  123. present = grub_calloc (current_priority, sizeof (present[0]));
  124. if (! ctx.scanline_events || !present)
  125. {
  126. grub_free (ctx.scanline_events);
  127. grub_free (present);
  128. return grub_errno;
  129. }
  130. ctx.i = 0;
  131. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  132. /* Register scanline events. */
  133. for (cur = grub_mmap_overlays; cur; cur = cur->next)
  134. {
  135. ctx.scanline_events[ctx.i].pos = cur->start;
  136. ctx.scanline_events[ctx.i].type = 0;
  137. ctx.scanline_events[ctx.i].memtype = cur->type;
  138. ctx.scanline_events[ctx.i].priority = cur->priority;
  139. ctx.i++;
  140. ctx.scanline_events[ctx.i].pos = cur->end;
  141. ctx.scanline_events[ctx.i].type = 1;
  142. ctx.scanline_events[ctx.i].memtype = cur->type;
  143. ctx.scanline_events[ctx.i].priority = cur->priority;
  144. ctx.i++;
  145. }
  146. #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
  147. grub_machine_mmap_iterate (fill_hook, &ctx);
  148. /* Primitive bubble sort. It has complexity O(n^2) but since we're
  149. unlikely to have more than 100 chunks it's probably one of the
  150. fastest for one purpose. */
  151. done = 1;
  152. while (done)
  153. {
  154. done = 0;
  155. for (i = 0; i < 2 * mmap_num - 1; i++)
  156. if (ctx.scanline_events[i + 1].pos < ctx.scanline_events[i].pos
  157. || (ctx.scanline_events[i + 1].pos == ctx.scanline_events[i].pos
  158. && ctx.scanline_events[i + 1].type == 0
  159. && ctx.scanline_events[i].type == 1))
  160. {
  161. t = ctx.scanline_events[i + 1];
  162. ctx.scanline_events[i + 1] = ctx.scanline_events[i];
  163. ctx.scanline_events[i] = t;
  164. done = 1;
  165. }
  166. }
  167. lastaddr = ctx.scanline_events[0].pos;
  168. lasttype = ctx.scanline_events[0].memtype;
  169. for (i = 0; i < 2 * mmap_num; i++)
  170. {
  171. /* Process event. */
  172. if (ctx.scanline_events[i].type)
  173. {
  174. if (present[ctx.scanline_events[i].priority].present)
  175. {
  176. if (present[ctx.scanline_events[i].priority].val == ctx.scanline_events[i].memtype)
  177. {
  178. if (present[ctx.scanline_events[i].priority].next)
  179. {
  180. struct mm_list *p = present[ctx.scanline_events[i].priority].next;
  181. present[ctx.scanline_events[i].priority] = *p;
  182. grub_free (p);
  183. }
  184. else
  185. {
  186. present[ctx.scanline_events[i].priority].present = 0;
  187. }
  188. }
  189. else
  190. {
  191. struct mm_list **q = &(present[ctx.scanline_events[i].priority].next), *p;
  192. for (; *q; q = &((*q)->next))
  193. if ((*q)->val == ctx.scanline_events[i].memtype)
  194. {
  195. p = *q;
  196. *q = p->next;
  197. grub_free (p);
  198. break;
  199. }
  200. }
  201. }
  202. }
  203. else
  204. {
  205. if (!present[ctx.scanline_events[i].priority].present)
  206. {
  207. present[ctx.scanline_events[i].priority].present = 1;
  208. present[ctx.scanline_events[i].priority].val = ctx.scanline_events[i].memtype;
  209. }
  210. else
  211. {
  212. struct mm_list *n = grub_malloc (sizeof (*n));
  213. n->val = ctx.scanline_events[i].memtype;
  214. n->present = 1;
  215. n->next = present[ctx.scanline_events[i].priority].next;
  216. present[ctx.scanline_events[i].priority].next = n;
  217. }
  218. }
  219. /* Determine current region type. */
  220. curtype = -1;
  221. {
  222. int k;
  223. for (k = current_priority - 1; k >= 0; k--)
  224. if (present[k].present)
  225. {
  226. curtype = present[k].val;
  227. break;
  228. }
  229. }
  230. /* Announce region to the hook if necessary. */
  231. if ((curtype == -1 || curtype != lasttype)
  232. && lastaddr != ctx.scanline_events[i].pos
  233. && lasttype != -1
  234. && lasttype != GRUB_MEMORY_HOLE
  235. && hook (lastaddr, ctx.scanline_events[i].pos - lastaddr, lasttype,
  236. hook_data))
  237. {
  238. grub_free (ctx.scanline_events);
  239. grub_free (present);
  240. return GRUB_ERR_NONE;
  241. }
  242. /* Update last values if necessary. */
  243. if (curtype == -1 || curtype != lasttype)
  244. {
  245. lasttype = curtype;
  246. lastaddr = ctx.scanline_events[i].pos;
  247. }
  248. }
  249. grub_free (ctx.scanline_events);
  250. grub_free (present);
  251. return GRUB_ERR_NONE;
  252. }
  253. #ifndef GRUB_MMAP_REGISTER_BY_FIRMWARE
  254. int
  255. grub_mmap_register (grub_uint64_t start, grub_uint64_t size, int type)
  256. {
  257. struct grub_mmap_region *cur;
  258. grub_dprintf ("mmap", "registering\n");
  259. cur = (struct grub_mmap_region *)
  260. grub_malloc (sizeof (struct grub_mmap_region));
  261. if (! cur)
  262. return 0;
  263. cur->next = grub_mmap_overlays;
  264. cur->start = start;
  265. cur->end = start + size;
  266. cur->type = type;
  267. cur->handle = curhandle++;
  268. cur->priority = current_priority++;
  269. grub_mmap_overlays = cur;
  270. if (grub_machine_mmap_register (start, size, type, curhandle))
  271. {
  272. grub_mmap_overlays = cur->next;
  273. grub_free (cur);
  274. return 0;
  275. }
  276. return cur->handle;
  277. }
  278. grub_err_t
  279. grub_mmap_unregister (int handle)
  280. {
  281. struct grub_mmap_region *cur, *prev;
  282. for (cur = grub_mmap_overlays, prev = 0; cur; prev = cur, cur = cur->next)
  283. if (handle == cur->handle)
  284. {
  285. grub_err_t err;
  286. err = grub_machine_mmap_unregister (handle);
  287. if (err)
  288. return err;
  289. if (prev)
  290. prev->next = cur->next;
  291. else
  292. grub_mmap_overlays = cur->next;
  293. grub_free (cur);
  294. return GRUB_ERR_NONE;
  295. }
  296. return grub_error (GRUB_ERR_BUG, "mmap overlay not found");
  297. }
  298. #endif /* ! GRUB_MMAP_REGISTER_BY_FIRMWARE */
  299. #define CHUNK_SIZE 0x400
  300. struct badram_entry {
  301. grub_uint64_t addr, mask;
  302. };
  303. static inline grub_uint64_t
  304. fill_mask (struct badram_entry *entry, grub_uint64_t iterator)
  305. {
  306. int i, j;
  307. grub_uint64_t ret = (entry->addr & entry->mask);
  308. /* Find first fixed bit. */
  309. for (i = 0; i < 64; i++)
  310. if ((entry->mask & (1ULL << i)) != 0)
  311. break;
  312. j = 0;
  313. for (; i < 64; i++)
  314. if ((entry->mask & (1ULL << i)) == 0)
  315. {
  316. if ((iterator & (1ULL << j)) != 0)
  317. ret |= 1ULL << i;
  318. j++;
  319. }
  320. return ret;
  321. }
  322. /* Helper for grub_cmd_badram. */
  323. static int
  324. badram_iter (grub_uint64_t addr, grub_uint64_t size,
  325. grub_memory_type_t type __attribute__ ((unused)), void *data)
  326. {
  327. struct badram_entry *entry = data;
  328. grub_uint64_t iterator, low, high, cur;
  329. int tail, var;
  330. int i;
  331. grub_dprintf ("badram", "hook %llx+%llx\n", (unsigned long long) addr,
  332. (unsigned long long) size);
  333. /* How many trailing zeros? */
  334. for (tail = 0; ! (entry->mask & (1ULL << tail)); tail++);
  335. /* How many zeros in mask? */
  336. var = 0;
  337. for (i = 0; i < 64; i++)
  338. if (! (entry->mask & (1ULL << i)))
  339. var++;
  340. if (fill_mask (entry, 0) >= addr)
  341. iterator = 0;
  342. else
  343. {
  344. low = 0;
  345. high = ~0ULL;
  346. /* Find starting value. Keep low and high such that
  347. fill_mask (low) < addr and fill_mask (high) >= addr;
  348. */
  349. while (high - low > 1)
  350. {
  351. cur = (low + high) / 2;
  352. if (fill_mask (entry, cur) >= addr)
  353. high = cur;
  354. else
  355. low = cur;
  356. }
  357. iterator = high;
  358. }
  359. for (; iterator < (1ULL << (var - tail))
  360. && (cur = fill_mask (entry, iterator)) < addr + size;
  361. iterator++)
  362. {
  363. grub_dprintf ("badram", "%llx (size %llx) is a badram range\n",
  364. (unsigned long long) cur, (1ULL << tail));
  365. grub_mmap_register (cur, (1ULL << tail), GRUB_MEMORY_HOLE);
  366. }
  367. return 0;
  368. }
  369. static grub_err_t
  370. grub_cmd_badram (grub_command_t cmd __attribute__ ((unused)),
  371. int argc, char **args)
  372. {
  373. const char *str;
  374. struct badram_entry entry;
  375. if (argc != 1)
  376. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
  377. grub_dprintf ("badram", "executing badram\n");
  378. str = args[0];
  379. while (1)
  380. {
  381. /* Parse address and mask. */
  382. entry.addr = grub_strtoull (str, &str, 16);
  383. if (*str == ',')
  384. str++;
  385. entry.mask = grub_strtoull (str, &str, 16);
  386. if (*str == ',')
  387. str++;
  388. if (grub_errno == GRUB_ERR_BAD_NUMBER)
  389. {
  390. grub_errno = 0;
  391. return GRUB_ERR_NONE;
  392. }
  393. /* When part of a page is tainted, we discard the whole of it. There's
  394. no point in providing sub-page chunks. */
  395. entry.mask &= ~(CHUNK_SIZE - 1);
  396. grub_dprintf ("badram", "badram %llx:%llx\n",
  397. (unsigned long long) entry.addr,
  398. (unsigned long long) entry.mask);
  399. grub_mmap_iterate (badram_iter, &entry);
  400. }
  401. }
  402. static grub_uint64_t
  403. parsemem (const char *str)
  404. {
  405. grub_uint64_t ret;
  406. const char *ptr;
  407. ret = grub_strtoul (str, &ptr, 0);
  408. switch (*ptr)
  409. {
  410. case 'K':
  411. return ret << 10;
  412. case 'M':
  413. return ret << 20;
  414. case 'G':
  415. return ret << 30;
  416. case 'T':
  417. return ret << 40;
  418. }
  419. return ret;
  420. }
  421. struct cutmem_range {
  422. grub_uint64_t from, to;
  423. };
  424. /* Helper for grub_cmd_cutmem. */
  425. static int
  426. cutmem_iter (grub_uint64_t addr, grub_uint64_t size,
  427. grub_memory_type_t type __attribute__ ((unused)), void *data)
  428. {
  429. struct cutmem_range *range = data;
  430. grub_uint64_t end = addr + size;
  431. if (addr <= range->from)
  432. addr = range->from;
  433. if (end >= range->to)
  434. end = range->to;
  435. if (end <= addr)
  436. return 0;
  437. grub_mmap_register (addr, end - addr, GRUB_MEMORY_HOLE);
  438. return 0;
  439. }
  440. static grub_err_t
  441. grub_cmd_cutmem (grub_command_t cmd __attribute__ ((unused)),
  442. int argc, char **args)
  443. {
  444. struct cutmem_range range;
  445. if (argc != 2)
  446. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("two arguments expected"));
  447. range.from = parsemem (args[0]);
  448. if (grub_errno)
  449. return grub_errno;
  450. range.to = parsemem (args[1]);
  451. if (grub_errno)
  452. return grub_errno;
  453. grub_mmap_iterate (cutmem_iter, &range);
  454. return GRUB_ERR_NONE;
  455. }
  456. static grub_command_t cmd, cmd_cut;
  457. GRUB_MOD_INIT(mmap)
  458. {
  459. cmd = grub_register_command_lockdown ("badram", grub_cmd_badram,
  460. N_("ADDR1,MASK1[,ADDR2,MASK2[,...]]"),
  461. N_("Declare memory regions as faulty (badram)."));
  462. cmd_cut = grub_register_command_lockdown ("cutmem", grub_cmd_cutmem,
  463. N_("FROM[K|M|G] TO[K|M|G]"),
  464. N_("Remove any memory regions in specified range."));
  465. }
  466. GRUB_MOD_FINI(mmap)
  467. {
  468. grub_unregister_command (cmd);
  469. grub_unregister_command (cmd_cut);
  470. }