misc.c (7284B)
1/* 2 * Definitions and wrapper functions for kernel decompressor 3 * 4 * (C) 2017 Helge Deller <deller@gmx.de> 5 */ 6 7#include <linux/uaccess.h> 8#include <linux/elf.h> 9#include <asm/unaligned.h> 10#include <asm/page.h> 11#include "sizes.h" 12 13/* 14 * gzip declarations 15 */ 16#define STATIC static 17 18#undef memmove 19#define memmove memmove 20#define memzero(s, n) memset((s), 0, (n)) 21 22#define malloc malloc_gzip 23#define free free_gzip 24 25/* Symbols defined by linker scripts */ 26extern char input_data[]; 27extern int input_len; 28/* output_len is inserted by the linker possibly at an unaligned address */ 29extern char output_len; 30extern char _text, _end; 31extern char _bss, _ebss; 32extern char _startcode_end; 33extern void startup_continue(void *entry, unsigned long cmdline, 34 unsigned long rd_start, unsigned long rd_end) __noreturn; 35 36void error(char *m) __noreturn; 37 38static unsigned long free_mem_ptr; 39static unsigned long free_mem_end_ptr; 40 41#ifdef CONFIG_KERNEL_GZIP 42#include "../../../../lib/decompress_inflate.c" 43#endif 44 45#ifdef CONFIG_KERNEL_BZIP2 46#include "../../../../lib/decompress_bunzip2.c" 47#endif 48 49#ifdef CONFIG_KERNEL_LZ4 50#include "../../../../lib/decompress_unlz4.c" 51#endif 52 53#ifdef CONFIG_KERNEL_LZMA 54#include "../../../../lib/decompress_unlzma.c" 55#endif 56 57#ifdef CONFIG_KERNEL_LZO 58#include "../../../../lib/decompress_unlzo.c" 59#endif 60 61#ifdef CONFIG_KERNEL_XZ 62#include "../../../../lib/decompress_unxz.c" 63#endif 64 65void *memmove(void *dest, const void *src, size_t n) 66{ 67 const char *s = src; 68 char *d = dest; 69 70 if (d <= s) { 71 while (n--) 72 *d++ = *s++; 73 } else { 74 d += n; 75 s += n; 76 while (n--) 77 *--d = *--s; 78 } 79 return dest; 80} 81 82void *memset(void *s, int c, size_t count) 83{ 84 char *xs = (char *)s; 85 86 while (count--) 87 *xs++ = c; 88 return s; 89} 90 91void *memcpy(void *d, const void *s, size_t len) 92{ 93 char *dest = (char *)d; 94 const char *source = (const char *)s; 95 96 while (len--) 97 *dest++ = *source++; 98 return d; 99} 100 101size_t strlen(const char *s) 102{ 103 const char *sc; 104 105 for (sc = s; *sc != '\0'; ++sc) 106 ; 107 return sc - s; 108} 109 110char *strchr(const char *s, int c) 111{ 112 while (*s) { 113 if (*s == (char)c) 114 return (char *)s; 115 ++s; 116 } 117 return NULL; 118} 119 120int puts(const char *s) 121{ 122 const char *nuline = s; 123 124 while ((nuline = strchr(s, '\n')) != NULL) { 125 if (nuline != s) 126 pdc_iodc_print(s, nuline - s); 127 pdc_iodc_print("\r\n", 2); 128 s = nuline + 1; 129 } 130 if (*s != '\0') 131 pdc_iodc_print(s, strlen(s)); 132 133 return 0; 134} 135 136static int putchar(int c) 137{ 138 char buf[2]; 139 140 buf[0] = c; 141 buf[1] = '\0'; 142 puts(buf); 143 return c; 144} 145 146void __noreturn error(char *x) 147{ 148 if (x) puts(x); 149 puts("\n -- System halted\n"); 150 while (1) /* wait forever */ 151 ; 152} 153 154static int print_num(unsigned long num, int base) 155{ 156 const char hex[] = "0123456789abcdef"; 157 char str[40]; 158 int i = sizeof(str)-1; 159 160 str[i--] = '\0'; 161 do { 162 str[i--] = hex[num % base]; 163 num = num / base; 164 } while (num); 165 166 if (base == 16) { 167 str[i--] = 'x'; 168 str[i] = '0'; 169 } else i++; 170 puts(&str[i]); 171 172 return 0; 173} 174 175int printf(const char *fmt, ...) 176{ 177 va_list args; 178 int i = 0; 179 180 va_start(args, fmt); 181 182 while (fmt[i]) { 183 if (fmt[i] != '%') { 184put: 185 putchar(fmt[i++]); 186 continue; 187 } 188 189 if (fmt[++i] == '%') 190 goto put; 191 print_num(va_arg(args, unsigned long), 192 fmt[i] == 'x' ? 16:10); 193 ++i; 194 } 195 196 va_end(args); 197 return 0; 198} 199 200/* helper functions for libgcc */ 201void abort(void) 202{ 203 error("aborted."); 204} 205 206#undef malloc 207void *malloc(size_t size) 208{ 209 return malloc_gzip(size); 210} 211 212#undef free 213void free(void *ptr) 214{ 215 return free_gzip(ptr); 216} 217 218 219static void flush_data_cache(char *start, unsigned long length) 220{ 221 char *end = start + length; 222 223 do { 224 asm volatile("fdc 0(%0)" : : "r" (start)); 225 asm volatile("fic 0(%%sr0,%0)" : : "r" (start)); 226 start += 16; 227 } while (start < end); 228 asm volatile("fdc 0(%0)" : : "r" (end)); 229 230 asm ("sync"); 231} 232 233static void parse_elf(void *output) 234{ 235#ifdef CONFIG_64BIT 236 Elf64_Ehdr ehdr; 237 Elf64_Phdr *phdrs, *phdr; 238#else 239 Elf32_Ehdr ehdr; 240 Elf32_Phdr *phdrs, *phdr; 241#endif 242 void *dest; 243 int i; 244 245 memcpy(&ehdr, output, sizeof(ehdr)); 246 if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || 247 ehdr.e_ident[EI_MAG1] != ELFMAG1 || 248 ehdr.e_ident[EI_MAG2] != ELFMAG2 || 249 ehdr.e_ident[EI_MAG3] != ELFMAG3) { 250 error("Kernel is not a valid ELF file"); 251 return; 252 } 253 254#ifdef DEBUG 255 printf("Parsing ELF... "); 256#endif 257 258 phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum); 259 if (!phdrs) 260 error("Failed to allocate space for phdrs"); 261 262 memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum); 263 264 for (i = 0; i < ehdr.e_phnum; i++) { 265 phdr = &phdrs[i]; 266 267 switch (phdr->p_type) { 268 case PT_LOAD: 269 dest = (void *)((unsigned long) phdr->p_paddr & 270 (__PAGE_OFFSET_DEFAULT-1)); 271 memmove(dest, output + phdr->p_offset, phdr->p_filesz); 272 break; 273 default: 274 break; 275 } 276 } 277 278 free(phdrs); 279} 280 281unsigned long decompress_kernel(unsigned int started_wide, 282 unsigned int command_line, 283 const unsigned int rd_start, 284 const unsigned int rd_end) 285{ 286 char *output; 287 unsigned long vmlinux_addr, vmlinux_len; 288 unsigned long kernel_addr, kernel_len; 289 290#ifdef CONFIG_64BIT 291 parisc_narrow_firmware = 0; 292#endif 293 294 set_firmware_width_unlocked(); 295 296 putchar('D'); /* if you get this D and no more, string storage */ 297 /* in $GLOBAL$ is wrong or %dp is wrong */ 298 puts("ecompressing Linux... "); 299 300 /* where the final bits are stored */ 301 kernel_addr = KERNEL_BINARY_TEXT_START; 302 kernel_len = __pa(SZ_end) - __pa(SZparisc_kernel_start); 303 if ((unsigned long) &_startcode_end > kernel_addr) 304 error("Bootcode overlaps kernel code"); 305 306 /* 307 * Calculate addr to where the vmlinux ELF file shall be decompressed. 308 * Assembly code in head.S positioned the stack directly behind bss, so 309 * leave 2 MB for the stack. 310 */ 311 vmlinux_addr = (unsigned long) &_ebss + 2*1024*1024; 312 vmlinux_len = get_unaligned_le32(&output_len); 313 output = (char *) vmlinux_addr; 314 315 /* 316 * Initialize free_mem_ptr and free_mem_end_ptr. 317 */ 318 free_mem_ptr = vmlinux_addr + vmlinux_len; 319 320 /* Limit memory for bootoader to 1GB */ 321 #define ARTIFICIAL_LIMIT (1*1024*1024*1024) 322 free_mem_end_ptr = PAGE0->imm_max_mem; 323 if (free_mem_end_ptr > ARTIFICIAL_LIMIT) 324 free_mem_end_ptr = ARTIFICIAL_LIMIT; 325 326#ifdef CONFIG_BLK_DEV_INITRD 327 /* if we have ramdisk this is at end of memory */ 328 if (rd_start && rd_start < free_mem_end_ptr) 329 free_mem_end_ptr = rd_start; 330#endif 331 332 if (free_mem_ptr >= free_mem_end_ptr) { 333 int free_ram; 334 free_ram = (free_mem_ptr >> 20) + 1; 335 if (free_ram < 32) 336 free_ram = 32; 337 printf("\nKernel requires at least %d MB RAM.\n", 338 free_ram); 339 error(NULL); 340 } 341 342#ifdef DEBUG 343 printf("\n"); 344 printf("startcode_end = %x\n", &_startcode_end); 345 printf("commandline = %x\n", command_line); 346 printf("rd_start = %x\n", rd_start); 347 printf("rd_end = %x\n", rd_end); 348 349 printf("free_ptr = %x\n", free_mem_ptr); 350 printf("free_ptr_end = %x\n", free_mem_end_ptr); 351 352 printf("input_data = %x\n", input_data); 353 printf("input_len = %x\n", input_len); 354 printf("output = %x\n", output); 355 printf("output_len = %x\n", vmlinux_len); 356 printf("kernel_addr = %x\n", kernel_addr); 357 printf("kernel_len = %x\n", kernel_len); 358#endif 359 360 __decompress(input_data, input_len, NULL, NULL, 361 output, 0, NULL, error); 362 parse_elf(output); 363 364 output = (char *) kernel_addr; 365 flush_data_cache(output, kernel_len); 366 367 printf("done.\nBooting the kernel.\n"); 368 369 return (unsigned long) output; 370}