main.c 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright (C) Paul Mackerras 1997.
  4. *
  5. * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner.
  6. */
  7. #include <stdarg.h>
  8. #include <stddef.h>
  9. #include "elf.h"
  10. #include "page.h"
  11. #include "string.h"
  12. #include "stdio.h"
  13. #include "ops.h"
  14. #include "reg.h"
  15. struct addr_range {
  16. void *addr;
  17. unsigned long size;
  18. };
  19. #undef DEBUG
  20. static struct addr_range prep_kernel(void)
  21. {
  22. char elfheader[256];
  23. unsigned char *vmlinuz_addr = (unsigned char *)_vmlinux_start;
  24. unsigned long vmlinuz_size = _vmlinux_end - _vmlinux_start;
  25. void *addr = 0;
  26. struct elf_info ei;
  27. long len;
  28. int uncompressed_image = 0;
  29. len = partial_decompress(vmlinuz_addr, vmlinuz_size,
  30. elfheader, sizeof(elfheader), 0);
  31. /* assume uncompressed data if -1 is returned */
  32. if (len == -1) {
  33. uncompressed_image = 1;
  34. memcpy(elfheader, vmlinuz_addr, sizeof(elfheader));
  35. printf("No valid compressed data found, assume uncompressed data\n\r");
  36. }
  37. if (!parse_elf64(elfheader, &ei) && !parse_elf32(elfheader, &ei))
  38. fatal("Error: not a valid PPC32 or PPC64 ELF file!\n\r");
  39. if (platform_ops.image_hdr)
  40. platform_ops.image_hdr(elfheader);
  41. /* We need to alloc the memsize: gzip will expand the kernel
  42. * text/data, then possible rubbish we don't care about. But
  43. * the kernel bss must be claimed (it will be zero'd by the
  44. * kernel itself)
  45. */
  46. printf("Allocating 0x%lx bytes for kernel...\n\r", ei.memsize);
  47. if (platform_ops.vmlinux_alloc) {
  48. addr = platform_ops.vmlinux_alloc(ei.memsize);
  49. } else {
  50. /*
  51. * Check if the kernel image (without bss) would overwrite the
  52. * bootwrapper. The device tree has been moved in fdt_init()
  53. * to an area allocated with malloc() (somewhere past _end).
  54. */
  55. if ((unsigned long)_start < ei.loadsize)
  56. fatal("Insufficient memory for kernel at address 0!"
  57. " (_start=%p, uncompressed size=%08lx)\n\r",
  58. _start, ei.loadsize);
  59. if ((unsigned long)_end < ei.memsize)
  60. fatal("The final kernel image would overwrite the "
  61. "device tree\n\r");
  62. }
  63. if (uncompressed_image) {
  64. memcpy(addr, vmlinuz_addr + ei.elfoffset, ei.loadsize);
  65. printf("0x%lx bytes of uncompressed data copied\n\r",
  66. ei.loadsize);
  67. goto out;
  68. }
  69. /* Finally, decompress the kernel */
  70. printf("Decompressing (0x%p <- 0x%p:0x%p)...\n\r", addr,
  71. vmlinuz_addr, vmlinuz_addr+vmlinuz_size);
  72. len = partial_decompress(vmlinuz_addr, vmlinuz_size,
  73. addr, ei.loadsize, ei.elfoffset);
  74. if (len < 0)
  75. fatal("Decompression failed with error code %ld\n\r", len);
  76. if (len != ei.loadsize)
  77. fatal("Decompression error: got 0x%lx bytes, expected 0x%lx.\n\r",
  78. len, ei.loadsize);
  79. printf("Done! Decompressed 0x%lx bytes\n\r", len);
  80. out:
  81. flush_cache(addr, ei.loadsize);
  82. return (struct addr_range){addr, ei.memsize};
  83. }
  84. static struct addr_range prep_initrd(struct addr_range vmlinux, void *chosen,
  85. unsigned long initrd_addr,
  86. unsigned long initrd_size)
  87. {
  88. /* If we have an image attached to us, it overrides anything
  89. * supplied by the loader. */
  90. if (&_initrd_end > &_initrd_start) {
  91. printf("Attached initrd image at 0x%p-0x%p\n\r",
  92. _initrd_start, _initrd_end);
  93. initrd_addr = (unsigned long)_initrd_start;
  94. initrd_size = _initrd_end - _initrd_start;
  95. } else if (initrd_size > 0) {
  96. printf("Using loader supplied ramdisk at 0x%lx-0x%lx\n\r",
  97. initrd_addr, initrd_addr + initrd_size);
  98. }
  99. /* If there's no initrd at all, we're done */
  100. if (! initrd_size)
  101. return (struct addr_range){0, 0};
  102. /*
  103. * If the initrd is too low it will be clobbered when the
  104. * kernel relocates to its final location. In this case,
  105. * allocate a safer place and move it.
  106. */
  107. if (initrd_addr < vmlinux.size) {
  108. void *old_addr = (void *)initrd_addr;
  109. printf("Allocating 0x%lx bytes for initrd ...\n\r",
  110. initrd_size);
  111. initrd_addr = (unsigned long)malloc(initrd_size);
  112. if (! initrd_addr)
  113. fatal("Can't allocate memory for initial "
  114. "ramdisk !\n\r");
  115. printf("Relocating initrd 0x%lx <- 0x%p (0x%lx bytes)\n\r",
  116. initrd_addr, old_addr, initrd_size);
  117. memmove((void *)initrd_addr, old_addr, initrd_size);
  118. }
  119. printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd_addr));
  120. /* Tell the kernel initrd address via device tree */
  121. setprop_val(chosen, "linux,initrd-start", (u32)(initrd_addr));
  122. setprop_val(chosen, "linux,initrd-end", (u32)(initrd_addr+initrd_size));
  123. return (struct addr_range){(void *)initrd_addr, initrd_size};
  124. }
  125. #ifdef __powerpc64__
  126. static void prep_esm_blob(struct addr_range vmlinux, void *chosen)
  127. {
  128. unsigned long esm_blob_addr, esm_blob_size;
  129. /* Do we have an ESM (Enter Secure Mode) blob? */
  130. if (&_esm_blob_end <= &_esm_blob_start)
  131. return;
  132. printf("Attached ESM blob at 0x%p-0x%p\n\r",
  133. _esm_blob_start, _esm_blob_end);
  134. esm_blob_addr = (unsigned long)_esm_blob_start;
  135. esm_blob_size = _esm_blob_end - _esm_blob_start;
  136. /*
  137. * If the ESM blob is too low it will be clobbered when the
  138. * kernel relocates to its final location. In this case,
  139. * allocate a safer place and move it.
  140. */
  141. if (esm_blob_addr < vmlinux.size) {
  142. void *old_addr = (void *)esm_blob_addr;
  143. printf("Allocating 0x%lx bytes for esm_blob ...\n\r",
  144. esm_blob_size);
  145. esm_blob_addr = (unsigned long)malloc(esm_blob_size);
  146. if (!esm_blob_addr)
  147. fatal("Can't allocate memory for ESM blob !\n\r");
  148. printf("Relocating ESM blob 0x%lx <- 0x%p (0x%lx bytes)\n\r",
  149. esm_blob_addr, old_addr, esm_blob_size);
  150. memmove((void *)esm_blob_addr, old_addr, esm_blob_size);
  151. }
  152. /* Tell the kernel ESM blob address via device tree. */
  153. setprop_val(chosen, "linux,esm-blob-start", (u32)(esm_blob_addr));
  154. setprop_val(chosen, "linux,esm-blob-end", (u32)(esm_blob_addr + esm_blob_size));
  155. }
  156. #else
  157. static inline void prep_esm_blob(struct addr_range vmlinux, void *chosen) { }
  158. #endif
  159. /* A buffer that may be edited by tools operating on a zImage binary so as to
  160. * edit the command line passed to vmlinux (by setting /chosen/bootargs).
  161. * The buffer is put in it's own section so that tools may locate it easier.
  162. */
  163. static char cmdline[BOOT_COMMAND_LINE_SIZE]
  164. __attribute__((__section__("__builtin_cmdline")));
  165. static void prep_cmdline(void *chosen)
  166. {
  167. unsigned int getline_timeout = 5000;
  168. int v;
  169. int n;
  170. /* Wait-for-input time */
  171. n = getprop(chosen, "linux,cmdline-timeout", &v, sizeof(v));
  172. if (n == sizeof(v))
  173. getline_timeout = v;
  174. if (cmdline[0] == '\0')
  175. getprop(chosen, "bootargs", cmdline, BOOT_COMMAND_LINE_SIZE-1);
  176. printf("\n\rLinux/PowerPC load: %s", cmdline);
  177. /* If possible, edit the command line */
  178. if (console_ops.edit_cmdline && getline_timeout)
  179. console_ops.edit_cmdline(cmdline, BOOT_COMMAND_LINE_SIZE, getline_timeout);
  180. printf("\n\r");
  181. /* Put the command line back into the devtree for the kernel */
  182. setprop_str(chosen, "bootargs", cmdline);
  183. }
  184. struct platform_ops platform_ops;
  185. struct dt_ops dt_ops;
  186. struct console_ops console_ops;
  187. struct loader_info loader_info;
  188. void start(void)
  189. {
  190. struct addr_range vmlinux, initrd;
  191. kernel_entry_t kentry;
  192. unsigned long ft_addr = 0;
  193. void *chosen;
  194. /* Do this first, because malloc() could clobber the loader's
  195. * command line. Only use the loader command line if a
  196. * built-in command line wasn't set by an external tool */
  197. if ((loader_info.cmdline_len > 0) && (cmdline[0] == '\0'))
  198. memmove(cmdline, loader_info.cmdline,
  199. min(loader_info.cmdline_len, BOOT_COMMAND_LINE_SIZE-1));
  200. if (console_ops.open && (console_ops.open() < 0))
  201. exit();
  202. if (platform_ops.fixups)
  203. platform_ops.fixups();
  204. printf("\n\rzImage starting: loaded at 0x%p (sp: 0x%p)\n\r",
  205. _start, get_sp());
  206. /* Ensure that the device tree has a /chosen node */
  207. chosen = finddevice("/chosen");
  208. if (!chosen)
  209. chosen = create_node(NULL, "chosen");
  210. vmlinux = prep_kernel();
  211. initrd = prep_initrd(vmlinux, chosen,
  212. loader_info.initrd_addr, loader_info.initrd_size);
  213. prep_esm_blob(vmlinux, chosen);
  214. prep_cmdline(chosen);
  215. printf("Finalizing device tree...");
  216. if (dt_ops.finalize)
  217. ft_addr = dt_ops.finalize();
  218. if (ft_addr)
  219. printf(" flat tree at 0x%lx\n\r", ft_addr);
  220. else
  221. printf(" using OF tree (promptr=%p)\n\r", loader_info.promptr);
  222. if (console_ops.close)
  223. console_ops.close();
  224. kentry = (kernel_entry_t) vmlinux.addr;
  225. if (ft_addr) {
  226. if(platform_ops.kentry)
  227. platform_ops.kentry(ft_addr, vmlinux.addr);
  228. else
  229. kentry(ft_addr, 0, NULL);
  230. }
  231. else
  232. kentry((unsigned long)initrd.addr, initrd.size,
  233. loader_info.promptr);
  234. /* console closed so printf in fatal below may not work */
  235. fatal("Error: Linux kernel returned to zImage boot wrapper!\n\r");
  236. }