openwrtv4/target/linux/brcm63xx/image/lzma-loader/src/loader.c
Jonas Gorski 701e2a38fe brcm63xx: add working lzma-loader and use it for initramfs
Add a working lzma loader and use it for generating initramfs kernels
to allow easily netbooting elf kernels on devices with a 4 MiB CFE
size limit.

Based on ar71xx's lzma-loader.

Signed-off-by: Jonas Gorski <jogo@openwrt.org>

SVN-Revision: 41940
2014-08-01 21:56:31 +00:00

175 lines
4 KiB
C

/*
* LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
*
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
*
* Some parts of this code was based on the OpenWrt specific lzma-loader
* for the BCM47xx and ADM5120 based boards:
* Copyright (C) 2004 Manuel Novoa III (mjn3@codepoet.org)
* Copyright (C) 2005 Mineharu Takahara <mtakahar@yahoo.com>
* Copyright (C) 2005 by Oleg I. Vdovikin <oleg@cs.msu.su>
*
* The image_header structure has been taken from the U-Boot project.
* (C) Copyright 2008 Semihalf
* (C) Copyright 2000-2005
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <stddef.h>
#include <stdint.h>
#include "config.h"
#include "cache.h"
#include "printf.h"
#include "LzmaDecode.h"
#define KSEG0 0x80000000
#define KSEG1 0xa0000000
#define KSEG1ADDR(a) ((((unsigned)(a)) & 0x1fffffffU) | KSEG1)
#undef LZMA_DEBUG
#ifdef LZMA_DEBUG
# define DBG(f, a...) printf(f, ## a)
#else
# define DBG(f, a...) do {} while (0)
#endif
/* beyond the image end, size not known in advance */
extern unsigned char workspace[];
extern void board_init(void);
static CLzmaDecoderState lzma_state;
static unsigned char *lzma_data;
static unsigned long lzma_datasize;
static unsigned long lzma_outsize;
static unsigned long kernel_la;
static void halt(void)
{
printf("\nSystem halted!\n");
for(;;);
}
static __inline__ unsigned char lzma_get_byte(void)
{
unsigned char c;
lzma_datasize--;
c = *lzma_data++;
return c;
}
static int lzma_init_props(void)
{
unsigned char props[LZMA_PROPERTIES_SIZE];
int res;
int i;
/* read lzma properties */
for (i = 0; i < LZMA_PROPERTIES_SIZE; i++)
props[i] = lzma_get_byte();
/* read the lower half of uncompressed size in the header */
lzma_outsize = ((SizeT) lzma_get_byte()) +
((SizeT) lzma_get_byte() << 8) +
((SizeT) lzma_get_byte() << 16) +
((SizeT) lzma_get_byte() << 24);
/* skip rest of the header (upper half of uncompressed size) */
for (i = 0; i < 4; i++)
lzma_get_byte();
res = LzmaDecodeProperties(&lzma_state.Properties, props,
LZMA_PROPERTIES_SIZE);
return res;
}
static int lzma_decompress(unsigned char *outStream)
{
SizeT ip, op;
int ret;
lzma_state.Probs = (CProb *) workspace;
ret = LzmaDecode(&lzma_state, lzma_data, lzma_datasize, &ip, outStream,
lzma_outsize, &op);
if (ret != LZMA_RESULT_OK) {
int i;
DBG("LzmaDecode error %d at %08x, osize:%d ip:%d op:%d\n",
ret, lzma_data + ip, lzma_outsize, ip, op);
for (i = 0; i < 16; i++)
DBG("%02x ", lzma_data[ip + i]);
DBG("\n");
}
return ret;
}
static void lzma_init_data(void)
{
extern unsigned char _lzma_data_start[];
extern unsigned char _lzma_data_end[];
kernel_la = LOADADDR;
lzma_data = _lzma_data_start;
lzma_datasize = _lzma_data_end - _lzma_data_start;
}
void loader_main(unsigned long reg_a0, unsigned long reg_a1,
unsigned long reg_a2, unsigned long reg_a3)
{
void (*kernel_entry) (unsigned long, unsigned long, unsigned long,
unsigned long);
int res;
board_init();
printf("\n\nOpenWrt kernel loader for BCM63XX\n");
printf("Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>\n");
printf("Copyright (C) 2014 Jonas Gorski <jogo@openwrt.org>\n");
lzma_init_data();
res = lzma_init_props();
if (res != LZMA_RESULT_OK) {
printf("Incorrect LZMA stream properties!\n");
halt();
}
printf("Decompressing kernel... ");
res = lzma_decompress((unsigned char *) kernel_la);
if (res != LZMA_RESULT_OK) {
printf("failed, ");
switch (res) {
case LZMA_RESULT_DATA_ERROR:
printf("data error!\n");
break;
default:
printf("unknown error %d!\n", res);
}
halt();
} else {
printf("done!\n");
}
flush_cache(kernel_la, lzma_outsize);
printf("Starting kernel at %08x...\n\n", kernel_la);
kernel_entry = (void *) kernel_la;
kernel_entry(reg_a0, reg_a1, reg_a2, reg_a3);
}