diff --git a/target/linux/realtek/image/rt-loader/Makefile b/target/linux/realtek/image/rt-loader/Makefile index 6479db705c..ade61697bf 100644 --- a/target/linux/realtek/image/rt-loader/Makefile +++ b/target/linux/realtek/image/rt-loader/Makefile @@ -5,45 +5,60 @@ # 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 -# -# define Build/rt-loader -# $(MAKE) all clean -C rt-loader CROSS_COMPILE="$(TARGET_CROSS)" \ -# KERNEL_IMG_IN="$@" KERNEL_IMG_OUT="$@.new" BUILD_DIR="$@.build" -# mv "$@.new" "$@" -# endef +# To add it into the OpenWrt toolchain just create the following new build commands # # define Build/rt-compress # $(STAGING_DIR_HOST)/bin/xz --format=lzma -9 --stdout "$@" > "$@.new" # mv "$@.new" "$@" # endef # -# Use them in a new kernel build recipe +# 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-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 +# +# 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 @@ -58,6 +73,8 @@ BUILD_DIR ?= build 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)) @@ -67,9 +84,13 @@ OBJECTS := $(OBJECTS_S:.S=.o) $(OBJECTS_C:.c=.o) 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) @@ -93,6 +114,10 @@ $(IMAGE_ELF): $(OBJECTS) $(KERNEL_IMG_OUT): $(IMAGE_ELF) $(OBJCOPY) -O binary $< $@ +$(KERNEL_IMG_IN): + @mkdir -p $(dir $@) + @echo "DUMMY-KERNEL-IMAGE" > $@ + clean: rm -rf $(BUILD_DIR)/ diff --git a/target/linux/realtek/image/rt-loader/include/globals.h b/target/linux/realtek/image/rt-loader/include/globals.h index 5ed65ba670..a8e970d7d4 100644 --- a/target/linux/realtek/image/rt-loader/include/globals.h +++ b/target/linux/realtek/image/rt-loader/include/globals.h @@ -6,6 +6,11 @@ #ifndef _GLOBALS_H_ #define _GLOBALS_H_ +#define UIMAGE_HDR_SIZE 64 +#define UIMAGE_OS_LINUX 5 +#define UIMAGE_COMP_NONE 0 +#define UIMAGE_COMP_LZMA 3 + #define KSEG0 0x80000000 #define STACK_SIZE 0x10000 #define HEAP_SIZE 0x40000 diff --git a/target/linux/realtek/image/rt-loader/src/main.c b/target/linux/realtek/image/rt-loader/src/main.c index 0a53fcef7d..acdd782ffd 100644 --- a/target/linux/realtek/image/rt-loader/src/main.c +++ b/target/linux/realtek/image/rt-loader/src/main.c @@ -10,6 +10,7 @@ * Copyright (C) 2011 Gabor Juhos */ +#include #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 kernel with %d bytes from 0x%08x to 0x%08x ...\n", len, in, out); + printf("Extract image with %d bytes from 0x%08x to 0x%08x ...\n", len, in, out); - if (unlzma(in, len, 0, 0, out, &outlen, 0, decompress_error)) + 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; + } + + 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); - - printf("Booting kernel from 0x%08x ...\n\n", fn); - fn(reg_a0, reg_a1, reg_a2, reg_a3); } + + /* + * 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); + + /* + * 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); } diff --git a/target/linux/realtek/image/rt-loader/src/startup.S b/target/linux/realtek/image/rt-loader/src/startup.S index 60eb1e480e..5d2f99338c 100644 --- a/target/linux/realtek/image/rt-loader/src/startup.S +++ b/target/linux/realtek/image/rt-loader/src/startup.S @@ -4,9 +4,9 @@ #include "globals.h" # This start code allows to run a position independent code (PIC) on bare metal. In that case -# all addresses are looked up via the global offset table (GOT). But that must be filled during -# this initialization sequence. Without a proper GOT using standard "la" instruction in the code -# will not work. Provide a macro that avoids the dependency. +# all addresses are looked up via the global offset table (GOT). That must be filled during +# this initialization sequence. Without a proper GOT using standard "la" instruction in the +# code will not work. Provide a macro that avoids the dependency. .macro _LA reg, symbol lui \reg, %hi(\symbol) @@ -48,15 +48,18 @@ _where_am_i: mtc0 $zero, CP0_COMPARE _EHB -# Check if this our first run (_kernel_load_addr = 0?) +# Increase run counter and check if this is the first run - _LA $t6, _kernel_load_addr + _LA $t6, _my_run_count lw $t7, 0($t6) + addi $t8, $t7, 1 + sw $t8, 0($t6) bne $zero, $t7, _init_done nop # During first run store the current load address as the target kernel load address. + _LA $t6, _kernel_load_addr sw $t9, 0($t6) # Same for the global variables in the BSS section. Clear them only during the first run. This @@ -179,6 +182,10 @@ _heap_addr: .globl _heap_addr_max _heap_addr_max: .word 0 +# number of loads + .globl _my_run_count +_my_run_count: + .word 0 # current program load address .globl _my_load_addr _my_load_addr: @@ -199,3 +206,7 @@ _kernel_data_addr: .globl _kernel_data_size _kernel_data_size: .word 0 +# compression method of attached kernel (usually LZMA) + .globl _kernel_comp_type +_kernel_comp_type: + .word UIMAGE_COMP_LZMA