1/* 2 * 3 * LZMA compressed kernel decompressor for ADM5120 boards 4 * 5 * Copyright (C) 2005 by Oleg I. Vdovikin <oleg@cs.msu.su> 6 * Copyright (C) 2007-2008 OpenWrt.org 7 * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22 * 23 * 24 * Please note, this was code based on the bunzip2 decompressor code 25 * by Manuel Novoa III (mjn3@codepoet.org), although the only thing left 26 * is an idea and part of original vendor code 27 * 28 * 29 * 12-Mar-2005 Mineharu Takahara <mtakahar@yahoo.com> 30 * pass actual output size to decoder (stream mode 31 * compressed input is not a requirement anymore) 32 * 33 * 24-Apr-2005 Oleg I. Vdovikin 34 * reordered functions using lds script, removed forward decl 35 * 36 * 24-Mar-2007 Gabor Juhos 37 * pass original values of the a0,a1,a2,a3 registers to the kernel 38 * 39 * 19-May-2007 Gabor Juhos 40 * endiannes related cleanups 41 * add support for decompressing an embedded kernel 42 * 43 */ 44 45#include <stddef.h> 46 47#include "config.h" 48#include "printf.h" 49#include "LzmaDecode.h" 50 51#define ADM5120_FLASH_START 0x1fc00000 /* Flash start */ 52#define ADM5120_FLASH_END 0x1fe00000 /* Flash end */ 53 54#define KSEG0 0x80000000 55#define KSEG1 0xa0000000 56 57#define KSEG1ADDR(a) ((((unsigned)(a)) & 0x1fffffffU) | KSEG1) 58 59#define Index_Invalidate_I 0x00 60#define Index_Writeback_Inv_D 0x01 61 62#define cache_unroll(base,op) \ 63 __asm__ __volatile__( \ 64 ".set noreorder;\n" \ 65 ".set mips3;\n" \ 66 "cache %1, (%0);\n" \ 67 ".set mips0;\n" \ 68 ".set reorder\n" \ 69 : \ 70 : "r" (base), \ 71 "i" (op)); 72 73#ifdef LZMA_DEBUG 74# define DBG(f, a...) printf(f, ## a) 75#else 76# define DBG(f, a...) do {} while (0) 77#endif 78 79static __inline__ void blast_icache(unsigned long size, unsigned long lsize) 80{ 81 unsigned long start = KSEG0; 82 unsigned long end = (start + size); 83 84 while(start < end) { 85 cache_unroll(start,Index_Invalidate_I); 86 start += lsize; 87 } 88} 89 90static __inline__ void blast_dcache(unsigned long size, unsigned long lsize) 91{ 92 unsigned long start = KSEG0; 93 unsigned long end = (start + size); 94 95 while(start < end) { 96 cache_unroll(start,Index_Writeback_Inv_D); 97 start += lsize; 98 } 99} 100 101#define TRX_MAGIC 0x30524448 /* "HDR0" */ 102#define TRX_ALIGN 0x1000 103 104struct trx_header { 105 unsigned int magic; /* "HDR0" */ 106 unsigned int len; /* Length of file including header */ 107 unsigned int crc32; /* 32-bit CRC from flag_version to end of file */ 108 unsigned int flag_version; /* 0:15 flags, 16:31 version */ 109 unsigned int offsets[3]; /* Offsets of partitions from start of header */ 110}; 111 112struct env_var { 113 char *name; 114 char *value; 115}; 116 117/* beyound the image end, size not known in advance */ 118extern unsigned char workspace[]; 119extern void board_init(void); 120 121static CLzmaDecoderState lzma_state; 122 123typedef void (*kernel_entry)(unsigned long reg_a0, unsigned long reg_a1, 124 unsigned long reg_a2, unsigned long reg_a3); 125 126static int decompress_data(CLzmaDecoderState *vs, unsigned char *outStream, 127 UInt32 outSize); 128 129#ifdef CONFIG_PASS_KARGS 130#define ENVV(n,v) {.name = (n), .value = (v)} 131struct env_var env_vars[] = { 132 ENVV("board_name", CONFIG_BOARD_NAME), 133 ENVV(NULL, NULL) 134}; 135#endif 136 137static void halt(void) 138{ 139 printf("\nSystem halted!\n"); 140 for(;;); 141} 142 143#if (LZMA_WRAPPER) 144extern unsigned char _lzma_data_start[]; 145extern unsigned char _lzma_data_end[]; 146 147unsigned char *data; 148unsigned long datalen; 149 150static __inline__ unsigned char get_byte(void) 151{ 152 datalen--; 153 return *data++; 154} 155 156static void decompress_init(void) 157{ 158 data = _lzma_data_start; 159 datalen = _lzma_data_end - _lzma_data_start; 160} 161 162static int decompress_data(CLzmaDecoderState *vs, unsigned char *outStream, 163 SizeT outSize) 164{ 165 SizeT ip, op; 166 167 return LzmaDecode(vs, data, datalen, &ip, outStream, outSize, &op); 168} 169#endif /* LZMA_WRAPPER */ 170 171#if !(LZMA_WRAPPER) 172 173#define FLASH_BANK_SIZE (2<<20) 174 175static unsigned char *flash_base = (unsigned char *) KSEG1ADDR(ADM5120_FLASH_START); 176static unsigned long flash_ofs = 0; 177static unsigned long flash_max = 0; 178static unsigned long flash_ofs_mask = (FLASH_BANK_SIZE-1); 179 180static __inline__ unsigned char get_byte(void) 181{ 182 return *(flash_base+flash_ofs++); 183} 184 185static int lzma_read_byte(void *object, const unsigned char **buffer, 186 SizeT *bufferSize) 187{ 188 unsigned long len; 189 190 if (flash_ofs >= flash_max) 191 return LZMA_RESULT_DATA_ERROR; 192 193 len = flash_max-flash_ofs; 194 195#if (CONFIG_FLASH_SIZE > FLASH_BANK_SIZE) 196 if (flash_ofs < FLASH_BANK_SIZE) { 197 /* switch to bank 0 */ 198 DBG("lzma_read_byte: switch to bank 0\n"); 199 200 if (len > FLASH_BANK_SIZE-flash_ofs) 201 len = FLASH_BANK_SIZE-flash_ofs; 202 } else { 203 /* switch to bank 1 */ 204 DBG("lzma_read_byte: switch to bank 1\n"); 205 } 206#endif 207 DBG("lzma_read_byte: ofs=%08X, len=%08X\n", flash_ofs, len); 208 209 *buffer = flash_base+(flash_ofs & flash_ofs_mask); 210 *bufferSize = len; 211 flash_ofs += len; 212 213 return LZMA_RESULT_OK; 214} 215 216static ILzmaInCallback lzma_callback = { 217 .Read = lzma_read_byte, 218}; 219 220static __inline__ unsigned int read_le32(void *buf) 221{ 222 unsigned char *p = buf; 223 224 return ((unsigned int)p[0] + ((unsigned int)p[1] << 8) + 225 ((unsigned int)p[2] << 16) +((unsigned int)p[3] << 24)); 226} 227 228static void decompress_init(void) 229{ 230 struct trx_header *hdr = NULL; 231 unsigned long kofs,klen; 232 233 printf("Looking for TRX header... "); 234 /* look for trx header, 32-bit data access */ 235 for (flash_ofs = 0; flash_ofs < FLASH_BANK_SIZE; flash_ofs += TRX_ALIGN) { 236 if (read_le32(&flash_base[flash_ofs]) == TRX_MAGIC) { 237 hdr = (struct trx_header *)&flash_base[flash_ofs]; 238 break; 239 } 240 } 241 242 if (hdr == NULL) { 243 printf("not found!\n"); 244 /* no compressed kernel found, halting */ 245 halt(); 246 } 247 248 /* compressed kernel is in the partition 0 or 1 */ 249 kofs = read_le32(&hdr->offsets[1]); 250 if (kofs == 0 || kofs > 65536) { 251 klen = kofs-read_le32(&hdr->offsets[0]); 252 kofs = read_le32(&hdr->offsets[0]); 253 } else { 254 klen = read_le32(&hdr->offsets[2]); 255 if (klen > kofs) 256 klen -= kofs; 257 else 258 klen = read_le32(&hdr->len)-kofs; 259 } 260 261 printf("found at %08X, kernel:%08X len:%08X\n", flash_ofs, 262 kofs, klen); 263 264 flash_ofs += kofs; 265 flash_max = flash_ofs+klen; 266} 267 268static int decompress_data(CLzmaDecoderState *vs, unsigned char *outStream, 269 SizeT outSize) 270{ 271 SizeT op; 272 273#if 0 274 vs->Buffer = data; 275 vs->BufferLim = datalen; 276#endif 277 278 return LzmaDecode(vs, &lzma_callback, outStream, outSize, &op); 279} 280#endif /* !(LZMA_WRAPPER) */ 281 282/* should be the first function */ 283void decompress_entry(unsigned long reg_a0, unsigned long reg_a1, 284 unsigned long reg_a2, unsigned long reg_a3, 285 unsigned long icache_size, unsigned long icache_lsize, 286 unsigned long dcache_size, unsigned long dcache_lsize) 287{ 288 unsigned char props[LZMA_PROPERTIES_SIZE]; 289 unsigned int i; /* temp value */ 290 SizeT osize; /* uncompressed size */ 291 int res; 292 293 board_init(); 294 295 printf("\n\nLZMA loader for " CONFIG_BOARD_NAME 296 ", Copyright (C) 2007-2008 OpenWrt.org\n\n"); 297 298 decompress_init(); 299 300 /* lzma args */ 301 for (i = 0; i < LZMA_PROPERTIES_SIZE; i++) 302 props[i] = get_byte(); 303 304 /* skip rest of the LZMA coder property */ 305 /* read the lower half of uncompressed size in the header */ 306 osize = ((SizeT)get_byte()) + 307 ((SizeT)get_byte() << 8) + 308 ((SizeT)get_byte() << 16) + 309 ((SizeT)get_byte() << 24); 310 311 /* skip rest of the header (upper half of uncompressed size) */ 312 for (i = 0; i < 4; i++) 313 get_byte(); 314 315 res = LzmaDecodeProperties(&lzma_state.Properties, props, 316 LZMA_PROPERTIES_SIZE); 317 if (res != LZMA_RESULT_OK) { 318 printf("Incorrect LZMA stream properties!\n"); 319 halt(); 320 } 321 322 printf("decompressing kernel... "); 323 324 lzma_state.Probs = (CProb *)workspace; 325 res = decompress_data(&lzma_state, (unsigned char *)LOADADDR, osize); 326 327 if (res != LZMA_RESULT_OK) { 328 printf("failed, "); 329 switch (res) { 330 case LZMA_RESULT_DATA_ERROR: 331 printf("data error!\n"); 332 break; 333 default: 334 printf("unknown error %d!\n", res); 335 } 336 halt(); 337 } else 338 printf("done!\n"); 339 340 blast_dcache(dcache_size, dcache_lsize); 341 blast_icache(icache_size, icache_lsize); 342 343 printf("launching kernel...\n\n"); 344 345#ifdef CONFIG_PASS_KARGS 346 reg_a0 = 0; 347 reg_a1 = 0; 348 reg_a2 = (unsigned long)env_vars; 349 reg_a3 = 0; 350#endif 351 /* Jump to load address */ 352 ((kernel_entry) LOADADDR)(reg_a0, reg_a1, reg_a2, reg_a3); 353} 354