main.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. /*
  2. * Copyright (C) Paul Mackerras 1997.
  3. *
  4. * Updates for PPC64 by Todd Inglett, Dave Engebretsen & Peter Bergner.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version
  9. * 2 of the License, or (at your option) any later version.
  10. */
  11. #include <stdarg.h>
  12. #include <stddef.h>
  13. #include "elf.h"
  14. #include "page.h"
  15. #include "string.h"
  16. #include "stdio.h"
  17. #include "prom.h"
  18. #include "zlib.h"
  19. extern void flush_cache(void *, unsigned long);
  20. /* Value picked to match that used by yaboot */
  21. #define PROG_START 0x01400000
  22. #define RAM_END (512<<20) // Fixme: use OF */
  23. #define ONE_MB 0x100000
  24. extern char _start[];
  25. extern char __bss_start[];
  26. extern char _end[];
  27. extern char _vmlinux_start[];
  28. extern char _vmlinux_end[];
  29. extern char _initrd_start[];
  30. extern char _initrd_end[];
  31. struct addr_range {
  32. unsigned long addr;
  33. unsigned long size;
  34. unsigned long memsize;
  35. };
  36. static struct addr_range vmlinux = {0, 0, 0};
  37. static struct addr_range vmlinuz = {0, 0, 0};
  38. static struct addr_range initrd = {0, 0, 0};
  39. static char scratch[46912]; /* scratch space for gunzip, from zlib_inflate_workspacesize() */
  40. static char elfheader[256];
  41. typedef void (*kernel_entry_t)( unsigned long,
  42. unsigned long,
  43. void *,
  44. void *);
  45. #undef DEBUG
  46. static unsigned long claim_base;
  47. #define HEAD_CRC 2
  48. #define EXTRA_FIELD 4
  49. #define ORIG_NAME 8
  50. #define COMMENT 0x10
  51. #define RESERVED 0xe0
  52. static void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
  53. {
  54. z_stream s;
  55. int r, i, flags;
  56. /* skip header */
  57. i = 10;
  58. flags = src[3];
  59. if (src[2] != Z_DEFLATED || (flags & RESERVED) != 0) {
  60. printf("bad gzipped data\n\r");
  61. exit();
  62. }
  63. if ((flags & EXTRA_FIELD) != 0)
  64. i = 12 + src[10] + (src[11] << 8);
  65. if ((flags & ORIG_NAME) != 0)
  66. while (src[i++] != 0)
  67. ;
  68. if ((flags & COMMENT) != 0)
  69. while (src[i++] != 0)
  70. ;
  71. if ((flags & HEAD_CRC) != 0)
  72. i += 2;
  73. if (i >= *lenp) {
  74. printf("gunzip: ran out of data in header\n\r");
  75. exit();
  76. }
  77. if (zlib_inflate_workspacesize() > sizeof(scratch)) {
  78. printf("gunzip needs more mem\n");
  79. exit();
  80. }
  81. memset(&s, 0, sizeof(s));
  82. s.workspace = scratch;
  83. r = zlib_inflateInit2(&s, -MAX_WBITS);
  84. if (r != Z_OK) {
  85. printf("inflateInit2 returned %d\n\r", r);
  86. exit();
  87. }
  88. s.next_in = src + i;
  89. s.avail_in = *lenp - i;
  90. s.next_out = dst;
  91. s.avail_out = dstlen;
  92. r = zlib_inflate(&s, Z_FULL_FLUSH);
  93. if (r != Z_OK && r != Z_STREAM_END) {
  94. printf("inflate returned %d msg: %s\n\r", r, s.msg);
  95. exit();
  96. }
  97. *lenp = s.next_out - (unsigned char *) dst;
  98. zlib_inflateEnd(&s);
  99. }
  100. static unsigned long try_claim(unsigned long size)
  101. {
  102. unsigned long addr = 0;
  103. for(; claim_base < RAM_END; claim_base += ONE_MB) {
  104. #ifdef DEBUG
  105. printf(" trying: 0x%08lx\n\r", claim_base);
  106. #endif
  107. addr = (unsigned long)claim(claim_base, size, 0);
  108. if ((void *)addr != (void *)-1)
  109. break;
  110. }
  111. if (addr == 0)
  112. return 0;
  113. claim_base = PAGE_ALIGN(claim_base + size);
  114. return addr;
  115. }
  116. void start(unsigned long a1, unsigned long a2, void *promptr)
  117. {
  118. unsigned long i;
  119. int len;
  120. kernel_entry_t kernel_entry;
  121. Elf64_Ehdr *elf64;
  122. Elf64_Phdr *elf64ph;
  123. memset(__bss_start, 0, _end - __bss_start);
  124. prom = (int (*)(void *)) promptr;
  125. chosen_handle = finddevice("/chosen");
  126. if (chosen_handle == (void *) -1)
  127. exit();
  128. if (getprop(chosen_handle, "stdout", &stdout, sizeof(stdout)) != 4)
  129. exit();
  130. stderr = stdout;
  131. if (getprop(chosen_handle, "stdin", &stdin, sizeof(stdin)) != 4)
  132. exit();
  133. printf("\n\rzImage starting: loaded at 0x%lx\n\r", (unsigned long) _start);
  134. /*
  135. * The first available claim_base must be above the end of the
  136. * the loaded kernel wrapper file (_start to _end includes the
  137. * initrd image if it is present) and rounded up to a nice
  138. * 1 MB boundary for good measure.
  139. */
  140. claim_base = _ALIGN_UP((unsigned long)_end, ONE_MB);
  141. #if defined(PROG_START)
  142. /*
  143. * Maintain a "magic" minimum address. This keeps some older
  144. * firmware platforms running.
  145. */
  146. if (claim_base < PROG_START)
  147. claim_base = PROG_START;
  148. #endif
  149. vmlinuz.addr = (unsigned long)_vmlinux_start;
  150. vmlinuz.size = (unsigned long)(_vmlinux_end - _vmlinux_start);
  151. /* gunzip the ELF header of the kernel */
  152. if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
  153. len = vmlinuz.size;
  154. gunzip(elfheader, sizeof(elfheader),
  155. (unsigned char *)vmlinuz.addr, &len);
  156. } else
  157. memcpy(elfheader, (const void *)vmlinuz.addr, sizeof(elfheader));
  158. elf64 = (Elf64_Ehdr *)elfheader;
  159. if ( elf64->e_ident[EI_MAG0] != ELFMAG0 ||
  160. elf64->e_ident[EI_MAG1] != ELFMAG1 ||
  161. elf64->e_ident[EI_MAG2] != ELFMAG2 ||
  162. elf64->e_ident[EI_MAG3] != ELFMAG3 ||
  163. elf64->e_ident[EI_CLASS] != ELFCLASS64 ||
  164. elf64->e_ident[EI_DATA] != ELFDATA2MSB ||
  165. elf64->e_type != ET_EXEC ||
  166. elf64->e_machine != EM_PPC64 )
  167. {
  168. printf("Error: not a valid PPC64 ELF file!\n\r");
  169. exit();
  170. }
  171. elf64ph = (Elf64_Phdr *)((unsigned long)elf64 +
  172. (unsigned long)elf64->e_phoff);
  173. for(i=0; i < (unsigned int)elf64->e_phnum ;i++,elf64ph++) {
  174. if (elf64ph->p_type == PT_LOAD && elf64ph->p_offset != 0)
  175. break;
  176. }
  177. vmlinux.size = (unsigned long)elf64ph->p_filesz;
  178. vmlinux.memsize = (unsigned long)elf64ph->p_memsz;
  179. printf("Allocating 0x%lx bytes for kernel ...\n\r", vmlinux.memsize);
  180. vmlinux.addr = try_claim(vmlinux.memsize);
  181. if (vmlinux.addr == 0) {
  182. printf("Can't allocate memory for kernel image !\n\r");
  183. exit();
  184. }
  185. /*
  186. * Now we try to claim memory for the initrd (and copy it there)
  187. */
  188. initrd.size = (unsigned long)(_initrd_end - _initrd_start);
  189. initrd.memsize = initrd.size;
  190. if ( initrd.size > 0 ) {
  191. printf("Allocating 0x%lx bytes for initrd ...\n\r", initrd.size);
  192. initrd.addr = try_claim(initrd.size);
  193. if (initrd.addr == 0) {
  194. printf("Can't allocate memory for initial ramdisk !\n\r");
  195. exit();
  196. }
  197. a1 = initrd.addr;
  198. a2 = initrd.size;
  199. printf("initial ramdisk moving 0x%lx <- 0x%lx (0x%lx bytes)\n\r",
  200. initrd.addr, (unsigned long)_initrd_start, initrd.size);
  201. memmove((void *)initrd.addr, (void *)_initrd_start, initrd.size);
  202. printf("initrd head: 0x%lx\n\r", *((unsigned long *)initrd.addr));
  203. }
  204. /* Eventually gunzip the kernel */
  205. if (*(unsigned short *)vmlinuz.addr == 0x1f8b) {
  206. printf("gunzipping (0x%lx <- 0x%lx:0x%0lx)...",
  207. vmlinux.addr, vmlinuz.addr, vmlinuz.addr+vmlinuz.size);
  208. len = vmlinuz.size;
  209. gunzip((void *)vmlinux.addr, vmlinux.memsize,
  210. (unsigned char *)vmlinuz.addr, &len);
  211. printf("done 0x%lx bytes\n\r", len);
  212. } else {
  213. memmove((void *)vmlinux.addr,(void *)vmlinuz.addr,vmlinuz.size);
  214. }
  215. /* Skip over the ELF header */
  216. #ifdef DEBUG
  217. printf("... skipping 0x%lx bytes of ELF header\n\r",
  218. (unsigned long)elf64ph->p_offset);
  219. #endif
  220. vmlinux.addr += (unsigned long)elf64ph->p_offset;
  221. flush_cache((void *)vmlinux.addr, vmlinux.size);
  222. kernel_entry = (kernel_entry_t)vmlinux.addr;
  223. #ifdef DEBUG
  224. printf( "kernel:\n\r"
  225. " entry addr = 0x%lx\n\r"
  226. " a1 = 0x%lx,\n\r"
  227. " a2 = 0x%lx,\n\r"
  228. " prom = 0x%lx,\n\r"
  229. " bi_recs = 0x%lx,\n\r",
  230. (unsigned long)kernel_entry, a1, a2,
  231. (unsigned long)prom, NULL);
  232. #endif
  233. kernel_entry( a1, a2, prom, NULL );
  234. printf("Error: Linux kernel returned to zImage bootloader!\n\r");
  235. exit();
  236. }