multiboot.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /* multiboot.c - boot a multiboot OS image. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008,2009,2010 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. /*
  20. * FIXME: The following features from the Multiboot specification still
  21. * need to be implemented:
  22. * - drives table
  23. * - ROM configuration table
  24. * - SMBIOS tables
  25. * - Networking information
  26. */
  27. #include <grub/loader.h>
  28. #include <grub/command.h>
  29. #include <grub/multiboot.h>
  30. #include <grub/cpu/multiboot.h>
  31. #include <grub/elf.h>
  32. #include <grub/aout.h>
  33. #include <grub/file.h>
  34. #include <grub/err.h>
  35. #include <grub/dl.h>
  36. #include <grub/mm.h>
  37. #include <grub/misc.h>
  38. #include <grub/env.h>
  39. #include <grub/cpu/relocator.h>
  40. #include <grub/video.h>
  41. #include <grub/memory.h>
  42. #include <grub/i18n.h>
  43. GRUB_MOD_LICENSE ("GPLv3+");
  44. #ifdef GRUB_MACHINE_EFI
  45. #include <grub/efi/efi.h>
  46. #endif
  47. struct grub_relocator *grub_multiboot_relocator = NULL;
  48. grub_uint32_t grub_multiboot_payload_eip;
  49. #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_MULTIBOOT) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
  50. #define DEFAULT_VIDEO_MODE "text"
  51. #else
  52. #define DEFAULT_VIDEO_MODE "auto"
  53. #endif
  54. static int accepts_video;
  55. static int accepts_ega_text;
  56. static int console_required;
  57. static grub_dl_t my_mod;
  58. /* Helper for grub_get_multiboot_mmap_count. */
  59. static int
  60. count_hook (grub_uint64_t addr __attribute__ ((unused)),
  61. grub_uint64_t size __attribute__ ((unused)),
  62. grub_memory_type_t type __attribute__ ((unused)), void *data)
  63. {
  64. grub_size_t *count = data;
  65. (*count)++;
  66. return 0;
  67. }
  68. /* Return the length of the Multiboot mmap that will be needed to allocate
  69. our platform's map. */
  70. grub_uint32_t
  71. grub_get_multiboot_mmap_count (void)
  72. {
  73. grub_size_t count = 0;
  74. grub_mmap_iterate (count_hook, &count);
  75. return count;
  76. }
  77. grub_err_t
  78. grub_multiboot_set_video_mode (void)
  79. {
  80. grub_err_t err;
  81. const char *modevar;
  82. #if GRUB_MACHINE_HAS_VGA_TEXT
  83. if (accepts_video)
  84. #endif
  85. {
  86. modevar = grub_env_get ("gfxpayload");
  87. if (! modevar || *modevar == 0)
  88. err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
  89. else
  90. {
  91. char *tmp;
  92. tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
  93. if (! tmp)
  94. return grub_errno;
  95. err = grub_video_set_mode (tmp, 0, 0);
  96. grub_free (tmp);
  97. }
  98. }
  99. #if GRUB_MACHINE_HAS_VGA_TEXT
  100. else
  101. err = grub_video_set_mode ("text", 0, 0);
  102. #endif
  103. return err;
  104. }
  105. #ifdef GRUB_MACHINE_EFI
  106. #ifdef __x86_64__
  107. #define grub_relocator_efi_boot grub_relocator64_efi_boot
  108. #define grub_relocator_efi_state grub_relocator64_efi_state
  109. #endif
  110. #endif
  111. #ifdef grub_relocator_efi_boot
  112. static void
  113. efi_boot (struct grub_relocator *rel,
  114. grub_uint32_t target)
  115. {
  116. struct grub_relocator_efi_state state_efi = MULTIBOOT_EFI_INITIAL_STATE;
  117. state_efi.MULTIBOOT_EFI_ENTRY_REGISTER = grub_multiboot_payload_eip;
  118. state_efi.MULTIBOOT_EFI_MBI_REGISTER = target;
  119. grub_relocator_efi_boot (rel, state_efi);
  120. }
  121. #else
  122. #define grub_efi_is_finished 1
  123. static void
  124. efi_boot (struct grub_relocator *rel __attribute__ ((unused)),
  125. grub_uint32_t target __attribute__ ((unused)))
  126. {
  127. }
  128. #endif
  129. #if defined (__i386__) || defined (__x86_64__)
  130. static void
  131. normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state)
  132. {
  133. grub_relocator32_boot (rel, state, 0);
  134. }
  135. #else
  136. static void
  137. normal_boot (struct grub_relocator *rel, struct grub_relocator32_state state)
  138. {
  139. grub_relocator32_boot (rel, state);
  140. }
  141. #endif
  142. static grub_err_t
  143. grub_multiboot_boot (void)
  144. {
  145. grub_err_t err;
  146. struct grub_relocator32_state state = MULTIBOOT_INITIAL_STATE;
  147. state.MULTIBOOT_ENTRY_REGISTER = grub_multiboot_payload_eip;
  148. err = grub_multiboot_make_mbi (&state.MULTIBOOT_MBI_REGISTER);
  149. if (err)
  150. return err;
  151. if (grub_efi_is_finished)
  152. normal_boot (grub_multiboot_relocator, state);
  153. else
  154. efi_boot (grub_multiboot_relocator, state.MULTIBOOT_MBI_REGISTER);
  155. /* Not reached. */
  156. return GRUB_ERR_NONE;
  157. }
  158. static grub_err_t
  159. grub_multiboot_unload (void)
  160. {
  161. grub_multiboot_free_mbi ();
  162. grub_relocator_unload (grub_multiboot_relocator);
  163. grub_multiboot_relocator = NULL;
  164. grub_dl_unref (my_mod);
  165. return GRUB_ERR_NONE;
  166. }
  167. static grub_uint64_t highest_load;
  168. #define MULTIBOOT_LOAD_ELF64
  169. #include "multiboot_elfxx.c"
  170. #undef MULTIBOOT_LOAD_ELF64
  171. #define MULTIBOOT_LOAD_ELF32
  172. #include "multiboot_elfxx.c"
  173. #undef MULTIBOOT_LOAD_ELF32
  174. /* Load ELF32 or ELF64. */
  175. grub_err_t
  176. grub_multiboot_load_elf (mbi_load_data_t *mld)
  177. {
  178. if (grub_multiboot_is_elf32 (mld->buffer))
  179. return grub_multiboot_load_elf32 (mld);
  180. else if (grub_multiboot_is_elf64 (mld->buffer))
  181. return grub_multiboot_load_elf64 (mld);
  182. return grub_error (GRUB_ERR_UNKNOWN_OS, N_("invalid arch-dependent ELF magic"));
  183. }
  184. grub_err_t
  185. grub_multiboot_set_console (int console_type, int accepted_consoles,
  186. int width, int height, int depth,
  187. int console_req)
  188. {
  189. console_required = console_req;
  190. if (!(accepted_consoles
  191. & (GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER
  192. | (GRUB_MACHINE_HAS_VGA_TEXT ? GRUB_MULTIBOOT_CONSOLE_EGA_TEXT : 0))))
  193. {
  194. if (console_required)
  195. return grub_error (GRUB_ERR_BAD_OS,
  196. "OS requires a console but none is available");
  197. grub_puts_ (N_("WARNING: no console will be available to OS"));
  198. accepts_video = 0;
  199. accepts_ega_text = 0;
  200. return GRUB_ERR_NONE;
  201. }
  202. if (console_type == GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER)
  203. {
  204. char *buf;
  205. if (depth && width && height)
  206. buf = grub_xasprintf ("%dx%dx%d,%dx%d,auto", width,
  207. height, depth, width, height);
  208. else if (width && height)
  209. buf = grub_xasprintf ("%dx%d,auto", width, height);
  210. else
  211. buf = grub_strdup ("auto");
  212. if (!buf)
  213. return grub_errno;
  214. grub_env_set ("gfxpayload", buf);
  215. grub_free (buf);
  216. }
  217. else
  218. {
  219. #if GRUB_MACHINE_HAS_VGA_TEXT
  220. grub_env_set ("gfxpayload", "text");
  221. #else
  222. /* Always use video if no VGA text is available. */
  223. grub_env_set ("gfxpayload", "auto");
  224. #endif
  225. }
  226. accepts_video = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_FRAMEBUFFER);
  227. accepts_ega_text = !!(accepted_consoles & GRUB_MULTIBOOT_CONSOLE_EGA_TEXT);
  228. return GRUB_ERR_NONE;
  229. }
  230. static grub_err_t
  231. grub_cmd_multiboot (grub_command_t cmd __attribute__ ((unused)),
  232. int argc, char *argv[])
  233. {
  234. grub_file_t file = 0;
  235. grub_err_t err;
  236. grub_loader_unset ();
  237. highest_load = 0;
  238. #ifndef GRUB_USE_MULTIBOOT2
  239. grub_multiboot_quirks = GRUB_MULTIBOOT_QUIRKS_NONE;
  240. int option_found = 0;
  241. do
  242. {
  243. option_found = 0;
  244. if (argc != 0 && grub_strcmp (argv[0], "--quirk-bad-kludge") == 0)
  245. {
  246. argc--;
  247. argv++;
  248. option_found = 1;
  249. grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_BAD_KLUDGE;
  250. }
  251. if (argc != 0 && grub_strcmp (argv[0], "--quirk-modules-after-kernel") == 0)
  252. {
  253. argc--;
  254. argv++;
  255. option_found = 1;
  256. grub_multiboot_quirks |= GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL;
  257. }
  258. } while (option_found);
  259. #endif
  260. if (argc == 0)
  261. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  262. file = grub_file_open (argv[0]);
  263. if (! file)
  264. return grub_errno;
  265. grub_dl_ref (my_mod);
  266. /* Skip filename. */
  267. grub_multiboot_init_mbi (argc - 1, argv + 1);
  268. grub_relocator_unload (grub_multiboot_relocator);
  269. grub_multiboot_relocator = grub_relocator_new ();
  270. if (!grub_multiboot_relocator)
  271. goto fail;
  272. err = grub_multiboot_load (file, argv[0]);
  273. if (err)
  274. goto fail;
  275. grub_multiboot_set_bootdev ();
  276. grub_loader_set (grub_multiboot_boot, grub_multiboot_unload, 0);
  277. fail:
  278. if (file)
  279. grub_file_close (file);
  280. if (grub_errno != GRUB_ERR_NONE)
  281. {
  282. grub_relocator_unload (grub_multiboot_relocator);
  283. grub_multiboot_relocator = NULL;
  284. grub_dl_unref (my_mod);
  285. }
  286. return grub_errno;
  287. }
  288. static grub_err_t
  289. grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
  290. int argc, char *argv[])
  291. {
  292. grub_file_t file = 0;
  293. grub_ssize_t size;
  294. void *module = NULL;
  295. grub_addr_t target;
  296. grub_err_t err;
  297. int nounzip = 0;
  298. grub_uint64_t lowest_addr = 0;
  299. if (argc == 0)
  300. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  301. if (grub_strcmp (argv[0], "--nounzip") == 0)
  302. {
  303. argv++;
  304. argc--;
  305. nounzip = 1;
  306. }
  307. if (argc == 0)
  308. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  309. if (!grub_multiboot_relocator)
  310. return grub_error (GRUB_ERR_BAD_ARGUMENT,
  311. N_("you need to load the kernel first"));
  312. if (nounzip)
  313. grub_file_filter_disable_compression ();
  314. file = grub_file_open (argv[0]);
  315. if (! file)
  316. return grub_errno;
  317. #ifndef GRUB_USE_MULTIBOOT2
  318. lowest_addr = 0x100000;
  319. if (grub_multiboot_quirks & GRUB_MULTIBOOT_QUIRK_MODULES_AFTER_KERNEL)
  320. lowest_addr = ALIGN_UP (highest_load + 1048576, 4096);
  321. #endif
  322. size = grub_file_size (file);
  323. if (size)
  324. {
  325. grub_relocator_chunk_t ch;
  326. err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
  327. lowest_addr, (0xffffffff - size) + 1,
  328. size, MULTIBOOT_MOD_ALIGN,
  329. GRUB_RELOCATOR_PREFERENCE_NONE, 1);
  330. if (err)
  331. {
  332. grub_file_close (file);
  333. return err;
  334. }
  335. module = get_virtual_current_address (ch);
  336. target = get_physical_target_address (ch);
  337. }
  338. else
  339. {
  340. module = 0;
  341. target = 0;
  342. }
  343. err = grub_multiboot_add_module (target, size, argc - 1, argv + 1);
  344. if (err)
  345. {
  346. grub_file_close (file);
  347. return err;
  348. }
  349. if (size && grub_file_read (file, module, size) != size)
  350. {
  351. grub_file_close (file);
  352. if (!grub_errno)
  353. grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
  354. argv[0]);
  355. return grub_errno;
  356. }
  357. grub_file_close (file);
  358. return GRUB_ERR_NONE;
  359. }
  360. static grub_command_t cmd_multiboot, cmd_module;
  361. GRUB_MOD_INIT(multiboot)
  362. {
  363. cmd_multiboot =
  364. #ifdef GRUB_USE_MULTIBOOT2
  365. grub_register_command ("multiboot2", grub_cmd_multiboot,
  366. 0, N_("Load a multiboot 2 kernel."));
  367. cmd_module =
  368. grub_register_command ("module2", grub_cmd_module,
  369. 0, N_("Load a multiboot 2 module."));
  370. #else
  371. grub_register_command ("multiboot", grub_cmd_multiboot,
  372. 0, N_("Load a multiboot kernel."));
  373. cmd_module =
  374. grub_register_command ("module", grub_cmd_module,
  375. 0, N_("Load a multiboot module."));
  376. #endif
  377. my_mod = mod;
  378. }
  379. GRUB_MOD_FINI(multiboot)
  380. {
  381. grub_unregister_command (cmd_multiboot);
  382. grub_unregister_command (cmd_module);
  383. }