u-boot启动代码分析

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走第一个分支, _start0x60800000, __bss_end0x608734a4, 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_addr0x6082f4dc, gd->env_valid0。通过nm u-boot命令确实可以看到default_environment0x6082f4dc, 位于.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_console1, 后续函数因为没有相关的宏定义直接跳过了。

/* 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_infoprint_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~0x7fffc000512K的空间是为重定向后的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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值