chainloader.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. /* chainloader.c - boot another boot loader */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2002,2004,2006,2007,2008 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /* TODO: support load options. */
  20. #include <grub/loader.h>
  21. #include <grub/file.h>
  22. #include <grub/err.h>
  23. #include <grub/device.h>
  24. #include <grub/disk.h>
  25. #include <grub/misc.h>
  26. #include <grub/charset.h>
  27. #include <grub/mm.h>
  28. #include <grub/types.h>
  29. #include <grub/dl.h>
  30. #include <grub/efi/api.h>
  31. #include <grub/efi/efi.h>
  32. #include <grub/efi/disk.h>
  33. #include <grub/command.h>
  34. #include <grub/i18n.h>
  35. #include <grub/net.h>
  36. #if defined (__i386__) || defined (__x86_64__)
  37. #include <grub/macho.h>
  38. #include <grub/i386/macho.h>
  39. #endif
  40. GRUB_MOD_LICENSE ("GPLv3+");
  41. static grub_dl_t my_mod;
  42. static grub_efi_physical_address_t address;
  43. static grub_efi_uintn_t pages;
  44. static grub_efi_device_path_t *file_path;
  45. static grub_efi_handle_t image_handle;
  46. static grub_efi_char16_t *cmdline;
  47. static grub_err_t
  48. grub_chainloader_unload (void)
  49. {
  50. grub_efi_boot_services_t *b;
  51. b = grub_efi_system_table->boot_services;
  52. efi_call_1 (b->unload_image, image_handle);
  53. efi_call_2 (b->free_pages, address, pages);
  54. grub_free (file_path);
  55. grub_free (cmdline);
  56. cmdline = 0;
  57. file_path = 0;
  58. grub_dl_unref (my_mod);
  59. return GRUB_ERR_NONE;
  60. }
  61. static grub_err_t
  62. grub_chainloader_boot (void)
  63. {
  64. grub_efi_boot_services_t *b;
  65. grub_efi_status_t status;
  66. grub_efi_uintn_t exit_data_size;
  67. grub_efi_char16_t *exit_data = NULL;
  68. b = grub_efi_system_table->boot_services;
  69. status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
  70. if (status != GRUB_EFI_SUCCESS)
  71. {
  72. if (exit_data)
  73. {
  74. char *buf;
  75. buf = grub_malloc (exit_data_size * 4 + 1);
  76. if (buf)
  77. {
  78. *grub_utf16_to_utf8 ((grub_uint8_t *) buf,
  79. exit_data, exit_data_size) = 0;
  80. grub_error (GRUB_ERR_BAD_OS, buf);
  81. grub_free (buf);
  82. }
  83. }
  84. else
  85. grub_error (GRUB_ERR_BAD_OS, "unknown error");
  86. }
  87. if (exit_data)
  88. efi_call_1 (b->free_pool, exit_data);
  89. grub_loader_unset ();
  90. return grub_errno;
  91. }
  92. static void
  93. copy_file_path (grub_efi_file_path_device_path_t *fp,
  94. const char *str, grub_efi_uint16_t len)
  95. {
  96. grub_efi_char16_t *p;
  97. grub_efi_uint16_t size;
  98. fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
  99. fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
  100. size = grub_utf8_to_utf16 (fp->path_name, len * GRUB_MAX_UTF16_PER_UTF8,
  101. (const grub_uint8_t *) str, len, 0);
  102. for (p = fp->path_name; p < fp->path_name + size; p++)
  103. if (*p == '/')
  104. *p = '\\';
  105. /* File Path is NULL terminated */
  106. fp->path_name[size++] = '\0';
  107. fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
  108. }
  109. static grub_efi_device_path_t *
  110. make_file_path (grub_efi_device_path_t *dp, const char *filename)
  111. {
  112. char *dir_start;
  113. char *dir_end;
  114. grub_size_t size;
  115. grub_efi_device_path_t *d;
  116. dir_start = grub_strchr (filename, ')');
  117. if (! dir_start)
  118. dir_start = (char *) filename;
  119. else
  120. dir_start++;
  121. dir_end = grub_strrchr (dir_start, '/');
  122. if (! dir_end)
  123. {
  124. grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
  125. return 0;
  126. }
  127. size = 0;
  128. d = dp;
  129. while (1)
  130. {
  131. size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
  132. if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
  133. break;
  134. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  135. }
  136. /* File Path is NULL terminated. Allocate space for 2 extra characters */
  137. /* FIXME why we split path in two components? */
  138. file_path = grub_malloc (size
  139. + ((grub_strlen (dir_start) + 2)
  140. * GRUB_MAX_UTF16_PER_UTF8
  141. * sizeof (grub_efi_char16_t))
  142. + sizeof (grub_efi_file_path_device_path_t) * 2);
  143. if (! file_path)
  144. return 0;
  145. grub_memcpy (file_path, dp, size);
  146. /* Fill the file path for the directory. */
  147. d = (grub_efi_device_path_t *) ((char *) file_path
  148. + ((char *) d - (char *) dp));
  149. grub_efi_print_device_path (d);
  150. copy_file_path ((grub_efi_file_path_device_path_t *) d,
  151. dir_start, dir_end - dir_start);
  152. /* Fill the file path for the file. */
  153. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  154. copy_file_path ((grub_efi_file_path_device_path_t *) d,
  155. dir_end + 1, grub_strlen (dir_end + 1));
  156. /* Fill the end of device path nodes. */
  157. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  158. d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
  159. d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
  160. d->length = sizeof (*d);
  161. return file_path;
  162. }
  163. static grub_err_t
  164. grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
  165. int argc, char *argv[])
  166. {
  167. grub_file_t file = 0;
  168. grub_ssize_t size;
  169. grub_efi_status_t status;
  170. grub_efi_boot_services_t *b;
  171. grub_device_t dev = 0;
  172. grub_efi_device_path_t *dp = 0;
  173. grub_efi_loaded_image_t *loaded_image;
  174. char *filename;
  175. void *boot_image = 0;
  176. grub_efi_handle_t dev_handle = 0;
  177. if (argc == 0)
  178. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  179. filename = argv[0];
  180. grub_dl_ref (my_mod);
  181. /* Initialize some global variables. */
  182. address = 0;
  183. image_handle = 0;
  184. file_path = 0;
  185. b = grub_efi_system_table->boot_services;
  186. file = grub_file_open (filename);
  187. if (! file)
  188. goto fail;
  189. /* Get the root device's device path. */
  190. dev = grub_device_open (0);
  191. if (! dev)
  192. goto fail;
  193. if (dev->disk)
  194. dev_handle = grub_efidisk_get_device_handle (dev->disk);
  195. else if (dev->net && dev->net->server)
  196. {
  197. grub_net_network_level_address_t addr;
  198. struct grub_net_network_level_interface *inf;
  199. grub_net_network_level_address_t gateway;
  200. grub_err_t err;
  201. err = grub_net_resolve_address (dev->net->server, &addr);
  202. if (err)
  203. goto fail;
  204. err = grub_net_route_address (addr, &gateway, &inf);
  205. if (err)
  206. goto fail;
  207. dev_handle = grub_efinet_get_device_handle (inf->card);
  208. }
  209. if (dev_handle)
  210. dp = grub_efi_get_device_path (dev_handle);
  211. if (! dp)
  212. {
  213. grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device");
  214. goto fail;
  215. }
  216. file_path = make_file_path (dp, filename);
  217. if (! file_path)
  218. goto fail;
  219. grub_printf ("file path: ");
  220. grub_efi_print_device_path (file_path);
  221. size = grub_file_size (file);
  222. if (!size)
  223. {
  224. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  225. filename);
  226. goto fail;
  227. }
  228. pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
  229. status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
  230. GRUB_EFI_LOADER_CODE,
  231. pages, &address);
  232. if (status != GRUB_EFI_SUCCESS)
  233. {
  234. grub_dprintf ("chain", "Failed to allocate %u pages\n",
  235. (unsigned int) pages);
  236. grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  237. goto fail;
  238. }
  239. boot_image = (void *) ((grub_addr_t) address);
  240. if (grub_file_read (file, boot_image, size) != size)
  241. {
  242. if (grub_errno == GRUB_ERR_NONE)
  243. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  244. filename);
  245. goto fail;
  246. }
  247. #if defined (__i386__) || defined (__x86_64__)
  248. if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
  249. {
  250. struct grub_macho_fat_header *head = boot_image;
  251. if (head->magic
  252. == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC))
  253. {
  254. grub_uint32_t i;
  255. struct grub_macho_fat_arch *archs
  256. = (struct grub_macho_fat_arch *) (head + 1);
  257. for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
  258. {
  259. if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
  260. break;
  261. }
  262. if (i == grub_cpu_to_le32 (head->nfat_arch))
  263. {
  264. grub_error (GRUB_ERR_BAD_OS, "no compatible arch found");
  265. goto fail;
  266. }
  267. if (grub_cpu_to_le32 (archs[i].offset)
  268. > ~grub_cpu_to_le32 (archs[i].size)
  269. || grub_cpu_to_le32 (archs[i].offset)
  270. + grub_cpu_to_le32 (archs[i].size)
  271. > (grub_size_t) size)
  272. {
  273. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  274. filename);
  275. goto fail;
  276. }
  277. boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
  278. size = grub_cpu_to_le32 (archs[i].size);
  279. }
  280. }
  281. #endif
  282. status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
  283. boot_image, size,
  284. &image_handle);
  285. if (status != GRUB_EFI_SUCCESS)
  286. {
  287. if (status == GRUB_EFI_OUT_OF_RESOURCES)
  288. grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
  289. else
  290. grub_error (GRUB_ERR_BAD_OS, "cannot load image");
  291. goto fail;
  292. }
  293. /* LoadImage does not set a device handler when the image is
  294. loaded from memory, so it is necessary to set it explicitly here.
  295. This is a mess. */
  296. loaded_image = grub_efi_get_loaded_image (image_handle);
  297. if (! loaded_image)
  298. {
  299. grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
  300. goto fail;
  301. }
  302. loaded_image->device_handle = dev_handle;
  303. if (argc > 1)
  304. {
  305. int i, len;
  306. grub_efi_char16_t *p16;
  307. for (i = 1, len = 0; i < argc; i++)
  308. len += grub_strlen (argv[i]) + 1;
  309. len *= sizeof (grub_efi_char16_t);
  310. cmdline = p16 = grub_malloc (len);
  311. if (! cmdline)
  312. goto fail;
  313. for (i = 1; i < argc; i++)
  314. {
  315. char *p8;
  316. p8 = argv[i];
  317. while (*p8)
  318. *(p16++) = *(p8++);
  319. *(p16++) = ' ';
  320. }
  321. *(--p16) = 0;
  322. loaded_image->load_options = cmdline;
  323. loaded_image->load_options_size = len;
  324. }
  325. grub_file_close (file);
  326. grub_device_close (dev);
  327. grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
  328. return 0;
  329. fail:
  330. if (dev)
  331. grub_device_close (dev);
  332. if (file)
  333. grub_file_close (file);
  334. grub_free (file_path);
  335. if (address)
  336. efi_call_2 (b->free_pages, address, pages);
  337. grub_dl_unref (my_mod);
  338. return grub_errno;
  339. }
  340. static grub_command_t cmd;
  341. GRUB_MOD_INIT(chainloader)
  342. {
  343. cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
  344. 0, N_("Load another boot loader."));
  345. my_mod = mod;
  346. }
  347. GRUB_MOD_FINI(chainloader)
  348. {
  349. grub_unregister_command (cmd);
  350. }