relocator.c 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658
  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. struct grub_relocator_fw_leftover *cur;
  445. for (cur = leftovers; cur; cur = cur->next)
  446. {
  447. int l = 0;
  448. unsigned i;
  449. for (i = 0; i < GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT; i++)
  450. {
  451. if (l != ((cur->freebytes[i / 8] >> (i % 8)) & 1))
  452. maxevents++;
  453. l = ((cur->freebytes[i / 8] >> (i % 8)) & 1);
  454. }
  455. if (l)
  456. maxevents++;
  457. }
  458. }
  459. #endif
  460. eventt = grub_malloc (maxevents * sizeof (events[0]));
  461. counter = grub_malloc ((DIGITSORT_MASK + 2) * sizeof (counter[0]));
  462. events = grub_malloc (maxevents * sizeof (events[0]));
  463. if (!events || !eventt || !counter)
  464. {
  465. grub_dprintf ("relocator", "events or counter allocation failed %d\n",
  466. maxevents);
  467. grub_free (events);
  468. grub_free (eventt);
  469. grub_free (counter);
  470. return 0;
  471. }
  472. if (collisioncheck && rel)
  473. {
  474. struct grub_relocator_chunk *chunk;
  475. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  476. {
  477. events[N].type = COLLISION_START;
  478. events[N].pos = chunk->target;
  479. N++;
  480. events[N].type = COLLISION_END;
  481. events[N].pos = chunk->target + chunk->size;
  482. N++;
  483. }
  484. }
  485. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  486. for (r = grub_mm_base; r; r = r->next)
  487. {
  488. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  489. grub_dprintf ("relocator", "Blocking at 0x%lx-0x%lx\n",
  490. (unsigned long) r - r->pre_size,
  491. (unsigned long) (r + 1) + r->size);
  492. #endif
  493. events[N].type = FIRMWARE_BLOCK_START;
  494. events[N].pos = (grub_addr_t) r - r->pre_size;
  495. N++;
  496. events[N].type = FIRMWARE_BLOCK_END;
  497. events[N].pos = (grub_addr_t) (r + 1) + r->size;
  498. N++;
  499. }
  500. {
  501. struct grub_relocator_extra_block *cur;
  502. for (cur = extra_blocks; cur; cur = cur->next)
  503. {
  504. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  505. grub_dprintf ("relocator", "Blocking at 0x%lx-0x%lx\n",
  506. (unsigned long) cur->start, (unsigned long) cur->end);
  507. #endif
  508. events[N].type = FIRMWARE_BLOCK_START;
  509. events[N].pos = cur->start;
  510. N++;
  511. events[N].type = FIRMWARE_BLOCK_END;
  512. events[N].pos = cur->end;
  513. N++;
  514. }
  515. }
  516. N += grub_relocator_firmware_fill_events (events + N);
  517. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  518. {
  519. struct grub_relocator_fw_leftover *cur;
  520. for (cur = leftovers; cur; cur = cur->next)
  521. {
  522. unsigned i;
  523. int l = 0;
  524. for (i = 0; i < GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT; i++)
  525. {
  526. if (l != ((cur->freebytes[i / 8] >> (i % 8)) & 1))
  527. {
  528. events[N].type = l ? REG_LEFTOVER_END : REG_LEFTOVER_START;
  529. events[N].pos = cur->quantstart + i;
  530. events[N].leftover = cur;
  531. N++;
  532. }
  533. l = ((cur->freebytes[i / 8] >> (i % 8)) & 1);
  534. }
  535. if (l)
  536. {
  537. events[N].type = REG_LEFTOVER_END;
  538. events[N].pos = cur->quantstart + i;
  539. events[N].leftover = cur;
  540. N++;
  541. }
  542. }
  543. }
  544. #endif
  545. #endif
  546. /* No malloc from this point. */
  547. base_saved = grub_mm_base;
  548. grub_mm_base = NULL;
  549. for (ra = &base_saved, r = *ra; r; ra = &(r->next), r = *ra)
  550. {
  551. pa = r->first;
  552. p = pa->next;
  553. if (p->magic == GRUB_MM_ALLOC_MAGIC)
  554. continue;
  555. do
  556. {
  557. if (p->magic != GRUB_MM_FREE_MAGIC)
  558. grub_fatal ("%s:%d free magic broken at %p (0x%x)\n",
  559. __FILE__,
  560. __LINE__, p, p->magic);
  561. if (p == (grub_mm_header_t) (r + 1))
  562. {
  563. events[N].type = REG_BEG_START;
  564. events[N].pos = grub_vtop (r) - r->pre_size;
  565. events[N].reg = r;
  566. events[N].regancestor = ra;
  567. events[N].head = p;
  568. events[N].hancestor = pa;
  569. N++;
  570. events[N].type = REG_BEG_END;
  571. events[N].pos = grub_vtop (p + p->size) - sizeof (*r)
  572. - sizeof (struct grub_mm_header);
  573. N++;
  574. }
  575. else
  576. {
  577. events[N].type = IN_REG_START;
  578. events[N].pos = grub_vtop (p);
  579. events[N].head = p;
  580. events[N].hancestor = pa;
  581. events[N].reg = r;
  582. N++;
  583. events[N].type = IN_REG_END;
  584. events[N].pos = grub_vtop (p + p->size);
  585. N++;
  586. }
  587. pa = p;
  588. p = pa->next;
  589. }
  590. while (pa != r->first);
  591. }
  592. /* Put ending events after starting events. */
  593. {
  594. int st = 0, e = N / 2;
  595. for (j = 0; j < N; j++)
  596. if (is_start (events[j].type) || events[j].type == COLLISION_START)
  597. eventt[st++] = events[j];
  598. else
  599. eventt[e++] = events[j];
  600. t = eventt;
  601. eventt = events;
  602. events = t;
  603. }
  604. {
  605. unsigned i;
  606. for (i = 0; i < (BITS_IN_BYTE * sizeof (grub_addr_t) / DIGITSORT_BITS);
  607. i++)
  608. {
  609. grub_memset (counter, 0, (1 + (1 << DIGITSORT_BITS)) * sizeof (counter[0]));
  610. for (j = 0; j < N; j++)
  611. counter[((events[j].pos >> (DIGITSORT_BITS * i))
  612. & DIGITSORT_MASK) + 1]++;
  613. for (j = 0; j <= DIGITSORT_MASK; j++)
  614. counter[j+1] += counter[j];
  615. for (j = 0; j < N; j++)
  616. eventt[counter[((events[j].pos >> (DIGITSORT_BITS * i))
  617. & DIGITSORT_MASK)]++] = events[j];
  618. t = eventt;
  619. eventt = events;
  620. events = t;
  621. }
  622. }
  623. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  624. retry:
  625. #endif
  626. /* Now events are nicely sorted. */
  627. {
  628. int nstarted = 0, ncollisions = 0, nstartedfw = 0, nblockfw = 0;
  629. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  630. int nlefto = 0;
  631. #else
  632. const int nlefto = 0;
  633. #endif
  634. grub_addr_t starta = 0;
  635. for (j = from_low_priv ? 0 : N - 1; from_low_priv ? j < N : (j + 1);
  636. from_low_priv ? j++ : j--)
  637. {
  638. int isinsidebefore, isinsideafter;
  639. isinsidebefore = (!ncollisions && (nstarted || (((nlefto || nstartedfw)
  640. && !nblockfw))));
  641. switch (events[j].type)
  642. {
  643. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  644. case REG_FIRMWARE_START:
  645. nstartedfw++;
  646. break;
  647. case REG_FIRMWARE_END:
  648. nstartedfw--;
  649. break;
  650. case FIRMWARE_BLOCK_START:
  651. nblockfw++;
  652. break;
  653. case FIRMWARE_BLOCK_END:
  654. nblockfw--;
  655. break;
  656. #endif
  657. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  658. case REG_LEFTOVER_START:
  659. nlefto++;
  660. break;
  661. case REG_LEFTOVER_END:
  662. nlefto--;
  663. break;
  664. #endif
  665. case COLLISION_START:
  666. ncollisions++;
  667. break;
  668. case COLLISION_END:
  669. ncollisions--;
  670. break;
  671. case IN_REG_START:
  672. case REG_BEG_START:
  673. nstarted++;
  674. break;
  675. case IN_REG_END:
  676. case REG_BEG_END:
  677. nstarted--;
  678. break;
  679. }
  680. isinsideafter = (!ncollisions && (nstarted || ((nlefto || nstartedfw)
  681. && !nblockfw)));
  682. if (from_low_priv) {
  683. if (!isinsidebefore && isinsideafter)
  684. starta = ALIGN_UP (events[j].pos, align);
  685. if (isinsidebefore && !isinsideafter)
  686. {
  687. target = starta;
  688. if (target < start)
  689. target = start;
  690. if (target + size <= end && target + size <= events[j].pos)
  691. /* Found an usable address. */
  692. goto found;
  693. }
  694. } else {
  695. if (!isinsidebefore && isinsideafter)
  696. {
  697. if (events[j].pos >= size)
  698. starta = ALIGN_DOWN (events[j].pos - size, align) + size;
  699. else
  700. starta = 0;
  701. }
  702. if (isinsidebefore && !isinsideafter && starta >= size)
  703. {
  704. target = starta - size;
  705. if (target > end - size)
  706. target = end - size;
  707. if (target >= start && target >= events[j].pos)
  708. goto found;
  709. }
  710. }
  711. }
  712. }
  713. grub_mm_base = base_saved;
  714. grub_free (events);
  715. grub_free (eventt);
  716. grub_free (counter);
  717. return 0;
  718. found:
  719. {
  720. int inreg = 0, regbeg = 0, ncol = 0;
  721. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  722. int fwin = 0, fwb = 0, fwlefto = 0;
  723. #endif
  724. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  725. int last_lo = 0;
  726. #endif
  727. int last_start = 0;
  728. for (j = 0; j < N; j++)
  729. {
  730. int typepre;
  731. if (ncol)
  732. typepre = -1;
  733. else if (regbeg)
  734. typepre = CHUNK_TYPE_REGION_START;
  735. else if (inreg)
  736. typepre = CHUNK_TYPE_IN_REGION;
  737. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  738. else if (fwin && !fwb)
  739. typepre = CHUNK_TYPE_FIRMWARE;
  740. else if (fwlefto && !fwb)
  741. typepre = CHUNK_TYPE_LEFTOVER;
  742. #endif
  743. else
  744. typepre = -1;
  745. if (j != 0 && events[j - 1].pos != events[j].pos)
  746. {
  747. grub_addr_t alloc_start, alloc_end;
  748. alloc_start = max (events[j - 1].pos, target);
  749. alloc_end = min (events[j].pos, target + size);
  750. if (alloc_end > alloc_start)
  751. {
  752. switch (typepre)
  753. {
  754. case CHUNK_TYPE_REGION_START:
  755. allocate_regstart (alloc_start, alloc_end - alloc_start,
  756. events[last_start].reg,
  757. events[last_start].regancestor,
  758. events[last_start].hancestor);
  759. /* TODO: maintain a reverse lookup tree for hancestor. */
  760. {
  761. unsigned k;
  762. for (k = 0; k < N; k++)
  763. if (events[k].hancestor == events[last_start].head)
  764. events[k].hancestor = events[last_start].hancestor;
  765. }
  766. break;
  767. case CHUNK_TYPE_IN_REGION:
  768. allocate_inreg (alloc_start, alloc_end - alloc_start,
  769. events[last_start].head,
  770. events[last_start].hancestor,
  771. events[last_start].reg);
  772. {
  773. unsigned k;
  774. for (k = 0; k < N; k++)
  775. if (events[k].hancestor == events[last_start].head)
  776. events[k].hancestor = events[last_start].hancestor;
  777. }
  778. break;
  779. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  780. case CHUNK_TYPE_FIRMWARE:
  781. {
  782. grub_addr_t fstart, fend;
  783. fstart
  784. = ALIGN_DOWN (alloc_start,
  785. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  786. fend
  787. = ALIGN_UP (alloc_end,
  788. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  789. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  790. grub_dprintf ("relocator", "requesting %lx-%lx\n",
  791. (unsigned long) fstart,
  792. (unsigned long) fend);
  793. #endif
  794. /* The failure here can be very expensive. */
  795. if (!grub_relocator_firmware_alloc_region (fstart,
  796. fend - fstart))
  797. {
  798. if (from_low_priv)
  799. start = fend;
  800. else
  801. end = fstart;
  802. goto retry;
  803. }
  804. break;
  805. }
  806. #endif
  807. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  808. case CHUNK_TYPE_LEFTOVER:
  809. {
  810. unsigned offstart = alloc_start
  811. % GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  812. unsigned offend = alloc_end
  813. % GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  814. struct grub_relocator_fw_leftover *lo
  815. = events[last_lo].leftover;
  816. if (offend == 0 && alloc_end != alloc_start)
  817. offend = GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  818. lo->freebytes[offstart / 8]
  819. &= ((1 << (8 - (start % 8))) - 1);
  820. grub_memset (lo->freebytes + (offstart + 7) / 8, 0,
  821. offend / 8 - (offstart + 7) / 8);
  822. lo->freebytes[offend / 8] &= ~((1 << (offend % 8)) - 1);
  823. }
  824. break;
  825. #endif
  826. }
  827. nallocs++;
  828. }
  829. }
  830. switch (events[j].type)
  831. {
  832. case REG_BEG_START:
  833. case IN_REG_START:
  834. if (events[j].type == REG_BEG_START &&
  835. (grub_addr_t) (events[j].reg + 1) > target)
  836. regbeg++;
  837. else
  838. inreg++;
  839. last_start = j;
  840. break;
  841. case REG_BEG_END:
  842. case IN_REG_END:
  843. if (regbeg)
  844. regbeg--;
  845. else
  846. inreg--;
  847. break;
  848. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  849. case REG_FIRMWARE_START:
  850. fwin++;
  851. break;
  852. case REG_FIRMWARE_END:
  853. fwin--;
  854. break;
  855. case FIRMWARE_BLOCK_START:
  856. fwb++;
  857. break;
  858. case FIRMWARE_BLOCK_END:
  859. fwb--;
  860. break;
  861. #endif
  862. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  863. case REG_LEFTOVER_START:
  864. fwlefto++;
  865. last_lo = j;
  866. break;
  867. case REG_LEFTOVER_END:
  868. fwlefto--;
  869. break;
  870. #endif
  871. case COLLISION_START:
  872. ncol++;
  873. break;
  874. case COLLISION_END:
  875. ncol--;
  876. break;
  877. }
  878. }
  879. }
  880. /* Malloc is available again. */
  881. grub_mm_base = base_saved;
  882. grub_free (eventt);
  883. grub_free (counter);
  884. {
  885. int last_start = 0;
  886. int inreg = 0, regbeg = 0, ncol = 0;
  887. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  888. int fwin = 0, fwlefto = 0, fwb = 0;
  889. #endif
  890. unsigned cural = 0;
  891. int oom = 0;
  892. res->subchunks = grub_malloc (sizeof (res->subchunks[0]) * nallocs);
  893. if (!res->subchunks)
  894. oom = 1;
  895. res->nsubchunks = nallocs;
  896. for (j = 0; j < N; j++)
  897. {
  898. int typepre;
  899. if (ncol)
  900. typepre = -1;
  901. else if (regbeg)
  902. typepre = CHUNK_TYPE_REGION_START;
  903. else if (inreg)
  904. typepre = CHUNK_TYPE_IN_REGION;
  905. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  906. else if (fwin && !fwb)
  907. typepre = CHUNK_TYPE_FIRMWARE;
  908. else if (fwlefto && !fwb)
  909. typepre = CHUNK_TYPE_LEFTOVER;
  910. #endif
  911. else
  912. typepre = -1;
  913. if (j != 0 && events[j - 1].pos != events[j].pos)
  914. {
  915. grub_addr_t alloc_start, alloc_end;
  916. struct grub_relocator_subchunk tofree;
  917. struct grub_relocator_subchunk *curschu = &tofree;
  918. if (!oom)
  919. curschu = &res->subchunks[cural];
  920. alloc_start = max (events[j - 1].pos, target);
  921. alloc_end = min (events[j].pos, target + size);
  922. if (alloc_end > alloc_start)
  923. {
  924. #ifdef DEBUG_RELOCATOR_NOMEM_DPRINTF
  925. grub_dprintf ("relocator", "subchunk 0x%lx-0x%lx, %d\n",
  926. (unsigned long) alloc_start,
  927. (unsigned long) alloc_end, typepre);
  928. #endif
  929. curschu->type = typepre;
  930. curschu->start = alloc_start;
  931. curschu->size = alloc_end - alloc_start;
  932. if (typepre == CHUNK_TYPE_REGION_START
  933. || typepre == CHUNK_TYPE_IN_REGION)
  934. {
  935. curschu->reg = events[last_start].reg;
  936. curschu->pre_size = alloc_start - events[j - 1].pos;
  937. }
  938. if (!oom && (typepre == CHUNK_TYPE_REGION_START
  939. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  940. || typepre == CHUNK_TYPE_FIRMWARE
  941. #endif
  942. ))
  943. {
  944. struct grub_relocator_extra_block *ne;
  945. ne = grub_malloc (sizeof (*ne));
  946. if (!ne)
  947. {
  948. oom = 1;
  949. grub_memcpy (&tofree, curschu, sizeof (tofree));
  950. }
  951. else
  952. {
  953. ne->start = alloc_start;
  954. ne->end = alloc_end;
  955. ne->next = extra_blocks;
  956. ne->prev = &extra_blocks;
  957. if (extra_blocks)
  958. extra_blocks->prev = &(ne->next);
  959. extra_blocks = ne;
  960. curschu->extra = ne;
  961. }
  962. }
  963. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  964. if (!oom && typepre == CHUNK_TYPE_FIRMWARE)
  965. {
  966. grub_addr_t fstart, fend;
  967. fstart
  968. = ALIGN_DOWN (alloc_start,
  969. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  970. fend
  971. = ALIGN_UP (alloc_end,
  972. GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT);
  973. {
  974. struct grub_relocator_fw_leftover *lo1 = NULL;
  975. struct grub_relocator_fw_leftover *lo2 = NULL;
  976. if (fstart != alloc_start)
  977. lo1 = grub_malloc (sizeof (*lo1));
  978. if (fend != alloc_end)
  979. lo2 = grub_malloc (sizeof (*lo2));
  980. if ((!lo1 && fstart != alloc_start)
  981. || (!lo2 && fend != alloc_end))
  982. {
  983. struct grub_relocator_extra_block *ne;
  984. grub_free (lo1);
  985. grub_free (lo2);
  986. lo1 = NULL;
  987. lo2 = NULL;
  988. oom = 1;
  989. grub_memcpy (&tofree, curschu, sizeof (tofree));
  990. ne = extra_blocks;
  991. extra_blocks = extra_blocks->next;
  992. grub_free (ne);
  993. }
  994. if (lo1)
  995. {
  996. lo1->quantstart = fstart;
  997. grub_memset (lo1->freebytes, 0xff,
  998. (alloc_start - fstart) / 8);
  999. lo1->freebytes[(alloc_start - fstart) / 8]
  1000. = (1 << ((alloc_start - fstart) % 8)) - 1;
  1001. grub_memset (lo1->freebytes
  1002. + ((alloc_start - fstart) / 8) + 1, 0,
  1003. sizeof (lo1->freebytes)
  1004. - (alloc_start - fstart) / 8 - 1);
  1005. lo1->next = leftovers;
  1006. lo1->prev = &leftovers;
  1007. if (leftovers)
  1008. leftovers->prev = &lo1->next;
  1009. leftovers = lo1;
  1010. }
  1011. if (lo2)
  1012. {
  1013. lo2->quantstart
  1014. = fend - GRUB_RELOCATOR_FIRMWARE_REQUESTS_QUANT;
  1015. grub_memset (lo2->freebytes, 0,
  1016. (alloc_end - lo2->quantstart) / 8);
  1017. lo2->freebytes[(alloc_end - lo2->quantstart) / 8]
  1018. = ~((1 << ((alloc_end - lo2->quantstart) % 8)) - 1);
  1019. grub_memset (lo2->freebytes
  1020. + ((alloc_end - lo2->quantstart) / 8)
  1021. + 1, 0, sizeof (lo2->freebytes)
  1022. - (alloc_end - lo2->quantstart) / 8 - 1);
  1023. lo2->prev = &leftovers;
  1024. if (leftovers)
  1025. leftovers->prev = &lo2->next;
  1026. lo2->next = leftovers;
  1027. leftovers = lo2;
  1028. }
  1029. curschu->pre = lo1;
  1030. curschu->post = lo2;
  1031. }
  1032. }
  1033. if (typepre == CHUNK_TYPE_LEFTOVER)
  1034. {
  1035. curschu->pre = events[last_start].leftover;
  1036. curschu->post = events[last_start].leftover;
  1037. }
  1038. #endif
  1039. if (!oom)
  1040. cural++;
  1041. else
  1042. free_subchunk (&tofree);
  1043. }
  1044. }
  1045. switch (events[j].type)
  1046. {
  1047. case REG_BEG_START:
  1048. case IN_REG_START:
  1049. if (events[j].type == REG_BEG_START &&
  1050. (grub_addr_t) (events[j].reg + 1) > target)
  1051. regbeg++;
  1052. else
  1053. inreg++;
  1054. last_start = j;
  1055. break;
  1056. case REG_BEG_END:
  1057. case IN_REG_END:
  1058. inreg = regbeg = 0;
  1059. break;
  1060. #if GRUB_RELOCATOR_HAVE_FIRMWARE_REQUESTS
  1061. case REG_FIRMWARE_START:
  1062. fwin++;
  1063. break;
  1064. case REG_FIRMWARE_END:
  1065. fwin--;
  1066. break;
  1067. case FIRMWARE_BLOCK_START:
  1068. fwb++;
  1069. break;
  1070. case FIRMWARE_BLOCK_END:
  1071. fwb--;
  1072. break;
  1073. #endif
  1074. #if GRUB_RELOCATOR_HAVE_LEFTOVERS
  1075. case REG_LEFTOVER_START:
  1076. fwlefto++;
  1077. break;
  1078. case REG_LEFTOVER_END:
  1079. fwlefto--;
  1080. break;
  1081. #endif
  1082. case COLLISION_START:
  1083. ncol++;
  1084. break;
  1085. case COLLISION_END:
  1086. ncol--;
  1087. break;
  1088. }
  1089. }
  1090. if (oom)
  1091. {
  1092. unsigned i;
  1093. for (i = 0; i < cural; i++)
  1094. free_subchunk (&res->subchunks[i]);
  1095. grub_free (res->subchunks);
  1096. grub_dprintf ("relocator", "allocation failed with out-of-memory\n");
  1097. grub_free (events);
  1098. return 0;
  1099. }
  1100. }
  1101. res->src = target;
  1102. res->size = size;
  1103. grub_free (events);
  1104. grub_dprintf ("relocator", "allocated: 0x%lx+0x%lx\n", (unsigned long) target,
  1105. (unsigned long) size);
  1106. return 1;
  1107. }
  1108. static void
  1109. adjust_limits (struct grub_relocator *rel,
  1110. grub_phys_addr_t *min_addr, grub_phys_addr_t *max_addr,
  1111. grub_phys_addr_t in_min, grub_phys_addr_t in_max)
  1112. {
  1113. struct grub_relocator_chunk *chunk;
  1114. *min_addr = 0;
  1115. *max_addr = rel->postchunks;
  1116. /* Keep chunks in memory in the same order as they'll be after relocation. */
  1117. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1118. {
  1119. if (chunk->target > in_max && chunk->src < *max_addr
  1120. && chunk->src < rel->postchunks)
  1121. *max_addr = chunk->src;
  1122. if (chunk->target + chunk->size <= in_min
  1123. && chunk->src + chunk->size > *min_addr
  1124. && chunk->src < rel->postchunks)
  1125. *min_addr = chunk->src + chunk->size;
  1126. }
  1127. }
  1128. grub_err_t
  1129. grub_relocator_alloc_chunk_addr (struct grub_relocator *rel,
  1130. grub_relocator_chunk_t *out,
  1131. grub_phys_addr_t target, grub_size_t size)
  1132. {
  1133. struct grub_relocator_chunk *chunk;
  1134. grub_phys_addr_t min_addr = 0, max_addr;
  1135. if (target > ~size)
  1136. return grub_error (GRUB_ERR_BUG, "address is out of range");
  1137. adjust_limits (rel, &min_addr, &max_addr, target, target);
  1138. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1139. if ((chunk->target <= target && target < chunk->target + chunk->size)
  1140. || (target <= chunk->target && chunk->target < target + size))
  1141. return grub_error (GRUB_ERR_BUG, "overlap detected");
  1142. chunk = grub_malloc (sizeof (struct grub_relocator_chunk));
  1143. if (!chunk)
  1144. return grub_errno;
  1145. grub_dprintf ("relocator",
  1146. "min_addr = 0x%llx, max_addr = 0x%llx, target = 0x%llx\n",
  1147. (unsigned long long) min_addr, (unsigned long long) max_addr,
  1148. (unsigned long long) target);
  1149. do
  1150. {
  1151. /* A trick to improve Linux allocation. */
  1152. #if defined (__i386__) || defined (__x86_64__)
  1153. if (target < 0x100000)
  1154. if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
  1155. size, chunk, 0, 1))
  1156. {
  1157. if (rel->postchunks > chunk->src)
  1158. rel->postchunks = chunk->src;
  1159. break;
  1160. }
  1161. #endif
  1162. if (malloc_in_range (rel, target, max_addr, 1, size, chunk, 1, 0))
  1163. break;
  1164. if (malloc_in_range (rel, min_addr, target, 1, size, chunk, 0, 0))
  1165. break;
  1166. if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
  1167. size, chunk, 0, 1))
  1168. {
  1169. if (rel->postchunks > chunk->src)
  1170. rel->postchunks = chunk->src;
  1171. break;
  1172. }
  1173. grub_dprintf ("relocator", "not allocated\n");
  1174. grub_free (chunk);
  1175. return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  1176. }
  1177. while (0);
  1178. grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
  1179. (unsigned long long) chunk->src, (unsigned long long) target);
  1180. if (rel->highestaddr < target + size)
  1181. rel->highestaddr = target + size;
  1182. if (rel->highestaddr < chunk->src + size)
  1183. rel->highestaddr = chunk->src + size;
  1184. if (chunk->src < rel->postchunks)
  1185. {
  1186. if (rel->highestnonpostaddr < target + size)
  1187. rel->highestnonpostaddr = target + size;
  1188. if (rel->highestnonpostaddr < chunk->src + size)
  1189. rel->highestnonpostaddr = chunk->src + size;
  1190. }
  1191. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1192. (unsigned long) rel->relocators_size);
  1193. if (chunk->src < target)
  1194. rel->relocators_size += grub_relocator_backward_size;
  1195. if (chunk->src > target)
  1196. rel->relocators_size += grub_relocator_forward_size;
  1197. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1198. (unsigned long) rel->relocators_size);
  1199. chunk->target = target;
  1200. chunk->size = size;
  1201. chunk->next = rel->chunks;
  1202. rel->chunks = chunk;
  1203. grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
  1204. rel->chunks->next);
  1205. chunk->srcv = grub_map_memory (chunk->src, chunk->size);
  1206. *out = chunk;
  1207. #ifdef DEBUG_RELOCATOR
  1208. grub_memset (chunk->srcv, 0xfa, chunk->size);
  1209. grub_mm_check ();
  1210. #endif
  1211. return GRUB_ERR_NONE;
  1212. }
  1213. /* Context for grub_relocator_alloc_chunk_align. */
  1214. struct grub_relocator_alloc_chunk_align_ctx
  1215. {
  1216. grub_phys_addr_t min_addr, max_addr;
  1217. grub_size_t size, align;
  1218. int preference;
  1219. struct grub_relocator_chunk *chunk;
  1220. int found;
  1221. };
  1222. /* Helper for grub_relocator_alloc_chunk_align. */
  1223. static int
  1224. grub_relocator_alloc_chunk_align_iter (grub_uint64_t addr, grub_uint64_t sz,
  1225. grub_memory_type_t type, void *data)
  1226. {
  1227. struct grub_relocator_alloc_chunk_align_ctx *ctx = data;
  1228. grub_uint64_t candidate;
  1229. if (type != GRUB_MEMORY_AVAILABLE)
  1230. return 0;
  1231. candidate = ALIGN_UP (addr, ctx->align);
  1232. if (candidate < ctx->min_addr)
  1233. candidate = ALIGN_UP (ctx->min_addr, ctx->align);
  1234. if (candidate + ctx->size > addr + sz
  1235. || candidate > ALIGN_DOWN (ctx->max_addr, ctx->align))
  1236. return 0;
  1237. if (ctx->preference == GRUB_RELOCATOR_PREFERENCE_HIGH)
  1238. candidate = ALIGN_DOWN (min (addr + sz - ctx->size, ctx->max_addr),
  1239. ctx->align);
  1240. if (!ctx->found || (ctx->preference == GRUB_RELOCATOR_PREFERENCE_HIGH
  1241. && candidate > ctx->chunk->target))
  1242. ctx->chunk->target = candidate;
  1243. if (!ctx->found || (ctx->preference == GRUB_RELOCATOR_PREFERENCE_LOW
  1244. && candidate < ctx->chunk->target))
  1245. ctx->chunk->target = candidate;
  1246. ctx->found = 1;
  1247. return 0;
  1248. }
  1249. grub_err_t
  1250. grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
  1251. grub_relocator_chunk_t *out,
  1252. grub_phys_addr_t min_addr,
  1253. grub_phys_addr_t max_addr,
  1254. grub_size_t size, grub_size_t align,
  1255. int preference,
  1256. int avoid_efi_boot_services)
  1257. {
  1258. struct grub_relocator_alloc_chunk_align_ctx ctx = {
  1259. .min_addr = min_addr,
  1260. .max_addr = max_addr,
  1261. .size = size,
  1262. .align = align,
  1263. .preference = preference,
  1264. .found = 0
  1265. };
  1266. grub_addr_t min_addr2 = 0, max_addr2;
  1267. if (max_addr > ~size)
  1268. max_addr = ~size;
  1269. #ifdef GRUB_MACHINE_PCBIOS
  1270. if (min_addr < 0x1000)
  1271. min_addr = 0x1000;
  1272. #endif
  1273. grub_dprintf ("relocator", "chunks = %p\n", rel->chunks);
  1274. ctx.chunk = grub_malloc (sizeof (struct grub_relocator_chunk));
  1275. if (!ctx.chunk)
  1276. return grub_errno;
  1277. if (malloc_in_range (rel, min_addr, max_addr, align,
  1278. size, ctx.chunk,
  1279. preference != GRUB_RELOCATOR_PREFERENCE_HIGH, 1))
  1280. {
  1281. grub_dprintf ("relocator", "allocated 0x%llx/0x%llx\n",
  1282. (unsigned long long) ctx.chunk->src,
  1283. (unsigned long long) ctx.chunk->src);
  1284. grub_dprintf ("relocator", "chunks = %p\n", rel->chunks);
  1285. ctx.chunk->target = ctx.chunk->src;
  1286. ctx.chunk->size = size;
  1287. ctx.chunk->next = rel->chunks;
  1288. rel->chunks = ctx.chunk;
  1289. ctx.chunk->srcv = grub_map_memory (ctx.chunk->src, ctx.chunk->size);
  1290. *out = ctx.chunk;
  1291. return GRUB_ERR_NONE;
  1292. }
  1293. adjust_limits (rel, &min_addr2, &max_addr2, min_addr, max_addr);
  1294. grub_dprintf ("relocator", "Adjusted limits from %lx-%lx to %lx-%lx\n",
  1295. (unsigned long) min_addr, (unsigned long) max_addr,
  1296. (unsigned long) min_addr2, (unsigned long) max_addr2);
  1297. do
  1298. {
  1299. if (malloc_in_range (rel, min_addr2, max_addr2, align,
  1300. size, ctx.chunk, 1, 1))
  1301. break;
  1302. if (malloc_in_range (rel, rel->highestnonpostaddr, ~(grub_addr_t)0, 1,
  1303. size, ctx.chunk, 0, 1))
  1304. {
  1305. if (rel->postchunks > ctx.chunk->src)
  1306. rel->postchunks = ctx.chunk->src;
  1307. break;
  1308. }
  1309. return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  1310. }
  1311. while (0);
  1312. {
  1313. #ifdef GRUB_MACHINE_EFI
  1314. grub_efi_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx,
  1315. avoid_efi_boot_services);
  1316. #elif defined (__powerpc__) || defined (GRUB_MACHINE_XEN)
  1317. (void) avoid_efi_boot_services;
  1318. grub_machine_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx);
  1319. #else
  1320. (void) avoid_efi_boot_services;
  1321. grub_mmap_iterate (grub_relocator_alloc_chunk_align_iter, &ctx);
  1322. #endif
  1323. if (!ctx.found)
  1324. return grub_error (GRUB_ERR_BAD_OS, "couldn't find suitable memory target");
  1325. }
  1326. while (1)
  1327. {
  1328. struct grub_relocator_chunk *chunk2;
  1329. for (chunk2 = rel->chunks; chunk2; chunk2 = chunk2->next)
  1330. if ((chunk2->target <= ctx.chunk->target
  1331. && ctx.chunk->target < chunk2->target + chunk2->size)
  1332. || (ctx.chunk->target <= chunk2->target && chunk2->target
  1333. < ctx.chunk->target + size))
  1334. {
  1335. if (preference == GRUB_RELOCATOR_PREFERENCE_HIGH)
  1336. ctx.chunk->target = ALIGN_DOWN (chunk2->target, align);
  1337. else
  1338. ctx.chunk->target = ALIGN_UP (chunk2->target + chunk2->size,
  1339. align);
  1340. break;
  1341. }
  1342. if (!chunk2)
  1343. break;
  1344. }
  1345. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1346. (unsigned long) rel->relocators_size);
  1347. if (ctx.chunk->src < ctx.chunk->target)
  1348. rel->relocators_size += grub_relocator_backward_size;
  1349. if (ctx.chunk->src > ctx.chunk->target)
  1350. rel->relocators_size += grub_relocator_forward_size;
  1351. grub_dprintf ("relocator", "relocators_size=%ld\n",
  1352. (unsigned long) rel->relocators_size);
  1353. ctx.chunk->size = size;
  1354. ctx.chunk->next = rel->chunks;
  1355. rel->chunks = ctx.chunk;
  1356. grub_dprintf ("relocator", "cur = %p, next = %p\n", rel->chunks,
  1357. rel->chunks->next);
  1358. ctx.chunk->srcv = grub_map_memory (ctx.chunk->src, ctx.chunk->size);
  1359. *out = ctx.chunk;
  1360. #ifdef DEBUG_RELOCATOR
  1361. grub_memset (ctx.chunk->srcv, 0xfa, ctx.chunk->size);
  1362. grub_mm_check ();
  1363. #endif
  1364. return GRUB_ERR_NONE;
  1365. }
  1366. void
  1367. grub_relocator_unload (struct grub_relocator *rel)
  1368. {
  1369. struct grub_relocator_chunk *chunk, *next;
  1370. if (!rel)
  1371. return;
  1372. for (chunk = rel->chunks; chunk; chunk = next)
  1373. {
  1374. unsigned i;
  1375. for (i = 0; i < chunk->nsubchunks; i++)
  1376. free_subchunk (&chunk->subchunks[i]);
  1377. grub_unmap_memory (chunk->srcv, chunk->size);
  1378. next = chunk->next;
  1379. grub_free (chunk->subchunks);
  1380. grub_free (chunk);
  1381. }
  1382. grub_free (rel);
  1383. }
  1384. grub_err_t
  1385. grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
  1386. void **relstart, grub_size_t *relsize)
  1387. {
  1388. grub_uint8_t *rels;
  1389. grub_uint8_t *rels0;
  1390. struct grub_relocator_chunk *sorted;
  1391. grub_size_t nchunks = 0;
  1392. unsigned j;
  1393. struct grub_relocator_chunk movers_chunk;
  1394. grub_dprintf ("relocator", "Preparing relocs (size=%ld)\n",
  1395. (unsigned long) rel->relocators_size);
  1396. if (!malloc_in_range (rel, 0, ~(grub_addr_t)0 - rel->relocators_size + 1,
  1397. grub_relocator_align,
  1398. rel->relocators_size, &movers_chunk, 1, 1))
  1399. return grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  1400. movers_chunk.srcv = rels = rels0
  1401. = grub_map_memory (movers_chunk.src, movers_chunk.size);
  1402. if (relsize)
  1403. *relsize = rel->relocators_size;
  1404. grub_dprintf ("relocator", "Relocs allocated at %p\n", movers_chunk.srcv);
  1405. {
  1406. unsigned i;
  1407. grub_size_t count[257];
  1408. struct grub_relocator_chunk *from, *to, *tmp;
  1409. grub_memset (count, 0, sizeof (count));
  1410. {
  1411. struct grub_relocator_chunk *chunk;
  1412. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1413. {
  1414. grub_dprintf ("relocator", "chunk %p->%p, 0x%lx\n",
  1415. (void *) chunk->src, (void *) chunk->target,
  1416. (unsigned long) chunk->size);
  1417. nchunks++;
  1418. count[(chunk->src & 0xff) + 1]++;
  1419. }
  1420. }
  1421. from = grub_malloc (nchunks * sizeof (sorted[0]));
  1422. to = grub_malloc (nchunks * sizeof (sorted[0]));
  1423. if (!from || !to)
  1424. {
  1425. grub_free (from);
  1426. grub_free (to);
  1427. return grub_errno;
  1428. }
  1429. for (j = 0; j < 256; j++)
  1430. count[j+1] += count[j];
  1431. {
  1432. struct grub_relocator_chunk *chunk;
  1433. for (chunk = rel->chunks; chunk; chunk = chunk->next)
  1434. from[count[chunk->src & 0xff]++] = *chunk;
  1435. }
  1436. for (i = 1; i < GRUB_CPU_SIZEOF_VOID_P; i++)
  1437. {
  1438. grub_memset (count, 0, sizeof (count));
  1439. for (j = 0; j < nchunks; j++)
  1440. count[((from[j].src >> (8 * i)) & 0xff) + 1]++;
  1441. for (j = 0; j < 256; j++)
  1442. count[j+1] += count[j];
  1443. for (j = 0; j < nchunks; j++)
  1444. to[count[(from[j].src >> (8 * i)) & 0xff]++] = from[j];
  1445. tmp = to;
  1446. to = from;
  1447. from = tmp;
  1448. }
  1449. sorted = from;
  1450. grub_free (to);
  1451. }
  1452. for (j = 0; j < nchunks; j++)
  1453. {
  1454. grub_dprintf ("relocator", "sorted chunk %p->%p, 0x%lx\n",
  1455. (void *) sorted[j].src, (void *) sorted[j].target,
  1456. (unsigned long) sorted[j].size);
  1457. if (sorted[j].src < sorted[j].target)
  1458. {
  1459. grub_cpu_relocator_backward ((void *) rels,
  1460. sorted[j].srcv,
  1461. grub_map_memory (sorted[j].target,
  1462. sorted[j].size),
  1463. sorted[j].size);
  1464. rels += grub_relocator_backward_size;
  1465. }
  1466. if (sorted[j].src > sorted[j].target)
  1467. {
  1468. grub_cpu_relocator_forward ((void *) rels,
  1469. sorted[j].srcv,
  1470. grub_map_memory (sorted[j].target,
  1471. sorted[j].size),
  1472. sorted[j].size);
  1473. rels += grub_relocator_forward_size;
  1474. }
  1475. if (sorted[j].src == sorted[j].target)
  1476. grub_arch_sync_caches (sorted[j].srcv, sorted[j].size);
  1477. }
  1478. grub_cpu_relocator_jumper ((void *) rels, (grub_addr_t) addr);
  1479. *relstart = rels0;
  1480. grub_free (sorted);
  1481. return GRUB_ERR_NONE;
  1482. }
  1483. void
  1484. grub_mm_check_real (const char *file, int line)
  1485. {
  1486. grub_mm_region_t r;
  1487. grub_mm_header_t p, pa;
  1488. for (r = grub_mm_base; r; r = r->next)
  1489. {
  1490. pa = r->first;
  1491. p = pa->next;
  1492. if (p->magic == GRUB_MM_ALLOC_MAGIC)
  1493. continue;
  1494. do
  1495. {
  1496. if ((grub_addr_t) p < (grub_addr_t) (r + 1)
  1497. || (grub_addr_t) p >= (grub_addr_t) (r + 1) + r->size)
  1498. grub_fatal ("%s:%d: out of range pointer: %p\n", file, line, p);
  1499. if (p->magic != GRUB_MM_FREE_MAGIC)
  1500. grub_fatal ("%s:%d free magic broken at %p (0x%x)\n", file,
  1501. line, p, p->magic);
  1502. pa = p;
  1503. p = pa->next;
  1504. }
  1505. while (pa != r->first);
  1506. }
  1507. }