get rid of $Id$ - it has never helped us and it has broken too many patches ;)
[openwrt.git] / target / linux / adm5120 / image / lzma-loader / src / decompress.c
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
79 static __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
90 static __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
104 struct 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
112 struct env_var {
113         char    *name;
114         char    *value;
115 };
116
117 /* beyound the image end, size not known in advance */
118 extern unsigned char workspace[];
119 extern void board_init(void);
120
121 static CLzmaDecoderState lzma_state;
122
123 typedef void (*kernel_entry)(unsigned long reg_a0, unsigned long reg_a1,
124         unsigned long reg_a2, unsigned long reg_a3);
125
126 static 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)}
131 struct env_var env_vars[] = {
132         ENVV("board_name",      CONFIG_BOARD_NAME),
133         ENVV(NULL, NULL)
134 };
135 #endif
136
137 static void halt(void)
138 {
139         printf("\nSystem halted!\n");
140         for(;;);
141 }
142
143 #if (LZMA_WRAPPER)
144 extern unsigned char _lzma_data_start[];
145 extern unsigned char _lzma_data_end[];
146
147 unsigned char *data;
148 unsigned long datalen;
149
150 static __inline__ unsigned char get_byte(void)
151 {
152         datalen--;
153         return *data++;
154 }
155
156 static void decompress_init(void)
157 {
158         data = _lzma_data_start;
159         datalen = _lzma_data_end - _lzma_data_start;
160 }
161
162 static 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
175 static unsigned char *flash_base = (unsigned char *) KSEG1ADDR(ADM5120_FLASH_START);
176 static unsigned long flash_ofs = 0;
177 static unsigned long flash_max = 0;
178 static unsigned long flash_ofs_mask = (FLASH_BANK_SIZE-1);
179
180 static __inline__ unsigned char get_byte(void)
181 {
182         return *(flash_base+flash_ofs++);
183 }
184
185 static 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
216 static ILzmaInCallback lzma_callback = {
217         .Read   = lzma_read_byte,
218 };
219
220 static __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
228 static 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
268 static 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 */
283 void 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 }