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 AR71XX_FLASH_START	0x1f000000
31#define AR71XX_FLASH_END	0x1fe00000
32
33#define KSEG0			0x80000000
34#define KSEG1			0xa0000000
35
36#define KSEG1ADDR(a)		((((unsigned)(a)) & 0x1fffffffU) | KSEG1)
37
38#undef LZMA_DEBUG
39
40#ifdef LZMA_DEBUG
41#  define DBG(f, a...)	printf(f, ## a)
42#else
43#  define DBG(f, a...)	do {} while (0)
44#endif
45
46#define IH_MAGIC_OKLI		0x4f4b4c49	/* 'OKLI' */
47
48#define IH_NMLEN		32	/* Image Name Length		*/
49
50typedef struct image_header {
51	uint32_t	ih_magic;	/* Image Header Magic Number	*/
52	uint32_t	ih_hcrc;	/* Image Header CRC Checksum	*/
53	uint32_t	ih_time;	/* Image Creation Timestamp	*/
54	uint32_t	ih_size;	/* Image Data Size		*/
55	uint32_t	ih_load;	/* Data	 Load  Address		*/
56	uint32_t	ih_ep;		/* Entry Point Address		*/
57	uint32_t	ih_dcrc;	/* Image Data CRC Checksum	*/
58	uint8_t		ih_os;		/* Operating System		*/
59	uint8_t		ih_arch;	/* CPU architecture		*/
60	uint8_t		ih_type;	/* Image Type			*/
61	uint8_t		ih_comp;	/* Compression Type		*/
62	uint8_t		ih_name[IH_NMLEN];	/* Image Name		*/
63} image_header_t;
64
65/* beyond the image end, size not known in advance */
66extern unsigned char workspace[];
67extern void board_init(void);
68
69static CLzmaDecoderState lzma_state;
70static unsigned char *lzma_data;
71static unsigned long lzma_datasize;
72static unsigned long lzma_outsize;
73static unsigned long kernel_la;
74
75#ifdef CONFIG_KERNEL_CMDLINE
76#define kernel_argc	2
77static const char kernel_cmdline[] = CONFIG_KERNEL_CMDLINE;
78static const char *kernel_argv[] = {
79	NULL,
80	kernel_cmdline,
81	NULL,
82};
83#endif /* CONFIG_KERNEL_CMDLINE */
84
85static void halt(void)
86{
87	printf("\nSystem halted!\n");
88	for(;;);
89}
90
91static __inline__ unsigned long get_be32(void *buf)
92{
93	unsigned char *p = buf;
94
95	return (((unsigned long) p[0] << 24) +
96	        ((unsigned long) p[1] << 16) +
97	        ((unsigned long) p[2] << 8) +
98	        (unsigned long) p[3]);
99}
100
101static __inline__ unsigned char lzma_get_byte(void)
102{
103	unsigned char c;
104
105	lzma_datasize--;
106	c = *lzma_data++;
107
108	return c;
109}
110
111static int lzma_init_props(void)
112{
113	unsigned char props[LZMA_PROPERTIES_SIZE];
114	int res;
115	int i;
116
117	/* read lzma properties */
118	for (i = 0; i < LZMA_PROPERTIES_SIZE; i++)
119		props[i] = lzma_get_byte();
120
121	/* read the lower half of uncompressed size in the header */
122	lzma_outsize = ((SizeT) lzma_get_byte()) +
123		       ((SizeT) lzma_get_byte() << 8) +
124		       ((SizeT) lzma_get_byte() << 16) +
125		       ((SizeT) lzma_get_byte() << 24);
126
127	/* skip rest of the header (upper half of uncompressed size) */
128	for (i = 0; i < 4; i++)
129		lzma_get_byte();
130
131	res = LzmaDecodeProperties(&lzma_state.Properties, props,
132					LZMA_PROPERTIES_SIZE);
133	return res;
134}
135
136static int lzma_decompress(unsigned char *outStream)
137{
138	SizeT ip, op;
139	int ret;
140
141	lzma_state.Probs = (CProb *) workspace;
142
143	ret = LzmaDecode(&lzma_state, lzma_data, lzma_datasize, &ip, outStream,
144			 lzma_outsize, &op);
145
146	if (ret != LZMA_RESULT_OK) {
147		int i;
148
149		DBG("LzmaDecode error %d at %08x, osize:%d ip:%d op:%d\n",
150		    ret, lzma_data + ip, lzma_outsize, ip, op);
151
152		for (i = 0; i < 16; i++)
153			DBG("%02x ", lzma_data[ip + i]);
154
155		DBG("\n");
156	}
157
158	return ret;
159}
160
161#if (LZMA_WRAPPER)
162static void lzma_init_data(void)
163{
164	extern unsigned char _lzma_data_start[];
165	extern unsigned char _lzma_data_end[];
166
167	kernel_la = LOADADDR;
168	lzma_data = _lzma_data_start;
169	lzma_datasize = _lzma_data_end - _lzma_data_start;
170}
171#else
172static void lzma_init_data(void)
173{
174	struct image_header *hdr = NULL;
175	unsigned char *flash_base;
176	unsigned long flash_ofs;
177	unsigned long kernel_ofs;
178	unsigned long kernel_size;
179
180	flash_base = (unsigned char *) KSEG1ADDR(AR71XX_FLASH_START);
181
182	printf("Looking for OpenWrt image... ");
183
184	for (flash_ofs = CONFIG_FLASH_OFFS;
185	     flash_ofs <= (CONFIG_FLASH_OFFS + CONFIG_FLASH_MAX);
186	     flash_ofs += CONFIG_FLASH_STEP) {
187		unsigned long magic;
188		unsigned char *p;
189
190		p = flash_base + flash_ofs;
191		magic = get_be32(p);
192		if (magic == IH_MAGIC_OKLI) {
193			hdr = (struct image_header *) p;
194			break;
195		}
196	}
197
198	if (hdr == NULL) {
199		printf("not found!\n");
200		halt();
201	}
202
203	printf("found at 0x%08x\n", flash_base + flash_ofs);
204
205	kernel_ofs = sizeof(struct image_header);
206	kernel_size = get_be32(&hdr->ih_size);
207	kernel_la = get_be32(&hdr->ih_load);
208
209	lzma_data = flash_base + flash_ofs + kernel_ofs;
210	lzma_datasize = kernel_size;
211}
212#endif /* (LZMA_WRAPPER) */
213
214void loader_main(unsigned long reg_a0, unsigned long reg_a1,
215		 unsigned long reg_a2, unsigned long reg_a3)
216{
217	void (*kernel_entry) (unsigned long, unsigned long, unsigned long,
218			      unsigned long);
219	int res;
220
221	board_init();
222
223	printf("\n\nOpenWrt kernel loader for AR7XXX/AR9XXX\n");
224	printf("Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>\n");
225
226	lzma_init_data();
227
228	res = lzma_init_props();
229	if (res != LZMA_RESULT_OK) {
230		printf("Incorrect LZMA stream properties!\n");
231		halt();
232	}
233
234	printf("Decompressing kernel... ");
235
236	res = lzma_decompress((unsigned char *) kernel_la);
237	if (res != LZMA_RESULT_OK) {
238		printf("failed, ");
239		switch (res) {
240		case LZMA_RESULT_DATA_ERROR:
241			printf("data error!\n");
242			break;
243		default:
244			printf("unknown error %d!\n", res);
245		}
246		halt();
247	} else {
248		printf("done!\n");
249	}
250
251	flush_cache(kernel_la, lzma_outsize);
252
253	printf("Starting kernel at %08x...\n\n", kernel_la);
254
255#ifdef CONFIG_KERNEL_CMDLINE
256	reg_a0 = kernel_argc;
257	reg_a1 = (unsigned long) kernel_argv;
258	reg_a2 = 0;
259	reg_a3 = 0;
260#endif
261
262	kernel_entry = (void *) kernel_la;
263	kernel_entry(reg_a0, reg_a1, reg_a2, reg_a3);
264}
265