mmap.c 13 KB

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