main.c 7.2 KB

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