mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-03-06 00:00:42 +01:00
The rt-loader currently only supports booting piggy backed lzma compressed kernels. This requires a data layout where the kernel directly follows the loader. That might not be sufficient for more complex flash layouts. Especially bootbase devices (like ZyXEL GS1920) will need some kind of chain loading that needs to be explored yet. Enhance the rt-loader as follows: - Allow to build as standalone version - In this case a flash start address is given - During boot loader will search the ROM starting from that address - If it finds a uImage this will be loaded into RAM - Afterwards it will be decompressed to its load address - While we are here add uncompressed uImage support As always the implementation tries to be as simple as possible. - uImage detection works without magics - uImage will be loaded to highest possible memory address - Documentation in Makefile has been adapted accordingly Funny side fact: A standalone rt-loader can chain load a piggy backed rt-loader from flash. During bootup loader will show rt-loader Running on RTL8380M (chip id 6275C) with 256MB Relocate 15760 bytes from 0x82000000 to 0x8ffa0000 Searching for uImage starting at 0xb45a0000 ... uImage 'MIPS OpenWrt Linux-6.12.40' found at 0xb45a0000 with load address 0x80100000 Copy 2923034 bytes of image data to 0x8fcd61e6 ... Extract image with 2923034 bytes from 0x8fcd61e6 to 0x80100000 ... Final kernel size is 2923034 bytes Booting kernel from 0x80100000 ... Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> Link: https://github.com/openwrt/openwrt/pull/19832 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
200 lines
5.3 KiB
C
200 lines
5.3 KiB
C
/*
|
|
* rt-loader main program
|
|
* (c) 2025 Markus Stockhausen
|
|
*
|
|
* This code was inspired by the OpenWrt lzma loader. Thanks to
|
|
*
|
|
* Copyright (C) 2004 Manuel Novoa III (mjn3@codepoet.org)
|
|
* Copyright (C) 2005 Mineharu Takahara <mtakahar@yahoo.com>
|
|
* Copyright (C) 2005 by Oleg I. Vdovikin <oleg@cs.msu.su>
|
|
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include "board.h"
|
|
#include "globals.h"
|
|
#include "memory.h"
|
|
|
|
#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1
|
|
#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_SMALL_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 0
|
|
#define NANOPRINTF_IMPLEMENTATION
|
|
#include "nanoprintf.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));
|
|
|
|
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.
|
|
*/
|
|
offs = (board_get_memory() - STACK_SIZE - HEAP_SIZE - len - 1024) & 0xfff0000;
|
|
addr = (void *)KSEG0 + offs;
|
|
|
|
printf("Relocate %d bytes from 0x%08x to 0x%08x\n", len, src, addr);
|
|
|
|
memcpy(addr, src, len);
|
|
flush_cache(addr, len);
|
|
|
|
return addr;
|
|
}
|
|
|
|
void welcome(void)
|
|
{
|
|
char system[80];
|
|
|
|
board_get_system(system, sizeof(system));
|
|
|
|
printf("\nrt-loader\n");
|
|
printf("Running on %s with %dMB\n", system, board_get_memory() >> 20);
|
|
}
|
|
|
|
void decompress_error(char *x)
|
|
{
|
|
printf("%s\n", 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;
|
|
}
|
|
|
|
if (ret)
|
|
board_panic();
|
|
|
|
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;
|
|
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
}
|