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 __le32 output_len __aligned(1); 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 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 puts("\n\n"); 149 puts(x); 150 puts("\n\n -- System halted"); 151 while (1) /* wait forever */ 152 ; 153 } 154 155 static int print_hex(unsigned long num) 156 { 157 const char hex[] = "0123456789abcdef"; 158 char str[40]; 159 int i = sizeof(str)-1; 160 161 str[i--] = '\0'; 162 do { 163 str[i--] = hex[num & 0x0f]; 164 num >>= 4; 165 } while (num); 166 167 str[i--] = 'x'; 168 str[i] = '0'; 169 puts(&str[i]); 170 171 return 0; 172 } 173 174 int printf(const char *fmt, ...) 175 { 176 va_list args; 177 int i = 0; 178 179 va_start(args, fmt); 180 181 while (fmt[i]) { 182 if (fmt[i] != '%') { 183 put: 184 putchar(fmt[i++]); 185 continue; 186 } 187 188 if (fmt[++i] == '%') 189 goto put; 190 ++i; 191 print_hex(va_arg(args, unsigned long)); 192 } 193 194 va_end(args); 195 return 0; 196 } 197 198 /* helper functions for libgcc */ 199 void abort(void) 200 { 201 error("aborted."); 202 } 203 204 #undef malloc 205 void *malloc(size_t size) 206 { 207 return malloc_gzip(size); 208 } 209 210 #undef free 211 void free(void *ptr) 212 { 213 return free_gzip(ptr); 214 } 215 216 217 static void flush_data_cache(char *start, unsigned long length) 218 { 219 char *end = start + length; 220 221 do { 222 asm volatile("fdc 0(%0)" : : "r" (start)); 223 asm volatile("fic 0(%%sr0,%0)" : : "r" (start)); 224 start += 16; 225 } while (start < end); 226 asm volatile("fdc 0(%0)" : : "r" (end)); 227 228 asm ("sync"); 229 } 230 231 static void parse_elf(void *output) 232 { 233 #ifdef CONFIG_64BIT 234 Elf64_Ehdr ehdr; 235 Elf64_Phdr *phdrs, *phdr; 236 #else 237 Elf32_Ehdr ehdr; 238 Elf32_Phdr *phdrs, *phdr; 239 #endif 240 void *dest; 241 int i; 242 243 memcpy(&ehdr, output, sizeof(ehdr)); 244 if (ehdr.e_ident[EI_MAG0] != ELFMAG0 || 245 ehdr.e_ident[EI_MAG1] != ELFMAG1 || 246 ehdr.e_ident[EI_MAG2] != ELFMAG2 || 247 ehdr.e_ident[EI_MAG3] != ELFMAG3) { 248 error("Kernel is not a valid ELF file"); 249 return; 250 } 251 252 #ifdef DEBUG 253 printf("Parsing ELF... "); 254 #endif 255 256 phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum); 257 if (!phdrs) 258 error("Failed to allocate space for phdrs"); 259 260 memcpy(phdrs, output + ehdr.e_phoff, sizeof(*phdrs) * ehdr.e_phnum); 261 262 for (i = 0; i < ehdr.e_phnum; i++) { 263 phdr = &phdrs[i]; 264 265 switch (phdr->p_type) { 266 case PT_LOAD: 267 dest = (void *)((unsigned long) phdr->p_paddr & 268 (__PAGE_OFFSET_DEFAULT-1)); 269 memmove(dest, output + phdr->p_offset, phdr->p_filesz); 270 break; 271 default: 272 break; 273 } 274 } 275 276 free(phdrs); 277 } 278 279 unsigned long decompress_kernel(unsigned int started_wide, 280 unsigned int command_line, 281 const unsigned int rd_start, 282 const unsigned int rd_end) 283 { 284 char *output; 285 unsigned long vmlinux_addr, vmlinux_len; 286 unsigned long kernel_addr, kernel_len; 287 288 #ifdef CONFIG_64BIT 289 parisc_narrow_firmware = 0; 290 #endif 291 292 set_firmware_width_unlocked(); 293 294 putchar('D'); /* if you get this D and no more, string storage */ 295 /* in $GLOBAL$ is wrong or %dp is wrong */ 296 puts("ecompressing Linux... "); 297 298 /* where the final bits are stored */ 299 kernel_addr = KERNEL_BINARY_TEXT_START; 300 kernel_len = __pa(SZ_end) - __pa(SZparisc_kernel_start); 301 if ((unsigned long) &_startcode_end > kernel_addr) 302 error("Bootcode overlaps kernel code"); 303 304 /* 305 * Calculate addr to where the vmlinux ELF file shall be decompressed. 306 * Assembly code in head.S positioned the stack directly behind bss, so 307 * leave 2 MB for the stack. 308 */ 309 vmlinux_addr = (unsigned long) &_ebss + 2*1024*1024; 310 vmlinux_len = get_unaligned_le32(&output_len); 311 output = (char *) vmlinux_addr; 312 313 /* 314 * Initialize free_mem_ptr and free_mem_end_ptr. 315 */ 316 free_mem_ptr = vmlinux_addr + vmlinux_len; 317 318 /* Limit memory for bootoader to 1GB */ 319 #define ARTIFICIAL_LIMIT (1*1024*1024*1024) 320 free_mem_end_ptr = PAGE0->imm_max_mem; 321 if (free_mem_end_ptr > ARTIFICIAL_LIMIT) 322 free_mem_end_ptr = ARTIFICIAL_LIMIT; 323 324 #ifdef CONFIG_BLK_DEV_INITRD 325 /* if we have ramdisk this is at end of memory */ 326 if (rd_start && rd_start < free_mem_end_ptr) 327 free_mem_end_ptr = rd_start; 328 #endif 329 330 if (free_mem_ptr >= free_mem_end_ptr) 331 error("Kernel too big for machine."); 332 333 #ifdef DEBUG 334 printf("\n"); 335 printf("startcode_end = %x\n", &_startcode_end); 336 printf("commandline = %x\n", command_line); 337 printf("rd_start = %x\n", rd_start); 338 printf("rd_end = %x\n", rd_end); 339 340 printf("free_ptr = %x\n", free_mem_ptr); 341 printf("free_ptr_end = %x\n", free_mem_end_ptr); 342 343 printf("input_data = %x\n", input_data); 344 printf("input_len = %x\n", input_len); 345 printf("output = %x\n", output); 346 printf("output_len = %x\n", vmlinux_len); 347 printf("kernel_addr = %x\n", kernel_addr); 348 printf("kernel_len = %x\n", kernel_len); 349 #endif 350 351 __decompress(input_data, input_len, NULL, NULL, 352 output, 0, NULL, error); 353 parse_elf(output); 354 355 output = (char *) kernel_addr; 356 flush_data_cache(output, kernel_len); 357 358 printf("done.\nBooting the kernel.\n"); 359 360 return (unsigned long) output; 361 } 362