| 1 | /* |
|---|
| 2 | * misc_lzma.c |
|---|
| 3 | * originally written for xscale based linux kernel decompression |
|---|
| 4 | * now adapted for AR531X based redboot stub and kernel loader |
|---|
| 5 | * copyright 2009 Sebastian Gottschall / NewMedia-NET GmbH / DD-WRT.COM |
|---|
| 6 | * licensed under GPL conditions |
|---|
| 7 | * this stub will load and decompress redboot if the reset button is pushed, otherwise it parses the redboot directory for a partition named linux*, vmlinux* or kernel* |
|---|
| 8 | * if such a partition has been found, it will be decompressed and executed, if not. redboot is started. if a decompression error occures while loading the linux partition, |
|---|
| 9 | * the redboot is started too. |
|---|
| 10 | * take care about the ramconfig.h header since it must contain the correct ram size and gpio button value for the reset button |
|---|
| 11 | * this code is partially based on redboot and linux sources |
|---|
| 12 | */ |
|---|
| 13 | |
|---|
| 14 | #ifdef STANDALONE_DEBUG |
|---|
| 15 | #define putstr printf |
|---|
| 16 | #else |
|---|
| 17 | |
|---|
| 18 | #include <linux/kernel.h> |
|---|
| 19 | |
|---|
| 20 | #include <asm/uaccess.h> |
|---|
| 21 | #include "ramconfig.h" |
|---|
| 22 | #include "uncompress.h" |
|---|
| 23 | #include "lib/lib.c" |
|---|
| 24 | #include "lib/print.c" |
|---|
| 25 | #include "lib/printf.c" |
|---|
| 26 | |
|---|
| 27 | #endif |
|---|
| 28 | |
|---|
| 29 | #define __ptr_t void * |
|---|
| 30 | |
|---|
| 31 | typedef unsigned char uch; |
|---|
| 32 | typedef unsigned short ush; |
|---|
| 33 | typedef unsigned long ulg; |
|---|
| 34 | |
|---|
| 35 | static uch *inbuf; /* input buffer */ |
|---|
| 36 | static unsigned int nvramdetect = 0; |
|---|
| 37 | |
|---|
| 38 | static unsigned insize = 0; /* valid bytes in inbuf */ |
|---|
| 39 | static unsigned inptr = 0; /* index of next byte to be processed in inbuf */ |
|---|
| 40 | static unsigned outcnt; /* bytes in output buffer */ |
|---|
| 41 | |
|---|
| 42 | static void fill_inbuf(void); |
|---|
| 43 | |
|---|
| 44 | static inline unsigned char get_byte(void) |
|---|
| 45 | { |
|---|
| 46 | static unsigned int vall; |
|---|
| 47 | |
|---|
| 48 | if (((unsigned int)inptr % 4) == 0) { |
|---|
| 49 | vall = *(unsigned int *)inbuf; |
|---|
| 50 | inbuf += 4; |
|---|
| 51 | } |
|---|
| 52 | return *(((unsigned char *)&vall) + (inptr++ & 3)); |
|---|
| 53 | } |
|---|
| 54 | |
|---|
| 55 | //#define get_byte() (inptr < insize ? inbuf[inptr++] : fill_inbuf()) |
|---|
| 56 | |
|---|
| 57 | static void flush_window(void); |
|---|
| 58 | static void error(char *m); |
|---|
| 59 | |
|---|
| 60 | extern unsigned char input_data[]; |
|---|
| 61 | extern unsigned char input_data_end[]; |
|---|
| 62 | |
|---|
| 63 | static ulg output_ptr = 0; |
|---|
| 64 | static uch *output_data; |
|---|
| 65 | static ulg bytes_out; |
|---|
| 66 | |
|---|
| 67 | extern int end; |
|---|
| 68 | static ulg free_mem_ptr; |
|---|
| 69 | static ulg free_mem_ptr_end; |
|---|
| 70 | |
|---|
| 71 | #define _LZMA_IN_CB |
|---|
| 72 | |
|---|
| 73 | #include "lib/LzmaDecode.h" |
|---|
| 74 | static unsigned int icnt = 0; |
|---|
| 75 | static inline int read_byte(unsigned char **buffer, UInt32 * bufferSize) |
|---|
| 76 | { |
|---|
| 77 | static unsigned char val; |
|---|
| 78 | *bufferSize = 1; |
|---|
| 79 | val = get_byte(); |
|---|
| 80 | *buffer = &val; |
|---|
| 81 | if (icnt++ % (1024 * 10) == 0) |
|---|
| 82 | putc('.'); |
|---|
| 83 | return LZMA_RESULT_OK; |
|---|
| 84 | } |
|---|
| 85 | |
|---|
| 86 | #include "lib/LzmaDecode.c" |
|---|
| 87 | |
|---|
| 88 | int bootoffset = 0x800004bc; |
|---|
| 89 | |
|---|
| 90 | /* |
|---|
| 91 | * Do the lzma decompression |
|---|
| 92 | */ |
|---|
| 93 | |
|---|
| 94 | static int disaster = 0; |
|---|
| 95 | static int lzma_unzip(void) |
|---|
| 96 | { |
|---|
| 97 | |
|---|
| 98 | unsigned int i; |
|---|
| 99 | unsigned int uncompressedSize = 0; |
|---|
| 100 | unsigned char *workspace; |
|---|
| 101 | unsigned int lc, lp, pb; |
|---|
| 102 | if (inptr >= insize) |
|---|
| 103 | fill_inbuf(); |
|---|
| 104 | |
|---|
| 105 | // lzma args |
|---|
| 106 | i = get_byte(); |
|---|
| 107 | lc = i % 9, i = i / 9; |
|---|
| 108 | lp = i % 5, pb = i / 5; |
|---|
| 109 | |
|---|
| 110 | // skip dictionary size |
|---|
| 111 | for (i = 0; i < 4; i++) |
|---|
| 112 | get_byte(); |
|---|
| 113 | // get uncompressed size |
|---|
| 114 | int a, b, c, d; |
|---|
| 115 | a = get_byte(); |
|---|
| 116 | b = get_byte(); |
|---|
| 117 | c = get_byte(); |
|---|
| 118 | d = get_byte(); |
|---|
| 119 | uncompressedSize = (a) + (b << 8) + (c << 16) + (d << 24); |
|---|
| 120 | if (uncompressedSize > 0x400000 || lc > 3 || pb > 3 || lp > 3) { |
|---|
| 121 | if (disaster) { |
|---|
| 122 | error |
|---|
| 123 | ("\ndata corrupted in recovery RedBoot too, this is a disaster condition. please re-jtag\n"); |
|---|
| 124 | } |
|---|
| 125 | disaster = 1; |
|---|
| 126 | puts("\ndata corrupted!\nswitching to recovery RedBoot\nloading"); |
|---|
| 127 | inbuf = input_data; |
|---|
| 128 | insize = &input_data_end[0] - &input_data[0]; |
|---|
| 129 | inptr = 0; |
|---|
| 130 | output_data = (uch *) 0x80000400; |
|---|
| 131 | bootoffset = 0x800004bc; |
|---|
| 132 | return lzma_unzip(); |
|---|
| 133 | |
|---|
| 134 | } |
|---|
| 135 | workspace = output_data + uncompressedSize; |
|---|
| 136 | // skip high order bytes |
|---|
| 137 | for (i = 0; i < 4; i++) |
|---|
| 138 | get_byte(); |
|---|
| 139 | // decompress kernel |
|---|
| 140 | if (LzmaDecode |
|---|
| 141 | (workspace, ~0, lc, lp, pb, (unsigned char *)output_data, |
|---|
| 142 | uncompressedSize, &i) == LZMA_RESULT_OK) { |
|---|
| 143 | if (i != uncompressedSize) { |
|---|
| 144 | if (disaster) { |
|---|
| 145 | error |
|---|
| 146 | ("data corrupted in recovery RedBoot too, this is a disaster condition. please re-jtag\n"); |
|---|
| 147 | } |
|---|
| 148 | disaster = 1; |
|---|
| 149 | puts("\ndata corrupted!\nswitching to recovery RedBoot\nloading"); |
|---|
| 150 | inbuf = input_data; |
|---|
| 151 | insize = &input_data_end[0] - &input_data[0]; |
|---|
| 152 | inptr = 0; |
|---|
| 153 | output_data = (uch *) 0x80000400; |
|---|
| 154 | bootoffset = 0x800004bc; |
|---|
| 155 | return lzma_unzip(); |
|---|
| 156 | } |
|---|
| 157 | //copy it back to low_buffer |
|---|
| 158 | bytes_out = i; |
|---|
| 159 | output_ptr = i; |
|---|
| 160 | return 0; |
|---|
| 161 | } |
|---|
| 162 | return 1; |
|---|
| 163 | } |
|---|
| 164 | |
|---|
| 165 | #ifdef AR5312 |
|---|
| 166 | #include "arch/ar5312.c" |
|---|
| 167 | #else |
|---|
| 168 | #include "arch/ar2315.c" |
|---|
| 169 | #endif |
|---|
| 170 | |
|---|
| 171 | #include "lib/fis.c" |
|---|
| 172 | /* =========================================================================== |
|---|
| 173 | * Fill the input buffer. This is called only when the buffer is empty |
|---|
| 174 | * and at least one byte is really needed. |
|---|
| 175 | */ |
|---|
| 176 | static int resettrigger = 0; |
|---|
| 177 | |
|---|
| 178 | static void fill_inbuf(void) |
|---|
| 179 | { |
|---|
| 180 | if (insize != 0) |
|---|
| 181 | error("ran out of input data"); |
|---|
| 182 | if (resettrigger) { |
|---|
| 183 | inbuf = (uch *) linuxaddr; |
|---|
| 184 | insize = 0x400000; |
|---|
| 185 | inptr = 0; |
|---|
| 186 | } else { |
|---|
| 187 | inbuf = input_data; |
|---|
| 188 | insize = &input_data_end[0] - &input_data[0]; |
|---|
| 189 | inptr = 0; |
|---|
| 190 | } |
|---|
| 191 | return; |
|---|
| 192 | } |
|---|
| 193 | |
|---|
| 194 | /* =========================================================================== |
|---|
| 195 | * Write the output window window[0..outcnt-1] and update crc and bytes_out. |
|---|
| 196 | * (Used for the decompressed data only.) |
|---|
| 197 | */ |
|---|
| 198 | |
|---|
| 199 | #ifndef arch_error |
|---|
| 200 | #define arch_error(x) |
|---|
| 201 | #endif |
|---|
| 202 | |
|---|
| 203 | static void error(char *x) |
|---|
| 204 | { |
|---|
| 205 | arch_error(x); |
|---|
| 206 | |
|---|
| 207 | printf("\n\n%s\n\n -- System halted", x); |
|---|
| 208 | |
|---|
| 209 | while (1) ; /* Halt */ |
|---|
| 210 | } |
|---|
| 211 | |
|---|
| 212 | /* |
|---|
| 213 | * checks if the reset button is pressed, return 1 if the button is pressed and 0 if not |
|---|
| 214 | */ |
|---|
| 215 | static int resetTouched(void) |
|---|
| 216 | { |
|---|
| 217 | int trigger = getGPIO(RESETBUTTON & 0x0f); |
|---|
| 218 | if (RESETBUTTON & 0xf0) |
|---|
| 219 | trigger = 1 - trigger; |
|---|
| 220 | return trigger; |
|---|
| 221 | } |
|---|
| 222 | |
|---|
| 223 | #include <lib/nvram.c> |
|---|
| 224 | |
|---|
| 225 | ulg |
|---|
| 226 | decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p) |
|---|
| 227 | { |
|---|
| 228 | output_data = (uch *) output_start; |
|---|
| 229 | free_mem_ptr = free_mem_ptr_p; |
|---|
| 230 | free_mem_ptr_end = free_mem_ptr_end_p; |
|---|
| 231 | disable_watchdog(); |
|---|
| 232 | arch_decomp_setup(); |
|---|
| 233 | printf("MicroRedBoot v1.3, (c) 2009 DD-WRT.COM (%s)\n", __DATE__); |
|---|
| 234 | nvram_init(); |
|---|
| 235 | char *ddboard = nvram_get("DD_BOARD"); |
|---|
| 236 | if (ddboard) |
|---|
| 237 | printf("Board: %s\n", ddboard); |
|---|
| 238 | char *resetbutton = nvram_get("resetbutton_enable"); |
|---|
| 239 | if (resetbutton && !strcmp(resetbutton, "1")) |
|---|
| 240 | puts("reset button manual override detected! (nvram var resetbutton_enable=1)\n"); |
|---|
| 241 | if (resetTouched() || (resetbutton && !strcmp(resetbutton, "1"))) { |
|---|
| 242 | puts("Reset Button triggered\nBooting Recovery RedBoot\n"); |
|---|
| 243 | |
|---|
| 244 | int count = 5; |
|---|
| 245 | while (count--) { |
|---|
| 246 | if (!resetTouched()) // check if reset button is unpressed again |
|---|
| 247 | break; |
|---|
| 248 | udelay(1000000); |
|---|
| 249 | } |
|---|
| 250 | if (count <= 0) { |
|---|
| 251 | puts("reset button 5 seconds pushed, erasing nvram\n"); |
|---|
| 252 | |
|---|
| 253 | if (!flashdetect()) |
|---|
| 254 | flash_erase_nvram(flashsize, sectorsize); |
|---|
| 255 | } |
|---|
| 256 | |
|---|
| 257 | bootoffset = 0x800004bc; |
|---|
| 258 | resettrigger = 0; |
|---|
| 259 | } else { |
|---|
| 260 | flashdetect(); |
|---|
| 261 | linuxaddr = getLinux(); |
|---|
| 262 | puts("Booting Linux\n"); |
|---|
| 263 | resettrigger = 1; |
|---|
| 264 | /* initialize clock */ |
|---|
| 265 | HAL_CLOCK_INITIALIZE(RTC_PERIOD); |
|---|
| 266 | |
|---|
| 267 | /* important, enable ethernet bus, if the following lines are not initialized linux will not be able to use the ethernet mac, taken from redboot source */ |
|---|
| 268 | enable_ethernet(); |
|---|
| 269 | } |
|---|
| 270 | puts("loading"); |
|---|
| 271 | lzma_unzip(); |
|---|
| 272 | puts("\n\n\n"); |
|---|
| 273 | |
|---|
| 274 | return output_ptr; |
|---|
| 275 | } |
|---|