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