relocator.c 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2009, 2010 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/relocator.h>
  19. #include <grub/relocator_private.h>
  20. #include <grub/mm_private.h>
  21. #include <grub/misc.h>
  22. #include <grub/cache.h>
  23. #include <grub/memory.h>
  24. #include <grub/dl.h>
  25. #include <grub/i18n.h>
  26. GRUB_MOD_LICENSE ("GPLv3+");
  27. struct grub_relocator
  28. {
  29. struct grub_relocator_chunk *chunks;
  30. grub_phys_addr_t postchunks;
  31. grub_phys_addr_t highestaddr;
  32. grub_phys_addr_t highestnonpostaddr;
  33. grub_size_t relocators_size;
  34. };
  35. struct grub_relocator_subchunk
  36. {
  37. enum {CHUNK_TYPE_IN_REGION, CHUNK_TYPE_REGION_START,
  38. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  39. CHUNK_TYPE_FIRMWARE, CHUNK_TYPE_LEFTOVER
  40. #endif
  41. } type;
  42. grub_mm_region_t reg;
  43. grub_phys_addr_t start;
  44. grub_size_t size;
  45. grub_size_t pre_size;
  46. struct grub_relocator_extra_block *extra;
  47. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  48. struct grub_relocator_fw_leftover *pre, *post;
  49. #endif
  50. };
  51. struct grub_relocator_chunk
  52. {
  53. struct grub_relocator_chunk *next;
  54. grub_phys_addr_t src;
  55. void *srcv;
  56. grub_phys_addr_t target;
  57. grub_size_t size;
  58. struct grub_relocator_subchunk *subchunks;
  59. unsigned nsubchunks;
  60. };
  61. struct grub_relocator_extra_block
  62. {
  63. struct grub_relocator_extra_block *next;
  64. struct grub_relocator_extra_block **prev;
  65. grub_phys_addr_t start;
  66. grub_phys_addr_t end;
  67. };
  68. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  69. struct grub_relocator_fw_leftover
  70. {
  71. struct grub_relocator_fw_leftover *next;
  72. struct grub_relocator_fw_leftover **prev;
  73. grub_phys_addr_t quantstart;
  74. grub_uint8_t freebytes[GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT / 8];
  75. };
  76. static struct grub_relocator_fw_leftover *leftovers;
  77. #endif
  78. static struct grub_relocator_extra_block *extra_blocks;
  79. void *
  80. get_virtual_current_address (grub_relocator_chunk_t in)
  81. {
  82. return in->srcv;
  83. }
  84. grub_phys_addr_t
  85. get_physical_target_address (grub_relocator_chunk_t in)
  86. {
  87. return in->target;
  88. }
  89. struct grub_relocator *
  90. grub_relocator_new (void)
  91. {
  92. struct grub_relocator *ret;
  93. grub_cpu_relocator_init ();
  94. ret = grub_zalloc (sizeof (struct grub_relocator));
  95. if (!ret)
  96. return NULL;
  97. ret->postchunks = ~(grub_phys_addr_t) 0;
  98. ret->relocators_size = grub_relocator_jumper_size;
  99. grub_dprintf ("relocator", "relocators_size=%lu\n",
  100. (unsigned long) ret->relocators_size);
  101. return ret;
  102. }
  103. #define DIGITSORT_BITS 8
  104. #define DIGITSORT_MASK ((1 << DIGITSORT_BITS) - 1)
  105. #define BITS_IN_BYTE 8
  106. #define max(a, b) (((a) > (b)) ? (a) : (b))
  107. #define min(a, b) (((a) < (b)) ? (a) : (b))
  108. static inline int
  109. is_start (int type)
  110. {
  111. return !(type & 1) && (type != COLLISION_START);
  112. }
  113. static void
  114. allocate_regstart (grub_phys_addr_t addr, grub_size_t size, grub_mm_region_t rb,
  115. grub_mm_region_t *regancestor, grub_mm_header_t hancestor)
  116. {
  117. grub_addr_t newreg_start, newreg_raw_start
  118. = (grub_addr_t) rb + (addr - grub_vtop (rb)) + size;
  119. grub_addr_t newreg_size, newreg_presize;
  120. grub_mm_header_t new_header;
  121. grub_mm_header_t hb = (grub_mm_header_t) (rb + 1);
  122. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  123. grub_dprintf ("relocator", "ra = %p, rb = %p\n", regancestor, rb);
  124. #endif
  125. newreg_start = ALIGN_UP (newreg_raw_start, GRUB_MM_ALIGN);
  126. newreg_presize = newreg_start - newreg_raw_start;
  127. newreg_size = rb->size - (newreg_start - (grub_addr_t) rb);
  128. if ((hb->size << GRUB_MM_ALIGN_LOG2) >= newreg_start
  129. - (grub_addr_t) rb)
  130. {
  131. grub_mm_header_t newhnext = hb->next;
  132. grub_size_t newhsize = ((hb->size << GRUB_MM_ALIGN_LOG2)
  133. - (newreg_start
  134. - (grub_addr_t) rb)) >> GRUB_MM_ALIGN_LOG2;
  135. new_header = (void *) (newreg_start + sizeof (*rb));
  136. if (newhnext == hb)
  137. newhnext = new_header;
  138. new_header->next = newhnext;
  139. new_header->size = newhsize;
  140. new_header->magic = GRUB_MM_FREE_MAGIC;
  141. }
  142. else
  143. {
  144. new_header = hb->next;
  145. if (new_header == hb)
  146. new_header = (void *) (newreg_start + sizeof (*rb));
  147. }
  148. {
  149. struct grub_mm_header *newregfirst = rb->first;
  150. struct grub_mm_region *newregnext = rb->next;
  151. struct grub_mm_region *newreg = (void *) newreg_start;
  152. hancestor->next = new_header;
  153. if (newregfirst == hb)
  154. newregfirst = new_header;
  155. newreg->first = newregfirst;
  156. newreg->next = newregnext;
  157. newreg->pre_size = newreg_presize;
  158. newreg->size = newreg_size;
  159. *regancestor = newreg;
  160. {
  161. grub_mm_header_t h = newreg->first, hp = NULL;
  162. do
  163. {
  164. if ((void *) h < (void *) (newreg + 1))
  165. grub_fatal ("Failed to adjust memory region: %p, %p, %p, %p, %p",
  166. newreg, newreg->first, h, hp, hb);
  167. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  168. if ((void *) h == (void *) (newreg + 1))
  169. grub_dprintf ("relocator",
  170. "Free start memory region: %p, %p, %p, %p, %p",
  171. newreg, newreg->first, h, hp, hb);
  172. #endif
  173. hp = h;
  174. h = h->next;
  175. }
  176. while (h != newreg->first);
  177. }
  178. }
  179. }
  180. static void
  181. allocate_inreg (grub_phys_addr_t paddr, grub_size_t size,
  182. grub_mm_header_t hb, grub_mm_header_t hbp,
  183. grub_mm_region_t rb)
  184. {
  185. struct grub_mm_header *foll = NULL;
  186. grub_addr_t vaddr = (grub_addr_t) hb + (paddr - grub_vtop (hb));
  187. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  188. grub_dprintf ("relocator", "inreg paddr = 0x%lx, size = %lu,"
  189. " hb = %p, hbp = %p, rb = %p, vaddr = 0x%lx\n",
  190. (unsigned long) paddr, (unsigned long) size, hb, hbp,
  191. rb, (unsigned long) vaddr);
  192. #endif
  193. if (ALIGN_UP (vaddr + size, GRUB_MM_ALIGN) + GRUB_MM_ALIGN
  194. <= (grub_addr_t) (hb + hb->size))
  195. {
  196. foll = (void *) ALIGN_UP (vaddr + size, GRUB_MM_ALIGN);
  197. foll->magic = GRUB_MM_FREE_MAGIC;
  198. foll->size = hb + hb->size - foll;
  199. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  200. grub_dprintf ("relocator", "foll = %p, foll->size = %lu\n", foll,
  201. (unsigned long) foll->size);
  202. #endif
  203. }
  204. if (vaddr - (grub_addr_t) hb >= sizeof (*hb))
  205. {
  206. hb->size = ((vaddr - (grub_addr_t) hb) >> GRUB_MM_ALIGN_LOG2);
  207. if (foll)
  208. {
  209. foll->next = hb;
  210. hbp->next = foll;
  211. if (rb->first == hb)
  212. {
  213. rb->first = foll;
  214. }
  215. }
  216. }
  217. else
  218. {
  219. if (foll)
  220. {
  221. foll->next = hb->next;
  222. }
  223. else
  224. foll = hb->next;
  225. hbp->next = foll;
  226. if (rb->first == hb)
  227. {
  228. rb->first = foll;
  229. }
  230. if (rb->first == hb)
  231. {
  232. rb->first = (void *) (rb + 1);
  233. }
  234. }
  235. }
  236. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  237. static void
  238. check_leftover (struct grub_relocator_fw_leftover *lo)
  239. {
  240. unsigned i;
  241. for (i = 0; i < sizeof (lo->freebytes); i++)
  242. if (lo->freebytes[i] != 0xff)
  243. return;
  244. grub_relocator_firmware_free_region (lo->quantstart,
  245. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  246. *lo->prev = lo->next;
  247. if (lo->next)
  248. lo->next->prev = lo->prev;
  249. }
  250. #endif
  251. static void
  252. free_subchunk (const struct grub_relocator_subchunk *subchu)
  253. {
  254. switch (subchu->type)
  255. {
  256. case CHUNK_TYPE_REGION_START:
  257. {
  258. grub_mm_region_t r1, r2, *rp;
  259. grub_mm_header_t h;
  260. grub_size_t pre_size;
  261. r1 = subchu->reg;
  262. r2 = (grub_mm_region_t) ALIGN_UP ((grub_addr_t) subchu->reg
  263. + (grub_vtop (subchu->reg)
  264. - subchu->start) + subchu->size,
  265. GRUB_MM_ALIGN);
  266. for (rp = &grub_mm_base; *rp && *rp != r2; rp = &((*rp)->next));
  267. pre_size = subchu->pre_size;
  268. if (*rp)
  269. {
  270. grub_mm_header_t h2, *hp;
  271. r1->first = r2->first;
  272. r1->next = r2->next;
  273. r1->pre_size = pre_size;
  274. r1->size = r2->size + (r2 - r1) * sizeof (*r2);
  275. *rp = r1;
  276. h = (grub_mm_header_t) (r1 + 1);
  277. h->next = r2->first;
  278. h->magic = GRUB_MM_FREE_MAGIC;
  279. h->size = (r2 - r1 - 1);
  280. for (hp = &r2->first, h2 = *hp; h2->next != r2->first;
  281. hp = &(h2->next), h2 = *hp)
  282. if (h2 == (grub_mm_header_t) (r2 + 1))
  283. break;
  284. if (h2 == (grub_mm_header_t) (r2 + 1))
  285. {
  286. h->size = h2->size + (h2 - h);
  287. h->next = h2->next;
  288. *hp = h;
  289. if (hp == &r2->first)
  290. {
  291. for (h2 = r2->first; h2->next != r2->first; h2 = h2->next);
  292. h2->next = h;
  293. }
  294. }
  295. else
  296. {
  297. h2->next = h;
  298. }
  299. }
  300. else
  301. {
  302. r1->pre_size = pre_size;
  303. r1->size = (r2 - r1) * sizeof (*r2);
  304. /* Find where to insert this region.
  305. Put a smaller one before bigger ones,
  306. to prevent fragmentation. */
  307. for (rp = &grub_mm_base; *rp; rp = &((*rp)->next))
  308. if ((*rp)->size > r1->size)
  309. break;
  310. r1->next = *rp;
  311. *rp = r1->next;
  312. h = (grub_mm_header_t) (r1 + 1);
  313. r1->first = h;
  314. h->next = h;
  315. h->magic = GRUB_MM_FREE_MAGIC;
  316. h->size = (r2 - r1 - 1);
  317. }
  318. for (r2 = grub_mm_base; r2; r2 = r2->next)
  319. if ((grub_addr_t) r2 + r2->size == (grub_addr_t) r1)
  320. break;
  321. if (r2)
  322. {
  323. grub_mm_header_t hl2, hl, g;
  324. g = (grub_mm_header_t) ((grub_addr_t) r2 + r2->size);
  325. g->size = (grub_mm_header_t) r1 - g;
  326. r2->size += r1->size;
  327. for (hl = r2->first; hl->next != r2->first; hl = hl->next);
  328. for (hl2 = r1->first; hl2->next != r1->first; hl2 = hl2->next);
  329. hl2->next = r2->first;
  330. r2->first = r1->first;
  331. hl->next = r2->first;
  332. *rp = (*rp)->next;
  333. grub_free (g + 1);
  334. }
  335. break;
  336. }
  337. case CHUNK_TYPE_IN_REGION:
  338. {
  339. grub_mm_header_t h = (grub_mm_header_t) ALIGN_DOWN ((grub_addr_t) subchu->start,
  340. GRUB_MM_ALIGN);
  341. h->size
  342. = ((subchu->start + subchu->size + GRUB_MM_ALIGN - 1) / GRUB_MM_ALIGN)
  343. - (subchu->start / GRUB_MM_ALIGN) - 1;
  344. h->next = h;
  345. h->magic = GRUB_MM_ALLOC_MAGIC;
  346. grub_free (h + 1);
  347. break;
  348. }
  349. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  350. case CHUNK_TYPE_FIRMWARE:
  351. case CHUNK_TYPE_LEFTOVER:
  352. {
  353. grub_addr_t fstart, fend;
  354. fstart = ALIGN_UP (subchu->start,
  355. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  356. fend = ALIGN_DOWN (subchu->start + subchu->size,
  357. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  358. if (fstart < fend)
  359. grub_relocator_firmware_free_region (fstart, fend - fstart);
  360. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  361. if (subchu->pre)
  362. {
  363. int off = subchu->start - fstart
  364. - GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  365. grub_memset (subchu->pre->freebytes + off / 8 + 1,
  366. 0xff, sizeof (subchu->pre->freebytes) - off / 8 - 1);
  367. subchu->pre->freebytes[off / 8] |= ~((1 << (off % 8)) - 1);
  368. check_leftover (subchu->pre);
  369. }
  370. if (subchu->post)
  371. {
  372. int off = subchu->start + subchu->size - fend;
  373. grub_memset (subchu->pre->freebytes,
  374. 0xff, sizeof (subchu->pre->freebytes) - off / 8);
  375. subchu->pre->freebytes[off / 8] |= ((1 << (8 - (off % 8))) - 1);
  376. check_leftover (subchu->post);
  377. }
  378. #endif
  379. *subchu->extra->prev = subchu->extra->next;
  380. grub_free (subchu->extra);
  381. }
  382. break;
  383. #endif
  384. }
  385. }
  386. static int
  387. malloc_in_range (struct grub_relocator *rel,
  388. grub_addr_t start, grub_addr_t end, grub_addr_t align,
  389. grub_size_t size, struct grub_relocator_chunk *res,
  390. int from_low_priv, int collisioncheck)
  391. {
  392. grub_mm_region_t r, *ra, base_saved;
  393. struct grub_relocator_mmap_event *events = NULL, *eventt = NULL, *t;
  394. /* 128 is just in case of additional malloc (shouldn't happen). */
  395. unsigned maxevents = 2 + 128;
  396. grub_mm_header_t p, pa;
  397. unsigned *counter;
  398. int nallocs = 0;
  399. unsigned j, N = 0;
  400. grub_addr_t target = 0;
  401. grub_dprintf ("relocator",
  402. "trying to allocate in 0x%lx-0x%lx aligned 0x%lx size 0x%lx\n",
  403. (unsigned long) start, (unsigned long) end,
  404. (unsigned long) align, (unsigned long) size);
  405. start = ALIGN_UP (start, align);
  406. end = ALIGN_DOWN (end - size, align) + size;
  407. if (end < start + size)
  408. return 0;
  409. /* We have to avoid any allocations when filling scanline events.
  410. Hence 2-stages.
  411. */
  412. for (r = grub_mm_base; r; r = r->next)
  413. {
  414. p = r->first;
  415. do
  416. {
  417. if ((grub_addr_t) p < (grub_addr_t) (r + 1)
  418. || (grub_addr_t) p >= (grub_addr_t) (r + 1) + r->size)
  419. grub_fatal ("%d: out of range pointer: %p\n", __LINE__, p);
  420. maxevents += 2;
  421. p = p->next;
  422. }
  423. while (p != r->first);
  424. maxevents += 4;
  425. }
  426. if (collisioncheck && rel)
  427. {
  428. struct grub_relocator_chunk *chunk;
  429. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  430. maxevents += 2;
  431. }
  432. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  433. {
  434. struct grub_relocator_extra_block *cur;
  435. for (cur = extra_blocks; cur; cur = cur->next)
  436. maxevents += 2;
  437. }
  438. for (r = grub_mm_base; r; r = r->next)
  439. maxevents += 2;
  440. maxevents += grub_relocator_firmware_get_max_events ();
  441. #endif
  442. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  443. {
  444. COMPILE_TIME_ASSERT (GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT % 8 == 0);
  445. struct grub_relocator_fw_leftover *cur;
  446. for (cur = leftovers; cur; cur = cur->next)
  447. {
  448. int l = 0;
  449. unsigned i;
  450. for (i = 0; i < GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT; i++)
  451. {
  452. if (l != ((cur->freebytes[i / 8] >> (i % 8)) & 1))
  453. maxevents++;
  454. l = ((cur->freebytes[i / 8] >> (i % 8)) & 1);
  455. }
  456. if (l)
  457. maxevents++;
  458. }
  459. }
  460. #endif
  461. eventt = grub_calloc (maxevents, sizeof (events[0]));
  462. counter = grub_malloc ((DIGITSORT_MASK + 2) * sizeof (counter[0]));
  463. events = grub_calloc (maxevents, sizeof (events[0]));
  464. if (!events || !eventt || !counter)
  465. {
  466. grub_dprintf ("relocator", "events or counter allocation failed %d\n",
  467. maxevents);
  468. grub_free (events);
  469. grub_free (eventt);
  470. grub_free (counter);
  471. return 0;
  472. }
  473. if (collisioncheck && rel)
  474. {
  475. struct grub_relocator_chunk *chunk;
  476. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  477. {
  478. events[N].type = COLLISION_START;
  479. events[N].pos = chunk->target;
  480. N++;
  481. events[N].type = COLLISION_END;
  482. events[N].pos = chunk->target + chunk->size;
  483. N++;
  484. }
  485. }
  486. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  487. for (r = grub_mm_base; r; r = r->next)
  488. {
  489. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  490. grub_dprintf ("relocator", "Blocking at 0x%lx-0x%lx\n",
  491. (unsigned long) r - r->pre_size,
  492. (unsigned long) (r + 1) + r->size);
  493. #endif
  494. events[N].type = FIRMWARE_BLOCK_START;
  495. events[N].pos = (grub_addr_t) r - r->pre_size;
  496. N++;
  497. events[N].type = FIRMWARE_BLOCK_END;
  498. events[N].pos = (grub_addr_t) (r + 1) + r->size;
  499. N++;
  500. }
  501. {
  502. struct grub_relocator_extra_block *cur;
  503. for (cur = extra_blocks; cur; cur = cur->next)
  504. {
  505. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  506. grub_dprintf ("relocator", "Blocking at 0x%lx-0x%lx\n",
  507. (unsigned long) cur->start, (unsigned long) cur->end);
  508. #endif
  509. events[N].type = FIRMWARE_BLOCK_START;
  510. events[N].pos = cur->start;
  511. N++;
  512. events[N].type = FIRMWARE_BLOCK_END;
  513. events[N].pos = cur->end;
  514. N++;
  515. }
  516. }
  517. N += grub_relocator_firmware_fill_events (events + N);
  518. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  519. {
  520. struct grub_relocator_fw_leftover *cur;
  521. for (cur = leftovers; cur; cur = cur->next)
  522. {
  523. unsigned i;
  524. int l = 0;
  525. for (i = 0; i < GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT; i++)
  526. {
  527. if (l != ((cur->freebytes[i / 8] >> (i % 8)) & 1))
  528. {
  529. events[N].type = l ? REG_LEFTOVER_END : REG_LEFTOVER_START;
  530. events[N].pos = cur->quantstart + i;
  531. events[N].leftover = cur;
  532. N++;
  533. }
  534. l = ((cur->freebytes[i / 8] >> (i % 8)) & 1);
  535. }
  536. if (l)
  537. {
  538. events[N].type = REG_LEFTOVER_END;
  539. events[N].pos = cur->quantstart + i;
  540. events[N].leftover = cur;
  541. N++;
  542. }
  543. }
  544. }
  545. #endif
  546. #endif
  547. /* No malloc from this point. */
  548. base_saved = grub_mm_base;
  549. grub_mm_base = NULL;
  550. for (ra = &base_saved, r = *ra; r; ra = &(r->next), r = *ra)
  551. {
  552. pa = r->first;
  553. p = pa->next;
  554. if (p->magic == GRUB_MM_ALLOC_MAGIC)
  555. continue;
  556. do
  557. {
  558. if (p->magic != GRUB_MM_FREE_MAGIC)
  559. grub_fatal ("%s:%d free magic broken at %p (0x%x)\n",
  560. __FILE__,
  561. __LINE__, p, p->magic);
  562. if (p == (grub_mm_header_t) (r + 1))
  563. {
  564. events[N].type = REG_BEG_START;
  565. events[N].pos = grub_vtop (r) - r->pre_size;
  566. events[N].reg = r;
  567. events[N].regancestor = ra;
  568. events[N].head = p;
  569. events[N].hancestor = pa;
  570. N++;
  571. events[N].type = REG_BEG_END;
  572. events[N].pos = grub_vtop (p + p->size) - sizeof (*r)
  573. - sizeof (struct grub_mm_header);
  574. N++;
  575. }
  576. else
  577. {
  578. events[N].type = IN_REG_START;
  579. events[N].pos = grub_vtop (p);
  580. events[N].head = p;
  581. events[N].hancestor = pa;
  582. events[N].reg = r;
  583. N++;
  584. events[N].type = IN_REG_END;
  585. events[N].pos = grub_vtop (p + p->size);
  586. N++;
  587. }
  588. pa = p;
  589. p = pa->next;
  590. }
  591. while (pa != r->first);
  592. }
  593. /* Put ending events after starting events. */
  594. {
  595. int st = 0, e = N / 2;
  596. for (j = 0; j < N; j++)
  597. if (is_start (events[j].type) || events[j].type == COLLISION_START)
  598. eventt[st++] = events[j];
  599. else
  600. eventt[e++] = events[j];
  601. t = eventt;
  602. eventt = events;
  603. events = t;
  604. }
  605. {
  606. unsigned i;
  607. for (i = 0; i < (BITS_IN_BYTE * sizeof (grub_addr_t) / DIGITSORT_BITS);
  608. i++)
  609. {
  610. grub_memset (counter, 0, (1 + (1 << DIGITSORT_BITS)) * sizeof (counter[0]));
  611. for (j = 0; j < N; j++)
  612. counter[((events[j].pos >> (DIGITSORT_BITS * i))
  613. & DIGITSORT_MASK) + 1]++;
  614. for (j = 0; j <= DIGITSORT_MASK; j++)
  615. counter[j+1] += counter[j];
  616. for (j = 0; j < N; j++)
  617. eventt[counter[((events[j].pos >> (DIGITSORT_BITS * i))
  618. & DIGITSORT_MASK)]++] = events[j];
  619. t = eventt;
  620. eventt = events;
  621. events = t;
  622. }
  623. }
  624. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  625. retry:
  626. #endif
  627. /* Now events are nicely sorted. */
  628. {
  629. int nstarted = 0, ncollisions = 0, nstartedfw = 0, nblockfw = 0;
  630. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  631. int nlefto = 0;
  632. #else
  633. const int nlefto = 0;
  634. #endif
  635. grub_addr_t starta = 0;
  636. for (j = from_low_priv ? 0 : N - 1; from_low_priv ? j < N : (j + 1);
  637. from_low_priv ? j++ : j--)
  638. {
  639. int isinsidebefore, isinsideafter;
  640. isinsidebefore = (!ncollisions && (nstarted || (((nlefto || nstartedfw)
  641. && !nblockfw))));
  642. switch (events[j].type)
  643. {
  644. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  645. case REG_FIRMWARE_START:
  646. nstartedfw++;
  647. break;
  648. case REG_FIRMWARE_END:
  649. nstartedfw--;
  650. break;
  651. case FIRMWARE_BLOCK_START:
  652. nblockfw++;
  653. break;
  654. case FIRMWARE_BLOCK_END:
  655. nblockfw--;
  656. break;
  657. #endif
  658. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  659. case REG_LEFTOVER_START:
  660. nlefto++;
  661. break;
  662. case REG_LEFTOVER_END:
  663. nlefto--;
  664. break;
  665. #endif
  666. case COLLISION_START:
  667. ncollisions++;
  668. break;
  669. case COLLISION_END:
  670. ncollisions--;
  671. break;
  672. case IN_REG_START:
  673. case REG_BEG_START:
  674. nstarted++;
  675. break;
  676. case IN_REG_END:
  677. case REG_BEG_END:
  678. nstarted--;
  679. break;
  680. }
  681. isinsideafter = (!ncollisions && (nstarted || ((nlefto || nstartedfw)
  682. && !nblockfw)));
  683. if (from_low_priv) {
  684. if (!isinsidebefore && isinsideafter)
  685. starta = ALIGN_UP (events[j].pos, align);
  686. if (isinsidebefore && !isinsideafter)
  687. {
  688. target = starta;
  689. if (target < start)
  690. target = ALIGN_UP (start, align);
  691. if (target + size <= end && target + size <= events[j].pos)
  692. /* Found an usable address. */
  693. goto found;
  694. }
  695. } else {
  696. if (!isinsidebefore && isinsideafter)
  697. {
  698. if (events[j].pos >= size)
  699. starta = ALIGN_DOWN (events[j].pos - size, align) + size;
  700. else
  701. starta = 0;
  702. }
  703. if (isinsidebefore && !isinsideafter && starta >= size)
  704. {
  705. target = starta - size;
  706. if (target > end - size)
  707. target = ALIGN_DOWN (end - size, align);
  708. if (target >= start && target >= events[j].pos)
  709. goto found;
  710. }
  711. }
  712. }
  713. }
  714. grub_mm_base = base_saved;
  715. grub_free (events);
  716. grub_free (eventt);
  717. grub_free (counter);
  718. return 0;
  719. found:
  720. {
  721. int inreg = 0, regbeg = 0, ncol = 0;
  722. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  723. int fwin = 0, fwb = 0, fwlefto = 0;
  724. #endif
  725. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  726. int last_lo = 0;
  727. #endif
  728. int last_start = 0;
  729. for (j = 0; j < N; j++)
  730. {
  731. int typepre;
  732. if (ncol)
  733. typepre = -1;
  734. else if (regbeg)
  735. typepre = CHUNK_TYPE_REGION_START;
  736. else if (inreg)
  737. typepre = CHUNK_TYPE_IN_REGION;
  738. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  739. else if (fwin && !fwb)
  740. typepre = CHUNK_TYPE_FIRMWARE;
  741. else if (fwlefto && !fwb)
  742. typepre = CHUNK_TYPE_LEFTOVER;
  743. #endif
  744. else
  745. typepre = -1;
  746. if (j != 0 && events[j - 1].pos != events[j].pos)
  747. {
  748. grub_addr_t alloc_start, alloc_end;
  749. alloc_start = max (events[j - 1].pos, target);
  750. alloc_end = min (events[j].pos, target + size);
  751. if (alloc_end > alloc_start)
  752. {
  753. switch (typepre)
  754. {
  755. case CHUNK_TYPE_REGION_START:
  756. allocate_regstart (alloc_start, alloc_end - alloc_start,
  757. events[last_start].reg,
  758. events[last_start].regancestor,
  759. events[last_start].hancestor);
  760. /* TODO: maintain a reverse lookup tree for hancestor. */
  761. {
  762. unsigned k;
  763. for (k = 0; k < N; k++)
  764. if (events[k].hancestor == events[last_start].head)
  765. events[k].hancestor = events[last_start].hancestor;
  766. }
  767. break;
  768. case CHUNK_TYPE_IN_REGION:
  769. allocate_inreg (alloc_start, alloc_end - alloc_start,
  770. events[last_start].head,
  771. events[last_start].hancestor,
  772. events[last_start].reg);
  773. {
  774. unsigned k;
  775. for (k = 0; k < N; k++)
  776. if (events[k].hancestor == events[last_start].head)
  777. events[k].hancestor = events[last_start].hancestor;
  778. }
  779. break;
  780. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  781. case CHUNK_TYPE_FIRMWARE:
  782. {
  783. grub_addr_t fstart, fend;
  784. fstart
  785. = ALIGN_DOWN (alloc_start,
  786. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  787. fend
  788. = ALIGN_UP (alloc_end,
  789. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  790. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  791. grub_dprintf ("relocator", "requesting %lx-%lx\n",
  792. (unsigned long) fstart,
  793. (unsigned long) fend);
  794. #endif
  795. /* The failure here can be very expensive. */
  796. if (!grub_relocator_firmware_alloc_region (fstart,
  797. fend - fstart))
  798. {
  799. if (from_low_priv)
  800. start = fend;
  801. else
  802. end = fstart;
  803. goto retry;
  804. }
  805. break;
  806. }
  807. #endif
  808. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  809. case CHUNK_TYPE_LEFTOVER:
  810. {
  811. unsigned offstart = alloc_start
  812. % GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  813. unsigned offend = alloc_end
  814. % GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  815. struct grub_relocator_fw_leftover *lo
  816. = events[last_lo].leftover;
  817. if (offend == 0 && alloc_end != alloc_start)
  818. offend = GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  819. lo->freebytes[offstart / 8]
  820. &= ((1 << (8 - (start % 8))) - 1);
  821. if (offend / 8 > (offstart + 7) / 8)
  822. grub_memset (lo->freebytes + (offstart + 7) / 8, 0,
  823. offend / 8 - (offstart + 7) / 8);
  824. if (offend < GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT)
  825. lo->freebytes[offend / 8] &= ~((1 << (offend % 8)) - 1);
  826. }
  827. break;
  828. #endif
  829. }
  830. nallocs++;
  831. }
  832. }
  833. switch (events[j].type)
  834. {
  835. case REG_BEG_START:
  836. case IN_REG_START:
  837. if (events[j].type == REG_BEG_START &&
  838. (grub_addr_t) (events[j].reg + 1) > target)
  839. regbeg++;
  840. else
  841. inreg++;
  842. last_start = j;
  843. break;
  844. case REG_BEG_END:
  845. case IN_REG_END:
  846. if (regbeg)
  847. regbeg--;
  848. else
  849. inreg--;
  850. break;
  851. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  852. case REG_FIRMWARE_START:
  853. fwin++;
  854. break;
  855. case REG_FIRMWARE_END:
  856. fwin--;
  857. break;
  858. case FIRMWARE_BLOCK_START:
  859. fwb++;
  860. break;
  861. case FIRMWARE_BLOCK_END:
  862. fwb--;
  863. break;
  864. #endif
  865. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  866. case REG_LEFTOVER_START:
  867. fwlefto++;
  868. last_lo = j;
  869. break;
  870. case REG_LEFTOVER_END:
  871. fwlefto--;
  872. break;
  873. #endif
  874. case COLLISION_START:
  875. ncol++;
  876. break;
  877. case COLLISION_END:
  878. ncol--;
  879. break;
  880. }
  881. }
  882. }
  883. /* Malloc is available again. */
  884. grub_mm_base = base_saved;
  885. grub_free (eventt);
  886. grub_free (counter);
  887. {
  888. int last_start = 0;
  889. int inreg = 0, regbeg = 0, ncol = 0;
  890. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  891. int fwin = 0, fwlefto = 0, fwb = 0;
  892. #endif
  893. unsigned cural = 0;
  894. int oom = 0;
  895. res->subchunks = grub_calloc (nallocs, sizeof (res->subchunks[0]));
  896. if (!res->subchunks)
  897. oom = 1;
  898. res->nsubchunks = nallocs;
  899. for (j = 0; j < N; j++)
  900. {
  901. int typepre;
  902. if (ncol)
  903. typepre = -1;
  904. else if (regbeg)
  905. typepre = CHUNK_TYPE_REGION_START;
  906. else if (inreg)
  907. typepre = CHUNK_TYPE_IN_REGION;
  908. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  909. else if (fwin && !fwb)
  910. typepre = CHUNK_TYPE_FIRMWARE;
  911. else if (fwlefto && !fwb)
  912. typepre = CHUNK_TYPE_LEFTOVER;
  913. #endif
  914. else
  915. typepre = -1;
  916. if (j != 0 && events[j - 1].pos != events[j].pos)
  917. {
  918. grub_addr_t alloc_start, alloc_end;
  919. struct grub_relocator_subchunk tofree = {0};
  920. struct grub_relocator_subchunk *curschu = &tofree;
  921. if (!oom)
  922. curschu = &res->subchunks[cural];
  923. alloc_start = max (events[j - 1].pos, target);
  924. alloc_end = min (events[j].pos, target + size);
  925. if (alloc_end > alloc_start)
  926. {
  927. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  928. grub_dprintf ("relocator", "subchunk 0x%lx-0x%lx, %d\n",
  929. (unsigned long) alloc_start,
  930. (unsigned long) alloc_end, typepre);
  931. #endif
  932. curschu->type = typepre;
  933. curschu->start = alloc_start;
  934. curschu->size = alloc_end - alloc_start;
  935. if (typepre == CHUNK_TYPE_REGION_START
  936. || typepre == CHUNK_TYPE_IN_REGION)
  937. {
  938. curschu->reg = events[last_start].reg;
  939. curschu->pre_size = alloc_start - events[j - 1].pos;
  940. }
  941. if (!oom && (typepre == CHUNK_TYPE_REGION_START
  942. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  943. || typepre == CHUNK_TYPE_FIRMWARE
  944. #endif
  945. ))
  946. {
  947. struct grub_relocator_extra_block *ne;
  948. ne = grub_malloc (sizeof (*ne));
  949. if (!ne)
  950. {
  951. oom = 1;
  952. grub_memcpy (&tofree, curschu, sizeof (tofree));
  953. }
  954. else
  955. {
  956. ne->start = alloc_start;
  957. ne->end = alloc_end;
  958. ne->next = extra_blocks;
  959. ne->prev = &extra_blocks;
  960. if (extra_blocks)
  961. extra_blocks->prev = &(ne->next);
  962. extra_blocks = ne;
  963. curschu->extra = ne;
  964. }
  965. }
  966. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  967. if (!oom && typepre == CHUNK_TYPE_FIRMWARE)
  968. {
  969. grub_addr_t fstart, fend;
  970. fstart
  971. = ALIGN_DOWN (alloc_start,
  972. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  973. fend
  974. = ALIGN_UP (alloc_end,
  975. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  976. {
  977. struct grub_relocator_fw_leftover *lo1 = NULL;
  978. struct grub_relocator_fw_leftover *lo2 = NULL;
  979. if (fstart != alloc_start)
  980. lo1 = grub_malloc (sizeof (*lo1));
  981. if (fend != alloc_end)
  982. lo2 = grub_malloc (sizeof (*lo2));
  983. if ((!lo1 && fstart != alloc_start)
  984. || (!lo2 && fend != alloc_end))
  985. {
  986. struct grub_relocator_extra_block *ne;
  987. grub_free (lo1);
  988. grub_free (lo2);
  989. lo1 = NULL;
  990. lo2 = NULL;
  991. oom = 1;
  992. grub_memcpy (&tofree, curschu, sizeof (tofree));
  993. ne = extra_blocks;
  994. extra_blocks = extra_blocks->next;
  995. grub_free (ne);
  996. }
  997. if (lo1)
  998. {
  999. lo1->quantstart = fstart;
  1000. grub_memset (lo1->freebytes, 0xff,
  1001. (alloc_start - fstart) / 8);
  1002. lo1->freebytes[(alloc_start - fstart) / 8]
  1003. = (1 << ((alloc_start - fstart) % 8)) - 1;
  1004. grub_memset (lo1->freebytes
  1005. + ((alloc_start - fstart) / 8) + 1, 0,
  1006. sizeof (lo1->freebytes)
  1007. - (alloc_start - fstart) / 8 - 1);
  1008. lo1->next = leftovers;
  1009. lo1->prev = &leftovers;
  1010. if (leftovers)
  1011. leftovers->prev = &lo1->next;
  1012. leftovers = lo1;
  1013. }
  1014. if (lo2)
  1015. {
  1016. lo2->quantstart
  1017. = fend - GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  1018. grub_memset (lo2->freebytes, 0,
  1019. (alloc_end - lo2->quantstart) / 8);
  1020. lo2->freebytes[(alloc_end - lo2->quantstart) / 8]
  1021. = ~((1 << ((alloc_end - lo2->quantstart) % 8)) - 1);
  1022. grub_memset (lo2->freebytes
  1023. + ((alloc_end - lo2->quantstart) / 8)
  1024. + 1, 0, sizeof (lo2->freebytes)
  1025. - (alloc_end - lo2->quantstart) / 8 - 1);
  1026. lo2->prev = &leftovers;
  1027. if (leftovers)
  1028. leftovers->prev = &lo2->next;
  1029. lo2->next = leftovers;
  1030. leftovers = lo2;
  1031. }
  1032. curschu->pre = lo1;
  1033. curschu->post = lo2;
  1034. }
  1035. }
  1036. if (typepre == CHUNK_TYPE_LEFTOVER)
  1037. {
  1038. curschu->pre = events[last_start].leftover;
  1039. curschu->post = events[last_start].leftover;
  1040. }
  1041. #endif
  1042. if (!oom)
  1043. cural++;
  1044. else
  1045. free_subchunk (&tofree);
  1046. }
  1047. }
  1048. switch (events[j].type)
  1049. {
  1050. case REG_BEG_START:
  1051. case IN_REG_START:
  1052. if (events[j].type == REG_BEG_START &&
  1053. (grub_addr_t) (events[j].reg + 1) > target)
  1054. regbeg++;
  1055. else
  1056. inreg++;
  1057. last_start = j;
  1058. break;
  1059. case REG_BEG_END:
  1060. case IN_REG_END:
  1061. inreg = regbeg = 0;
  1062. break;
  1063. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  1064. case REG_FIRMWARE_START:
  1065. fwin++;
  1066. break;
  1067. case REG_FIRMWARE_END:
  1068. fwin--;
  1069. break;
  1070. case FIRMWARE_BLOCK_START:
  1071. fwb++;
  1072. break;
  1073. case FIRMWARE_BLOCK_END:
  1074. fwb--;
  1075. break;
  1076. #endif
  1077. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  1078. case REG_LEFTOVER_START:
  1079. fwlefto++;
  1080. break;
  1081. case REG_LEFTOVER_END:
  1082. fwlefto--;
  1083. break;
  1084. #endif
  1085. case COLLISION_START:
  1086. ncol++;
  1087. break;
  1088. case COLLISION_END:
  1089. ncol--;
  1090. break;
  1091. }
  1092. }
  1093. if (oom)
  1094. {
  1095. unsigned i;
  1096. for (i = 0; i < cural; i++)
  1097. free_subchunk (&res->subchunks[i]);
  1098. grub_free (res->subchunks);
  1099. grub_dprintf ("relocator", "allocation failed with out-of-memory\n");
  1100. grub_free (events);
  1101. return 0;
  1102. }
  1103. }
  1104. res->src = target;
  1105. res->size = size;
  1106. grub_free (events);
  1107. grub_dprintf ("relocator", "allocated: 0x%lx+0x%lx\n", (unsigned long) target,
  1108. (unsigned long) size);
  1109. return 1;
  1110. }
  1111. static void
  1112. adjust_limits (struct grub_relocator *rel,
  1113. grub_phys_addr_t *min_addr, grub_phys_addr_t *max_addr,
  1114. grub_phys_addr_t in_min, grub_phys_addr_t in_max)
  1115. {
  1116. struct grub_relocator_chunk *chunk;
  1117. *min_addr = 0;
  1118. *max_addr = rel->postchunks;
  1119. /* Keep chunks in memory in the same order as they'll be after relocation. */
  1120. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1121. {
  1122. if (chunk->target > in_max && chunk->src < *max_addr
  1123. && chunk->src < rel->postchunks)
  1124. *max_addr = chunk->src;
  1125. if (chunk->target + chunk->size <= in_min
  1126. && chunk->src + chunk->size > *min_addr
  1127. && chunk->src < rel->postchunks)
  1128. *min_addr = chunk->src + chunk->size;
  1129. }
  1130. }
  1131. grub_err_t
  1132. grub_relocator_alloc_chunk_addr (struct grub_relocator *rel,
  1133. grub_relocator_chunk_t *out,
  1134. grub_phys_addr_t target, grub_size_t size)
  1135. {
  1136. struct grub_relocator_chunk *chunk;
  1137. grub_phys_addr_t min_addr = 0, max_addr;
  1138. if (target > ~size)
  1139. return grub_error (GRUB_ERR_BUG, "address is out of range");
  1140. adjust_limits (rel, &min_addr, &max_addr, target, target);
  1141. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1142. if ((chunk->target <= target && target < chunk->target + chunk->size)
  1143. || (target <= chunk->target && chunk->target < target + size))
  1144. return grub_error (GRUB_ERR_BUG, "overlap detected");
  1145. chunk = grub_malloc (sizeof (struct grub_relocator_chunk));
  1146. if (!chunk)
  1147. return grub_errno;
  1148. grub_dprintf ("relocator",
  1149. "min_addr = 0x%llx, max_addr = 0x%llx, target = 0x%llx\n",
  1150. (unsigned long long) min_addr, (unsigned long long) max_addr,
  1151. (unsigned long long) target);
  1152. do
  1153. {
  1154. /* A trick to improve Linux allocation. */
  1155. #if defined (__i386__) || defined (__x86_64__)
  1156. if (target < 0x100000)
  1157. if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
  1158. size, chunk, 0, 1))
  1159. {
  1160. if (rel->postchunks > chunk->src)
  1161. rel->postchunks = chunk->src;
  1162. break;
  1163. }
  1164. #endif
  1165. if (malloc_in_range (rel, target, max_addr, 1, size, chunk, 1, 0))
  1166. break;
  1167. if (malloc_in_range (rel, min_addr, target, 1, size, chunk, 0, 0))
  1168. break;
  1169. if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
  1170. size, chunk, 0, 1))
  1171. {
  1172. if (rel->postchunks > chunk->src)
  1173. rel->postchunks = chunk->src;
  1174. break;
  1175. }
  1176. grub_dprintf ("relocator", "not allocated\n");
  1177. grub_free (chunk);
  1178. return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  1179. }
  1180. while (0);
  1181. grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
  1182. (unsigned long long) chunk->src, (unsigned long long) target);
  1183. if (rel->highestaddr < target + size)
  1184. rel->highestaddr = target + size;
  1185. if (rel->highestaddr < chunk->src + size)
  1186. rel->highestaddr = chunk->src + size;
  1187. if (chunk->src < rel->postchunks)
  1188. {
  1189. if (rel->highestnonpostaddr < target + size)
  1190. rel->highestnonpostaddr = target + size;
  1191. if (rel->highestnonpostaddr < chunk->src + size)
  1192. rel->highestnonpostaddr = chunk->src + size;
  1193. }
  1194. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1195. (unsigned long) rel->relocators_size);
  1196. if (chunk->src < target)
  1197. rel->relocators_size += grub_relocator_backward_size;
  1198. if (chunk->src > target)
  1199. rel->relocators_size += grub_relocator_forward_size;
  1200. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1201. (unsigned long) rel->relocators_size);
  1202. chunk->target = target;
  1203. chunk->size = size;
  1204. chunk->next = rel->chunks;
  1205. rel->chunks = chunk;
  1206. grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
  1207. rel->chunks->next);
  1208. chunk->srcv = grub_map_memory (chunk->src, chunk->size);
  1209. *out = chunk;
  1210. #ifdef DEBUG_RELOCATOR
  1211. grub_memset (chunk->srcv, 0xfa, chunk->size);
  1212. grub_mm_check ();
  1213. #endif
  1214. return GRUB_ERR_NONE;
  1215. }
  1216. /* Context for grub_relocator_alloc_chunk_align. */
  1217. struct grub_relocator_alloc_chunk_align_ctx
  1218. {
  1219. grub_phys_addr_t min_addr, max_addr;
  1220. grub_size_t size, align;
  1221. int preference;
  1222. struct grub_relocator_chunk *chunk;
  1223. int found;
  1224. };
  1225. /* Helper for grub_relocator_alloc_chunk_align. */
  1226. static int
  1227. grub_relocator_alloc_chunk_align_iter (grub_uint64_t addr, grub_uint64_t sz,
  1228. grub_memory_type_t type, void *data)
  1229. {
  1230. struct grub_relocator_alloc_chunk_align_ctx *ctx = data;
  1231. grub_uint64_t candidate;
  1232. if (type != GRUB_MEMORY_AVAILABLE)
  1233. return 0;
  1234. candidate = ALIGN_UP (addr, ctx->align);
  1235. if (candidate < ctx->min_addr)
  1236. candidate = ALIGN_UP (ctx->min_addr, ctx->align);
  1237. if (candidate + ctx->size > addr + sz
  1238. || candidate > ALIGN_DOWN (ctx->max_addr, ctx->align))
  1239. return 0;
  1240. if (ctx->preference == GRUB_RELOCATOR_PREFERENCE_HIGH)
  1241. candidate = ALIGN_DOWN (min (addr + sz - ctx->size, ctx->max_addr),
  1242. ctx->align);
  1243. if (!ctx->found || (ctx->preference == GRUB_RELOCATOR_PREFERENCE_HIGH
  1244. && candidate > ctx->chunk->target))
  1245. ctx->chunk->target = candidate;
  1246. if (!ctx->found || (ctx->preference == GRUB_RELOCATOR_PREFERENCE_LOW
  1247. && candidate < ctx->chunk->target))
  1248. ctx->chunk->target = candidate;
  1249. ctx->found = 1;
  1250. return 0;
  1251. }
  1252. grub_err_t
  1253. grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
  1254. grub_relocator_chunk_t *out,
  1255. grub_phys_addr_t min_addr,
  1256. grub_phys_addr_t max_addr,
  1257. grub_size_t size, grub_size_t align,
  1258. int preference,
  1259. int avoid_efi_boot_services)
  1260. {
  1261. struct grub_relocator_alloc_chunk_align_ctx ctx = {
  1262. .min_addr = min_addr,
  1263. .max_addr = max_addr,
  1264. .size = size,
  1265. .align = align,
  1266. .preference = preference,
  1267. .found = 0
  1268. };
  1269. grub_addr_t min_addr2 = 0, max_addr2;
  1270. if (size && (max_addr > ~size))
  1271. max_addr = ~size + 1;
  1272. #ifdef GRUB_MACHINE_PCBIOS
  1273. if (min_addr < 0x1000)
  1274. min_addr = 0x1000;
  1275. #endif
  1276. grub_dprintf ("relocator", "chunks = %p\n", rel->chunks);
  1277. ctx.chunk = grub_malloc (sizeof (struct grub_relocator_chunk));
  1278. if (!ctx.chunk)
  1279. return grub_errno;
  1280. if (malloc_in_range (rel, min_addr, max_addr, align,
  1281. size, ctx.chunk,
  1282. preference != GRUB_RELOCATOR_PREFERENCE_HIGH, 1))
  1283. {
  1284. grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
  1285. (unsigned long long) ctx.chunk->src,
  1286. (unsigned long long) ctx.chunk->src);
  1287. grub_dprintf ("relocator", "chunks = %p\n", rel->chunks);
  1288. ctx.chunk->target = ctx.chunk->src;
  1289. ctx.chunk->size = size;
  1290. ctx.chunk->next = rel->chunks;
  1291. rel->chunks = ctx.chunk;
  1292. ctx.chunk->srcv = grub_map_memory (ctx.chunk->src, ctx.chunk->size);
  1293. *out = ctx.chunk;
  1294. return GRUB_ERR_NONE;
  1295. }
  1296. adjust_limits (rel, &min_addr2, &max_addr2, min_addr, max_addr);
  1297. grub_dprintf ("relocator", "Adjusted limits from %lx-%lx to %lx-%lx\n",
  1298. (unsigned long) min_addr, (unsigned long) max_addr,
  1299. (unsigned long) min_addr2, (unsigned long) max_addr2);
  1300. do
  1301. {
  1302. if (malloc_in_range (rel, min_addr2, max_addr2, align,
  1303. size, ctx.chunk, 1, 1))
  1304. break;
  1305. if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
  1306. size, ctx.chunk, 0, 1))
  1307. {
  1308. if (rel->postchunks > ctx.chunk->src)
  1309. rel->postchunks = ctx.chunk->src;
  1310. break;
  1311. }
  1312. return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  1313. }
  1314. while (0);
  1315. {
  1316. #ifdef GRUB_MACHINE_EFI
  1317. grub_efi_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx,
  1318. avoid_efi_boot_services);
  1319. #elif defined (__powerpc__) || defined (GRUB_MACHINE_XEN)
  1320. (void) avoid_efi_boot_services;
  1321. grub_machine_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx);
  1322. #else
  1323. (void) avoid_efi_boot_services;
  1324. grub_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx);
  1325. #endif
  1326. if (!ctx.found)
  1327. return grub_error (GRUB_ERR_BAD_OS, "couldn't find suitable memory target");
  1328. }
  1329. while (1)
  1330. {
  1331. struct grub_relocator_chunk *chunk2;
  1332. for (chunk2 = rel->chunks; chunk2; chunk2 = chunk2->next)
  1333. if ((chunk2->target <= ctx.chunk->target
  1334. && ctx.chunk->target < chunk2->target + chunk2->size)
  1335. || (ctx.chunk->target <= chunk2->target && chunk2->target
  1336. < ctx.chunk->target + size))
  1337. {
  1338. if (preference == GRUB_RELOCATOR_PREFERENCE_HIGH)
  1339. ctx.chunk->target = ALIGN_DOWN (chunk2->target, align);
  1340. else
  1341. ctx.chunk->target = ALIGN_UP (chunk2->target + chunk2->size,
  1342. align);
  1343. break;
  1344. }
  1345. if (!chunk2)
  1346. break;
  1347. }
  1348. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1349. (unsigned long) rel->relocators_size);
  1350. if (ctx.chunk->src < ctx.chunk->target)
  1351. rel->relocators_size += grub_relocator_backward_size;
  1352. if (ctx.chunk->src > ctx.chunk->target)
  1353. rel->relocators_size += grub_relocator_forward_size;
  1354. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1355. (unsigned long) rel->relocators_size);
  1356. ctx.chunk->size = size;
  1357. ctx.chunk->next = rel->chunks;
  1358. rel->chunks = ctx.chunk;
  1359. grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
  1360. rel->chunks->next);
  1361. ctx.chunk->srcv = grub_map_memory (ctx.chunk->src, ctx.chunk->size);
  1362. *out = ctx.chunk;
  1363. #ifdef DEBUG_RELOCATOR
  1364. grub_memset (ctx.chunk->srcv, 0xfa, ctx.chunk->size);
  1365. grub_mm_check ();
  1366. #endif
  1367. return GRUB_ERR_NONE;
  1368. }
  1369. void
  1370. grub_relocator_unload (struct grub_relocator *rel)
  1371. {
  1372. struct grub_relocator_chunk *chunk, *next;
  1373. if (!rel)
  1374. return;
  1375. for (chunk = rel->chunks; chunk; chunk = next)
  1376. {
  1377. unsigned i;
  1378. for (i = 0; i < chunk->nsubchunks; i++)
  1379. free_subchunk (&chunk->subchunks[i]);
  1380. grub_unmap_memory (chunk->srcv, chunk->size);
  1381. next = chunk->next;
  1382. grub_free (chunk->subchunks);
  1383. grub_free (chunk);
  1384. }
  1385. grub_free (rel);
  1386. }
  1387. grub_err_t
  1388. grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
  1389. void **relstart, grub_size_t *relsize)
  1390. {
  1391. grub_uint8_t *rels;
  1392. grub_uint8_t *rels0;
  1393. struct grub_relocator_chunk *sorted;
  1394. grub_size_t nchunks = 0;
  1395. unsigned j;
  1396. struct grub_relocator_chunk movers_chunk;
  1397. grub_dprintf ("relocator", "Preparing relocs (size=%ld)\n",
  1398. (unsigned long) rel->relocators_size);
  1399. if (!malloc_in_range (rel, 0, ~(grub_addr_t)0 - rel->relocators_size + 1,
  1400. grub_relocator_align,
  1401. rel->relocators_size, &movers_chunk, 1, 1))
  1402. return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  1403. movers_chunk.srcv = rels = rels0
  1404. = grub_map_memory (movers_chunk.src, movers_chunk.size);
  1405. if (relsize)
  1406. *relsize = rel->relocators_size;
  1407. grub_dprintf ("relocator", "Relocs allocated at %p\n", movers_chunk.srcv);
  1408. {
  1409. unsigned i;
  1410. grub_size_t count[257];
  1411. struct grub_relocator_chunk *from, *to, *tmp;
  1412. grub_memset (count, 0, sizeof (count));
  1413. {
  1414. struct grub_relocator_chunk *chunk;
  1415. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1416. {
  1417. grub_dprintf ("relocator", "chunk %p->%p, 0x%lx\n",
  1418. (void *) chunk->src, (void *) chunk->target,
  1419. (unsigned long) chunk->size);
  1420. nchunks++;
  1421. count[(chunk->src & 0xff) + 1]++;
  1422. }
  1423. }
  1424. from = grub_calloc (nchunks, sizeof (sorted[0]));
  1425. to = grub_calloc (nchunks, sizeof (sorted[0]));
  1426. if (!from || !to)
  1427. {
  1428. grub_free (from);
  1429. grub_free (to);
  1430. return grub_errno;
  1431. }
  1432. for (j = 0; j < 256; j++)
  1433. count[j+1] += count[j];
  1434. {
  1435. struct grub_relocator_chunk *chunk;
  1436. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1437. from[count[chunk->src & 0xff]++] = *chunk;
  1438. }
  1439. for (i = 1; i < GRUB_CPU_SIZEOF_VOID_P; i++)
  1440. {
  1441. grub_memset (count, 0, sizeof (count));
  1442. for (j = 0; j < nchunks; j++)
  1443. count[((from[j].src >> (8 * i)) & 0xff) + 1]++;
  1444. for (j = 0; j < 256; j++)
  1445. count[j+1] += count[j];
  1446. for (j = 0; j < nchunks; j++)
  1447. to[count[(from[j].src >> (8 * i)) & 0xff]++] = from[j];
  1448. tmp = to;
  1449. to = from;
  1450. from = tmp;
  1451. }
  1452. sorted = from;
  1453. grub_free (to);
  1454. }
  1455. for (j = 0; j < nchunks; j++)
  1456. {
  1457. grub_dprintf ("relocator", "sorted chunk %p->%p, 0x%lx\n",
  1458. (void *) sorted[j].src, (void *) sorted[j].target,
  1459. (unsigned long) sorted[j].size);
  1460. if (sorted[j].src < sorted[j].target)
  1461. {
  1462. grub_cpu_relocator_backward ((void *) rels,
  1463. sorted[j].srcv,
  1464. grub_map_memory (sorted[j].target,
  1465. sorted[j].size),
  1466. sorted[j].size);
  1467. rels += grub_relocator_backward_size;
  1468. }
  1469. if (sorted[j].src > sorted[j].target)
  1470. {
  1471. grub_cpu_relocator_forward ((void *) rels,
  1472. sorted[j].srcv,
  1473. grub_map_memory (sorted[j].target,
  1474. sorted[j].size),
  1475. sorted[j].size);
  1476. rels += grub_relocator_forward_size;
  1477. }
  1478. if (sorted[j].src == sorted[j].target)
  1479. grub_arch_sync_caches (sorted[j].srcv, sorted[j].size);
  1480. }
  1481. grub_cpu_relocator_jumper ((void *) rels, (grub_addr_t) addr);
  1482. *relstart = rels0;
  1483. grub_free (sorted);
  1484. return GRUB_ERR_NONE;
  1485. }
  1486. void
  1487. grub_mm_check_real (const char *file, int line)
  1488. {
  1489. grub_mm_region_t r;
  1490. grub_mm_header_t p, pa;
  1491. for (r = grub_mm_base; r; r = r->next)
  1492. {
  1493. pa = r->first;
  1494. p = pa->next;
  1495. if (p->magic == GRUB_MM_ALLOC_MAGIC)
  1496. continue;
  1497. do
  1498. {
  1499. if ((grub_addr_t) p < (grub_addr_t) (r + 1)
  1500. || (grub_addr_t) p >= (grub_addr_t) (r + 1) + r->size)
  1501. grub_fatal ("%s:%d: out of range pointer: %p\n", file, line, p);
  1502. if (p->magic != GRUB_MM_FREE_MAGIC)
  1503. grub_fatal ("%s:%d free magic broken at %p (0x%x)\n", file,
  1504. line, p, p->magic);
  1505. pa = p;
  1506. p = pa->next;
  1507. }
  1508. while (pa != r->first);
  1509. }
  1510. }