1. 程式人生 > >基於am335x的u-boot 2013.01.01的啟動分析

基於am335x的u-boot 2013.01.01的啟動分析

1,am335x的cpu上電後,會跳到哪個地址去執行?

答:

晶片到uboot啟動流程 :ROM → MLO(SPL)→ uboot.img

AM335x 中bootloader被分成了 3 個部分:

第一級 bootloader:引導載入程式,板子上電後會自動執行這些程式碼,如選擇哪種方式啟動(NAND,SDcard,UART。。。),然後跳轉轉到第二級 bootloader。這些程式碼應該是存放在 176KB 的 ROM 中。

第二級 bootloader:MLO(SPL),用以硬體初始化:關閉看門狗,關閉中斷,設定 CPU 時鐘頻率、速度等操作。然後會跳轉到第三級bootloader。MLO檔案應該會被對映到 64 KB的 Internal SRAM 中。

第三級 bootloader:uboot.img,C程式碼的入口。

其中第一級 bootloader 是板子固化的,第二級和第三級是通過編譯 uboot 所得的。

2,第二級 bootloader:MLO(SPL)做了哪些事情?

MLO(SPL)記憶體分佈如下:

SPL記憶體重對映:

< PATH : /arch/arm/cpu/armv7/omap-common/u-boot-spl.lds >

MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,\
		LENGTH = CONFIG_SPL_MAX_SIZE }
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR, \
		LENGTH = CONFIG_SPL_BSS_MAX_SIZE }

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
	.text      :
	{
		__start = .;
		arch/arm/cpu/armv7/start.o	(.text)
		*(.text*)
	} >.sram

	. = ALIGN(4);
	.rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram

	. = ALIGN(4);
	.data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram

	.u_boot_list : {
		#include <u-boot.lst>
	}

	. = ALIGN(4);
	__image_copy_end = .;
	_end = .;

	.bss :
	{
		. = ALIGN(4);
		__bss_start = .;
		*(.bss*)
		. = ALIGN(4);
		__bss_end__ = .;
	} >.sdram
}
<PATH : /include/configs/am335x_evm.h>
#define CONFIG_SPL_TEXT_BASE		0x402F0400
#define CONFIG_SPL_MAX_SIZE		(101 * 1024)
#define CONFIG_SPL_STACK		CONFIG_SYS_INIT_SP_ADDR

#define CONFIG_SPL_BSS_START_ADDR	0x80000000
#define CONFIG_SPL_BSS_MAX_SIZE		0x80000		/* 512 KB */

#define PHYS_DRAM_1            0x80000000    /* DRAM Bank #1 */
#define CONFIG_SYS_SDRAM_BASE        PHYS_DRAM_1
#define CONFIG_SYS_INIT_SP_ADDR         (NON_SECURE_SRAM_END - \
                        GENERATED_GBL_DATA_SIZE)

#ifdef CONFIG_NOR_BOOT
#define CONFIG_SYS_TEXT_BASE        0x08000000
#else
#define CONFIG_SYS_TEXT_BASE        0x80800000
#endif

GENERATED_GBL_DATA_SIZE

<lib/asm-offsets.c>
int main(void)
{
	/* Round up to make sure size gives nice stack alignment */
	DEFINE(GENERATED_GBL_DATA_SIZE,
		(sizeof(struct global_data) + 15) & ~15);

	DEFINE(GENERATED_BD_INFO_SIZE,
		(sizeof(struct bd_info) + 15) & ~15);

	DEFINE(GD_SIZE, sizeof(struct global_data));

	DEFINE(GD_BD, offsetof(struct global_data, bd));

#if defined(CONFIG_ARM)

	DEFINE(GD_RELOCADDR, offsetof(struct global_data, relocaddr));

	DEFINE(GD_RELOC_OFF, offsetof(struct global_data, reloc_off));

	DEFINE(GD_START_ADDR_SP, offsetof(struct global_data, start_addr_sp));

#endif

	return 0;
}
<PATH : /arch/arm/include/asm/arch-am33xx/omap.h>

#define NON_SECURE_SRAM_START	0x40304000
#define NON_SECURE_SRAM_END	0x4030E000

a1、儲存啟動引數 bl   save_boot_params

<PATH : /arch/arm/cpu/armv7/start.S>
#include <asm-offsets.h>
#include <config.h>
#include <version.h>
#include <asm/system.h>
#include <linux/linkage.h>

.globl _start
_start: b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
...............
reset:
	bl	save_boot_params
	/*
	 * set the cpu to SVC32 mode
	 */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr,r0

/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
	/* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
	mrc	p15, 0, r0, c1, c0, 0	@ Read CP15 SCTRL Register
	bic	r0, #CR_V		@ V = 0
	mcr	p15, 0, r0, c1, c0, 0	@ Write CP15 SCTRL Register

	/* Set vector address in CP15 VBAR register */
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR
#endif

	/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	bl	cpu_init_cp15
	bl	cpu_init_crit
#endif

	bl	_main
............
ENTRY(cpu_init_crit)
	/*
	 * Jump to board specific initialization...
	 * The Mask ROM will have already initialized
	 * basic memory. Go here to bump up clock rate and handle
	 * wake up conditions.
	 */
	b	lowlevel_init		@ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
<PATH : /arch/arm/cpu/armv7/omap-common/lowlevel_init.S>
#include <asm/arch/omap.h>
#include <asm/arch/spl.h>
#include <linux/linkage.h>

ENTRY(save_boot_params)
	/*
	 * See if the rom code passed pointer is valid:
	 * It is not valid if it is not in non-secure SRAM
	 * This may happen if you are booting with the help of
	 * debugger
	 */
	ldr     r2, =NON_SECURE_SRAM_START
	cmp	r2, r0
	bgt	1f
	ldr	r2, =NON_SECURE_SRAM_END
	cmp	r2, r0
	blt	1f

	/*
	 * store the boot params passed from rom code or saved
	 * and passed by SPL
	 */
	cmp	r0, #0
	beq	1f
	ldr	r1, =boot_params
	str	r0, [r1]
#ifdef CONFIG_SPL_BUILD
	/* Store the boot device in spl_boot_device */
	ldrb	r2, [r0, #BOOT_DEVICE_OFFSET]	@ r1 <- value of boot device
	and	r2, #BOOT_DEVICE_MASK
	ldr	r3, =boot_params
	strb	r2, [r3, #BOOT_DEVICE_OFFSET]	@ spl_boot_device <- r1

	/* boot mode is passed only for devices that can raw/fat mode */
	cmp	r2, #BOOT_DEVICE_XIP
	blt	2f
	cmp	r2, #BOOT_DEVICE_MMC2
	bgt	2f
	/* Store the boot mode (raw/FAT) in omap_bootmode */
	ldr	r2, [r0, #DEV_DESC_PTR_OFFSET]	@ get the device descriptor ptr
	ldr	r2, [r2, #DEV_DATA_PTR_OFFSET]	@ get the pDeviceData ptr
	ldr	r2, [r2, #BOOT_MODE_OFFSET]	@ get the boot mode
	ldr	r3, =omap_bootmode
	str	r2, [r3]
#endif
2:
	ldrb	r2, [r0, #CH_FLAGS_OFFSET]
	ldr	r3, =boot_params
	strb	r2, [r3, #CH_FLAGS_OFFSET]
1:
	bx	lr
ENDPROC(save_boot_params)
<PATH : /arch/arm/include/asm/arch-am33xx/omap.h>

#define NON_SECURE_SRAM_START	0x40304000
#define NON_SECURE_SRAM_END	0x4030E000

問題:這些引數是儲存在哪裡的?大概有哪些引數?

答:

這些引數儲存的記憶體地址為 64 KB 的 OCM RAM 中:

注:Dowloaded Image 區域:是用來儲存 MLO(SPL) 檔案的,其最大可達到109 KB

a2、設定 CPU SVC32 模式

<PATH : /arch/arm/cpu/armv7/start.S>
	/*
	 * set the cpu to SVC32 mode
	 */
	mrs	r0, cpsr
	bic	r0, r0, #0x1f
	orr	r0, r0, #0xd3
	msr	cpsr,r0

CPSR:程式狀態暫存器(current program status register)(當前程式狀態暫存器),在任何處理器模式下被訪問。它包含了條件標誌位、中斷禁止位、當前處理器模式標誌以及其他的一些控制和狀態位。 CPSR在使用者級程式設計時用於儲存條件碼。

SPSR:程式狀態儲存暫存器(saved programstatusregister),每一種處理器模式下都有一個狀態暫存器SPSR,SPSR用於儲存CPSR的狀態,以便異常返回後恢復異常發生時的工作狀態。當特定的異常中斷髮生時,這個暫存器用於存放當前程式狀態暫存器的內容。在異常中斷退出時,可以用SPSR來恢復CPSR。由於使用者模式和系統模式不是異常中斷模式,所以他沒有SPSR。當用戶在使用者模式或系統模式訪問SPSR,將產生不可預知的後果。

CPSR格式如下所示。SPSR和CPSR格式相同。 31 30 29 28 27 26 7 6 5 4 3 2 1 0 N Z C V Q DNM(RAZ) I F T M4 M3 M2 M1 M0

a3CPU的初始化

<PATH : /arch/arm/cpu/armv7/lowlevel_init.S>
#include <asm-offsets.h>
#include <config.h>
#include <linux/linkage.h>

ENTRY(lowlevel_init)
	/*
	 * Setup a temporary stack
	 */
	ldr	sp, =CONFIG_SYS_INIT_SP_ADDR
	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */

	/*
	 * Save the old lr(passed in ip) and the current lr to stack
	 */
	push	{ip, lr}

	/*
	 * go setup pll, mux, memory
	 */
	bl	s_init
	pop	{ip, pc}
ENDPROC(lowlevel_init)

問題:CPU的初始化有哪些內容?

答:

           b1、首先要設定堆疊區,因為將會呼叫 C函式來實現CPU的初始化

問題:這個堆疊在什麼位置,其記憶體大小是多少?

《PATH :/include/configs/am335x_evm.h》
#define CONFIG_SYS_INIT_SP_ADDR         (NON_SECURE_SRAM_END - \
						GENERATED_GBL_DATA_SIZE)
<lib/asm-offsets.c>
int main(void)
{
	/* Round up to make sure size gives nice stack alignment */
	DEFINE(GENERATED_GBL_DATA_SIZE,
		(sizeof(struct global_data) + 15) & ~15);

	DEFINE(GENERATED_BD_INFO_SIZE,
		(sizeof(struct bd_info) + 15) & ~15);

	DEFINE(GD_SIZE, sizeof(struct global_data));

	DEFINE(GD_BD, offsetof(struct global_data, bd));

#if defined(CONFIG_ARM)

	DEFINE(GD_RELOCADDR, offsetof(struct global_data, relocaddr));

	DEFINE(GD_RELOC_OFF, offsetof(struct global_data, reloc_off));

	DEFINE(GD_START_ADDR_SP, offsetof(struct global_data, start_addr_sp));

#endif

	return 0;
}

b2執行 s_init() 函式,實現 CPU 的初始化

<PATH : /board/ti/am335x/board.c>

void s_init(void)
{
	__maybe_unused struct am335x_baseboard_id header;
#ifdef CONFIG_NOR_BOOT
	asm("stmfd      sp!, {r2 - r4}");
	asm("movw       r4, #0x8A4");
	asm("movw       r3, #0x44E1");
	asm("orr        r4, r4, r3, lsl #16");
	asm("mov        r2, #9");
	asm("mov        r3, #8");
	asm("gpmc_mux:  str     r2, [r4], #4");
	asm("subs       r3, r3, #1");
	asm("bne        gpmc_mux");
	asm("ldmfd      sp!, {r2 - r4}");
#endif

	/* WDT1 is already running when the bootloader gets control
	 * Disable it to avoid "random" resets
	 */
	writel(0xAAAA, &wdtimer->wdtwspr);
	while (readl(&wdtimer->wdtwwps) != 0x0)
		;
	writel(0x5555, &wdtimer->wdtwspr);
	while (readl(&wdtimer->wdtwwps) != 0x0)
		;

#if defined(CONFIG_SPL_BUILD) || defined(CONFIG_NOR_BOOT)
	/* Setup the PLLs and the clocks for the peripherals */
	pll_init();

	/* Enable RTC32K clock */
	rtc32k_enable();

	/* UART softreset */
	u32 regVal;

#ifdef CONFIG_SERIAL1
	enable_uart0_pin_mux();
#endif /* CONFIG_SERIAL1 */
#ifdef CONFIG_SERIAL2
	enable_uart1_pin_mux();
#endif /* CONFIG_SERIAL2 */
#ifdef CONFIG_SERIAL3
	enable_uart2_pin_mux();
#endif /* CONFIG_SERIAL3 */
#ifdef CONFIG_SERIAL4
	enable_uart3_pin_mux();
#endif /* CONFIG_SERIAL4 */
#ifdef CONFIG_SERIAL5
	enable_uart4_pin_mux();
#endif /* CONFIG_SERIAL5 */
#ifdef CONFIG_SERIAL6
	enable_uart5_pin_mux();
#endif /* CONFIG_SERIAL6 */

	regVal = readl(&uart_base->uartsyscfg);
	regVal |= UART_RESET;
	writel(regVal, &uart_base->uartsyscfg);
	while ((readl(&uart_base->uartsyssts) &
		UART_CLK_RUNNING_MASK) != UART_CLK_RUNNING_MASK)
		;

	/* Disable smart idle */
	regVal = readl(&uart_base->uartsyscfg);
	regVal |= UART_SMART_IDLE_EN;
	writel(regVal, &uart_base->uartsyscfg);

#if defined(CONFIG_NOR_BOOT)
	gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
	gd->baudrate = CONFIG_BAUDRATE;
	serial_init();
	gd->have_console = 1;
#else
	gd = &gdata;

	preloader_console_init();
#endif

	/* Initalize the board header */
	enable_i2c0_pin_mux();
	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#ifndef CONFIG_NOR_BOOT
	if (read_eeprom() < 0)
		puts("Could not get board ID.\n");
#endif

	/* Check if baseboard eeprom is available */
	if (i2c_probe(CONFIG_SYS_I2C_EEPROM_ADDR)) {
		puts("Could not probe the EEPROM; something fundamentally "
			"wrong on the I2C bus.\n");
	}

	/* read the eeprom using i2c */
	if (i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, 0, 2, (uchar *)&header,
							sizeof(header))) {
		puts("Could not read the EEPROM; something fundamentally"
			" wrong on the I2C bus.\n");
	}

	if (header.magic != 0xEE3355AA) {
		/*
		 * read the eeprom using i2c again,
		 * but use only a 1 byte address
		 */
		if (i2c_read(CONFIG_SYS_I2C_EEPROM_ADDR, 0, 1,
					(uchar *)&header, sizeof(header))) {
			puts("Could not read the EEPROM; something "
				"fundamentally wrong on the I2C bus.\n");
			hang();
		}

		if (header.magic != 0xEE3355AA) {
			printf("Incorrect magic number (0x%x) in EEPROM\n",
					header.magic);
			hang();
		}
	}

	enable_board_pin_mux(&header);
	if (!strncmp("A335X_SK", header.name, HDR_NAME_LEN)) {
		/*
		 * EVM SK 1.2A and later use gpio0_7 to enable DDR3.
		 * This is safe enough to do on older revs.
		 */
		gpio_request(GPIO_DDR_VTT_EN, "ddr_vtt_en");
		gpio_direction_output(GPIO_DDR_VTT_EN, 1);
	}

#ifdef CONFIG_NOR_BOOT
	am33xx_spl_board_init();
#endif

	if (!strncmp("A335X_SK", header.name, HDR_NAME_LEN))
		config_ddr(303, MT41J128MJT125_IOCTRL_VALUE, &ddr3_data,
			   &ddr3_cmd_ctrl_data, &ddr3_emif_reg_data);
	else if  (!strncmp("A335BNLT", header.name, 8))
		config_ddr(400, MT41K256M16HA125E_IOCTRL_VALUE,
			   &ddr3_beagleblack_data,
			   &ddr3_beagleblack_cmd_ctrl_data,
			   &ddr3_beagleblack_emif_reg_data);
	else if (!strncmp("A33515BB", header.name, 8) &&
				strncmp("1.5", header.version, 3) <= 0)
		config_ddr(303, MT41J512M8RH125_IOCTRL_VALUE, &ddr3_evm_data,
			   &ddr3_evm_cmd_ctrl_data, &ddr3_evm_emif_reg_data);
	else
		config_ddr(266, MT47H128M16RT25E_IOCTRL_VALUE, &ddr2_data,
			   &ddr2_cmd_ctrl_data, &ddr2_emif_reg_data);
#endif
}

@[email protected] 關閉看門狗(WDT

/* WDT1 is already running when the bootloader gets control
	 * Disable it to avoid "random" resets
	 */
	writel(0xAAAA, &wdtimer->wdtwspr);
	while (readl(&wdtimer->wdtwwps) != 0x0)
		;
	writel(0x5555, &wdtimer->wdtwspr);
	while (readl(&wdtimer->wdtwwps) != 0x0)
		;
<PATH : /arch/arm/include/asm/arch-am33xx/hardware.h>
/* Watchdog Timer */
#define WDT_BASE			0x44E35000

@[email protected] 給外設設定好 PLL 和 時鐘頻率等

    pll_init();

<PATH : /arch/arm/cpu/armv7/am33xx/clock.c>
/*
 * Configure the PLL/PRCM for necessary peripherals
 */
void pll_init()
{
	/* Start at 550MHz, will be tweaked up if possible. */
	mpu_pll_config(MPUPLL_M_300);
	core_pll_config(OPP_50);
	per_pll_config();

	/* Enable the required interconnect clocks */
	enable_interface_clocks();

	/* Power domain wake up transition */
	power_domain_wkup_transition();

	/* Enable the required peripherals */
	enable_per_clocks();
}

@[email protected] 使能 32-KHz 頻率的實時時鐘

rtc32k_enable();

<PATH : /board/ti/am335x/board.c>
static void rtc32k_enable(void)
{
	struct rtc_regs *rtc = (struct rtc_regs *)AM335X_RTC_BASE;

	/*
	 * Unlock the RTC's registers.  For more details please see the
	 * RTC_SS section of the TRM.  In order to unlock we need to
	 * write these specific values (keys) in this order.
	 */
	writel(0x83e70b13, &rtc->kick0r);
	writel(0x95a4f1e0, &rtc->kick1r);

	/* Enable the RTC 32K OSC by setting bits 3 and 6. */
	writel((1 << 3) | (1 << 6), &rtc->osc);
}
<PATH : /arch/arm/include/asm/arch-am33xx/hardware.h>
/* RTC base address */
#define AM335X_RTC_BASE			0x44E3E000

@[email protected] 初始化控制檯,通過UART可以檢視相關資訊

<PATH : /common/spl/spl.c>
/*
 * This requires UART clocks to be enabled.  In order for this to work the
 * caller must ensure that the gd pointer is valid.
 */
void preloader_console_init(void)
{
	gd->bd = &bdata;
	gd->baudrate = CONFIG_BAUDRATE;

	serial_init();		/* serial communications setup */

	gd->have_console = 1;

	puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \
			U_BOOT_TIME ")\n");
#ifdef CONFIG_SPL_DISPLAY_PRINT
	spl_display_print();
#endif
}


/* Define board data structure */
static bd_t bdata __attribute__ ((section(".data")));
/* Module base addresses */
#define UART0_BASE			0x44E09000

@[email protected] 配置 DDR

<path=arch/arm/cpu/armv7/am33xx/emif4.c>
void config_ddr(unsigned int pll, unsigned int ioctrl,
		const struct ddr_data *data, const struct cmd_control *ctrl,
		const struct emif_regs *regs)
{
	enable_emif_clocks();
	ddr_pll_config(pll);
	config_vtp();
	config_cmd_ctrl(ctrl);

	config_ddr_data(0, data);
	config_ddr_data(1, data);

	config_io_ctrl(ioctrl);

	/* Set CKE to be controlled by EMIF/DDR PHY */
	writel(DDR_CKE_CTRL_NORMAL, &ddrctrl->ddrckectrl);

	/* Program EMIF instance */
	config_ddr_phy(regs);
	set_sdram_timings(regs);
	config_sdram(regs);
}

@c [email protected]

           @[email protected]

bl_main

<arch/arm/lib/crt0.S>
/*
 * entry point of crt0 sequence
 */

.global _main

_main:

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_NAND_SPL)
	/* deprecated, use instead CONFIG_SPL_BUILD */
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
#elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
	ldr	sp, =(CONFIG_SPL_STACK)
#else
	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
	sub	sp, #GD_SIZE	/* allocate one GD above SP */
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
	mov	r8, sp		/* GD is above SP */
	mov	r0, #0
	bl	board_init_f

#if ! defined(CONFIG_SPL_BUILD)

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_sp, gd, addr_moni). Trick here is that
 * we'll return 'here' but relocated.
 */

	ldr	sp, [r8, #GD_START_ADDR_SP]	/* r8 = gd->start_addr_sp */
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
	ldr	r8, [r8, #GD_BD]		/* r8 = gd->bd */
	sub	r8, r8, #GD_SIZE		/* new GD is below bd */

	adr	lr, here
	ldr	r0, [r8, #GD_RELOC_OFF]		/* lr = gd->start_addr_sp */
	add	lr, lr, r0
	ldr	r0, [r8, #GD_START_ADDR_SP]	/* r0 = gd->start_addr_sp */
	mov	r1, r8				/* r1 = gd */
	ldr	r2, [r8, #GD_RELOCADDR]		/* r2 = gd->relocaddr */
	b	relocate_code
here:

/* Set up final (full) environment */

	bl	c_runtime_cpu_setup	/* we still call old routine here */

	ldr	r0, =__bss_start	/* this is auto-relocated! */
	ldr	r1, =__bss_end__	/* this is auto-relocated! */

	mov	r2, #0x00000000		/* prepare zero to clear BSS */

clbss_l:cmp	r0, r1			/* while not at end of BSS */
	strlo	r2, [r0]		/* clear 32-bit BSS word */
	addlo	r0, r0, #4		/* move to next */
	blo	clbss_l

	bl coloured_LED_init
	bl red_led_on

#if defined(CONFIG_NAND_SPL)

	/* call _nand_boot() */
	ldr     pc, =nand_boot

#else

	/* call board_init_r(gd_t *id, ulong dest_addr) */
	mov	r0, r8			/* gd_t */
	ldr	r1, [r8, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
	ldr	pc, =board_init_r	/* this is auto-relocated! */

#endif

	/* we should not return here. */

#endif

@[email protected] 設定 internal RAM 記憶體空間的棧指標,呼叫 board_init_f()函式

<arch/arm/lib/board.c>

void board_init_f(ulong bootflag)
{
	bd_t *bd;
	init_fnc_t **init_fnc_ptr;
	gd_t *id;
	ulong addr, addr_sp;
#ifdef CONFIG_PRAM
	ulong reg;
#endif
	void *new_fdt = NULL;
	size_t fdt_size = 0;

	memset((void *)gd, 0, sizeof(gd_t));

	gd->mon_len = _bss_end_ofs;
#ifdef CONFIG_OF_EMBED
	/* Get a pointer to the FDT */
	gd->fdt_blob = _binary_dt_dtb_start;
#elif defined CONFIG_OF_SEPARATE
	/* FDT is at end of image */
	gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
#endif
	/* Allow the early environment to override the fdt address */
	gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
						(uintptr_t)gd->fdt_blob);

	for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
		if ((*init_fnc_ptr)() != 0) {
			hang ();
		}
	}

#ifdef CONFIG_OF_CONTROL
	/* For now, put this check after the console is ready */
	if (fdtdec_prepare_fdt()) {
		panic("** CONFIG_OF_CONTROL defined but no FDT - please see "
			"doc/README.fdt-control");
	}
#endif

	debug("monitor len: %08lX\n", gd->mon_len);
	/*
	 * Ram is setup, size stored in gd !!
	 */
	debug("ramsize: %08lX\n", gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
	/*
	 * 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 should work
	 * for arch/ppc and arch/powerpc. Only Linux board ports in
	 * arch/powerpc with bootwrapper support, that recalculate the
	 * memory size from the SDRAM controller setup will have to
	 * get fixed.
	 */
	gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif

	addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;

#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
	/* reserve kernel log buffer */
	addr -= (LOGBUFF_RESERVE);
	debug("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN,
		addr);
#endif
#endif

#ifdef CONFIG_PRAM
	/*
	 * reserve protected RAM
	 */
	reg = getenv_ulong("pram", 10, CONFIG_PRAM);
	addr -= (reg << 10);		/* size is in kB */
	debug("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */

#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
	/* reserve TLB table */
	gd->tlb_size = 4096 * 4;
	addr -= gd->tlb_size;

	/* round down to next 64 kB limit */
	addr &= ~(0x10000 - 1);

	gd->tlb_addr = addr;
	debug("TLB table from %08lx to %08lx\n", addr, addr + gd->tlb_size);
#endif

	/* round down to next 4 kB limit */
	addr &= ~(4096 - 1);
	debug("Top of RAM usable for U-Boot at: %08lx\n", addr);

#ifdef CONFIG_LCD
#ifdef CONFIG_FB_ADDR
	gd->fb_base = CONFIG_FB_ADDR;
#else
	/* reserve memory for LCD display (always full pages) */
	addr = lcd_setmem(addr);
	gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */

	/*
	 * reserve memory for U-Boot code, data & bss
	 * round down to next 4 kB limit
	 */
	addr -= gd->mon_len;
	addr &= ~(4096 - 1);

	debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);

#ifndef CONFIG_SPL_BUILD
	/*
	 * reserve memory for malloc() arena
	 */
	addr_sp = addr - TOTAL_MALLOC_LEN;
	debug("Reserving %dk for malloc() at: %08lx\n",
			TOTAL_MALLOC_LEN >> 10, addr_sp);
	/*
	 * (permanently) allocate a Board Info struct
	 * and a permanent copy of the "global" data
	 */
	addr_sp -= sizeof (bd_t);
	bd = (bd_t *) addr_sp;
	gd->bd = bd;
	debug("Reserving %zu Bytes for Board Info at: %08lx\n",
			sizeof (bd_t), addr_sp);

#ifdef CONFIG_MACH_TYPE
	gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif

	addr_sp -= sizeof (gd_t);
	id = (gd_t *) addr_sp;
	debug("Reserving %zu Bytes for Global Data at: %08lx\n",
			sizeof (gd_t), addr_sp);

#if defined(CONFIG_OF_SEPARATE) && defined(CONFIG_OF_CONTROL)
	/*
	 * If the device tree is sitting immediate 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) {
		fdt_size = ALIGN(fdt_totalsize(gd->fdt_blob) + 0x1000, 32);

		addr_sp -= fdt_size;
		new_fdt = (void *)addr_sp;
		debug("Reserving %zu Bytes for FDT at: %08lx\n",
		      fdt_size, addr_sp);
	}
#endif

	/* setup stackpointer for exeptions */
	gd->irq_sp = addr_sp;
#ifdef CONFIG_USE_IRQ
	addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
	debug("Reserving %zu Bytes for IRQ stack at: %08lx\n",
		CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
	/* leave 3 words for abort-stack    */
	addr_sp -= 12;

	/* 8-byte alignment for ABI compliance */
	addr_sp &= ~0x07;
#else
	addr_sp += 128;	/* leave 32 words for abort-stack   */
	gd->irq_sp = addr_sp;
#endif

	debug("New Stack Pointer is: %08lx\n", addr_sp);

#ifdef CONFIG_POST
	post_bootmode_init();
	post_run(NULL, POST_ROM | post_bootmode_get(0));
#endif

	gd->bd->bi_baudrate = gd->baudrate;
	/* Ram ist board specific, so move it to board code ... */
	dram_init_banksize();
	display_dram_config();	/* and display it */

	gd->relocaddr = addr;
	gd->start_addr_sp = addr_sp;
	gd->reloc_off = addr - _TEXT_BASE;
	debug("relocation Offset is: %08lx\n", gd->reloc_off);
	if (new_fdt) {
		memcpy(new_fdt, gd->fdt_blob, fdt_size);
		gd->fdt_blob = new_fdt;
	}
	memcpy(id, (void *)gd, sizeof(gd_t));
} 

bl relocate_code

<arch/arm/lib/crt0.S>
/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_sp, gd, addr_moni). Trick here is that
 * we'll return 'here' but relocated.
 */

	ldr	sp, [r8, #GD_START_ADDR_SP]	/* r8 = gd->start_addr_sp */
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
	ldr	r8, [r8, #GD_BD]		/* r8 = gd->bd */
	sub	r8, r8, #GD_SIZE		/* new GD is below bd */

	adr	lr, here
	ldr	r0, [r8, #GD_RELOC_OFF]		/* lr = gd->start_addr_sp */
	add	lr, lr, r0
	ldr	r0, [r8, #GD_START_ADDR_SP]	/* r0 = gd->start_addr_sp */
	mov	r1, r8				/* r1 = gd */
	ldr	r2, [r8, #GD_RELOCADDR]		/* r2 = gd->relocaddr */
	b	relocate_code

@[email protected] 程式碼重定位

程式碼重定向,它首先檢測自己(MLO)是否已經在記憶體中:

如果是直接跳到下面的堆疊初始化程式碼clear_bss

如果不是就將自己從Nor Flash中拷貝到記憶體中。

Nor Flash Nand Flash 本質區別就在於是否進行程式碼拷貝,也就是下面程式碼所表述:無論Nor Flash 還是Nand Flash,核心思想就是將 uboot 程式碼搬運到記憶體中去執行,但是沒有拷bss 後面這段程式碼,只拷貝bss 前面的程式碼,bss 程式碼是放置全域性變數的。Bss 段程式碼是為了清零,拷貝過去再清零重複操作

<arch/arm/cpu/armv7/start.s>
ENTRY(relocate_code)
	mov	r4, r0	/* save addr_sp */
	mov	r5, r1	/* save addr of gd */
	mov	r6, r2	/* save addr of destination */

	adr	r0, _start
	cmp	r0, r6
	moveq	r9, #0		/* no relocation. relocation offset(r9) = 0 */
	beq	relocate_done		/* skip relocation */
	mov	r1, r6			/* r1 <- scratch for copy_loop */
	ldr	r3, _image_copy_end_ofs
	add	r2, r0, r3		/* r2 <- source end address	    */

copy_loop:
	ldmia	r0!, {r9-r10}		/* copy from source address [r0]    */
	stmia	r1!, {r9-r10}		/* copy to   target address [r1]    */
	cmp	r0, r2			/* until source end address [r2]    */
	blo	copy_loop

	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r0, _TEXT_BASE		/* r0 <- Text base */
	sub	r9, r6, r0		/* r9 <- relocation offset */
	ldr	r10, _dynsym_start_ofs	/* r10 <- sym table ofs */
	add	r10, r10, r0		/* r10 <- sym table in FLASH */
	ldr	r2, _rel_dyn_start_ofs	/* r2 <- rel dyn start ofs */
	add	r2, r2, r0		/* r2 <- rel dyn start in FLASH */
	ldr	r3, _rel_dyn_end_ofs	/* r3 <- rel dyn end ofs */
	add	r3, r3, r0		/* r3 <- rel dyn end in FLASH */
fixloop:
	ldr	r0, [r2]		/* r0 <- location to fix up, IN FLASH! */
	add	r0, r0, r9		/* r0 <- location to fix up in RAM */
	ldr	r1, [r2, #4]
	and	r7, r1, #0xff
	cmp	r7, #23			/* relative fixup? */
	beq	fixrel
	cmp	r7, #2			/* absolute fixup? */
	beq	fixabs
	/* ignore unknown type of fixup */
	b	fixnext
fixabs:
	/* absolute fix: set location to (offset) symbol value */
	mov	r1, r1, LSR #4		/* r1 <- symbol index in .dynsym */
	add	r1, r10, r1		/* r1 <- address of symbol in table */
	ldr	r1, [r1, #4]		/* r1 <- symbol value */
	add	r1, r1, r9		/* r1 <- relocated sym addr */
	b	fixnext
fixrel:
	/* relative fix: increase location by offset */
	ldr	r1, [r0]
	add	r1, r1, r9
fixnext:
	str	r1, [r0]
	add	r2, r2, #8		/* each rel.dyn entry is 8 bytes */
	cmp	r2, r3
	blo	fixloop

relocate_done:

	bx	lr

_rel_dyn_start_ofs:
	.word __rel_dyn_start - _start
_rel_dyn_end_ofs:
	.word __rel_dyn_end - _start
_dynsym_start_ofs:
	.word __dynsym_start - _start
ENDPROC(relocate_code)

@[email protected] 呼叫函式 board_init_r,用以完成 MLOSPI)階段的所有初始化,並跳轉到 uboot.img 階段

<arch/arm/lib/crt0.s>
#if defined(CONFIG_NAND_SPL)

	/* call _nand_boot() */
	ldr     pc, =nand_boot

#else

	/* call board_init_r(gd_t *id, ulong dest_addr) */
	mov	r0, r8			/* gd_t */
	ldr	r1, [r8, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
	ldr	pc, =board_init_r	/* this is auto-relocated! */

#endif
<arch/arm/lib/board.c>
void board_init_r(gd_t *id, ulong dest_addr)
{
	ulong malloc_start;
#if !defined(CONFIG_SYS_NO_FLASH)
	ulong flash_size;
#endif

	gd->flags |= GD_FLG_RELOC;	/* tell others: relocation done */
	bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");

	monitor_flash_len = _end_ofs;

	/* Enable caches */
	enable_caches();

	debug("monitor flash len: %08lX\n", monitor_flash_len);
	board_init();	/* Setup chipselects */
	/*
	 * TODO: printing of the clock inforamtion of the board is now
	 * implemented as part of bdinfo command. Currently only support for
	 * davinci SOC's is added. Remove this check once all the board
	 * implement this.
	 */
#ifdef CONFIG_CLOCKS
	set_cpu_clk_info(); /* Setup clock information */
#endif
	serial_initialize();

	debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);

#ifdef CONFIG_LOGBUFFER
	logbuff_init_ptrs();
#endif
#ifdef CONFIG_POST
	post_output_backlog();
#endif

	/* The Malloc area is immediately below the monitor copy in DRAM */
	malloc_start = dest_addr - TOTAL_MALLOC_LEN;
	mem_malloc_init (malloc_start, TOTAL_MALLOC_LEN);

#ifdef CONFIG_ARCH_EARLY_INIT_R
	arch_early_init_r();
#endif
	power_init_board();

#if !defined(CONFIG_SYS_NO_FLASH)
	puts("Flash: ");

	flash_size = flash_init();
	if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
		print_size(flash_size, "");
		/*
		 * Compute and print flash CRC if flashchecksum is set to 'y'
		 *
		 * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
		 */
		if (getenv_yesno("flashchecksum") == 1) {
			printf("  CRC: %08X", crc32(0,
				(const unsigned char *) CONFIG_SYS_FLASH_BASE,
				flash_size));
		}
		putc('\n');
# else	/* !CONFIG_SYS_FLASH_CHECKSUM */
		print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
	} else {
		puts(failed);
		hang();
	}
#endif

#if defined(CONFIG_CMD_NAND)
	puts("NAND:  ");
	nand_init();		/* go init the NAND */
#endif

#if defined(CONFIG_CMD_ONENAND)
	onenand_init();
#endif

#ifdef CONFIG_GENERIC_MMC
	puts("MMC:   ");
	mmc_initialize(gd->bd);
#endif

#ifdef CONFIG_HAS_DATAFLASH
	AT91F_DataflashInit();
	dataflash_print_info();
#endif

	/* initialize environment */
	if (should_load_env())
		env_relocate();
	else
		set_default_env(NULL);

#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
	arm_pci_init();
#endif

	stdio_init();	/* get the devices list going. */

	jumptable_init();

#if defined(CONFIG_API)
	/* Initialize API */
	api_init();
#endif

	console_init_r();	/* fully init console as a device */

#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
# ifdef CONFIG_OF_CONTROL
	/* Put this here so it appears on the LCD, now it is ready */
	display_fdt_model(gd->fdt_blob);
# else
	checkboard();
# endif
#endif

#if defined(CONFIG_ARCH_MISC_INIT)
	/* miscellaneous arch dependent initialisations */
	arch_misc_init();
#endif
#if defined(CONFIG_MISC_INIT_R)
	/* miscellaneous platform dependent initialisations */
	misc_init_r();
#endif

	 /* set up exceptions */
	interrupt_init();
	/* enable exceptions */
	enable_interrupts();

	/* Initialize from environment */
	load_addr = getenv_ulong("loadaddr", 16, load_addr);

#ifdef CONFIG_BOARD_LATE_INIT
	board_late_init();
#endif

#ifdef CONFIG_BITBANGMII
	bb_miiphy_init();
#endif
#if defined(CONFIG_CMD_NET)
	puts("Net:   ");
	eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
	debug("Reset Ethernet PHY\n");
	reset_phy();
#endif
#endif

#ifdef CONFIG_POST
	post_run(NULL, POST_RAM | post_bootmode_get(0));
#endif

#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
	/*
	 * Export available size of memory for Linux,
	 * taking into account the protected RAM at top of memory
	 */
	{
		ulong pram = 0;
		uchar memsz[32];

#ifdef CONFIG_PRAM
		pram = getenv_ulong("pram", 10, CONFIG_PRAM);
#endif
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
		/* Also take the logbuffer into account (pram is in kB) */
		pram += (LOGBUFF_LEN + LOGBUFF_OVERHEAD) / 1024;
#endif
#endif
		sprintf((char *)memsz, "%ldk", (gd->ram_size / 1024) - pram);
		setenv("mem", (char *)memsz);
	}
#endif

	/* main_loop() can return to retry autoboot, if so just run it again. */
	for (;;) {
		main_loop();
	}

	/* NOTREACHED - no way out of command loop except booting */
}

@a [email protected]

3,第三級bootloader:uboot.img做了哪些事情?

uboot.img 記憶體分佈如下:

訪問/arch/arm/lib/board.c中的 board_init_f() 函式:

在 uboot.img 執行過程中,有兩個非常重要的結構體:gd_t 和 bd_t 。

其中gd_t :global_data 資料結構的定義,位於:/arch/arm/include/asm/global_data.h中。

                其成員主要是一些全域性的系統初始化引數。

其中bd_t :bd_info 資料結構的定義,位於:/arch/arm/include/asm/u-boot.h中。

                其成員是開發板的相關引數。

其中DECLARE_GLOBAL_DATA_PTR巨集定義在系統初始化過程中會被頻繁呼叫,

其的作用是,宣告gd這麼一個全域性的指標,這個指標指向gd_t結構體型別,並且這個gd指標是儲存在ARM的r8這個暫存器裡面的。

uboot.img 第一個執行的檔案還是 start.o,其在執行訪問的 board_init_f() 函式定義在 /arch/arm/lib/board.c 中:

因此,系統初始化引數將會被儲存在(儲存 MLO(SPL)檔案的記憶體空間的)末尾 2 KB 處。

通過計算的 gb 指標指向的記憶體空間地址為 gb = 0x4030B000

gb_t結構體中某些元素的值是來自於 uboot.img's header,這個header的資料儲存在記憶體的0x807FFFCO,大小為 64位元組

參考:

http://blog.chinaunix.net/uid-28458801-id-3486399.html