chainloader.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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. fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
  106. }
  107. static grub_efi_device_path_t *
  108. make_file_path (grub_efi_device_path_t *dp, const char *filename)
  109. {
  110. char *dir_start;
  111. char *dir_end;
  112. grub_size_t size;
  113. grub_efi_device_path_t *d;
  114. dir_start = grub_strchr (filename, ')');
  115. if (! dir_start)
  116. dir_start = (char *) filename;
  117. else
  118. dir_start++;
  119. dir_end = grub_strrchr (dir_start, '/');
  120. if (! dir_end)
  121. {
  122. grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
  123. return 0;
  124. }
  125. size = 0;
  126. d = dp;
  127. while (1)
  128. {
  129. size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
  130. if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
  131. break;
  132. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  133. }
  134. file_path = grub_malloc (size
  135. + ((grub_strlen (dir_start) + 1)
  136. * GRUB_MAX_UTF16_PER_UTF8
  137. * sizeof (grub_efi_char16_t))
  138. + sizeof (grub_efi_file_path_device_path_t) * 2);
  139. if (! file_path)
  140. return 0;
  141. grub_memcpy (file_path, dp, size);
  142. /* Fill the file path for the directory. */
  143. d = (grub_efi_device_path_t *) ((char *) file_path
  144. + ((char *) d - (char *) dp));
  145. grub_efi_print_device_path (d);
  146. copy_file_path ((grub_efi_file_path_device_path_t *) d,
  147. dir_start, dir_end - dir_start);
  148. /* Fill the file path for the file. */
  149. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  150. copy_file_path ((grub_efi_file_path_device_path_t *) d,
  151. dir_end + 1, grub_strlen (dir_end + 1));
  152. /* Fill the end of device path nodes. */
  153. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  154. d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
  155. d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
  156. d->length = sizeof (*d);
  157. return file_path;
  158. }
  159. static grub_err_t
  160. grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
  161. int argc, char *argv[])
  162. {
  163. grub_file_t file = 0;
  164. grub_ssize_t size;
  165. grub_efi_status_t status;
  166. grub_efi_boot_services_t *b;
  167. grub_device_t dev = 0;
  168. grub_efi_device_path_t *dp = 0;
  169. grub_efi_loaded_image_t *loaded_image;
  170. char *filename;
  171. void *boot_image = 0;
  172. grub_efi_handle_t dev_handle = 0;
  173. if (argc == 0)
  174. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  175. filename = argv[0];
  176. grub_dl_ref (my_mod);
  177. /* Initialize some global variables. */
  178. address = 0;
  179. image_handle = 0;
  180. file_path = 0;
  181. b = grub_efi_system_table->boot_services;
  182. file = grub_file_open (filename);
  183. if (! file)
  184. goto fail;
  185. /* Get the root device's device path. */
  186. dev = grub_device_open (0);
  187. if (! dev)
  188. goto fail;
  189. if (dev->disk)
  190. dev_handle = grub_efidisk_get_device_handle (dev->disk);
  191. else if (dev->net && dev->net->server)
  192. {
  193. grub_net_network_level_address_t addr;
  194. struct grub_net_network_level_interface *inf;
  195. grub_net_network_level_address_t gateway;
  196. grub_err_t err;
  197. err = grub_net_resolve_address (dev->net->server, &addr);
  198. if (err)
  199. goto fail;
  200. err = grub_net_route_address (addr, &gateway, &inf);
  201. if (err)
  202. goto fail;
  203. dev_handle = grub_efinet_get_device_handle (inf->card);
  204. }
  205. if (dev_handle)
  206. dp = grub_efi_get_device_path (dev_handle);
  207. if (! dp)
  208. {
  209. grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device");
  210. goto fail;
  211. }
  212. file_path = make_file_path (dp, filename);
  213. if (! file_path)
  214. goto fail;
  215. grub_printf ("file path: ");
  216. grub_efi_print_device_path (file_path);
  217. size = grub_file_size (file);
  218. if (!size)
  219. {
  220. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  221. filename);
  222. goto fail;
  223. }
  224. pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
  225. status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
  226. GRUB_EFI_LOADER_CODE,
  227. pages, &address);
  228. if (status != GRUB_EFI_SUCCESS)
  229. {
  230. grub_dprintf ("chain", "Failed to allocate %u pages\n",
  231. (unsigned int) pages);
  232. grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  233. goto fail;
  234. }
  235. boot_image = (void *) ((grub_addr_t) address);
  236. if (grub_file_read (file, boot_image, size) != size)
  237. {
  238. if (grub_errno == GRUB_ERR_NONE)
  239. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  240. filename);
  241. goto fail;
  242. }
  243. #if defined (__i386__) || defined (__x86_64__)
  244. if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
  245. {
  246. struct grub_macho_fat_header *head = boot_image;
  247. if (head->magic
  248. == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC))
  249. {
  250. grub_uint32_t i;
  251. struct grub_macho_fat_arch *archs
  252. = (struct grub_macho_fat_arch *) (head + 1);
  253. for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
  254. {
  255. if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
  256. break;
  257. }
  258. if (i == grub_cpu_to_le32 (head->nfat_arch))
  259. {
  260. grub_error (GRUB_ERR_BAD_OS, "no compatible arch found");
  261. goto fail;
  262. }
  263. if (grub_cpu_to_le32 (archs[i].offset)
  264. > ~grub_cpu_to_le32 (archs[i].size)
  265. || grub_cpu_to_le32 (archs[i].offset)
  266. + grub_cpu_to_le32 (archs[i].size)
  267. > (grub_size_t) size)
  268. {
  269. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  270. filename);
  271. goto fail;
  272. }
  273. boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
  274. size = grub_cpu_to_le32 (archs[i].size);
  275. }
  276. }
  277. #endif
  278. status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
  279. boot_image, size,
  280. &image_handle);
  281. if (status != GRUB_EFI_SUCCESS)
  282. {
  283. if (status == GRUB_EFI_OUT_OF_RESOURCES)
  284. grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
  285. else
  286. grub_error (GRUB_ERR_BAD_OS, "cannot load image");
  287. goto fail;
  288. }
  289. /* LoadImage does not set a device handler when the image is
  290. loaded from memory, so it is necessary to set it explicitly here.
  291. This is a mess. */
  292. loaded_image = grub_efi_get_loaded_image (image_handle);
  293. if (! loaded_image)
  294. {
  295. grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
  296. goto fail;
  297. }
  298. loaded_image->device_handle = dev_handle;
  299. if (argc > 1)
  300. {
  301. int i, len;
  302. grub_efi_char16_t *p16;
  303. for (i = 1, len = 0; i < argc; i++)
  304. len += grub_strlen (argv[i]) + 1;
  305. len *= sizeof (grub_efi_char16_t);
  306. cmdline = p16 = grub_malloc (len);
  307. if (! cmdline)
  308. goto fail;
  309. for (i = 1; i < argc; i++)
  310. {
  311. char *p8;
  312. p8 = argv[i];
  313. while (*p8)
  314. *(p16++) = *(p8++);
  315. *(p16++) = ' ';
  316. }
  317. *(--p16) = 0;
  318. loaded_image->load_options = cmdline;
  319. loaded_image->load_options_size = len;
  320. }
  321. grub_file_close (file);
  322. grub_device_close (dev);
  323. grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
  324. return 0;
  325. fail:
  326. if (dev)
  327. grub_device_close (dev);
  328. if (file)
  329. grub_file_close (file);
  330. grub_free (file_path);
  331. if (address)
  332. efi_call_2 (b->free_pages, address, pages);
  333. grub_dl_unref (my_mod);
  334. return grub_errno;
  335. }
  336. static grub_command_t cmd;
  337. GRUB_MOD_INIT(chainloader)
  338. {
  339. cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
  340. 0, N_("Load another boot loader."));
  341. my_mod = mod;
  342. }
  343. GRUB_MOD_FINI(chainloader)
  344. {
  345. grub_unregister_command (cmd);
  346. }