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