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 */ 26 extern char input_data[]; 27 extern int input_len; 28 /* output_len is inserted by the linker possibly at an unaligned address */ 29 extern char output_len; 30 extern char _text, _end; 31 extern char _bss, _ebss; 32 extern char _startcode_end; 33 extern void startup_continue(void *entry, unsigned long cmdline, 34 unsigned long rd_start, unsigned long rd_end) __noreturn; 35 36 void error(char *m) __noreturn; 37 38 static unsigned long free_mem_ptr; 39 static 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 65 void *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 82 void *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 91 void *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 101 size_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 110 char *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 120 static int 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 136 static 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 146 void __noreturn error(char *x) 147 { 148 if (x) puts(x); 149 puts("\n -- System halted\n"); 150 while (1) /* wait forever */ 151 ; 152 } 153 154 static 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 175 static int 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] != '%') { 184 put: 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 */ 201 void abort(void) 202 { 203 error("aborted."); 204 } 205 206 #undef malloc 207 static void *malloc(size_t size) 208 { 209 return malloc_gzip(size); 210 } 211 212 #undef free 213 static void free(void *ptr) 214 { 215 return free_gzip(ptr); 216 } 217 218 219 static 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 233 static 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 281 asmlinkage unsigned long __visible 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 } 371