linux.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. /* linux.c - boot Linux */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2003,2004,2005,2007,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. #include <grub/elf.h>
  20. #include <grub/elfload.h>
  21. #include <grub/loader.h>
  22. #include <grub/dl.h>
  23. #include <grub/mm.h>
  24. #include <grub/misc.h>
  25. #include <grub/command.h>
  26. #include <grub/mips/relocator.h>
  27. #include <grub/memory.h>
  28. #include <grub/i18n.h>
  29. #include <grub/lib/cmdline.h>
  30. #include <grub/linux.h>
  31. GRUB_MOD_LICENSE ("GPLv3+");
  32. #pragma GCC diagnostic ignored "-Wcast-align"
  33. /* For frequencies. */
  34. #include <grub/machine/time.h>
  35. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  36. #include <grub/pci.h>
  37. #include <grub/machine/kernel.h>
  38. const char loongson_machtypes[][60] =
  39. {
  40. [GRUB_ARCH_MACHINE_YEELOONG] = "machtype=lemote-yeeloong-2f-8.9inches",
  41. [GRUB_ARCH_MACHINE_FULOONG2F] = "machtype=lemote-fuloong-2f-box",
  42. [GRUB_ARCH_MACHINE_FULOONG2E] = "machtype=lemote-fuloong-2e-unknown"
  43. };
  44. #endif
  45. static grub_dl_t my_mod;
  46. static int loaded;
  47. static grub_size_t linux_size;
  48. static struct grub_relocator *relocator;
  49. static grub_uint8_t *playground;
  50. static grub_addr_t target_addr, entry_addr;
  51. #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
  52. static char *params;
  53. #else
  54. static int linux_argc;
  55. static grub_off_t argv_off;
  56. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  57. static grub_off_t envp_off;
  58. #endif
  59. static grub_off_t rd_addr_arg_off, rd_size_arg_off;
  60. #endif
  61. static int initrd_loaded = 0;
  62. static grub_err_t
  63. grub_linux_boot (void)
  64. {
  65. struct grub_relocator32_state state;
  66. grub_memset (&state, 0, sizeof (state));
  67. /* Boot the kernel. */
  68. state.gpr[1] = entry_addr;
  69. #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
  70. {
  71. grub_err_t err;
  72. grub_relocator_chunk_t ch;
  73. grub_uint32_t *memsize;
  74. grub_uint32_t *magic;
  75. char *str;
  76. err = grub_relocator_alloc_chunk_addr (relocator, &ch,
  77. ((16 << 20) - 264),
  78. grub_strlen (params) + 1 + 8);
  79. if (err)
  80. return err;
  81. memsize = get_virtual_current_address (ch);
  82. magic = memsize + 1;
  83. *memsize = grub_mmap_get_lower ();
  84. *magic = 0x12345678;
  85. str = (char *) (magic + 1);
  86. grub_strcpy (str, params);
  87. }
  88. #endif
  89. #ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
  90. state.gpr[4] = linux_argc;
  91. state.gpr[5] = target_addr + argv_off;
  92. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  93. state.gpr[6] = target_addr + envp_off;
  94. #else
  95. state.gpr[6] = 0;
  96. #endif
  97. state.gpr[7] = 0;
  98. #endif
  99. state.jumpreg = 1;
  100. grub_relocator32_boot (relocator, state);
  101. return GRUB_ERR_NONE;
  102. }
  103. static grub_err_t
  104. grub_linux_unload (void)
  105. {
  106. grub_relocator_unload (relocator);
  107. grub_dl_unref (my_mod);
  108. #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
  109. grub_free (params);
  110. params = 0;
  111. #endif
  112. loaded = 0;
  113. return GRUB_ERR_NONE;
  114. }
  115. static grub_err_t
  116. grub_linux_load32 (grub_elf_t elf, const char *filename,
  117. void **extra_mem, grub_size_t extra_size)
  118. {
  119. Elf32_Addr base;
  120. int extraoff;
  121. grub_err_t err;
  122. /* Linux's entry point incorrectly contains a virtual address. */
  123. entry_addr = elf->ehdr.ehdr32.e_entry;
  124. linux_size = grub_elf32_size (elf, &base, 0);
  125. if (linux_size == 0)
  126. return grub_errno;
  127. target_addr = base;
  128. /* Pad it; the kernel scribbles over memory beyond its load address. */
  129. linux_size += 0x100000;
  130. linux_size = ALIGN_UP (base + linux_size, 4) - base;
  131. extraoff = linux_size;
  132. linux_size += extra_size;
  133. relocator = grub_relocator_new ();
  134. if (!relocator)
  135. return grub_errno;
  136. {
  137. grub_relocator_chunk_t ch;
  138. err = grub_relocator_alloc_chunk_addr (relocator, &ch,
  139. target_addr & 0x1fffffff,
  140. linux_size);
  141. if (err)
  142. return err;
  143. playground = get_virtual_current_address (ch);
  144. }
  145. *extra_mem = playground + extraoff;
  146. /* Now load the segments into the area we claimed. */
  147. return grub_elf32_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
  148. }
  149. static grub_err_t
  150. grub_linux_load64 (grub_elf_t elf, const char *filename,
  151. void **extra_mem, grub_size_t extra_size)
  152. {
  153. Elf64_Addr base;
  154. int extraoff;
  155. grub_err_t err;
  156. /* Linux's entry point incorrectly contains a virtual address. */
  157. entry_addr = elf->ehdr.ehdr64.e_entry;
  158. linux_size = grub_elf64_size (elf, &base, 0);
  159. if (linux_size == 0)
  160. return grub_errno;
  161. target_addr = base;
  162. /* Pad it; the kernel scribbles over memory beyond its load address. */
  163. linux_size += 0x100000;
  164. linux_size = ALIGN_UP (base + linux_size, 4) - base;
  165. extraoff = linux_size;
  166. linux_size += extra_size;
  167. relocator = grub_relocator_new ();
  168. if (!relocator)
  169. return grub_errno;
  170. {
  171. grub_relocator_chunk_t ch;
  172. err = grub_relocator_alloc_chunk_addr (relocator, &ch,
  173. target_addr & 0x1fffffff,
  174. linux_size);
  175. if (err)
  176. return err;
  177. playground = get_virtual_current_address (ch);
  178. }
  179. *extra_mem = playground + extraoff;
  180. /* Now load the segments into the area we claimed. */
  181. return grub_elf64_load (elf, filename, playground - base, GRUB_ELF_LOAD_FLAGS_NONE, 0, 0);
  182. }
  183. static grub_err_t
  184. grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
  185. int argc, char *argv[])
  186. {
  187. grub_elf_t elf = 0;
  188. int size;
  189. void *extra = NULL;
  190. #ifndef GRUB_MACHINE_MIPS_QEMU_MIPS
  191. int i;
  192. grub_uint32_t *linux_argv;
  193. char *linux_args;
  194. #endif
  195. grub_err_t err;
  196. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  197. char *linux_envs;
  198. grub_uint32_t *linux_envp;
  199. #endif
  200. if (argc == 0)
  201. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  202. elf = grub_elf_open (argv[0]);
  203. if (! elf)
  204. return grub_errno;
  205. if (elf->ehdr.ehdr32.e_type != ET_EXEC)
  206. {
  207. grub_elf_close (elf);
  208. return grub_error (GRUB_ERR_UNKNOWN_OS,
  209. N_("this ELF file is not of the right type"));
  210. }
  211. /* Release the previously used memory. */
  212. grub_loader_unset ();
  213. loaded = 0;
  214. #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
  215. size = 0;
  216. #else
  217. /* For arguments. */
  218. linux_argc = argc;
  219. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  220. linux_argc++;
  221. #endif
  222. /* Main arguments. */
  223. size = (linux_argc) * sizeof (grub_uint32_t);
  224. /* Initrd address and size. */
  225. size += 2 * sizeof (grub_uint32_t);
  226. /* NULL terminator. */
  227. size += sizeof (grub_uint32_t);
  228. /* First argument is always "a0". */
  229. size += ALIGN_UP (sizeof ("a0"), 4);
  230. /* Normal arguments. */
  231. for (i = 1; i < argc; i++)
  232. size += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
  233. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  234. size += ALIGN_UP (sizeof (loongson_machtypes[0]), 4);
  235. #endif
  236. /* rd arguments. */
  237. size += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
  238. size += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
  239. /* For the environment. */
  240. size += sizeof (grub_uint32_t);
  241. size += 4 * sizeof (grub_uint32_t);
  242. size += ALIGN_UP (sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"), 4)
  243. + ALIGN_UP (sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"), 4)
  244. + ALIGN_UP (sizeof ("busclock=XXXXXXXXXX"), 4)
  245. + ALIGN_UP (sizeof ("cpuclock=XXXXXXXXXX"), 4);
  246. #endif
  247. if (grub_elf_is_elf32 (elf))
  248. err = grub_linux_load32 (elf, argv[0], &extra, size);
  249. else
  250. if (grub_elf_is_elf64 (elf))
  251. err = grub_linux_load64 (elf, argv[0], &extra, size);
  252. else
  253. err = grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
  254. grub_elf_close (elf);
  255. if (err)
  256. return err;
  257. #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
  258. /* Create kernel command line. */
  259. size = grub_loader_cmdline_size(argc, argv);
  260. params = grub_malloc (size + sizeof (LINUX_IMAGE));
  261. if (! params)
  262. {
  263. grub_linux_unload ();
  264. return grub_errno;
  265. }
  266. grub_memcpy (params, LINUX_IMAGE, sizeof (LINUX_IMAGE));
  267. grub_create_loader_cmdline (argc, argv, params + sizeof (LINUX_IMAGE) - 1,
  268. size);
  269. #else
  270. linux_argv = extra;
  271. argv_off = (grub_uint8_t *) linux_argv - (grub_uint8_t *) playground;
  272. extra = linux_argv + (linux_argc + 1 + 2);
  273. linux_args = extra;
  274. grub_memcpy (linux_args, "a0", sizeof ("a0"));
  275. *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
  276. + target_addr;
  277. linux_argv++;
  278. linux_args += ALIGN_UP (sizeof ("a0"), 4);
  279. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  280. {
  281. unsigned mtype = grub_arch_machine;
  282. if (mtype >= ARRAY_SIZE (loongson_machtypes))
  283. mtype = 0;
  284. /* In Loongson platform, it is the responsibility of the bootloader/firmware
  285. to supply the OS kernel with machine type information. */
  286. grub_memcpy (linux_args, loongson_machtypes[mtype],
  287. sizeof (loongson_machtypes[mtype]));
  288. *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
  289. + target_addr;
  290. linux_argv++;
  291. linux_args += ALIGN_UP (sizeof (loongson_machtypes[mtype]), 4);
  292. }
  293. #endif
  294. for (i = 1; i < argc; i++)
  295. {
  296. grub_memcpy (linux_args, argv[i], grub_strlen (argv[i]) + 1);
  297. *linux_argv = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground
  298. + target_addr;
  299. linux_argv++;
  300. linux_args += ALIGN_UP (grub_strlen (argv[i]) + 1, 4);
  301. }
  302. /* Reserve space for rd arguments. */
  303. rd_addr_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
  304. linux_args += ALIGN_UP (sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), 4);
  305. *linux_argv = 0;
  306. linux_argv++;
  307. rd_size_arg_off = (grub_uint8_t *) linux_args - (grub_uint8_t *) playground;
  308. linux_args += ALIGN_UP (sizeof ("rd_size=0xXXXXXXXXXXXXXXXX"), 4);
  309. *linux_argv = 0;
  310. linux_argv++;
  311. *linux_argv = 0;
  312. extra = linux_args;
  313. #ifdef GRUB_MACHINE_MIPS_LOONGSON
  314. linux_envp = extra;
  315. envp_off = (grub_uint8_t *) linux_envp - (grub_uint8_t *) playground;
  316. linux_envs = (char *) (linux_envp + 5);
  317. grub_snprintf (linux_envs, sizeof ("memsize=XXXXXXXXXXXXXXXXXXXX"),
  318. "memsize=%lld",
  319. (unsigned long long) grub_mmap_get_lower () >> 20);
  320. linux_envp[0] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
  321. + target_addr;
  322. linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
  323. grub_snprintf (linux_envs, sizeof ("highmemsize=XXXXXXXXXXXXXXXXXXXX"),
  324. "highmemsize=%lld",
  325. (unsigned long long) grub_mmap_get_upper () >> 20);
  326. linux_envp[1] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
  327. + target_addr;
  328. linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
  329. grub_snprintf (linux_envs, sizeof ("busclock=XXXXXXXXXX"),
  330. "busclock=%d", grub_arch_busclock);
  331. linux_envp[2] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
  332. + target_addr;
  333. linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
  334. grub_snprintf (linux_envs, sizeof ("cpuclock=XXXXXXXXXX"),
  335. "cpuclock=%d", grub_arch_cpuclock);
  336. linux_envp[3] = (grub_uint8_t *) linux_envs - (grub_uint8_t *) playground
  337. + target_addr;
  338. linux_envs += ALIGN_UP (grub_strlen (linux_envs) + 1, 4);
  339. linux_envp[4] = 0;
  340. #endif
  341. #endif
  342. grub_loader_set (grub_linux_boot, grub_linux_unload, 1);
  343. initrd_loaded = 0;
  344. loaded = 1;
  345. grub_dl_ref (my_mod);
  346. return GRUB_ERR_NONE;
  347. }
  348. static grub_err_t
  349. grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
  350. int argc, char *argv[])
  351. {
  352. grub_size_t size = 0;
  353. void *initrd_src;
  354. grub_addr_t initrd_dest;
  355. grub_err_t err;
  356. struct grub_linux_initrd_context initrd_ctx = { 0, 0, 0 };
  357. if (argc == 0)
  358. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  359. if (!loaded)
  360. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
  361. if (initrd_loaded)
  362. return grub_error (GRUB_ERR_BAD_ARGUMENT, "only one initrd command can be issued.");
  363. if (grub_initrd_init (argc, argv, &initrd_ctx))
  364. goto fail;
  365. size = grub_get_initrd_size (&initrd_ctx);
  366. {
  367. grub_relocator_chunk_t ch;
  368. err = grub_relocator_alloc_chunk_align (relocator, &ch,
  369. (target_addr & 0x1fffffff)
  370. + linux_size + 0x10000,
  371. (0x10000000 - size),
  372. size, 0x10000,
  373. GRUB_RELOCATOR_PREFERENCE_NONE, 0);
  374. if (err)
  375. goto fail;
  376. initrd_src = get_virtual_current_address (ch);
  377. initrd_dest = get_physical_target_address (ch) | 0x80000000;
  378. }
  379. if (grub_initrd_load (&initrd_ctx, argv, initrd_src))
  380. goto fail;
  381. #ifdef GRUB_MACHINE_MIPS_QEMU_MIPS
  382. {
  383. char *tmp;
  384. tmp = grub_xasprintf ("%s rd_start=0x%" PRIxGRUB_ADDR
  385. " rd_size=0x%" PRIxGRUB_ADDR, params,
  386. initrd_dest, size);
  387. if (!tmp)
  388. goto fail;
  389. grub_free (params);
  390. params = tmp;
  391. }
  392. #else
  393. grub_snprintf ((char *) playground + rd_addr_arg_off,
  394. sizeof ("rd_start=0xXXXXXXXXXXXXXXXX"), "rd_start=0x%llx",
  395. (unsigned long long) initrd_dest);
  396. ((grub_uint32_t *) (playground + argv_off))[linux_argc]
  397. = target_addr + rd_addr_arg_off;
  398. linux_argc++;
  399. grub_snprintf ((char *) playground + rd_size_arg_off,
  400. sizeof ("rd_size=0xXXXXXXXXXXXXXXXXX"), "rd_size=0x%llx",
  401. (unsigned long long) size);
  402. ((grub_uint32_t *) (playground + argv_off))[linux_argc]
  403. = target_addr + rd_size_arg_off;
  404. linux_argc++;
  405. #endif
  406. initrd_loaded = 1;
  407. fail:
  408. grub_initrd_close (&initrd_ctx);
  409. return grub_errno;
  410. }
  411. static grub_command_t cmd_linux, cmd_initrd;
  412. GRUB_MOD_INIT(linux)
  413. {
  414. cmd_linux = grub_register_command ("linux", grub_cmd_linux,
  415. 0, N_("Load Linux."));
  416. cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd,
  417. 0, N_("Load initrd."));
  418. my_mod = mod;
  419. }
  420. GRUB_MOD_FINI(linux)
  421. {
  422. grub_unregister_command (cmd_linux);
  423. grub_unregister_command (cmd_initrd);
  424. }