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 */
44extern unsigned char workspace[];
45
46
47extern void board_init(void);
48
49static CLzmaDecoderState lzma_state;
50static unsigned char *lzma_data;
51static unsigned long lzma_datasize;
52static unsigned long lzma_outsize;
53static unsigned long kernel_la;
54
55static void halt(void)
56{
57	printf("\nSystem halted!\n");
58	for(;;);
59}
60
61static __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
71static 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
96static 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
121static 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
131void 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}
176