u-boot启动代码分析
_start -> reset -> _main
主要涉及一些协处理器的处理, 暂且略过。
进入到_main
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) @ sp = 0x60000f50
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r0, sp
bl board_init_f_alloc_reserve @ 栈顶预留 gd_t 的内存, gd_t 这里是 0xB0(16字节对齐了)
mov sp, r0 @ sp = 0x60000f50-0xb0 = 0x60000ea0
/* set up gd here, outside any C code */
mov r9, r0 @ r9 = gd_t 开始地址, AAPCS 文档规定
bl board_init_f_init_reserve @ 对 gd_t 里面的数据清零, 如果有定义 CONFIG_SYS_MALLOC_F
@ 将会对栈预留 malloc 的 CONFIG_SYS_MALLOC_F_LEN 空间
mov r0, #0
bl board_init_f @ 下面分析
/*
* Set up intermediate environment (new sp and gd) and call
* relocate_code(addr_moni). Trick here is that we'll return
* 'here' but relocated.
*/
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */
adr lr, here
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
add lr, lr, r0
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code
here:
/*
* now relocate vectors
*/
bl relocate_vectors
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
board_init_f所做的工作
arm-linux-gnueabi-readelf -S u-boot:
There are 30 section headers, starting at offset 0x190b84:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 60800000 008000 02d90c 00 AX 0 0 32
[ 2] .rodata PROGBITS 6082d910 035910 00bfd1 00 A 0 0 8
[ 3] .hash HASH 608398e4 0418e4 00002c 04 A 13 0 4
[ 4] .data PROGBITS 60839910 041910 001b30 00 WA 0 0 8
[ 5] .got.plt PROGBITS 6083b440 043440 00000c 04 WA 0 0 4
[ 6] .u_boot_list PROGBITS 6083b44c 04344c 000774 00 WA 0 0 4
[ 7] .efi_runtime PROGBITS 6083bbc0 043bc0 000100 00 WAX 0 0 8
[ 8] .efi_runtime_rel REL 6083bcc0 043cc0 000090 08 A 13 0 4
[ 9] .rel.dyn REL 6083bd50 043d50 007210 08 A 13 0 4
[10] .bss_start PROGBITS 6083bd50 04b07d 000000 00 W 0 0 1
[11] .bss NOBITS 6083bd50 000000 037754 00 WA 0 0 64
[12] .bss_end PROGBITS 608734a4 04b07d 000000 00 W 0 0 1
[13] .dynsym DYNSYM 60842f60 04af60 000060 10 A 14 3 4
[14] .dynstr STRTAB 60842fc0 04afc0 00002a 00 A 0 0 1
[15] .dynamic DYNAMIC 60842fec 04afec 000080 08 WA 14 0 4
[16] .interp PROGBITS 6084306c 04b06c 000011 00 A 0 0 1
[17] .ARM.attributes ARM_ATTRIBUTES 00000000 04b07d 000029 00 0 0 1
[18] .comment PROGBITS 00000000 04b0a6 00002b 01 MS 0 0 1
[19] .debug_line PROGBITS 00000000 04b0d1 01959d 00 0 0 1
[20] .debug_info PROGBITS 00000000 06466e 0a09fa 00 0 0 1
[21] .debug_abbrev PROGBITS 00000000 105068 01bb68 00 0 0 1
[22] .debug_aranges PROGBITS 00000000 120bd0 0037c0 00 0 0 8
[23] .debug_frame PROGBITS 00000000 124390 008fa8 00 0 0 4
[24] .debug_str PROGBITS 00000000 12d338 01096d 01 MS 0 0 1
[25] .debug_loc PROGBITS 00000000 13dca5 04a9e7 00 0 0 1
[26] .debug_ranges PROGBITS 00000000 188690 0083c8 00 0 0 8
[27] .shstrtab STRTAB 00000000 190a58 00012b 00 0 0 1
[28] .symtab SYMTAB 00000000 191034 00ff30 10 29 3241 4
[29] .strtab STRTAB 00000000 1a0f64 0065c1 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
setup_mon_len走第一个分支, _start为0x60800000, __bss_end为0x608734a4, gd->mon_len当前为0x734a4。其中__bss_end通过readelf -S u-boot查看, _start通过nm查看。
static int setup_mon_len(void)
{
#if defined(__ARM__) || defined(__MICROBLAZE__)
gd->mon_len = (ulong)&__bss_end - (ulong)_start;
#elif ...
#endif
return 0;
}
这里因为没有定义相关的宏, 所以直接返回。
int initf_malloc(void)
{
#ifdef CONFIG_SYS_MALLOC_F_LEN
assert(gd->malloc_base); /* Set up by crt0.S */
gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN;
gd->malloc_ptr = 0;
#endif
return 0;
}
static int initf_console_record(void)
{
#if defined(CONFIG_CONSOLE_RECORD) && defined(CONFIG_SYS_MALLOC_F_LEN)
return console_record_init();
#else
return 0;
#endif
}
__weak int arch_cpu_init(void)
{
return 0;
}
__weak int mach_cpu_init(void)
{
return 0;
}
/*
* u-boot下的设备模型也没有定义。
*/
static int initf_dm(void)
{
#if defined(CONFIG_DM) && defined(CONFIG_SYS_MALLOC_F_LEN)
int ret;
ret = dm_init_and_scan(true);
if (ret)
return ret;
#endif
#ifdef CONFIG_TIMER_EARLY
ret = dm_timer_init();
if (ret)
return ret;
#endif
return 0;
}
__weak int arch_cpu_init_dm(void)
{
return 0;
}
/* Record the board_init_f() bootstage (after arch_cpu_init()) */
static int mark_bootstage(void)
{
bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");
return 0;
}
int __weak timer_init(void)
{
return 0;
}
env_init没有进入if分支, gd->env_addr是0x6082f4dc, gd->env_valid是0。通过nm u-boot命令确实可以看到default_environment是0x6082f4dc, 位于.rodata数据段。
int env_init(void)
{
if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) {
gd->env_addr = (ulong)&(env_ptr->data);
gd->env_valid = 1;
return 0;
}
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 0;
return 0;
}
设置gd->baudrate, getenv_ulong调用getenv, 由于当前环境变量没有设置, 所以使用默认波特率CONFIG_BAUDRATE, 这里是38400。
static int init_baud_rate(void)
{
gd->baudrate = getenv_ulong("baudrate", 10, CONFIG_BAUDRATE);
return 0;
}
/**
* Decode the integer value of an environment variable and return it.
*
* @param name Name of environemnt variable
* @param base Number base to use (normally 10, or 16 for hex)
* @param default_val Default value to return if the variable is not
* found
* @return the decoded value, or default_val if not found
*/
ulong getenv_ulong(const char *name, int base, ulong default_val)
{
/*
* We can use getenv() here, even before relocation, since the
* environment variable value is an integer and thus short.
*/
const char *str = getenv(name);
return str ? simple_strtoul(str, NULL, base) : default_val;
}
紧接着走到[drivers/serial/serial.c]的serial_init, 此时的gd->flags被赋值GD_FLG_SERIAL_READY, get_current因为返回default_serial_console(), 返回[drivers/serial/serial_pl01x.c]的结构体pl01x_serial_drv, 接着调用start()进行初始化。
/**
* serial_init() - Initialize currently selected serial port
*
* This function initializes the currently selected serial port. This
* usually involves setting up the registers of that particular port,
* enabling clock and such. This function uses the get_current() call
* to determine which port is selected.
*
* Returns 0 on success, negative on error.
*/
int serial_init(void)
{
gd->flags |= GD_FLG_SERIAL_READY;
return get_current()->start();
}
/**
* get_current() - Return pointer to currently selected serial port
*
* This function returns a pointer to currently selected serial port.
* The currently selected serial port is altered by serial_assign()
* function.
*
* In case this function is called before relocation or before any serial
* port is configured, this function calls default_serial_console() to
* determine the serial port. Otherwise, the configured serial port is
* returned.
*
* Returns pointer to the currently selected serial port on success,
* NULL on error.
*/
static struct serial_device *get_current(void)
{
struct serial_device *dev;
if (!(gd->flags & GD_FLG_RELOC))
dev = default_serial_console();
else if (!serial_current)
dev = default_serial_console();
else
dev = serial_current;
/* We must have a console device */
if (!dev) {
#ifdef CONFIG_SPL_BUILD
puts("Cannot find console\n");
hang();
#else
panic("Cannot find console\n");
#endif
}
return dev;
}
static struct serial_device pl01x_serial_drv = {
.name = "pl01x_serial",
.start = pl01x_serial_init,
.stop = NULL,
.setbrg = pl01x_serial_setbrg,
.putc = pl01x_serial_putc,
.puts = default_serial_puts,
.getc = pl01x_serial_getc,
.tstc = pl01x_serial_tstc,
};
__weak struct serial_device *default_serial_console(void)
{
return &pl01x_serial_drv;
}
接着标记gd->have_console为1, 后续函数因为没有相关的宏定义直接跳过了。
/* Called before relocation - use serial functions */
int console_init_f(void)
{
gd->have_console = 1;
console_update_silent();
print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT1_SERIAL);
return 0;
}
打印版本信息U-Boot 2016.11 (Jan 17 2020 - 10:23:54 +0800):
int display_options (void)
{
#if defined(BUILD_TAG)
printf ("\n\n%s, Build: %s\n\n", version_string, BUILD_TAG);
#else
printf ("\n\n%s\n\n", version_string);
#endif
return 0;
}
display_text_info和print_cpuinfo信息打印。
static int display_text_info(void)
{
#if !defined(CONFIG_SANDBOX) && !defined(CONFIG_EFI_APP)
ulong bss_start, bss_end, text_base;
bss_start = (ulong)&__bss_start;
bss_end = (ulong)&__bss_end;
#ifdef CONFIG_SYS_TEXT_BASE
text_base = CONFIG_SYS_TEXT_BASE;
#else
text_base = CONFIG_SYS_MONITOR_BASE;
#endif
debug("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",
text_base, bss_start, bss_end);
#endif
#ifdef CONFIG_USE_IRQ
debug("IRQ Stack: %08lx\n", IRQ_STACK_START);
debug("FIQ Stack: %08lx\n", FIQ_STACK_START);
#endif
return 0;
}
static inline int print_cpuinfo(void)
{
return 0;
}
static int announce_dram_init(void)
{
puts("DRAM: ");
return 0;
}
[board/armltd/vexpress/vexpress_common.c]的dram_init赋值gd->ram_size内存大小。
int dram_init(void)
{
gd->ram_size =
get_ram_size((long *)CONFIG_SYS_SDRAM_BASE, PHYS_SDRAM_1_SIZE);
return 0;
}
setup_dest_addr函数在这里对gd->ram_size, gd->ram_top, gd->relocaddr赋值, gd->ram_size还是0x20000000, 后两个都是0x80000000。
static int setup_dest_addr(void)
{
debug("Monitor len: %08lX\n", gd->mon_len);
/*
* Ram is setup, size stored in gd !!
*/
debug("Ram size: %08lX\n", (ulong)gd->ram_size);
#ifdef CONFIG_SYS_MEM_RESERVE_SECURE
/* Reserve memory for secure MMU tables, and/or security monitor */
gd->ram_size -= CONFIG_SYS_MEM_RESERVE_SECURE;
/*
* Record secure memory location. Need recalcuate if memory splits
* into banks, or the ram base is not zero.
*/
gd->arch.secure_ram = gd->ram_size;
#endif
/*
* Subtract specified amount of memory to hide so that it won't
* get "touched" at all by U-Boot. By fixing up gd->ram_size
* the Linux kernel should now get passed the now "corrected"
* memory size and won't touch it either. This has been used
* by arch/powerpc exclusively. Now ARMv8 takes advantage of
* thie mechanism. If memory is split into banks, addresses
* need to be calculated.
*/
/*
* gd->ram_size 在这里是`0x20000000`, `512M`
*/
gd->ram_size = board_reserve_ram_top(gd->ram_size);
#ifdef CONFIG_SYS_SDRAM_BASE
gd->ram_top = CONFIG_SYS_SDRAM_BASE;
#endif
/*
* gd->ram_top 和 gd->relocaddr 在这里是`0x80000000`
*/
gd->ram_top += get_effective_memsize();
gd->ram_top = board_get_usable_ram_top(gd->mon_len);
gd->relocaddr = gd->ram_top;
debug("Ram top: %08lX\n", (ulong)gd->ram_top);
#if defined(CONFIG_MP) && (defined(CONFIG_MPC86xx) || defined(CONFIG_E500))
/*
* We need to make sure the location we intend to put secondary core
* boot code is reserved and not used by any part of u-boot
*/
if (gd->relocaddr > determine_mp_bootpg(NULL)) {
gd->relocaddr = determine_mp_bootpg(NULL);
debug("Reserving MP boot page to %08lx\n", gd->relocaddr);
}
#endif
return 0;
}
reserve_round_4k进行4K对齐。
/* Round memory pointer down to next 4 kB limit */
static int reserve_round_4k(void)
{
gd->relocaddr &= ~(4096 - 1);
return 0;
}
经过reserve_mmu函数之后, gd->relocaddr下移了64K, 用来存TLB。
static int reserve_mmu(void)
{
/* reserve TLB table */
gd->arch.tlb_size = PGTABLE_SIZE; /* 0x4000, 16K */
gd->relocaddr -= gd->arch.tlb_size; /* 0x7fffc000, 向下移动了 16K */
/* round down to next 64 kB limit */
gd->relocaddr &= ~(0x10000 - 1); /* 64K 对齐 */
gd->arch.tlb_addr = gd->relocaddr;
debug("TLB table from %08lx to %08lx\n", gd->arch.tlb_addr,
gd->arch.tlb_addr + gd->arch.tlb_size);
#ifdef CONFIG_SYS_MEM_RESERVE_SECURE
/*
* Record allocated tlb_addr in case gd->tlb_addr to be overwritten
* with location within secure ram.
*/
gd->arch.tlb_allocated = gd->arch.tlb_addr;
#endif
return 0;
}
reserve_trace里面相关的宏定义, 走出。
static int reserve_trace(void)
{
#ifdef CONFIG_TRACE
gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE;
gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE);
debug("Reserving %dk for trace data at: %08lx\n",
CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr);
#endif
return 0;
}
经过reserve_uboot函数后, 内存顶端有为u-boot的代码和数据段预留了空间(4K对齐), 并将gd->start_addr_sp赋值为当前预留空间的底部, 作为新栈顶。gd->start_addr_sp这里为0x7ff7c000。
static int reserve_uboot(void)
{
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
/*
* 函数开始时, gd->relocaddr 是 0x7fff0000, gd->mon_len 是 0x734a4 .
*/
gd->relocaddr -= gd->mon_len; /* 0x7ff7cb5c */
gd->relocaddr &= ~(4096 - 1); /* 0x7ff7c000 */
#ifdef CONFIG_E500
/* round down to next 64 kB limit so that IVPR stays aligned */
gd->relocaddr &= ~(65536 - 1);
#endif
debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10,
gd->relocaddr);
gd->start_addr_sp = gd->relocaddr; /* 0x7ff7c000 */
return 0;
}
再将栈顶下移, 预留malloc的空间。TOTAL_MALLOC_LEN=0x7ff7c000-0x7fedc000=0xA0000, 640K。
/* reserve memory for malloc() area */
static int reserve_malloc(void)
{
gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; /* 0x7fedc000 */
debug("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> 10, gd->start_addr_sp);
return 0;
}
再将栈顶下移, 为bd_t结构体预留空间。
/* (permanently) allocate a Board Info struct */
static int reserve_board(void)
{
if (!gd->bd) {
gd->start_addr_sp -= sizeof(bd_t); /* sizeof(bd_t) = 0x58, 0x7fedc000 - 0x58 = 0x7fedbfa8 */
gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t));
memset(gd->bd, '\0', sizeof(bd_t));
debug("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof(bd_t), gd->start_addr_sp);
}
return 0;
}
setup_machine暂时定义CONFIG_MACH_TYPE宏。
static int setup_machine(void)
{
#ifdef CONFIG_MACH_TYPE
gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif
return 0;
}
再将栈顶下移, 为gd_t结构体预留空间。
static int reserve_global_data(void)
{
gd->start_addr_sp -= sizeof(gd_t); /* sizeof(gd_t) = 0xa8, 0x7fedbfa8 - 0xa8 = 0x7fedbf00 */
gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t));
debug("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof(gd_t), gd->start_addr_sp);
return 0;
}
再将栈顶下移, 为fdt结构体预留空间。但由于gd->fdt_blob为空, 这里会退出。
static int reserve_fdt(void)
{
#ifndef CONFIG_OF_EMBED
/*
* If the device tree is sitting immediately above our image then we
* must relocate it. If it is embedded in the data section, then it
* will be relocated with other data.
*/
if (gd->fdt_blob) {
gd->fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);
gd->start_addr_sp -= gd->fdt_size;
gd->new_fdt = map_sysmem(gd->start_addr_sp, gd->fdt_size);
debug("Reserving %lu Bytes for FDT at: %08lx\n",
gd->fdt_size, gd->start_addr_sp);
}
#endif
return 0;
}
reserve_arch没有定义。
/* Architecture-specific memory reservation */
__weak int reserve_arch(void)
{
return 0;
}
reserve_stacks预留栈空间,
static int reserve_stacks(void)
{
/* make stack pointer 16-byte aligned */
gd->start_addr_sp -= 16; /* 0x7fedbf00 - 0x10 = 0x7fedbef0*/
gd->start_addr_sp &= ~0xf;
/*
* let the architecture-specific code tailor gd->start_addr_sp and
* gd->irq_sp
*/
return arch_reserve_stacks();
}
/* [arch/arm/lib/stack.c] */
int arch_reserve_stacks(void)
{
#ifdef CONFIG_SPL_BUILD
gd->start_addr_sp -= 128; /* leave 32 words for abort-stack */
gd->irq_sp = gd->start_addr_sp;
#else
/* setup stack pointer for exceptions */
gd->irq_sp = gd->start_addr_sp; /* 0x7fedbef0 */
# if !defined(CONFIG_ARM64)
# ifdef CONFIG_USE_IRQ
gd->start_addr_sp -= (CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ);
debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ, gd->start_addr_sp);
/* 8-byte alignment for ARM ABI compliance */
gd->start_addr_sp &= ~0x07;
# endif
/* leave 3 words for abort-stack, plus 1 for alignment */
gd->start_addr_sp -= 16; /* 0x7fedbef0 -0x10 = 0x7fedbee0 */
# endif
#endif
return 0;
}
setup_dram_config初始化gd->bd->bi_dram的信息。
static int setup_dram_config(void)
{
/* Ram is board specific, so move it to board code ... */
dram_init_banksize();
return 0;
}
/* board/armltd/vexpress/vexpress_common.c */
void dram_init_banksize(void)
{
/*
* {start = 0x60000000, size = 0x20000000},
* {start = 0x80000000, size = 0x0}
*/
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size =
get_ram_size((long *)PHYS_SDRAM_1, PHYS_SDRAM_1_SIZE);
gd->bd->bi_dram[1].start = PHYS_SDRAM_2;
gd->bd->bi_dram[1].size =
get_ram_size((long *)PHYS_SDRAM_2, PHYS_SDRAM_2_SIZE);
}
show_dram_config打印内存容量。
static int show_dram_config(void)
{
unsigned long long size;
#ifdef CONFIG_NR_DRAM_BANKS
int i;
debug("\nRAM Configuration:\n");
for (i = size = 0; i < CONFIG_NR_DRAM_BANKS; i++) {
size += gd->bd->bi_dram[i].size;
debug("Bank #%d: %llx ", i,
(unsigned long long)(gd->bd->bi_dram[i].start));
#ifdef DEBUG
print_size(gd->bd->bi_dram[i].size, "\n");
#endif
}
debug("\nDRAM: ");
#else
size = gd->ram_size;
#endif
print_size(size, "");
board_add_ram_info(0);
putc('\n');
return 0;
}
display_new_sp调试信息。
static int display_new_sp(void)
{
debug("New Stack Pointer is: %08lx\n", gd->start_addr_sp);
return 0;
}
搬移fdt到新地址, 但由于这里gd->new_fdt指针为空, 所以没有执行。
static int reloc_fdt(void)
{
#ifndef CONFIG_OF_EMBED
if (gd->flags & GD_FLG_SKIP_RELOC)
return 0;
if (gd->new_fdt) {
memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size);
gd->fdt_blob = gd->new_fdt;
}
#endif
return 0;
}
setup_reloc计算重定位的偏移gd->reloc_off, 然后将gd搬移到之前预留的地址gd->new_gd。
static int setup_reloc(void)
{
if (gd->flags & GD_FLG_SKIP_RELOC) {
debug("Skipping relocation due to flag\n");
return 0;
}
#ifdef CONFIG_SYS_TEXT_BASE
/*
* gd->relocaddr = 0x7ff7c000
* CONFIG_SYS_TEXT_BASE = 0x60800000
* gd->reloc_off = 0x1f77c000
*/
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
#ifdef CONFIG_M68K
/*
* On all ColdFire arch cpu, monitor code starts always
* just after the default vector table location, so at 0x400
*/
gd->reloc_off = gd->relocaddr - (CONFIG_SYS_TEXT_BASE + 0x400);
#endif
#endif
memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));
#ifndef CONFIG_MINI_BOOT
printf("Relocation Offset is: %08lx\n", gd->reloc_off);
printf("Relocating to %08lx, new gd at %08lx, sp at %08lx\n",
gd->relocaddr, (ulong)map_to_sysmem(gd->new_gd),
gd->start_addr_sp);
#endif
return 0;
}
当前栈的情况如下:
------------- 0x8000 0000
| TLB table
------------- 0x7fff c000
| u-boot(new)
------------- 0x7ff 7c000
| malloc
------------- 0x7fed c000
| bd_t
------------- 0x7fed bfa8
| gd_t
------------- 0x7fed bf00
| reserve
------------- 0x7fed bef0
| abort-stack
------------- 0x7fed bee0
|
-------------
|
重新回到_main
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
*/
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) @ sp = 0x60000f50
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r0, sp
bl board_init_f_alloc_reserve @ 栈顶预留 gd_t 的内存, gd_t 这里是 0xB0(16字节对齐了)
mov sp, r0 @ sp = 0x60000f50-0xb0 = 0x60000ea0
/* set up gd here, outside any C code */
mov r9, r0 @ r9 = gd_t 开始地址, AAPCS 文档规定
bl board_init_f_init_reserve @ 对 gd_t 里面的数据清零, 如果有定义 CONFIG_SYS_MALLOC_F
@ 将会对栈预留 malloc 的 CONFIG_SYS_MALLOC_F_LEN 空间
mov r0, #0
bl board_init_f @ 从上面的过程可以看到, 这个函数是将新栈放到内存的高端地址,
@ 并为不同的段预留了各自的内存, 比如 fdt, gd, 等等。
@ 并计算了 u-boot 需要搬移的地址, 以及里面搬移地址相对
@ 当前运行地址的偏移量等工作
/*
* Set up intermediate environment (new sp and gd) and call
* relocate_code(addr_moni). Trick here is that we'll return
* 'here' but relocated.
*/
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
@ 在上面可以看到 r9 是 gd_t 的基地址, 这里就是取 board_init_f
@ 初始化完之后的 start_addr_sp 新的栈顶地址。
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
@ board_init_f 的 reserve_board 为 bd 预留的内存地址, 这里为 0x7fedbfa8
sub r9, r9, #GD_SIZE /* new GD is below bd */
@ 0x7fedbfa8 - 0xa8 = 0x7fedbf00, 这里是获取 gd_t 新的基地址,
@ 这是由于 board_init_f 初始时预留的空间 gb_t 就在 bd_t 下面
adr lr, here
@ lr = 0x60800660, 这里是下面的 label 的地址, 当前代码没有进行搬移
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
@ 获得 gd->reloc_off, 这里是 0x1f77c000, 具体看上面的 board_init_f 过程
add lr, lr, r0 @ 重新计算 lr 的地址, 加上偏移 here + 0x1f77c000,
@ 即重定位后的 here 的地址为 0x7ff7c660
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
@ 由上面的 计算为 board_init_f 0x7ff7c000
b relocate_code
here:
/*
* now relocate vectors
*/
bl relocate_vectors
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
relocate_code重定向
从上面的board_init_f过程中我们可以看到0x7ff7c000~0x7fffc000的512K的空间是为重定向后的u-boot预留的内存空间。接下来看重定向的代码:
/*
* void relocate_code(addr_moni)
*
* This function relocates the monitor code.
*
* NOTE:
* To prevent the code below from containing references with an R_ARM_ABS32
* relocation record type, we never refer to linker-defined symbols directly.
* Instead, we declare literals which contain their relative location with
* respect to relocate_code, and at run time, add relocate_code back to them.
*/
ENTRY(relocate_code)
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
@ __image_copy_start = 0x60800000, 可以用 `nm` 工具看到
subs r4, r0, r1 /* r4 <- relocation offset */
@ r0 = 0x7ff7c000, r1 = 0x60800000, r4 = 0x1f77c000
beq relocate_done /* skip relocation */
ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */
@ __image_copy_end = 0x6083bd50, 可以用 `nm` 工具看到
@ 从上面的准备可以看到, r0 指向的是目标地址, r1 执行源地址,
@ 0 = 0x7ff7c000, r1 = 0x60800000, r2 = 0x6083bd50, r4 = 0x1f77c000
@ 接下来就是拷贝过程了, 将 r1 指向的地址的内容 load 到 r10-r11 寄存器,
@ 然后再将 r10-r11 的内容 stor 到 r0 指向的地址, r0 和 r1 的地址都递增,
@ 在 r1 小于 r2 时一直执行 copy_loop 循环.
copy_loop:
ldmia r1!, {r10-r11} /* copy from source address [r1] */
stmia r0!, {r10-r11} /* copy to target address [r0] */
cmp r1, r2 /* until source end address [r2] */
blo copy_loop
/*
* fix .rel.dyn relocations
*/
@ 对 .rel.dyn 段进行重定向
@ r2 = 0x6083bd50, r3 = 0x60842f60 可以用 `nm` 工具看到 __rel_dyn_start 和 __rel_dyn_end
ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
cmp r2, r3
beq relocate_done
@
fixloop:
ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
and r1, r1, #0xff @ ??? 这为什么
cmp r1, #23 /* relative fixup? */
bne fixnext
/* relative fix: increase location by offset */
add r0, r0, r4
ldr r1, [r0]
add r1, r1, r4
str r1, [r0]
fixnext:
cmp r2, r3
blo fixloop
relocate_done:
#ifdef __XSCALE__
/*
* On xscale, icache must be invalidated and write buffers drained,
* even with cache disabled - 4.2.7 of xscale core developer's manual
*/
mcr p15, 0, r0, c7, c7, 0 /* invalidate icache */
mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */
#endif
/* ARMv4- don't know bx lr but the assembler fails to see that */
#ifdef __ARM_ARCH_4__
mov pc, lr
#else
bx lr
#endif
ENDPROC(relocate_code)

1197

被折叠的 条评论
为什么被折叠?



