brcm63xx: add working lzma-loader and use it for initramfs
[librecmc/librecmc.git] / target / linux / brcm63xx / image / lzma-loader / src / loader.c
1 /*
2  * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
3  *
4  * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
5  *
6  * Some parts of this code was based on the OpenWrt specific lzma-loader
7  * for the BCM47xx and ADM5120 based boards:
8  *      Copyright (C) 2004 Manuel Novoa III (mjn3@codepoet.org)
9  *      Copyright (C) 2005 Mineharu Takahara <mtakahar@yahoo.com>
10  *      Copyright (C) 2005 by Oleg I. Vdovikin <oleg@cs.msu.su>
11  *
12  * The image_header structure has been taken from the U-Boot project.
13  *      (C) Copyright 2008 Semihalf
14  *      (C) Copyright 2000-2005
15  *      Wolfgang Denk, DENX Software Engineering, wd@denx.de.
16  *
17  * This program is free software; you can redistribute it and/or modify it
18  * under the terms of the GNU General Public License version 2 as published
19  * by the Free Software Foundation.
20  */
21
22 #include <stddef.h>
23 #include <stdint.h>
24
25 #include "config.h"
26 #include "cache.h"
27 #include "printf.h"
28 #include "LzmaDecode.h"
29
30 #define KSEG0                   0x80000000
31 #define KSEG1                   0xa0000000
32
33 #define KSEG1ADDR(a)            ((((unsigned)(a)) & 0x1fffffffU) | KSEG1)
34
35 #undef LZMA_DEBUG
36
37 #ifdef LZMA_DEBUG
38 #  define DBG(f, a...)  printf(f, ## a)
39 #else
40 #  define DBG(f, a...)  do {} while (0)
41 #endif
42
43 /* beyond the image end, size not known in advance */
44 extern unsigned char workspace[];
45
46
47 extern void board_init(void);
48
49 static CLzmaDecoderState lzma_state;
50 static unsigned char *lzma_data;
51 static unsigned long lzma_datasize;
52 static unsigned long lzma_outsize;
53 static unsigned long kernel_la;
54
55 static void halt(void)
56 {
57         printf("\nSystem halted!\n");
58         for(;;);
59 }
60
61 static __inline__ unsigned char lzma_get_byte(void)
62 {
63         unsigned char c;
64
65         lzma_datasize--;
66         c = *lzma_data++;
67
68         return c;
69 }
70
71 static int lzma_init_props(void)
72 {
73         unsigned char props[LZMA_PROPERTIES_SIZE];
74         int res;
75         int i;
76
77         /* read lzma properties */
78         for (i = 0; i < LZMA_PROPERTIES_SIZE; i++)
79                 props[i] = lzma_get_byte();
80
81         /* read the lower half of uncompressed size in the header */
82         lzma_outsize = ((SizeT) lzma_get_byte()) +
83                        ((SizeT) lzma_get_byte() << 8) +
84                        ((SizeT) lzma_get_byte() << 16) +
85                        ((SizeT) lzma_get_byte() << 24);
86
87         /* skip rest of the header (upper half of uncompressed size) */
88         for (i = 0; i < 4; i++)
89                 lzma_get_byte();
90
91         res = LzmaDecodeProperties(&lzma_state.Properties, props,
92                                         LZMA_PROPERTIES_SIZE);
93         return res;
94 }
95
96 static int lzma_decompress(unsigned char *outStream)
97 {
98         SizeT ip, op;
99         int ret;
100
101         lzma_state.Probs = (CProb *) workspace;
102
103         ret = LzmaDecode(&lzma_state, lzma_data, lzma_datasize, &ip, outStream,
104                          lzma_outsize, &op);
105
106         if (ret != LZMA_RESULT_OK) {
107                 int i;
108
109                 DBG("LzmaDecode error %d at %08x, osize:%d ip:%d op:%d\n",
110                     ret, lzma_data + ip, lzma_outsize, ip, op);
111
112                 for (i = 0; i < 16; i++)
113                         DBG("%02x ", lzma_data[ip + i]);
114
115                 DBG("\n");
116         }
117
118         return ret;
119 }
120
121 static void lzma_init_data(void)
122 {
123         extern unsigned char _lzma_data_start[];
124         extern unsigned char _lzma_data_end[];
125
126         kernel_la = LOADADDR;
127         lzma_data = _lzma_data_start;
128         lzma_datasize = _lzma_data_end - _lzma_data_start;
129 }
130
131 void loader_main(unsigned long reg_a0, unsigned long reg_a1,
132                  unsigned long reg_a2, unsigned long reg_a3)
133 {
134         void (*kernel_entry) (unsigned long, unsigned long, unsigned long,
135                               unsigned long);
136         int res;
137
138         board_init();
139
140         printf("\n\nOpenWrt kernel loader for BCM63XX\n");
141         printf("Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>\n");
142         printf("Copyright (C) 2014 Jonas Gorski <jogo@openwrt.org>\n");
143
144         lzma_init_data();
145
146         res = lzma_init_props();
147         if (res != LZMA_RESULT_OK) {
148                 printf("Incorrect LZMA stream properties!\n");
149                 halt();
150         }
151
152         printf("Decompressing kernel... ");
153
154         res = lzma_decompress((unsigned char *) kernel_la);
155         if (res != LZMA_RESULT_OK) {
156                 printf("failed, ");
157                 switch (res) {
158                 case LZMA_RESULT_DATA_ERROR:
159                         printf("data error!\n");
160                         break;
161                 default:
162                         printf("unknown error %d!\n", res);
163                 }
164                 halt();
165         } else {
166                 printf("done!\n");
167         }
168
169         flush_cache(kernel_la, lzma_outsize);
170
171         printf("Starting kernel at %08x...\n\n", kernel_la);
172
173         kernel_entry = (void *) kernel_la;
174         kernel_entry(reg_a0, reg_a1, reg_a2, reg_a3);
175 }