# avoid copying files around where possible. Therefore it is controlled by the following
# input parameters
#
-# KERNEL_IMG_IN: The filename of an LZMA compressed kernel image. This is required
+# KERNEL_IMG_IN: The filename of an LZMA compressed kernel. If given the loader
+# and the kernel will be concatenated (piggy back loading).
+# FLASH_ADDR: The kernel address in the ROM. If given, the loeader will be
+# created standalone and search a LZMA compressed uImage on the
+# target device starting from this address.
# KERNEL_IMG_OUT: The filename of the kernel image with the rt-loader prepended.
# If not given it will be created as image.bin into the BUILD_DIR.
# BUILD_DIR: The temporary build dir. If not given it will be set to "build".
#
-# To add it into the OpenWrt toolchain just create two new build commands
+# To add it into the OpenWrt toolchain just create the following new build commands
#
-# define Build/rt-loader
+# define Build/rt-compress
+# $(STAGING_DIR_HOST)/bin/xz --format=lzma -9 --stdout "$@" > "$@.new"
+# mv "$@.new" "$@"
+# endef
+#
+# define Build/rt-loader-piggy-back
# $(MAKE) all clean -C rt-loader CROSS_COMPILE="$(TARGET_CROSS)" \
# KERNEL_IMG_IN="$@" KERNEL_IMG_OUT="$@.new" BUILD_DIR="$@.build"
# mv "$@.new" "$@"
# endef
#
-# define Build/rt-compress
-# $(STAGING_DIR_HOST)/bin/xz --format=lzma -9 --stdout "$@" > "$@.new"
+# define Build/rt-loader-standalone
+# $(MAKE) all clean -C rt-loader CROSS_COMPILE="$(TARGET_CROSS)" \
+# FLASH_ADDR=$(FLASH_ADDR) KERNEL_IMG_OUT="$@.new" BUILD_DIR="$@.build"
# mv "$@.new" "$@"
# endef
#
-# Use them in a new kernel build recipe
+# Finally use them in new kernel build recipes. E.g. as all-in-one self extracting uImage.
#
# define Device/uimage-rt-loader
-# KERNEL/rt-loader := kernel-bin | append-dtb | rt-compress | rt-loader
+# KERNEL/rt-loader := kernel-bin | append-dtb | rt-compress | rt-loader-piggy-back
# KERNEL := $$(KERNEL/rt-loader) | uImage none
# KERNEL_INITRAMFS := $$(KERNEL/rt-loader) | uImage none
# endef
#
-# And finally add it to the target device. E.g.
+# Or as direct startable standlone version plus a kernel uImage
#
-# define Device/linksys_lgs310c
-# $(Device/uimage-rt-loader)
-# ...
+# define Device/rt-loader-uimage
+# FLASH_ADDR := 0xb4000000
+# KERNEL/rt-loader := rt-loader-standalone
+# KERNEL := kernel-bin | append-dtb | rt-compress | uImage lzma
# endef
+FLASH_ADDR_NONE := 0x0
+FLASH_ADDR ?= $(FLASH_ADDR_NONE)
+
CC := $(CROSS_COMPILE)gcc
LD := $(CROSS_COMPILE)ld
OBJCOPY := $(CROSS_COMPILE)objcopy
OBJDUMP := $(CROSS_COMPILE)objdump
CFLAGS = -fpic -mabicalls -O2 -fno-builtin-printf -Iinclude
+CFLAGS += -DFLASH_ADDR=$(FLASH_ADDR)
ASFLAGS = -fpic -msoft-float -Iinclude
IMAGE_OBJ := $(BUILD_DIR)/image.o
IMAGE_ELF := $(BUILD_DIR)/image.elf
+KERNEL_IMG_NONE := $(BUILD_DIR)/empty_kernel.bin
+KERNEL_IMG_IN ?= $(KERNEL_IMG_NONE)
KERNEL_IMG_OUT ?= $(BUILD_DIR)/image.bin
OBJECTS_C = $(filter %.c,$(SOURCES))
OBJECTS := $(patsubst %.o, $(BUILD_DIR)/%.o, $(OBJECTS)) $(IMAGE_OBJ)
ifneq ($(MAKECMDGOALS),clean)
-ifndef KERNEL_IMG_IN
-$(error Compressed kernel image not given via KERNEL_IMG_IN)
-endif
+ ifeq ($(KERNEL_IMG_IN)$(FLASH_ADDR),$(KERNEL_IMG_NONE)$(FLASH_ADDR_NONE))
+ $(error Set either KERNEL_IMG_IN or FLASH_ADDR)
+ else ifneq ($(FLASH_ADDR),$(FLASH_ADDR_NONE))
+ $(info Create standalone rt-loader, loading uimage from address $(FLASH_ADDR))
+ else
+ $(info Create piggy backed rt-loader, loading appended kernel binary "$(KERNEL_IMG_IN)")
+ endif
endif
all: $(KERNEL_IMG_OUT)
$(KERNEL_IMG_OUT): $(IMAGE_ELF)
$(OBJCOPY) -O binary $< $@
+$(KERNEL_IMG_IN):
+ @mkdir -p $(dir $@)
+ @echo "DUMMY-KERNEL-IMAGE" > $@
+
clean:
rm -rf $(BUILD_DIR)/
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
*/
+#include <stdbool.h>
#include "board.h"
#include "globals.h"
#include "memory.h"
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;
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);
}