xnu.c 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556
  1. /* xnu.c - load xnu kernel. Thanks to Florian Idelberger for all the
  2. time he spent testing this
  3. */
  4. /*
  5. * GRUB -- GRand Unified Bootloader
  6. * Copyright (C) 2009 Free Software Foundation, Inc.
  7. *
  8. * GRUB is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * GRUB is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <grub/file.h>
  22. #include <grub/xnu.h>
  23. #include <grub/cpu/xnu.h>
  24. #include <grub/mm.h>
  25. #include <grub/dl.h>
  26. #include <grub/loader.h>
  27. #include <grub/machoload.h>
  28. #include <grub/macho.h>
  29. #include <grub/cpu/macho.h>
  30. #include <grub/command.h>
  31. #include <grub/misc.h>
  32. #include <grub/extcmd.h>
  33. #include <grub/env.h>
  34. #include <grub/i18n.h>
  35. #include <grub/verify.h>
  36. #include <grub/safemath.h>
  37. GRUB_MOD_LICENSE ("GPLv3+");
  38. #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
  39. #include <grub/autoefi.h>
  40. #endif
  41. struct grub_xnu_devtree_key *grub_xnu_devtree_root = 0;
  42. static int driverspackagenum = 0;
  43. static int driversnum = 0;
  44. int grub_xnu_is_64bit = 0;
  45. int grub_xnu_darwin_version = 0;
  46. grub_addr_t grub_xnu_heap_target_start = 0;
  47. grub_size_t grub_xnu_heap_size = 0;
  48. struct grub_relocator *grub_xnu_relocator;
  49. static grub_err_t
  50. grub_xnu_register_memory (const char *prefix, int *suffix,
  51. grub_addr_t addr, grub_size_t size);
  52. grub_err_t
  53. grub_xnu_heap_malloc (int size, void **src, grub_addr_t *target)
  54. {
  55. grub_err_t err;
  56. grub_relocator_chunk_t ch;
  57. grub_addr_t tgt;
  58. if (grub_add (grub_xnu_heap_target_start, grub_xnu_heap_size, &tgt))
  59. return GRUB_ERR_OUT_OF_RANGE;
  60. err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch, tgt, size);
  61. if (err)
  62. return err;
  63. *src = get_virtual_current_address (ch);
  64. *target = tgt;
  65. grub_xnu_heap_size += size;
  66. grub_dprintf ("xnu", "val=%p\n", *src);
  67. return GRUB_ERR_NONE;
  68. }
  69. /* Make sure next block of the heap will be aligned.
  70. Please notice: aligned are pointers AFTER relocation
  71. and not the current ones. */
  72. grub_err_t
  73. grub_xnu_align_heap (int align)
  74. {
  75. grub_xnu_heap_size
  76. = ALIGN_UP (grub_xnu_heap_target_start+ grub_xnu_heap_size, align)
  77. - grub_xnu_heap_target_start;
  78. return GRUB_ERR_NONE;
  79. }
  80. /* Free subtree pointed by CUR. */
  81. void
  82. grub_xnu_free_devtree (struct grub_xnu_devtree_key *cur)
  83. {
  84. struct grub_xnu_devtree_key *d;
  85. while (cur)
  86. {
  87. grub_free (cur->name);
  88. if (cur->datasize == -1)
  89. grub_xnu_free_devtree (cur->first_child);
  90. else if (cur->data)
  91. grub_free (cur->data);
  92. d = cur->next;
  93. grub_free (cur);
  94. cur = d;
  95. }
  96. }
  97. /* Compute the size of device tree in xnu format. */
  98. static grub_size_t
  99. grub_xnu_writetree_get_size (struct grub_xnu_devtree_key *start,
  100. const char *name)
  101. {
  102. grub_size_t ret;
  103. struct grub_xnu_devtree_key *cur;
  104. /* Key header. */
  105. ret = 2 * sizeof (grub_uint32_t);
  106. /* "name" value. */
  107. ret += 32 + sizeof (grub_uint32_t)
  108. + grub_strlen (name) + 4
  109. - (grub_strlen (name) % 4);
  110. for (cur = start; cur; cur = cur->next)
  111. if (cur->datasize != -1)
  112. {
  113. int align_overhead;
  114. align_overhead = 4 - (cur->datasize % 4);
  115. if (align_overhead == 4)
  116. align_overhead = 0;
  117. ret += 32 + sizeof (grub_uint32_t) + cur->datasize + align_overhead;
  118. }
  119. else
  120. ret += grub_xnu_writetree_get_size (cur->first_child, cur->name);
  121. return ret;
  122. }
  123. /* Write devtree in XNU format at curptr assuming the head is named NAME.*/
  124. static void *
  125. grub_xnu_writetree_toheap_real (void *curptr,
  126. struct grub_xnu_devtree_key *start,
  127. const char *name)
  128. {
  129. struct grub_xnu_devtree_key *cur;
  130. int nkeys = 0, nvals = 0;
  131. for (cur = start; cur; cur = cur->next)
  132. {
  133. if (cur->datasize == -1)
  134. nkeys++;
  135. else
  136. nvals++;
  137. }
  138. /* For the name. */
  139. nvals++;
  140. *((grub_uint32_t *) curptr) = nvals;
  141. curptr = ((grub_uint32_t *) curptr) + 1;
  142. *((grub_uint32_t *) curptr) = nkeys;
  143. curptr = ((grub_uint32_t *) curptr) + 1;
  144. /* First comes "name" value. */
  145. grub_memset (curptr, 0, 32);
  146. grub_memcpy (curptr, "name", 4);
  147. curptr = ((grub_uint8_t *) curptr) + 32;
  148. *((grub_uint32_t *)curptr) = grub_strlen (name) + 1;
  149. curptr = ((grub_uint32_t *) curptr) + 1;
  150. grub_memcpy (curptr, name, grub_strlen (name));
  151. curptr = ((grub_uint8_t *) curptr) + grub_strlen (name);
  152. grub_memset (curptr, 0, 4 - (grub_strlen (name) % 4));
  153. curptr = ((grub_uint8_t *) curptr) + (4 - (grub_strlen (name) % 4));
  154. /* Then the other values. */
  155. for (cur = start; cur; cur = cur->next)
  156. if (cur->datasize != -1)
  157. {
  158. int align_overhead;
  159. align_overhead = 4 - (cur->datasize % 4);
  160. if (align_overhead == 4)
  161. align_overhead = 0;
  162. grub_memset (curptr, 0, 32);
  163. grub_strncpy (curptr, cur->name, 31);
  164. curptr = ((grub_uint8_t *) curptr) + 32;
  165. *((grub_uint32_t *) curptr) = cur->datasize;
  166. curptr = ((grub_uint32_t *) curptr) + 1;
  167. grub_memcpy (curptr, cur->data, cur->datasize);
  168. curptr = ((grub_uint8_t *) curptr) + cur->datasize;
  169. grub_memset (curptr, 0, align_overhead);
  170. curptr = ((grub_uint8_t *) curptr) + align_overhead;
  171. }
  172. /* And then the keys. Recursively use this function. */
  173. for (cur = start; cur; cur = cur->next)
  174. if (cur->datasize == -1)
  175. {
  176. curptr = grub_xnu_writetree_toheap_real (curptr,
  177. cur->first_child,
  178. cur->name);
  179. if (!curptr)
  180. return 0;
  181. }
  182. return curptr;
  183. }
  184. grub_err_t
  185. grub_xnu_writetree_toheap (grub_addr_t *target, grub_size_t *size)
  186. {
  187. struct grub_xnu_devtree_key *chosen;
  188. struct grub_xnu_devtree_key *memorymap;
  189. struct grub_xnu_devtree_key *driverkey;
  190. struct grub_xnu_extdesc *extdesc;
  191. grub_err_t err;
  192. void *src;
  193. err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
  194. if (err)
  195. return err;
  196. /* Device tree itself is in the memory map of device tree. */
  197. /* Create a dummy value in memory-map. */
  198. chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
  199. if (! chosen)
  200. return grub_errno;
  201. memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
  202. if (! memorymap)
  203. return grub_errno;
  204. driverkey = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*driverkey));
  205. if (! driverkey)
  206. return grub_errno;
  207. driverkey->name = grub_strdup ("DeviceTree");
  208. if (! driverkey->name)
  209. {
  210. err = grub_errno;
  211. goto fail;
  212. }
  213. driverkey->datasize = sizeof (*extdesc);
  214. driverkey->next = memorymap->first_child;
  215. memorymap->first_child = driverkey;
  216. driverkey->data = extdesc
  217. = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
  218. if (! driverkey->data)
  219. {
  220. err = grub_errno;
  221. goto fail;
  222. }
  223. /* Allocate the space based on the size with dummy value. */
  224. *size = grub_xnu_writetree_get_size (grub_xnu_devtree_root, "/");
  225. err = grub_xnu_heap_malloc (ALIGN_UP (*size + 1, GRUB_XNU_PAGESIZE),
  226. &src, target);
  227. if (err)
  228. goto fail;
  229. /* Put real data in the dummy. */
  230. extdesc->addr = *target;
  231. extdesc->size = (grub_uint32_t) *size;
  232. /* Write the tree to heap. */
  233. grub_xnu_writetree_toheap_real (src, grub_xnu_devtree_root, "/");
  234. return GRUB_ERR_NONE;
  235. fail:
  236. memorymap->first_child = NULL;
  237. grub_free (driverkey->data);
  238. grub_free (driverkey->name);
  239. grub_free (driverkey);
  240. return err;
  241. }
  242. /* Find a key or value in parent key. */
  243. struct grub_xnu_devtree_key *
  244. grub_xnu_find_key (struct grub_xnu_devtree_key *parent, const char *name)
  245. {
  246. struct grub_xnu_devtree_key *cur;
  247. for (cur = parent; cur; cur = cur->next)
  248. if (grub_strcmp (cur->name, name) == 0)
  249. return cur;
  250. return 0;
  251. }
  252. struct grub_xnu_devtree_key *
  253. grub_xnu_create_key (struct grub_xnu_devtree_key **parent, const char *name)
  254. {
  255. struct grub_xnu_devtree_key *ret;
  256. ret = grub_xnu_find_key (*parent, name);
  257. if (ret)
  258. return ret;
  259. ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
  260. if (! ret)
  261. return 0;
  262. ret->name = grub_strdup (name);
  263. if (! ret->name)
  264. {
  265. grub_free (ret);
  266. return 0;
  267. }
  268. ret->datasize = -1;
  269. ret->next = *parent;
  270. *parent = ret;
  271. return ret;
  272. }
  273. struct grub_xnu_devtree_key *
  274. grub_xnu_create_value (struct grub_xnu_devtree_key **parent, const char *name)
  275. {
  276. struct grub_xnu_devtree_key *ret;
  277. ret = grub_xnu_find_key (*parent, name);
  278. if (ret)
  279. {
  280. if (ret->datasize == -1)
  281. grub_xnu_free_devtree (ret->first_child);
  282. else if (ret->datasize)
  283. grub_free (ret->data);
  284. ret->datasize = 0;
  285. ret->data = 0;
  286. return ret;
  287. }
  288. ret = (struct grub_xnu_devtree_key *) grub_zalloc (sizeof (*ret));
  289. if (! ret)
  290. return 0;
  291. ret->name = grub_strdup (name);
  292. if (! ret->name)
  293. {
  294. grub_free (ret);
  295. return 0;
  296. }
  297. ret->next = *parent;
  298. *parent = ret;
  299. return ret;
  300. }
  301. static grub_err_t
  302. grub_xnu_unload (void)
  303. {
  304. grub_cpu_xnu_unload ();
  305. grub_xnu_free_devtree (grub_xnu_devtree_root);
  306. grub_xnu_devtree_root = 0;
  307. /* Free loaded image. */
  308. driversnum = 0;
  309. driverspackagenum = 0;
  310. grub_relocator_unload (grub_xnu_relocator);
  311. grub_xnu_relocator = NULL;
  312. grub_xnu_heap_target_start = 0;
  313. grub_xnu_heap_size = 0;
  314. grub_xnu_unlock ();
  315. return GRUB_ERR_NONE;
  316. }
  317. static grub_err_t
  318. grub_cmd_xnu_kernel (grub_command_t cmd __attribute__ ((unused)),
  319. int argc, char *args[])
  320. {
  321. grub_err_t err;
  322. grub_macho_t macho;
  323. grub_uint32_t startcode, endcode;
  324. int i;
  325. char *ptr;
  326. void *loadaddr;
  327. grub_addr_t loadaddr_target;
  328. if (argc < 1)
  329. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  330. grub_xnu_unload ();
  331. macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL, 0);
  332. if (! macho)
  333. return grub_errno;
  334. err = grub_macho_size32 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
  335. args[0]);
  336. if (err)
  337. {
  338. grub_macho_close (macho);
  339. grub_xnu_unload ();
  340. return err;
  341. }
  342. grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
  343. (unsigned long) endcode, (unsigned long) startcode);
  344. grub_xnu_relocator = grub_relocator_new ();
  345. if (!grub_xnu_relocator)
  346. return grub_errno;
  347. grub_xnu_heap_target_start = startcode;
  348. err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr,
  349. &loadaddr_target);
  350. if (err)
  351. {
  352. grub_macho_close (macho);
  353. grub_xnu_unload ();
  354. return err;
  355. }
  356. /* Load kernel. */
  357. err = grub_macho_load32 (macho, args[0], (char *) loadaddr - startcode,
  358. GRUB_MACHO_NOBSS, &grub_xnu_darwin_version);
  359. if (err)
  360. {
  361. grub_macho_close (macho);
  362. grub_xnu_unload ();
  363. return err;
  364. }
  365. grub_xnu_entry_point = grub_macho_get_entry_point32 (macho, args[0]);
  366. if (! grub_xnu_entry_point)
  367. {
  368. grub_macho_close (macho);
  369. grub_xnu_unload ();
  370. return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
  371. }
  372. grub_macho_close (macho);
  373. err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
  374. if (err)
  375. {
  376. grub_xnu_unload ();
  377. return err;
  378. }
  379. /* Copy parameters to kernel command line. */
  380. ptr = grub_xnu_cmdline;
  381. for (i = 1; i < argc; i++)
  382. {
  383. if (ptr + grub_strlen (args[i]) + 1
  384. >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
  385. break;
  386. grub_memcpy (ptr, args[i], grub_strlen (args[i]));
  387. ptr += grub_strlen (args[i]);
  388. *ptr = ' ';
  389. ptr++;
  390. }
  391. /* Replace last space by '\0'. */
  392. if (ptr != grub_xnu_cmdline)
  393. *(ptr - 1) = 0;
  394. err = grub_verify_string (grub_xnu_cmdline, GRUB_VERIFY_KERNEL_CMDLINE);
  395. if (err)
  396. return err;
  397. #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
  398. err = grub_efiemu_autocore ();
  399. if (err)
  400. return err;
  401. #endif
  402. grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
  403. grub_xnu_lock ();
  404. grub_xnu_is_64bit = 0;
  405. return 0;
  406. }
  407. static grub_err_t
  408. grub_cmd_xnu_kernel64 (grub_command_t cmd __attribute__ ((unused)),
  409. int argc, char *args[])
  410. {
  411. grub_err_t err;
  412. grub_macho_t macho;
  413. grub_uint64_t startcode, endcode;
  414. int i;
  415. char *ptr;
  416. void *loadaddr;
  417. grub_addr_t loadaddr_target;
  418. if (argc < 1)
  419. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  420. grub_xnu_unload ();
  421. macho = grub_macho_open (args[0], GRUB_FILE_TYPE_XNU_KERNEL, 1);
  422. if (! macho)
  423. return grub_errno;
  424. err = grub_macho_size64 (macho, &startcode, &endcode, GRUB_MACHO_NOBSS,
  425. args[0]);
  426. if (err)
  427. {
  428. grub_macho_close (macho);
  429. grub_xnu_unload ();
  430. return err;
  431. }
  432. startcode &= 0x0fffffff;
  433. endcode &= 0x0fffffff;
  434. grub_dprintf ("xnu", "endcode = %lx, startcode = %lx\n",
  435. (unsigned long) endcode, (unsigned long) startcode);
  436. grub_xnu_relocator = grub_relocator_new ();
  437. if (!grub_xnu_relocator)
  438. return grub_errno;
  439. grub_xnu_heap_target_start = startcode;
  440. err = grub_xnu_heap_malloc (endcode - startcode, &loadaddr,
  441. &loadaddr_target);
  442. if (err)
  443. {
  444. grub_macho_close (macho);
  445. grub_xnu_unload ();
  446. return err;
  447. }
  448. /* Load kernel. */
  449. err = grub_macho_load64 (macho, args[0], (char *) loadaddr - startcode,
  450. GRUB_MACHO_NOBSS, &grub_xnu_darwin_version);
  451. if (err)
  452. {
  453. grub_macho_close (macho);
  454. grub_xnu_unload ();
  455. return err;
  456. }
  457. grub_xnu_entry_point = grub_macho_get_entry_point64 (macho, args[0])
  458. & 0x0fffffff;
  459. if (! grub_xnu_entry_point)
  460. {
  461. grub_macho_close (macho);
  462. grub_xnu_unload ();
  463. return grub_error (GRUB_ERR_BAD_OS, "couldn't find entry point");
  464. }
  465. grub_macho_close (macho);
  466. err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
  467. if (err)
  468. {
  469. grub_xnu_unload ();
  470. return err;
  471. }
  472. /* Copy parameters to kernel command line. */
  473. ptr = grub_xnu_cmdline;
  474. for (i = 1; i < argc; i++)
  475. {
  476. if (ptr + grub_strlen (args[i]) + 1
  477. >= grub_xnu_cmdline + sizeof (grub_xnu_cmdline))
  478. break;
  479. grub_memcpy (ptr, args[i], grub_strlen (args[i]));
  480. ptr += grub_strlen (args[i]);
  481. *ptr = ' ';
  482. ptr++;
  483. }
  484. /* Replace last space by '\0'. */
  485. if (ptr != grub_xnu_cmdline)
  486. *(ptr - 1) = 0;
  487. err = grub_verify_string (grub_xnu_cmdline, GRUB_VERIFY_KERNEL_CMDLINE);
  488. if (err)
  489. return err;
  490. #if defined (__i386) && !defined (GRUB_MACHINE_EFI)
  491. err = grub_efiemu_autocore ();
  492. if (err)
  493. return err;
  494. #endif
  495. grub_loader_set (grub_xnu_boot, grub_xnu_unload, 0);
  496. grub_xnu_lock ();
  497. grub_xnu_is_64bit = 1;
  498. return 0;
  499. }
  500. /* Register a memory in a memory map under name PREFIXSUFFIX
  501. and increment SUFFIX. */
  502. static grub_err_t
  503. grub_xnu_register_memory (const char *prefix, int *suffix,
  504. grub_addr_t addr, grub_size_t size)
  505. {
  506. struct grub_xnu_devtree_key *chosen;
  507. struct grub_xnu_devtree_key *memorymap;
  508. struct grub_xnu_devtree_key *driverkey;
  509. struct grub_xnu_extdesc *extdesc;
  510. if (! grub_xnu_heap_size)
  511. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  512. chosen = grub_xnu_create_key (&grub_xnu_devtree_root, "chosen");
  513. if (! chosen)
  514. return grub_errno;
  515. memorymap = grub_xnu_create_key (&(chosen->first_child), "memory-map");
  516. if (! memorymap)
  517. return grub_errno;
  518. driverkey = (struct grub_xnu_devtree_key *) grub_malloc (sizeof (*driverkey));
  519. if (! driverkey)
  520. return grub_errno;
  521. if (suffix)
  522. driverkey->name = grub_xasprintf ("%s%d", prefix, (*suffix)++);
  523. else
  524. driverkey->name = grub_strdup (prefix);
  525. if (!driverkey->name)
  526. {
  527. grub_free (driverkey);
  528. return grub_errno;
  529. }
  530. driverkey->datasize = sizeof (*extdesc);
  531. driverkey->next = memorymap->first_child;
  532. driverkey->data = extdesc
  533. = (struct grub_xnu_extdesc *) grub_malloc (sizeof (*extdesc));
  534. if (! driverkey->data)
  535. {
  536. grub_free (driverkey->name);
  537. grub_free (driverkey);
  538. return grub_errno;
  539. }
  540. memorymap->first_child = driverkey;
  541. extdesc->addr = addr;
  542. extdesc->size = (grub_uint32_t) size;
  543. return GRUB_ERR_NONE;
  544. }
  545. static inline char *
  546. get_name_ptr (char *name)
  547. {
  548. char *p = name, *p2;
  549. /* Skip Info.plist. */
  550. p2 = grub_strrchr (p, '/');
  551. if (!p2)
  552. return name;
  553. if (p2 == name)
  554. return name + 1;
  555. p = p2 - 1;
  556. p2 = grub_strrchr (p, '/');
  557. if (!p2)
  558. return name;
  559. if (p2 == name)
  560. return name + 1;
  561. if (grub_memcmp (p2, "/Contents/", sizeof ("/Contents/") - 1) != 0)
  562. return p2 + 1;
  563. p = p2 - 1;
  564. p2 = grub_strrchr (p, '/');
  565. if (!p2)
  566. return name;
  567. return p2 + 1;
  568. }
  569. /* Load .kext. */
  570. static grub_err_t
  571. grub_xnu_load_driver (char *infoplistname, grub_file_t binaryfile,
  572. const char *filename)
  573. {
  574. grub_macho_t macho;
  575. grub_err_t err;
  576. grub_file_t infoplist;
  577. struct grub_xnu_extheader *exthead;
  578. int neededspace = sizeof (*exthead);
  579. grub_uint8_t *buf;
  580. void *buf0;
  581. grub_addr_t buf_target;
  582. grub_size_t infoplistsize = 0, machosize = 0;
  583. char *name, *nameend;
  584. int namelen;
  585. if (infoplistname == NULL)
  586. return grub_error (GRUB_ERR_BAD_FILENAME, N_("missing p-list filename"));
  587. name = get_name_ptr (infoplistname);
  588. nameend = grub_strchr (name, '/');
  589. if (nameend)
  590. namelen = nameend - name;
  591. else
  592. namelen = grub_strlen (name);
  593. neededspace += namelen + 1;
  594. if (! grub_xnu_heap_size)
  595. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  596. /* Compute the needed space. */
  597. if (binaryfile)
  598. {
  599. macho = grub_macho_file (binaryfile, filename, grub_xnu_is_64bit);
  600. if (!macho)
  601. grub_file_close (binaryfile);
  602. else
  603. {
  604. if (grub_xnu_is_64bit)
  605. machosize = grub_macho_filesize64 (macho);
  606. else
  607. machosize = grub_macho_filesize32 (macho);
  608. }
  609. neededspace += machosize;
  610. }
  611. else
  612. macho = 0;
  613. infoplist = grub_file_open (infoplistname, GRUB_FILE_TYPE_XNU_INFO_PLIST);
  614. grub_errno = GRUB_ERR_NONE;
  615. if (infoplist)
  616. {
  617. infoplistsize = grub_file_size (infoplist);
  618. neededspace += infoplistsize + 1;
  619. }
  620. else
  621. infoplistsize = 0;
  622. /* Allocate the space. */
  623. err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
  624. if (err)
  625. goto fail;
  626. err = grub_xnu_heap_malloc (neededspace, &buf0, &buf_target);
  627. if (err)
  628. goto fail;
  629. buf = buf0;
  630. exthead = (struct grub_xnu_extheader *) buf;
  631. grub_memset (exthead, 0, sizeof (*exthead));
  632. buf += sizeof (*exthead);
  633. /* Load the binary. */
  634. if (macho)
  635. {
  636. exthead->binaryaddr = buf_target + (buf - (grub_uint8_t *) buf0);
  637. exthead->binarysize = machosize;
  638. if (grub_xnu_is_64bit)
  639. err = grub_macho_readfile64 (macho, filename, buf);
  640. else
  641. err = grub_macho_readfile32 (macho, filename, buf);
  642. if (err)
  643. goto fail;
  644. grub_macho_close (macho);
  645. buf += machosize;
  646. }
  647. grub_errno = GRUB_ERR_NONE;
  648. /* Load the plist. */
  649. if (infoplist)
  650. {
  651. exthead->infoplistaddr = buf_target + (buf - (grub_uint8_t *) buf0);
  652. exthead->infoplistsize = infoplistsize + 1;
  653. if (grub_file_read (infoplist, buf, infoplistsize)
  654. != (grub_ssize_t) (infoplistsize))
  655. {
  656. grub_file_close (infoplist);
  657. if (!grub_errno)
  658. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  659. infoplistname);
  660. return grub_errno;
  661. }
  662. grub_file_close (infoplist);
  663. buf[infoplistsize] = 0;
  664. buf += infoplistsize + 1;
  665. }
  666. grub_errno = GRUB_ERR_NONE;
  667. exthead->nameaddr = (buf - (grub_uint8_t *) buf0) + buf_target;
  668. exthead->namesize = namelen + 1;
  669. grub_memcpy (buf, name, namelen);
  670. buf[namelen] = 0;
  671. buf += namelen + 1;
  672. /* Announce to kernel */
  673. return grub_xnu_register_memory ("Driver-", &driversnum, buf_target,
  674. neededspace);
  675. fail:
  676. if (macho)
  677. grub_macho_close (macho);
  678. return err;
  679. }
  680. /* Load mkext. */
  681. static grub_err_t
  682. grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
  683. int argc, char *args[])
  684. {
  685. grub_file_t file;
  686. void *loadto;
  687. grub_addr_t loadto_target;
  688. grub_err_t err;
  689. grub_off_t readoff = 0;
  690. grub_ssize_t readlen = -1;
  691. struct grub_macho_fat_header head;
  692. struct grub_macho_fat_arch *archs;
  693. int narchs, i;
  694. if (argc != 1)
  695. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  696. if (! grub_xnu_heap_size)
  697. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  698. file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_MKEXT);
  699. if (! file)
  700. return grub_errno;
  701. /* Sometimes caches are fat binary. Errgh. */
  702. if (grub_file_read (file, &head, sizeof (head))
  703. != (grub_ssize_t) (sizeof (head)))
  704. {
  705. /* I don't know the internal structure of package but
  706. can hardly imagine a valid package shorter than 20 bytes. */
  707. grub_file_close (file);
  708. if (!grub_errno)
  709. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
  710. return grub_errno;
  711. }
  712. /* Find the corresponding architecture. */
  713. if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
  714. {
  715. narchs = grub_be_to_cpu32 (head.nfat_arch);
  716. archs = grub_calloc (narchs, sizeof (struct grub_macho_fat_arch));
  717. if (! archs)
  718. {
  719. grub_file_close (file);
  720. return grub_errno;
  721. }
  722. if (grub_file_read (file, archs,
  723. sizeof (struct grub_macho_fat_arch) * narchs)
  724. != (grub_ssize_t) sizeof(struct grub_macho_fat_arch) * narchs)
  725. {
  726. grub_free (archs);
  727. if (!grub_errno)
  728. grub_error (GRUB_ERR_READ_ERROR, N_("premature end of file %s"),
  729. args[0]);
  730. return grub_errno;
  731. }
  732. for (i = 0; i < narchs; i++)
  733. {
  734. if (!grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST32
  735. (grub_be_to_cpu32 (archs[i].cputype)))
  736. {
  737. readoff = grub_be_to_cpu32 (archs[i].offset);
  738. readlen = grub_be_to_cpu32 (archs[i].size);
  739. }
  740. if (grub_xnu_is_64bit && GRUB_MACHO_CPUTYPE_IS_HOST64
  741. (grub_be_to_cpu32 (archs[i].cputype)))
  742. {
  743. readoff = grub_be_to_cpu32 (archs[i].offset);
  744. readlen = grub_be_to_cpu32 (archs[i].size);
  745. }
  746. }
  747. grub_free (archs);
  748. }
  749. else
  750. {
  751. /* It's a flat file. Some sane people still exist. */
  752. readoff = 0;
  753. readlen = grub_file_size (file);
  754. }
  755. if (readlen == -1)
  756. {
  757. grub_file_close (file);
  758. return grub_error (GRUB_ERR_BAD_OS, "no suitable architecture is found");
  759. }
  760. /* Allocate space. */
  761. err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
  762. if (err)
  763. {
  764. grub_file_close (file);
  765. return err;
  766. }
  767. err = grub_xnu_heap_malloc (readlen, &loadto, &loadto_target);
  768. if (err)
  769. {
  770. grub_file_close (file);
  771. return err;
  772. }
  773. /* Read the file. */
  774. grub_file_seek (file, readoff);
  775. if (grub_file_read (file, loadto, readlen) != (grub_ssize_t) (readlen))
  776. {
  777. grub_file_close (file);
  778. if (!grub_errno)
  779. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
  780. return grub_errno;
  781. }
  782. grub_file_close (file);
  783. /* Pass it to kernel. */
  784. return grub_xnu_register_memory ("DriversPackage-", &driverspackagenum,
  785. loadto_target, readlen);
  786. }
  787. static grub_err_t
  788. grub_cmd_xnu_ramdisk (grub_command_t cmd __attribute__ ((unused)),
  789. int argc, char *args[])
  790. {
  791. grub_file_t file;
  792. void *loadto;
  793. grub_addr_t loadto_target;
  794. grub_err_t err;
  795. grub_size_t size;
  796. if (argc != 1)
  797. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  798. if (! grub_xnu_heap_size)
  799. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  800. file = grub_file_open (args[0], GRUB_FILE_TYPE_XNU_RAMDISK);
  801. if (! file)
  802. return grub_errno;
  803. err = grub_xnu_align_heap (GRUB_XNU_PAGESIZE);
  804. if (err)
  805. return err;
  806. size = grub_file_size (file);
  807. err = grub_xnu_heap_malloc (size, &loadto, &loadto_target);
  808. if (err)
  809. return err;
  810. if (grub_file_read (file, loadto, size) != (grub_ssize_t) (size))
  811. {
  812. grub_file_close (file);
  813. if (!grub_errno)
  814. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), args[0]);
  815. return grub_errno;
  816. }
  817. return grub_xnu_register_memory ("RAMDisk", 0, loadto_target, size);
  818. }
  819. /* Returns true if the kext should be loaded according to plist
  820. and osbundlereq. Also fill BINNAME. */
  821. static int
  822. grub_xnu_check_os_bundle_required (char *plistname,
  823. const char *osbundlereq,
  824. char **binname)
  825. {
  826. grub_file_t file;
  827. char *buf = 0, *tagstart = 0, *ptr1 = 0, *keyptr = 0;
  828. char *stringptr = 0, *ptr2 = 0;
  829. grub_size_t size;
  830. int depth = 0;
  831. int ret;
  832. int osbundlekeyfound = 0, binnamekeyfound = 0;
  833. if (binname)
  834. *binname = 0;
  835. file = grub_file_open (plistname, GRUB_FILE_TYPE_XNU_INFO_PLIST);
  836. if (! file)
  837. return 0;
  838. size = grub_file_size (file);
  839. buf = grub_malloc (size);
  840. if (! buf)
  841. {
  842. grub_file_close (file);
  843. return 0;
  844. }
  845. if (grub_file_read (file, buf, size) != (grub_ssize_t) (size))
  846. {
  847. grub_file_close (file);
  848. if (!grub_errno)
  849. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), plistname);
  850. return 0;
  851. }
  852. grub_file_close (file);
  853. /* Set the return value for the case when no OSBundleRequired tag is found. */
  854. if (osbundlereq)
  855. ret = grub_strword (osbundlereq, "all") || grub_strword (osbundlereq, "-");
  856. else
  857. ret = 1;
  858. /* Parse plist. It's quite dirty and inextensible but does its job. */
  859. for (ptr1 = buf; ptr1 < buf + size; ptr1++)
  860. switch (*ptr1)
  861. {
  862. case '<':
  863. tagstart = ptr1;
  864. *ptr1 = 0;
  865. if (keyptr && depth == 4
  866. && grub_strcmp (keyptr, "OSBundleRequired") == 0)
  867. osbundlekeyfound = 1;
  868. if (keyptr && depth == 4 &&
  869. grub_strcmp (keyptr, "CFBundleExecutable") == 0)
  870. binnamekeyfound = 1;
  871. if (stringptr && osbundlekeyfound && osbundlereq && depth == 4)
  872. {
  873. for (ptr2 = stringptr; *ptr2; ptr2++)
  874. *ptr2 = grub_tolower (*ptr2);
  875. ret = grub_strword (osbundlereq, stringptr)
  876. || grub_strword (osbundlereq, "all");
  877. }
  878. if (stringptr && binnamekeyfound && binname && depth == 4)
  879. {
  880. if (*binname)
  881. grub_free (*binname);
  882. *binname = grub_strdup (stringptr);
  883. }
  884. *ptr1 = '<';
  885. keyptr = 0;
  886. stringptr = 0;
  887. break;
  888. case '>':
  889. if (! tagstart)
  890. {
  891. grub_free (buf);
  892. grub_error (GRUB_ERR_BAD_OS, "can't parse %s", plistname);
  893. return 0;
  894. }
  895. *ptr1 = 0;
  896. if (tagstart[1] == '?' || ptr1[-1] == '/')
  897. {
  898. osbundlekeyfound = 0;
  899. *ptr1 = '>';
  900. break;
  901. }
  902. if (depth == 3 && grub_strcmp (tagstart + 1, "key") == 0)
  903. keyptr = ptr1 + 1;
  904. if (depth == 3 && grub_strcmp (tagstart + 1, "string") == 0)
  905. stringptr = ptr1 + 1;
  906. else if (grub_strcmp (tagstart + 1, "/key") != 0)
  907. {
  908. osbundlekeyfound = 0;
  909. binnamekeyfound = 0;
  910. }
  911. *ptr1 = '>';
  912. if (tagstart[1] == '/')
  913. depth--;
  914. else
  915. depth++;
  916. break;
  917. }
  918. grub_free (buf);
  919. return ret;
  920. }
  921. /* Context for grub_xnu_scan_dir_for_kexts. */
  922. struct grub_xnu_scan_dir_for_kexts_ctx
  923. {
  924. char *dirname;
  925. const char *osbundlerequired;
  926. int maxrecursion;
  927. };
  928. /* Helper for grub_xnu_scan_dir_for_kexts. */
  929. static int
  930. grub_xnu_scan_dir_for_kexts_load (const char *filename,
  931. const struct grub_dirhook_info *info,
  932. void *data)
  933. {
  934. struct grub_xnu_scan_dir_for_kexts_ctx *ctx = data;
  935. char *newdirname;
  936. if (! info->dir)
  937. return 0;
  938. if (filename[0] == '.')
  939. return 0;
  940. if (grub_strlen (filename) < 5 ||
  941. grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
  942. return 0;
  943. newdirname
  944. = grub_malloc (grub_strlen (ctx->dirname) + grub_strlen (filename) + 2);
  945. /* It's a .kext. Try to load it. */
  946. if (newdirname)
  947. {
  948. grub_strcpy (newdirname, ctx->dirname);
  949. newdirname[grub_strlen (newdirname) + 1] = 0;
  950. newdirname[grub_strlen (newdirname)] = '/';
  951. grub_strcpy (newdirname + grub_strlen (newdirname), filename);
  952. grub_xnu_load_kext_from_dir (newdirname, ctx->osbundlerequired,
  953. ctx->maxrecursion);
  954. if (grub_errno == GRUB_ERR_BAD_OS)
  955. grub_errno = GRUB_ERR_NONE;
  956. grub_free (newdirname);
  957. }
  958. return 0;
  959. }
  960. /* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
  961. grub_err_t
  962. grub_xnu_scan_dir_for_kexts (char *dirname, const char *osbundlerequired,
  963. int maxrecursion)
  964. {
  965. struct grub_xnu_scan_dir_for_kexts_ctx ctx = {
  966. .dirname = dirname,
  967. .osbundlerequired = osbundlerequired,
  968. .maxrecursion = maxrecursion
  969. };
  970. grub_device_t dev;
  971. char *device_name;
  972. grub_fs_t fs;
  973. const char *path;
  974. if (! grub_xnu_heap_size)
  975. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  976. device_name = grub_file_get_device_name (dirname);
  977. dev = grub_device_open (device_name);
  978. if (dev)
  979. {
  980. fs = grub_fs_probe (dev);
  981. path = grub_strchr (dirname, ')');
  982. if (! path)
  983. path = dirname;
  984. else
  985. path++;
  986. if (fs)
  987. (fs->fs_dir) (dev, path, grub_xnu_scan_dir_for_kexts_load, &ctx);
  988. grub_device_close (dev);
  989. }
  990. grub_free (device_name);
  991. return GRUB_ERR_NONE;
  992. }
  993. /* Context for grub_xnu_load_kext_from_dir. */
  994. struct grub_xnu_load_kext_from_dir_ctx
  995. {
  996. char *dirname;
  997. const char *osbundlerequired;
  998. int maxrecursion;
  999. char *plistname;
  1000. char *newdirname;
  1001. int usemacos;
  1002. };
  1003. /* Helper for grub_xnu_load_kext_from_dir. */
  1004. static int
  1005. grub_xnu_load_kext_from_dir_load (const char *filename,
  1006. const struct grub_dirhook_info *info,
  1007. void *data)
  1008. {
  1009. struct grub_xnu_load_kext_from_dir_ctx *ctx = data;
  1010. if (grub_strlen (filename) > 15)
  1011. return 0;
  1012. grub_strcpy (ctx->newdirname + grub_strlen (ctx->dirname) + 1, filename);
  1013. /* If the kext contains directory "Contents" all real stuff is in
  1014. this directory. */
  1015. if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
  1016. grub_xnu_load_kext_from_dir (ctx->newdirname, ctx->osbundlerequired,
  1017. ctx->maxrecursion - 1);
  1018. /* Directory "Plugins" contains nested kexts. */
  1019. if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
  1020. grub_xnu_scan_dir_for_kexts (ctx->newdirname, ctx->osbundlerequired,
  1021. ctx->maxrecursion - 1);
  1022. /* Directory "MacOS" contains executable, otherwise executable is
  1023. on the top. */
  1024. if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
  1025. ctx->usemacos = 1;
  1026. /* Info.plist is the file which governs our future actions. */
  1027. if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
  1028. && ! ctx->plistname)
  1029. ctx->plistname = grub_strdup (ctx->newdirname);
  1030. return 0;
  1031. }
  1032. /* Load extension DIRNAME. (extensions are directories in xnu) */
  1033. grub_err_t
  1034. grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired,
  1035. int maxrecursion)
  1036. {
  1037. struct grub_xnu_load_kext_from_dir_ctx ctx = {
  1038. .dirname = dirname,
  1039. .osbundlerequired = osbundlerequired,
  1040. .maxrecursion = maxrecursion,
  1041. .plistname = 0,
  1042. .usemacos = 0
  1043. };
  1044. grub_device_t dev;
  1045. char *newpath;
  1046. char *device_name;
  1047. grub_fs_t fs;
  1048. const char *path;
  1049. char *binsuffix;
  1050. grub_file_t binfile;
  1051. ctx.newdirname = grub_malloc (grub_strlen (dirname) + 20);
  1052. if (! ctx.newdirname)
  1053. return grub_errno;
  1054. grub_strcpy (ctx.newdirname, dirname);
  1055. ctx.newdirname[grub_strlen (dirname)] = '/';
  1056. ctx.newdirname[grub_strlen (dirname) + 1] = 0;
  1057. device_name = grub_file_get_device_name (dirname);
  1058. dev = grub_device_open (device_name);
  1059. if (dev)
  1060. {
  1061. fs = grub_fs_probe (dev);
  1062. path = grub_strchr (dirname, ')');
  1063. if (! path)
  1064. path = dirname;
  1065. else
  1066. path++;
  1067. newpath = grub_strchr (ctx.newdirname, ')');
  1068. if (! newpath)
  1069. newpath = ctx.newdirname;
  1070. else
  1071. newpath++;
  1072. /* Look at the directory. */
  1073. if (fs)
  1074. (fs->fs_dir) (dev, path, grub_xnu_load_kext_from_dir_load, &ctx);
  1075. if (ctx.plistname && grub_xnu_check_os_bundle_required
  1076. (ctx.plistname, osbundlerequired, &binsuffix))
  1077. {
  1078. if (binsuffix)
  1079. {
  1080. /* Open the binary. */
  1081. char *binname = grub_malloc (grub_strlen (dirname)
  1082. + grub_strlen (binsuffix)
  1083. + sizeof ("/MacOS/"));
  1084. grub_strcpy (binname, dirname);
  1085. if (ctx.usemacos)
  1086. grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
  1087. else
  1088. grub_strcpy (binname + grub_strlen (binname), "/");
  1089. grub_strcpy (binname + grub_strlen (binname), binsuffix);
  1090. grub_dprintf ("xnu", "%s:%s\n", ctx.plistname, binname);
  1091. binfile = grub_file_open (binname, GRUB_FILE_TYPE_XNU_KEXT);
  1092. if (! binfile)
  1093. grub_errno = GRUB_ERR_NONE;
  1094. /* Load the extension. */
  1095. grub_xnu_load_driver (ctx.plistname, binfile,
  1096. binname);
  1097. grub_free (binname);
  1098. grub_free (binsuffix);
  1099. }
  1100. else
  1101. {
  1102. grub_dprintf ("xnu", "%s:0\n", ctx.plistname);
  1103. grub_xnu_load_driver (ctx.plistname, 0, 0);
  1104. }
  1105. }
  1106. grub_free (ctx.plistname);
  1107. grub_device_close (dev);
  1108. }
  1109. grub_free (device_name);
  1110. return GRUB_ERR_NONE;
  1111. }
  1112. static int locked=0;
  1113. static grub_dl_t my_mod;
  1114. /* Load the kext. */
  1115. static grub_err_t
  1116. grub_cmd_xnu_kext (grub_command_t cmd __attribute__ ((unused)),
  1117. int argc, char *args[])
  1118. {
  1119. grub_file_t binfile = 0;
  1120. if (! grub_xnu_heap_size)
  1121. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  1122. if (argc == 2)
  1123. {
  1124. /* User explicitly specified plist and binary. */
  1125. if (grub_strcmp (args[1], "-") != 0)
  1126. {
  1127. binfile = grub_file_open (args[1], GRUB_FILE_TYPE_XNU_KEXT);
  1128. if (! binfile)
  1129. return grub_errno;
  1130. }
  1131. return grub_xnu_load_driver (grub_strcmp (args[0], "-") ? args[0] : 0,
  1132. binfile, args[1]);
  1133. }
  1134. /* load kext normally. */
  1135. if (argc == 1)
  1136. return grub_xnu_load_kext_from_dir (args[0], 0, 10);
  1137. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  1138. }
  1139. /* Load a directory containing kexts. */
  1140. static grub_err_t
  1141. grub_cmd_xnu_kextdir (grub_command_t cmd __attribute__ ((unused)),
  1142. int argc, char *args[])
  1143. {
  1144. if (argc != 1 && argc != 2)
  1145. return grub_error (GRUB_ERR_BAD_ARGUMENT, "directory name required");
  1146. if (! grub_xnu_heap_size)
  1147. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  1148. if (argc == 1)
  1149. return grub_xnu_scan_dir_for_kexts (args[0],
  1150. "console,root,local-root,network-root",
  1151. 10);
  1152. else
  1153. {
  1154. char *osbundlerequired = grub_strdup (args[1]), *ptr;
  1155. grub_err_t err;
  1156. if (! osbundlerequired)
  1157. return grub_errno;
  1158. for (ptr = osbundlerequired; *ptr; ptr++)
  1159. *ptr = grub_tolower (*ptr);
  1160. err = grub_xnu_scan_dir_for_kexts (args[0], osbundlerequired, 10);
  1161. grub_free (osbundlerequired);
  1162. return err;
  1163. }
  1164. }
  1165. static inline int
  1166. hextoval (char c)
  1167. {
  1168. if (c >= '0' && c <= '9')
  1169. return c - '0';
  1170. if (c >= 'a' && c <= 'z')
  1171. return c - 'a' + 10;
  1172. if (c >= 'A' && c <= 'Z')
  1173. return c - 'A' + 10;
  1174. return 0;
  1175. }
  1176. static inline void
  1177. unescape (char *name, char *curdot, char *nextdot, int *len)
  1178. {
  1179. char *ptr, *dptr;
  1180. dptr = name;
  1181. for (ptr = curdot; ptr < nextdot;)
  1182. if (ptr + 2 < nextdot && *ptr == '%')
  1183. {
  1184. *dptr = (hextoval (ptr[1]) << 4) | (hextoval (ptr[2]));
  1185. ptr += 3;
  1186. dptr++;
  1187. }
  1188. else
  1189. {
  1190. *dptr = *ptr;
  1191. ptr++;
  1192. dptr++;
  1193. }
  1194. *len = dptr - name;
  1195. }
  1196. grub_err_t
  1197. grub_xnu_fill_devicetree (void)
  1198. {
  1199. struct grub_env_var *var;
  1200. FOR_SORTED_ENV (var)
  1201. {
  1202. char *nextdot = 0, *curdot;
  1203. struct grub_xnu_devtree_key **curkey = &grub_xnu_devtree_root;
  1204. struct grub_xnu_devtree_key *curvalue;
  1205. char *name = 0, *data;
  1206. int len;
  1207. if (grub_memcmp (var->name, "XNU.DeviceTree.",
  1208. sizeof ("XNU.DeviceTree.") - 1) != 0)
  1209. continue;
  1210. curdot = var->name + sizeof ("XNU.DeviceTree.") - 1;
  1211. nextdot = grub_strchr (curdot, '.');
  1212. if (nextdot)
  1213. nextdot++;
  1214. while (nextdot)
  1215. {
  1216. name = grub_realloc (name, nextdot - curdot + 1);
  1217. if (!name)
  1218. return grub_errno;
  1219. unescape (name, curdot, nextdot, &len);
  1220. name[len - 1] = 0;
  1221. curkey = &(grub_xnu_create_key (curkey, name)->first_child);
  1222. curdot = nextdot;
  1223. nextdot = grub_strchr (nextdot, '.');
  1224. if (nextdot)
  1225. nextdot++;
  1226. }
  1227. nextdot = curdot + grub_strlen (curdot) + 1;
  1228. name = grub_realloc (name, nextdot - curdot + 1);
  1229. if (!name)
  1230. return grub_errno;
  1231. unescape (name, curdot, nextdot, &len);
  1232. name[len] = 0;
  1233. curvalue = grub_xnu_create_value (curkey, name);
  1234. grub_free (name);
  1235. if (!curvalue)
  1236. return grub_errno;
  1237. data = grub_malloc (grub_strlen (var->value) + 1);
  1238. if (!data)
  1239. return grub_errno;
  1240. unescape (data, var->value, var->value + grub_strlen (var->value),
  1241. &len);
  1242. curvalue->datasize = len;
  1243. curvalue->data = data;
  1244. }
  1245. return grub_errno;
  1246. }
  1247. struct grub_video_bitmap *grub_xnu_bitmap = 0;
  1248. grub_xnu_bitmap_mode_t grub_xnu_bitmap_mode;
  1249. /* Option array indices. */
  1250. #define XNU_SPLASH_CMD_ARGINDEX_MODE 0
  1251. static const struct grub_arg_option xnu_splash_cmd_options[] =
  1252. {
  1253. {"mode", 'm', 0, N_("Background image mode."), N_("stretch|normal"),
  1254. ARG_TYPE_STRING},
  1255. {0, 0, 0, 0, 0, 0}
  1256. };
  1257. static grub_err_t
  1258. grub_cmd_xnu_splash (grub_extcmd_context_t ctxt,
  1259. int argc, char *args[])
  1260. {
  1261. grub_err_t err;
  1262. if (argc != 1)
  1263. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  1264. if (! grub_xnu_heap_size)
  1265. return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
  1266. if (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].set &&
  1267. grub_strcmp (ctxt->state[XNU_SPLASH_CMD_ARGINDEX_MODE].arg,
  1268. "stretch") == 0)
  1269. grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_STRETCH;
  1270. else
  1271. grub_xnu_bitmap_mode = GRUB_XNU_BITMAP_CENTER;
  1272. err = grub_video_bitmap_load (&grub_xnu_bitmap, args[0]);
  1273. if (err)
  1274. grub_xnu_bitmap = 0;
  1275. return err;
  1276. }
  1277. #ifndef GRUB_MACHINE_EMU
  1278. static grub_err_t
  1279. grub_cmd_xnu_resume (grub_command_t cmd __attribute__ ((unused)),
  1280. int argc, char *args[])
  1281. {
  1282. if (argc != 1)
  1283. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  1284. return grub_xnu_resume (args[0]);
  1285. }
  1286. #endif
  1287. void
  1288. grub_xnu_lock (void)
  1289. {
  1290. if (!locked)
  1291. grub_dl_ref (my_mod);
  1292. locked = 1;
  1293. }
  1294. void
  1295. grub_xnu_unlock (void)
  1296. {
  1297. if (locked)
  1298. grub_dl_unref (my_mod);
  1299. locked = 0;
  1300. }
  1301. static grub_command_t cmd_kernel64, cmd_kernel, cmd_mkext, cmd_kext;
  1302. static grub_command_t cmd_kextdir, cmd_ramdisk, cmd_resume;
  1303. static grub_extcmd_t cmd_splash;
  1304. GRUB_MOD_INIT(xnu)
  1305. {
  1306. cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0,
  1307. N_("Load XNU image."));
  1308. cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64,
  1309. 0, N_("Load 64-bit XNU image."));
  1310. cmd_mkext = grub_register_command_lockdown ("xnu_mkext", grub_cmd_xnu_mkext, 0,
  1311. N_("Load XNU extension package."));
  1312. cmd_kext = grub_register_command_lockdown ("xnu_kext", grub_cmd_xnu_kext, 0,
  1313. N_("Load XNU extension."));
  1314. cmd_kextdir = grub_register_command_lockdown ("xnu_kextdir", grub_cmd_xnu_kextdir,
  1315. /*
  1316. * TRANSLATORS: OSBundleRequired is
  1317. * a variable name in xnu extensions
  1318. * manifests. It behaves mostly like
  1319. * GNU/Linux runlevels.
  1320. */
  1321. N_("DIRECTORY [OSBundleRequired]"),
  1322. /*
  1323. * TRANSLATORS: There are many extensions
  1324. * in extension directory.
  1325. */
  1326. N_("Load XNU extension directory."));
  1327. cmd_ramdisk = grub_register_command ("xnu_ramdisk", grub_cmd_xnu_ramdisk, 0,
  1328. /* TRANSLATORS: ramdisk here isn't identifier. It can be translated. */
  1329. N_("Load XNU ramdisk. "
  1330. "It will be available in OS as md0."));
  1331. cmd_splash = grub_register_extcmd ("xnu_splash",
  1332. grub_cmd_xnu_splash, 0, 0,
  1333. N_("Load a splash image for XNU."),
  1334. xnu_splash_cmd_options);
  1335. #ifndef GRUB_MACHINE_EMU
  1336. cmd_resume = grub_register_command ("xnu_resume", grub_cmd_xnu_resume,
  1337. 0, N_("Load an image of hibernated"
  1338. " XNU."));
  1339. #endif
  1340. grub_cpu_xnu_init ();
  1341. my_mod = mod;
  1342. }
  1343. GRUB_MOD_FINI(xnu)
  1344. {
  1345. #ifndef GRUB_MACHINE_EMU
  1346. grub_unregister_command (cmd_resume);
  1347. #endif
  1348. grub_unregister_command (cmd_mkext);
  1349. grub_unregister_command (cmd_kext);
  1350. grub_unregister_command (cmd_kextdir);
  1351. grub_unregister_command (cmd_ramdisk);
  1352. grub_unregister_command (cmd_kernel);
  1353. grub_unregister_extcmd (cmd_splash);
  1354. grub_unregister_command (cmd_kernel64);
  1355. grub_cpu_xnu_fini ();
  1356. }