chainloader.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  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/efi/memory.h>
  34. #include <grub/command.h>
  35. #include <grub/i18n.h>
  36. #include <grub/net.h>
  37. #if defined (__i386__) || defined (__x86_64__)
  38. #include <grub/macho.h>
  39. #include <grub/i386/macho.h>
  40. #endif
  41. GRUB_MOD_LICENSE ("GPLv3+");
  42. static grub_dl_t my_mod;
  43. static grub_err_t
  44. grub_chainloader_unload (void *context)
  45. {
  46. grub_efi_handle_t image_handle = (grub_efi_handle_t) context;
  47. grub_efi_loaded_image_t *loaded_image;
  48. grub_efi_boot_services_t *b;
  49. loaded_image = grub_efi_get_loaded_image (image_handle);
  50. if (loaded_image != NULL)
  51. grub_free (loaded_image->load_options);
  52. b = grub_efi_system_table->boot_services;
  53. b->unload_image (image_handle);
  54. grub_dl_unref (my_mod);
  55. return GRUB_ERR_NONE;
  56. }
  57. static grub_err_t
  58. grub_chainloader_boot (void *context)
  59. {
  60. grub_efi_handle_t image_handle = (grub_efi_handle_t) context;
  61. grub_efi_boot_services_t *b;
  62. grub_efi_status_t status;
  63. grub_efi_uintn_t exit_data_size;
  64. grub_efi_char16_t *exit_data = NULL;
  65. b = grub_efi_system_table->boot_services;
  66. status = b->start_image (image_handle, &exit_data_size, &exit_data);
  67. if (status != GRUB_EFI_SUCCESS)
  68. {
  69. if (exit_data)
  70. {
  71. char *buf;
  72. buf = grub_malloc (exit_data_size * 4 + 1);
  73. if (buf)
  74. {
  75. *grub_utf16_to_utf8 ((grub_uint8_t *) buf,
  76. exit_data, exit_data_size) = 0;
  77. grub_error (GRUB_ERR_BAD_OS, "%s", buf);
  78. grub_free (buf);
  79. }
  80. }
  81. else
  82. grub_error (GRUB_ERR_BAD_OS, "unknown error");
  83. }
  84. if (exit_data)
  85. b->free_pool (exit_data);
  86. grub_loader_unset ();
  87. return grub_errno;
  88. }
  89. static grub_err_t
  90. copy_file_path (grub_efi_file_path_device_path_t *fp,
  91. const char *str, grub_efi_uint16_t len)
  92. {
  93. grub_efi_char16_t *p, *path_name;
  94. grub_efi_uint16_t size;
  95. fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
  96. fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
  97. path_name = grub_calloc (len, GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
  98. if (!path_name)
  99. return grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate path buffer");
  100. size = grub_utf8_to_utf16 (path_name, len * GRUB_MAX_UTF16_PER_UTF8,
  101. (const grub_uint8_t *) str, len, 0);
  102. for (p = path_name; p < path_name + size; p++)
  103. if (*p == '/')
  104. *p = '\\';
  105. grub_memcpy (fp->path_name, path_name, size * sizeof (*fp->path_name));
  106. /* File Path is NULL terminated */
  107. fp->path_name[size++] = '\0';
  108. fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
  109. grub_free (path_name);
  110. return GRUB_ERR_NONE;
  111. }
  112. static grub_efi_device_path_t *
  113. make_file_path (grub_efi_device_path_t *dp, const char *filename)
  114. {
  115. char *dir_start;
  116. char *dir_end;
  117. grub_size_t size;
  118. grub_efi_device_path_t *d, *file_path;
  119. dir_start = grub_strchr (filename, ')');
  120. if (! dir_start)
  121. dir_start = (char *) filename;
  122. else
  123. dir_start++;
  124. dir_end = grub_strrchr (dir_start, '/');
  125. if (! dir_end)
  126. {
  127. grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
  128. return 0;
  129. }
  130. size = 0;
  131. d = dp;
  132. while (d)
  133. {
  134. grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (d);
  135. if (len < 4)
  136. {
  137. grub_error (GRUB_ERR_OUT_OF_RANGE,
  138. "malformed EFI Device Path node has length=%" PRIuGRUB_SIZE, len);
  139. return NULL;
  140. }
  141. size += len;
  142. if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
  143. break;
  144. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  145. }
  146. /* File Path is NULL terminated. Allocate space for 2 extra characters */
  147. /* FIXME why we split path in two components? */
  148. file_path = grub_malloc (size
  149. + ((grub_strlen (dir_start) + 2)
  150. * GRUB_MAX_UTF16_PER_UTF8
  151. * sizeof (grub_efi_char16_t))
  152. + sizeof (grub_efi_file_path_device_path_t) * 2);
  153. if (! file_path)
  154. return 0;
  155. grub_memcpy (file_path, dp, size);
  156. /* Fill the file path for the directory. */
  157. d = (grub_efi_device_path_t *) ((char *) file_path
  158. + ((char *) d - (char *) dp));
  159. if (copy_file_path ((grub_efi_file_path_device_path_t *) d,
  160. dir_start, dir_end - dir_start) != GRUB_ERR_NONE)
  161. {
  162. fail:
  163. grub_free (file_path);
  164. return 0;
  165. }
  166. /* Fill the file path for the file. */
  167. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  168. if (copy_file_path ((grub_efi_file_path_device_path_t *) d,
  169. dir_end + 1, grub_strlen (dir_end + 1)) != GRUB_ERR_NONE)
  170. goto fail;
  171. /* Fill the end of device path nodes. */
  172. d = GRUB_EFI_NEXT_DEVICE_PATH (d);
  173. d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
  174. d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
  175. d->length = sizeof (*d);
  176. return file_path;
  177. }
  178. static grub_err_t
  179. grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
  180. int argc, char *argv[])
  181. {
  182. grub_file_t file = 0;
  183. grub_ssize_t size;
  184. grub_efi_status_t status;
  185. grub_efi_boot_services_t *b;
  186. grub_device_t dev = 0;
  187. grub_efi_device_path_t *dp = NULL, *file_path = NULL;
  188. grub_efi_loaded_image_t *loaded_image;
  189. char *filename;
  190. void *boot_image = 0;
  191. grub_efi_handle_t dev_handle = 0;
  192. grub_efi_physical_address_t address = 0;
  193. grub_efi_uintn_t pages = 0;
  194. grub_efi_char16_t *cmdline = NULL;
  195. grub_efi_handle_t image_handle = NULL;
  196. if (argc == 0)
  197. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  198. filename = argv[0];
  199. grub_dl_ref (my_mod);
  200. b = grub_efi_system_table->boot_services;
  201. file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
  202. if (! file)
  203. goto fail;
  204. /* Get the root device's device path. */
  205. dev = grub_device_open (0);
  206. if (dev == NULL)
  207. ;
  208. else if (dev->disk)
  209. dev_handle = grub_efidisk_get_device_handle (dev->disk);
  210. else if (dev->net && dev->net->server)
  211. {
  212. grub_net_network_level_address_t addr;
  213. struct grub_net_network_level_interface *inf;
  214. grub_net_network_level_address_t gateway;
  215. grub_err_t err;
  216. err = grub_net_resolve_address (dev->net->server, &addr);
  217. if (err)
  218. goto fail;
  219. err = grub_net_route_address (addr, &gateway, &inf);
  220. if (err)
  221. goto fail;
  222. dev_handle = grub_efinet_get_device_handle (inf->card);
  223. }
  224. if (dev_handle)
  225. dp = grub_efi_get_device_path (dev_handle);
  226. if (dp != NULL)
  227. {
  228. file_path = make_file_path (dp, filename);
  229. if (file_path == NULL)
  230. goto fail;
  231. }
  232. size = grub_file_size (file);
  233. if (!size)
  234. {
  235. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  236. filename);
  237. goto fail;
  238. }
  239. pages = (grub_efi_uintn_t) GRUB_EFI_BYTES_TO_PAGES (size);
  240. status = b->allocate_pages (GRUB_EFI_ALLOCATE_ANY_PAGES,
  241. GRUB_EFI_LOADER_CODE,
  242. pages, &address);
  243. if (status != GRUB_EFI_SUCCESS)
  244. {
  245. grub_dprintf ("chain", "Failed to allocate %u pages\n",
  246. (unsigned int) pages);
  247. grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
  248. goto fail;
  249. }
  250. boot_image = (void *) ((grub_addr_t) address);
  251. if (grub_file_read (file, boot_image, size) != size)
  252. {
  253. if (grub_errno == GRUB_ERR_NONE)
  254. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  255. filename);
  256. goto fail;
  257. }
  258. #if defined (__i386__) || defined (__x86_64__)
  259. if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
  260. {
  261. struct grub_macho_fat_header *head = boot_image;
  262. if (head->magic
  263. == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC))
  264. {
  265. grub_uint32_t i;
  266. struct grub_macho_fat_arch *archs
  267. = (struct grub_macho_fat_arch *) (head + 1);
  268. for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
  269. {
  270. if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
  271. break;
  272. }
  273. if (i == grub_cpu_to_le32 (head->nfat_arch))
  274. {
  275. grub_error (GRUB_ERR_BAD_OS, "no compatible arch found");
  276. goto fail;
  277. }
  278. if (grub_cpu_to_le32 (archs[i].offset)
  279. > ~grub_cpu_to_le32 (archs[i].size)
  280. || grub_cpu_to_le32 (archs[i].offset)
  281. + grub_cpu_to_le32 (archs[i].size)
  282. > (grub_size_t) size)
  283. {
  284. grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
  285. filename);
  286. goto fail;
  287. }
  288. boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
  289. size = grub_cpu_to_le32 (archs[i].size);
  290. }
  291. }
  292. #endif
  293. status = b->load_image (0, grub_efi_image_handle, file_path,
  294. boot_image, size,
  295. &image_handle);
  296. if (status != GRUB_EFI_SUCCESS)
  297. {
  298. if (status == GRUB_EFI_OUT_OF_RESOURCES)
  299. grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
  300. else
  301. grub_error (GRUB_ERR_BAD_OS, "cannot load image");
  302. goto fail;
  303. }
  304. /* LoadImage does not set a device handler when the image is
  305. loaded from memory, so it is necessary to set it explicitly here.
  306. This is a mess. */
  307. loaded_image = grub_efi_get_loaded_image (image_handle);
  308. if (! loaded_image)
  309. {
  310. grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
  311. goto fail;
  312. }
  313. loaded_image->device_handle = dev_handle;
  314. /* Build load options with arguments from chainloader command line. */
  315. if (argc > 1)
  316. {
  317. int i, len;
  318. grub_efi_char16_t *p16;
  319. for (i = 1, len = 0; i < argc; i++)
  320. len += grub_strlen (argv[i]) + 1;
  321. len *= sizeof (grub_efi_char16_t);
  322. cmdline = p16 = grub_malloc (len);
  323. if (! cmdline)
  324. goto fail;
  325. for (i = 1; i < argc; i++)
  326. {
  327. char *p8;
  328. p8 = argv[i];
  329. while (*p8)
  330. *(p16++) = *(p8++);
  331. *(p16++) = ' ';
  332. }
  333. *(--p16) = 0;
  334. loaded_image->load_options = cmdline;
  335. loaded_image->load_options_size = len;
  336. }
  337. grub_file_close (file);
  338. grub_device_close (dev);
  339. /* We're finished with the source image buffer and file path now. */
  340. b->free_pages (address, pages);
  341. grub_free (file_path);
  342. grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0);
  343. return 0;
  344. fail:
  345. if (dev)
  346. grub_device_close (dev);
  347. if (file)
  348. grub_file_close (file);
  349. grub_free (cmdline);
  350. grub_free (file_path);
  351. if (address)
  352. b->free_pages (address, pages);
  353. if (image_handle != NULL)
  354. b->unload_image (image_handle);
  355. grub_dl_unref (my_mod);
  356. return grub_errno;
  357. }
  358. static grub_command_t cmd;
  359. GRUB_MOD_INIT(chainloader)
  360. {
  361. cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
  362. 0, N_("Load another boot loader."));
  363. my_mod = mod;
  364. }
  365. GRUB_MOD_FINI(chainloader)
  366. {
  367. grub_unregister_command (cmd);
  368. }