|
|
@@ -10,6 +10,7 @@
|
|
|
* Copyright (C) 2011 Gabor Juhos <[email protected]>
|
|
|
*/
|
|
|
|
|
|
+#include <stdbool.h>
|
|
|
#include "board.h"
|
|
|
#include "globals.h"
|
|
|
#include "memory.h"
|
|
|
@@ -27,31 +28,41 @@
|
|
|
extern void *_kernel_load_addr;
|
|
|
extern void *_kernel_data_addr;
|
|
|
extern int _kernel_data_size;
|
|
|
+extern int _kernel_comp_type;
|
|
|
extern void *_my_load_addr;
|
|
|
extern int _my_load_size;
|
|
|
+extern int _my_run_count;
|
|
|
|
|
|
-extern int unlzma(unsigned char *buf, long in_len,
|
|
|
- long (*fill)(void*, unsigned long),
|
|
|
- long (*flush)(void*, unsigned long),
|
|
|
- unsigned char *output,
|
|
|
- long *outlen,
|
|
|
- long *posp,
|
|
|
- void(*error)(char *x));
|
|
|
+extern int unlzma(unsigned char *buf, long in_len, long (*fill)(void*, unsigned long),
|
|
|
+ long (*flush)(void*, unsigned long), unsigned char *output,
|
|
|
+ long *outlen, long *posp, void(*error)(char *x));
|
|
|
|
|
|
typedef void (*entry_func_t)(unsigned long reg_a0, unsigned long reg_a1,
|
|
|
unsigned long reg_a2, unsigned long reg_a3);
|
|
|
|
|
|
+
|
|
|
+static bool is_uimage(void *m)
|
|
|
+{
|
|
|
+ unsigned int data[UIMAGE_HDR_SIZE / sizeof(int)];
|
|
|
+ unsigned int image_crc;
|
|
|
+
|
|
|
+ memcpy(data, m, UIMAGE_HDR_SIZE);
|
|
|
+ image_crc = data[1];
|
|
|
+ data[1] = 0;
|
|
|
+
|
|
|
+ return image_crc == crc32(data, UIMAGE_HDR_SIZE);
|
|
|
+}
|
|
|
+
|
|
|
void *relocate(void *src, int len)
|
|
|
{
|
|
|
void *addr;
|
|
|
unsigned int offs;
|
|
|
|
|
|
/*
|
|
|
- * Relocate to highest possible memory address. This is usually the RAM size minus some
|
|
|
- * space for the heap and the stack pointer. As we do not have any highmem features
|
|
|
- * limit this to 256MB.
|
|
|
+ * Relocate to highest possible memory address. This is usually the RAM size
|
|
|
+ * minus some space for the heap and the stack pointer. As we do not have any
|
|
|
+ * highmem features limit this to 256MB.
|
|
|
*/
|
|
|
-
|
|
|
offs = (board_get_memory() - STACK_SIZE - HEAP_SIZE - len - 1024) & 0xfff0000;
|
|
|
addr = (void *)KSEG0 + offs;
|
|
|
|
|
|
@@ -81,43 +92,109 @@ void decompress_error(char *x)
|
|
|
void *decompress(void *out, void *in, int len)
|
|
|
{
|
|
|
long outlen;
|
|
|
+ int ret = 1;
|
|
|
+
|
|
|
+ printf("Extract image with %d bytes from 0x%08x to 0x%08x ...\n", len, in, out);
|
|
|
+
|
|
|
+ switch (_kernel_comp_type) {
|
|
|
+ case UIMAGE_COMP_LZMA:
|
|
|
+ ret = unlzma(in, len, 0, 0, out, &outlen, 0, decompress_error);
|
|
|
+ break;
|
|
|
+ case UIMAGE_COMP_NONE:
|
|
|
+ memcpy(out, in, len);
|
|
|
+ outlen = len;
|
|
|
+ ret = 0;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ printf("Unknown uImage compression type %d\n", _kernel_comp_type);
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- printf("Extract kernel with %d bytes from 0x%08x to 0x%08x ...\n", len, in, out);
|
|
|
-
|
|
|
- if (unlzma(in, len, 0, 0, out, &outlen, 0, decompress_error))
|
|
|
+ if (ret)
|
|
|
board_panic();
|
|
|
|
|
|
- printf("Extracted kernel size is %d bytes\n", outlen);
|
|
|
+ printf("Final kernel size is %d bytes\n", outlen);
|
|
|
flush_cache(out, outlen);
|
|
|
|
|
|
return out;
|
|
|
}
|
|
|
|
|
|
+void search_image(void **flash_addr, int *flash_size, void **load_addr)
|
|
|
+{
|
|
|
+ unsigned char *addr = *flash_addr;
|
|
|
+ unsigned int image_size = 0;
|
|
|
+ unsigned int *maxaddr;
|
|
|
+
|
|
|
+ printf("Searching for uImage starting at 0x%08x ...\n", addr);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The most basic way to find a uImage is to lookup the operating system
|
|
|
+ * opcode (for Linux it is 5). Then verify the header checksum. This is
|
|
|
+ * reasonably fast and all other magic value or constants can be avoided.
|
|
|
+ */
|
|
|
+ *flash_addr = NULL;
|
|
|
+ for (int i = 0; i < 256 * 1024; i += 4, addr += 4) {
|
|
|
+ if ((addr[28] == UIMAGE_OS_LINUX) && is_uimage(addr)) {
|
|
|
+ *flash_addr = addr;
|
|
|
+ *flash_size = *(int *)(addr + 12);
|
|
|
+ *load_addr = *(void **)(addr + 16);
|
|
|
+ _kernel_comp_type = addr[31];
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void load_kernel(void *flash_start)
|
|
|
+{
|
|
|
+ void *flash_addr = flash_start;
|
|
|
+
|
|
|
+ search_image(&flash_addr, &_kernel_data_size, &_kernel_load_addr);
|
|
|
+ _kernel_data_addr = _my_load_addr - _kernel_data_size - 1024;
|
|
|
+
|
|
|
+ if (!flash_addr) {
|
|
|
+ printf("Kernel uImage not found\n");
|
|
|
+ board_panic();
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("uImage '%s' found at 0x%08x with load address 0x%08x\n",
|
|
|
+ (char *)(flash_addr + 32), flash_addr, _kernel_load_addr);
|
|
|
+ printf("Copy %d bytes of image data to 0x%08x ...\n",
|
|
|
+ _kernel_data_size, _kernel_data_addr);
|
|
|
+
|
|
|
+ memcpy(_kernel_data_addr, flash_addr + UIMAGE_HDR_SIZE, _kernel_data_size);
|
|
|
+}
|
|
|
+
|
|
|
void main(unsigned long reg_a0, unsigned long reg_a1,
|
|
|
unsigned long reg_a2, unsigned long reg_a3)
|
|
|
{
|
|
|
+ void *flash_start = (void *)FLASH_ADDR; /* from makefile */
|
|
|
entry_func_t fn;
|
|
|
|
|
|
- if (_kernel_load_addr == _my_load_addr) {
|
|
|
- /*
|
|
|
- * During first run relocate the whole package to the end of memory. Use
|
|
|
- * _my_load_size as relocation length. That includes the bss section, aka
|
|
|
- * uninitialized globals. So it is possible to initialize globals during
|
|
|
- * first run and have them at hand after relocation.
|
|
|
- */
|
|
|
-
|
|
|
+ /*
|
|
|
+ * During first run relocate the whole package to the end of memory. Use
|
|
|
+ * _my_load_size as relocation length. That includes the bss section, aka
|
|
|
+ * uninitialized globals. So it is possible to initialize globals during
|
|
|
+ * first run and have them at hand after relocation.
|
|
|
+ */
|
|
|
+ if (_my_run_count == 1) {
|
|
|
welcome();
|
|
|
fn = relocate(_my_load_addr, _my_load_size);
|
|
|
fn(reg_a0, reg_a1, reg_a2, reg_a3);
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * During second run extract the attached kernel image to the memory address
|
|
|
- * that the loader was loaded to in the first run.
|
|
|
- */
|
|
|
+ }
|
|
|
|
|
|
- fn = decompress(_kernel_load_addr, _kernel_data_addr, _kernel_data_size);
|
|
|
+ /*
|
|
|
+ * Check if we have been started standalone. So no piggy backed kernel.
|
|
|
+ * Search flash for kernel uImage and copy it to memory before the loader.
|
|
|
+ */
|
|
|
+ if (flash_start)
|
|
|
+ load_kernel(flash_start);
|
|
|
|
|
|
- printf("Booting kernel from 0x%08x ...\n\n", fn);
|
|
|
- fn(reg_a0, reg_a1, reg_a2, reg_a3);
|
|
|
- }
|
|
|
+ /*
|
|
|
+ * Finally extract the attached kernel image to the load address. This is
|
|
|
+ * either the first load address or what was found in uImage on flash
|
|
|
+ */
|
|
|
+ fn = decompress(_kernel_load_addr, _kernel_data_addr, _kernel_data_size);
|
|
|
+
|
|
|
+ printf("Booting kernel from 0x%08x ...\n\n", fn);
|
|
|
+ fn(reg_a0, reg_a1, reg_a2, reg_a3);
|
|
|
}
|