chainloader.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. /*
  2. * GRUB -- GRand Unified Bootloader
  3. * Copyright (C) 2011 Free Software Foundation, Inc.
  4. *
  5. * GRUB is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GRUB is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <grub/loader.h>
  19. #include <grub/memory.h>
  20. #include <grub/i386/memory.h>
  21. #include <grub/file.h>
  22. #include <grub/err.h>
  23. #include <grub/dl.h>
  24. #include <grub/mm.h>
  25. #include <grub/elfload.h>
  26. #include <grub/video.h>
  27. #include <grub/relocator.h>
  28. #include <grub/i386/relocator.h>
  29. #include <grub/command.h>
  30. #include <grub/i18n.h>
  31. #include <grub/cbfs_core.h>
  32. #include <grub/lib/LzmaDec.h>
  33. #include <grub/efi/pe32.h>
  34. #include <grub/i386/cpuid.h>
  35. GRUB_MOD_LICENSE ("GPLv3+");
  36. static grub_addr_t entry;
  37. static struct grub_relocator *relocator = NULL;
  38. static grub_err_t
  39. grub_chain_boot (void)
  40. {
  41. struct grub_relocator32_state state;
  42. grub_video_set_mode ("text", 0, 0);
  43. state.eip = entry;
  44. return grub_relocator32_boot (relocator, state, 0);
  45. }
  46. static grub_err_t
  47. grub_chain_unload (void)
  48. {
  49. grub_relocator_unload (relocator);
  50. relocator = NULL;
  51. return GRUB_ERR_NONE;
  52. }
  53. static grub_err_t
  54. load_elf (grub_file_t file, const char *filename)
  55. {
  56. grub_elf_t elf;
  57. Elf32_Phdr *phdr;
  58. grub_err_t err;
  59. elf = grub_elf_file (file, filename);
  60. if (!elf)
  61. return grub_errno;
  62. if (!grub_elf_is_elf32 (elf))
  63. return grub_error (GRUB_ERR_BAD_OS, "only ELF32 can be coreboot payload");
  64. entry = elf->ehdr.ehdr32.e_entry;
  65. FOR_ELF32_PHDRS(elf, phdr)
  66. {
  67. grub_uint8_t *load_addr;
  68. grub_relocator_chunk_t ch;
  69. if (phdr->p_type != PT_LOAD)
  70. continue;
  71. err = grub_relocator_alloc_chunk_addr (relocator, &ch,
  72. phdr->p_paddr, phdr->p_memsz);
  73. if (err)
  74. {
  75. elf->file = 0;
  76. grub_elf_close (elf);
  77. return err;
  78. }
  79. load_addr = get_virtual_current_address (ch);
  80. if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
  81. {
  82. elf->file = 0;
  83. grub_elf_close (elf);
  84. return grub_errno;
  85. }
  86. if (phdr->p_filesz)
  87. {
  88. grub_ssize_t read;
  89. read = grub_file_read (elf->file, load_addr, phdr->p_filesz);
  90. if (read != (grub_ssize_t) phdr->p_filesz)
  91. {
  92. if (!grub_errno)
  93. grub_error (GRUB_ERR_FILE_READ_ERROR,
  94. N_("premature end of file %s"),
  95. filename);
  96. elf->file = 0;
  97. grub_elf_close (elf);
  98. return grub_errno;
  99. }
  100. }
  101. if (phdr->p_filesz < phdr->p_memsz)
  102. grub_memset ((load_addr + phdr->p_filesz),
  103. 0, phdr->p_memsz - phdr->p_filesz);
  104. }
  105. elf->file = 0;
  106. grub_elf_close (elf);
  107. return GRUB_ERR_NONE;
  108. }
  109. static void *SzAlloc(void *p __attribute__ ((unused)), size_t size) { return grub_malloc (size); }
  110. static void SzFree(void *p __attribute__ ((unused)), void *address) { grub_free (address); }
  111. static ISzAlloc g_Alloc = { SzAlloc, SzFree };
  112. static grub_err_t
  113. load_segment (grub_file_t file, const char *filename,
  114. void *load_addr, grub_uint32_t comp,
  115. grub_size_t *size, grub_size_t max_size)
  116. {
  117. switch (comp)
  118. {
  119. case grub_cpu_to_be32_compile_time (CBFS_COMPRESS_NONE):
  120. if (grub_file_read (file, load_addr, *size)
  121. != (grub_ssize_t) *size)
  122. {
  123. if (!grub_errno)
  124. grub_error (GRUB_ERR_FILE_READ_ERROR,
  125. N_("premature end of file %s"),
  126. filename);
  127. return grub_errno;
  128. }
  129. return GRUB_ERR_NONE;
  130. case grub_cpu_to_be32_compile_time (CBFS_COMPRESS_LZMA):
  131. {
  132. grub_uint8_t *buf;
  133. grub_size_t outsize, insize;
  134. SRes res;
  135. SizeT src_len, dst_len;
  136. ELzmaStatus status;
  137. if (*size < 13)
  138. return grub_error (GRUB_ERR_BAD_OS, "invalid compressed chunk");
  139. buf = grub_malloc (*size);
  140. if (!buf)
  141. return grub_errno;
  142. if (grub_file_read (file, buf, *size)
  143. != (grub_ssize_t) *size)
  144. {
  145. if (!grub_errno)
  146. grub_error (GRUB_ERR_FILE_READ_ERROR,
  147. N_("premature end of file %s"),
  148. filename);
  149. grub_free (buf);
  150. return grub_errno;
  151. }
  152. outsize = grub_get_unaligned64 (buf + 5);
  153. if (outsize > max_size)
  154. {
  155. grub_free (buf);
  156. return grub_error (GRUB_ERR_BAD_OS, "invalid compressed chunk");
  157. }
  158. insize = *size - 13;
  159. src_len = insize;
  160. dst_len = outsize;
  161. res = LzmaDecode (load_addr, &dst_len, buf + 13, &src_len,
  162. buf, 5, LZMA_FINISH_END, &status, &g_Alloc);
  163. /* ELzmaFinishMode finishMode,
  164. ELzmaStatus *status, ISzAlloc *alloc)*/
  165. grub_free (buf);
  166. grub_dprintf ("chain", "%x, %x, %x, %x\n",
  167. insize, src_len, outsize, dst_len);
  168. if (res != SZ_OK
  169. || src_len != insize || dst_len != outsize)
  170. return grub_error (GRUB_ERR_BAD_OS, "decompression failure %d", res);
  171. *size = outsize;
  172. }
  173. return GRUB_ERR_NONE;
  174. default:
  175. return grub_error (GRUB_ERR_BAD_OS, "unsupported compression %d",
  176. grub_be_to_cpu32 (comp));
  177. }
  178. }
  179. static grub_err_t
  180. load_tianocore (grub_file_t file)
  181. {
  182. grub_uint16_t header_length;
  183. grub_uint32_t section_head;
  184. grub_uint8_t mz[2], pe[4];
  185. struct grub_pe32_coff_header coff_head;
  186. struct file_header
  187. {
  188. grub_uint8_t unused[18];
  189. grub_uint8_t type;
  190. grub_uint8_t unused2;
  191. grub_uint8_t size[3];
  192. grub_uint8_t unused3;
  193. } file_head;
  194. grub_relocator_chunk_t ch;
  195. if (grub_file_seek (file, 48) == (grub_off_t) -1
  196. || grub_file_read (file, &header_length, sizeof (header_length))
  197. != sizeof (header_length)
  198. || grub_file_seek (file, header_length) == (grub_off_t) -1)
  199. goto fail;
  200. while (1)
  201. {
  202. grub_off_t off;
  203. if (grub_file_read (file, &file_head, sizeof (file_head))
  204. != sizeof (file_head))
  205. goto fail;
  206. if (file_head.type != 0xf0)
  207. break;
  208. off = grub_get_unaligned32 (file_head.size) & 0xffffff;
  209. if (off < sizeof (file_head))
  210. goto fail;
  211. if (grub_file_seek (file, grub_file_tell (file) + off
  212. - sizeof (file_head)) == (grub_off_t) -1)
  213. goto fail;
  214. }
  215. if (file_head.type != 0x03)
  216. goto fail;
  217. while (1)
  218. {
  219. if (grub_file_read (file, &section_head, sizeof (section_head))
  220. != sizeof (section_head))
  221. goto fail;
  222. if ((section_head >> 24) != 0x19)
  223. break;
  224. if ((section_head & 0xffffff) < sizeof (section_head))
  225. goto fail;
  226. if (grub_file_seek (file, grub_file_tell (file)
  227. + (section_head & 0xffffff)
  228. - sizeof (section_head)) == (grub_off_t) -1)
  229. goto fail;
  230. }
  231. if ((section_head >> 24) != 0x10)
  232. goto fail;
  233. grub_off_t exe_start = grub_file_tell (file);
  234. if (grub_file_read (file, &mz, sizeof (mz)) != sizeof (mz))
  235. goto fail;
  236. if (mz[0] != 'M' || mz[1] != 'Z')
  237. goto fail;
  238. if (grub_file_seek (file, grub_file_tell (file) + 0x3a) == (grub_off_t) -1)
  239. goto fail;
  240. if (grub_file_read (file, &section_head, sizeof (section_head))
  241. != sizeof (section_head))
  242. goto fail;
  243. if (section_head < 0x40)
  244. goto fail;
  245. if (grub_file_seek (file, grub_file_tell (file)
  246. + section_head - 0x40) == (grub_off_t) -1)
  247. goto fail;
  248. if (grub_file_read (file, &pe, sizeof (pe))
  249. != sizeof (pe))
  250. goto fail;
  251. if (pe[0] != 'P' || pe[1] != 'E' || pe[2] != '\0' || pe[3] != '\0')
  252. goto fail;
  253. if (grub_file_read (file, &coff_head, sizeof (coff_head))
  254. != sizeof (coff_head))
  255. goto fail;
  256. grub_uint32_t loadaddr;
  257. switch (coff_head.machine)
  258. {
  259. case GRUB_PE32_MACHINE_I386:
  260. {
  261. struct grub_pe32_optional_header oh;
  262. if (grub_file_read (file, &oh, sizeof (oh))
  263. != sizeof (oh))
  264. goto fail;
  265. if (oh.magic != GRUB_PE32_PE32_MAGIC)
  266. goto fail;
  267. loadaddr = oh.image_base - exe_start;
  268. entry = oh.image_base + oh.entry_addr;
  269. break;
  270. }
  271. case GRUB_PE32_MACHINE_X86_64:
  272. {
  273. struct grub_pe64_optional_header oh;
  274. if (! grub_cpuid_has_longmode)
  275. {
  276. grub_error (GRUB_ERR_BAD_OS, "your CPU does not implement AMD64 architecture");
  277. goto fail;
  278. }
  279. if (grub_file_read (file, &oh, sizeof (oh))
  280. != sizeof (oh))
  281. goto fail;
  282. if (oh.magic != GRUB_PE32_PE64_MAGIC)
  283. goto fail;
  284. loadaddr = oh.image_base - exe_start;
  285. entry = oh.image_base + oh.entry_addr;
  286. break;
  287. }
  288. default:
  289. goto fail;
  290. }
  291. if (grub_file_seek (file, 0) == (grub_off_t) -1)
  292. goto fail;
  293. grub_size_t fz = grub_file_size (file);
  294. if (grub_relocator_alloc_chunk_addr (relocator, &ch,
  295. loadaddr, fz))
  296. goto fail;
  297. if (grub_file_read (file, get_virtual_current_address (ch), fz)
  298. != (grub_ssize_t) fz)
  299. goto fail;
  300. return GRUB_ERR_NONE;
  301. fail:
  302. if (!grub_errno)
  303. grub_error (GRUB_ERR_BAD_OS, "fv volume is invalid");
  304. return grub_errno;
  305. }
  306. static grub_err_t
  307. load_chewed (grub_file_t file, const char *filename)
  308. {
  309. grub_size_t i;
  310. for (i = 0;; i++)
  311. {
  312. struct cbfs_payload_segment segment;
  313. grub_err_t err;
  314. if (grub_file_seek (file, sizeof (segment) * i) == (grub_off_t) -1
  315. || grub_file_read (file, &segment, sizeof (segment))
  316. != sizeof (segment))
  317. {
  318. if (!grub_errno)
  319. return grub_error (GRUB_ERR_BAD_OS,
  320. "payload is too short");
  321. return grub_errno;
  322. }
  323. switch (segment.type)
  324. {
  325. case PAYLOAD_SEGMENT_PARAMS:
  326. break;
  327. case PAYLOAD_SEGMENT_ENTRY:
  328. entry = grub_be_to_cpu64 (segment.load_addr);
  329. return GRUB_ERR_NONE;
  330. case PAYLOAD_SEGMENT_BSS:
  331. segment.len = 0;
  332. segment.offset = 0;
  333. segment.len = 0;
  334. /* Fallthrough. */
  335. case PAYLOAD_SEGMENT_CODE:
  336. case PAYLOAD_SEGMENT_DATA:
  337. {
  338. grub_uint32_t target = grub_be_to_cpu64 (segment.load_addr);
  339. grub_uint32_t memsize = grub_be_to_cpu32 (segment.mem_len);
  340. grub_uint32_t filesize = grub_be_to_cpu32 (segment.len);
  341. grub_uint8_t *load_addr;
  342. grub_relocator_chunk_t ch;
  343. if (memsize < filesize)
  344. memsize = filesize;
  345. grub_dprintf ("chain", "%x+%x\n", target, memsize);
  346. err = grub_relocator_alloc_chunk_addr (relocator, &ch,
  347. target, memsize);
  348. if (err)
  349. return err;
  350. load_addr = get_virtual_current_address (ch);
  351. if (filesize)
  352. {
  353. if (grub_file_seek (file, grub_be_to_cpu32 (segment.offset))
  354. == (grub_off_t) -1)
  355. return grub_errno;
  356. err = load_segment (file, filename, load_addr,
  357. segment.compression, &filesize, memsize);
  358. if (err)
  359. return err;
  360. }
  361. if (filesize < memsize)
  362. grub_memset ((load_addr + filesize),
  363. 0, memsize - filesize);
  364. }
  365. }
  366. }
  367. }
  368. static grub_err_t
  369. grub_cmd_chain (grub_command_t cmd __attribute__ ((unused)),
  370. int argc, char *argv[])
  371. {
  372. grub_err_t err;
  373. grub_file_t file;
  374. grub_uint32_t head;
  375. if (argc != 1)
  376. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
  377. grub_loader_unset ();
  378. file = grub_file_open (argv[0], GRUB_FILE_TYPE_COREBOOT_CHAINLOADER);
  379. if (!file)
  380. return grub_errno;
  381. relocator = grub_relocator_new ();
  382. if (!relocator)
  383. {
  384. grub_file_close (file);
  385. return grub_errno;
  386. }
  387. if (grub_file_read (file, &head, sizeof (head)) != sizeof (head)
  388. || grub_file_seek (file, 0) == (grub_off_t) -1)
  389. {
  390. grub_file_close (file);
  391. grub_relocator_unload (relocator);
  392. relocator = 0;
  393. if (!grub_errno)
  394. return grub_error (GRUB_ERR_BAD_OS,
  395. "payload is too short");
  396. return grub_errno;
  397. }
  398. switch (head)
  399. {
  400. case ELFMAG0 | (ELFMAG1 << 8) | (ELFMAG2 << 16) | (ELFMAG3 << 24):
  401. err = load_elf (file, argv[0]);
  402. break;
  403. case PAYLOAD_SEGMENT_CODE:
  404. case PAYLOAD_SEGMENT_DATA:
  405. case PAYLOAD_SEGMENT_PARAMS:
  406. case PAYLOAD_SEGMENT_BSS:
  407. case PAYLOAD_SEGMENT_ENTRY:
  408. err = load_chewed (file, argv[0]);
  409. break;
  410. default:
  411. if (grub_file_seek (file, 40) == (grub_off_t) -1
  412. || grub_file_read (file, &head, sizeof (head)) != sizeof (head)
  413. || grub_file_seek (file, 0) == (grub_off_t) -1
  414. || head != 0x4856465f)
  415. err = grub_error (GRUB_ERR_BAD_OS, "unrecognised payload type");
  416. else
  417. err = load_tianocore (file);
  418. break;
  419. }
  420. grub_file_close (file);
  421. if (err)
  422. {
  423. grub_relocator_unload (relocator);
  424. relocator = 0;
  425. return err;
  426. }
  427. grub_loader_set (grub_chain_boot, grub_chain_unload, 0);
  428. return GRUB_ERR_NONE;
  429. }
  430. static grub_command_t cmd_chain;
  431. GRUB_MOD_INIT (chain)
  432. {
  433. cmd_chain = grub_register_command ("chainloader", grub_cmd_chain,
  434. N_("FILE"),
  435. /* TRANSLATORS: "payload" is a term used
  436. by coreboot and must be translated in
  437. sync with coreboot. If unsure,
  438. let it untranslated. */
  439. N_("Load another coreboot payload"));
  440. }
  441. GRUB_MOD_FINI (chain)
  442. {
  443. grub_unregister_command (cmd_chain);
  444. grub_chain_unload ();
  445. }