OpenWrt – Rev 1

Subversion Repositories:
Rev:
/*
 * 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);
}