转载地址:https://blog.csdn.net/p942005405/article/details/83376464
写的非常好,收藏学习
参考文件:
1,AM335x ARM Cortex-A8 Microprocessors (MPUs) Technical Reference Manual.pdf;
2,am3359.pdf;
1,am335x的cpu上电后,会跳到哪个地址去执行?
答:
芯片到uboot启动流程 :ROM → MLO(SPL)→ uboot.img
AM335x 中bootloader被分成了 3 个部分:
第一级 bootloader:引导加载程序,板子上电后会自动执行这些代码,如选择哪种方式启动(NAND,SDcard,UART。。。),然后跳转转到第二级 bootloader。这些代码应该是存放在 176KB 的 ROM 中。
![](https://i-blog.csdnimg.cn/blog_migrate/cc9a7560c724d6a3196f3cb260dd78ff.png)
![](https://i-blog.csdnimg.cn/blog_migrate/850ba788105232337e3e4b23ca7be18c.png)
第二级 bootloader:MLO(SPL),用以硬件初始化:关闭看门狗,关闭中断,设置 CPU 时钟频率、速度等操作。然后会跳转到第三级bootloader。MLO文件应该会被映射到 64 KB的 Internal SRAM 中。
![](https://i-blog.csdnimg.cn/blog_migrate/377f3e569d54a81bf8c091e6697e4f9f.png)
第三级 bootloader:uboot.img,C代码的入口。
![](https://i-blog.csdnimg.cn/blog_migrate/0a0c4db984cc385ee74c6c98ba3e221a.png)
其中第一级 bootloader 是板子固化的,第二级和第三级是通过编译 uboot 所得的。
![](https://i-blog.csdnimg.cn/blog_migrate/5c103e589245788d64b1b2fd9a6e3ae1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fbe5005a5978bfe77f5ce686acbdd8f7.png)
2,第二级 bootloader:MLO(SPL)做了哪些事情?
MLO(SPL)内存分布如下:
![](https://i-blog.csdnimg.cn/blog_migrate/00e65d4072b6642fef7b78de9c15cfbe.png)
SPL内存重映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | < 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 . = ALIGN(4); __image_copy_end = .; _end = .; .bss : { . = ALIGN(4); __bss_start = .; *(.bss*) . = ALIGN(4); __bss_end__ = .; } >.sdram } |
1 2 3 4 5 6 7 | <PATH : /include/configs/am335x_evm.h> #define CONFIG_SPL_TEXT_BASE 0x402F0400 #define CONFIG_SPL_MAX_SIZE (46 * 1024) #define CONFIG_SPL_STACK LOW_LEVEL_SRAM_STACK #define CONFIG_SPL_BSS_START_ADDR 0x80000000 #define CONFIG_SPL_BSS_MAX_SIZE 0x80000 /* 512 KB */ <span style="font-size:16px;color:#003399;"><strong></strong></span> |
@1@ 保存启动参数 bl save_boot_params
1 2 3 4 5 6 7 | <PATH : /arch/arm/cpu/armv7/start .S> /* * the actual reset code */ reset: bl save_boot_params |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <PATH : /arch/arm/cpu/armv7/omap-common/lowlevel_init .S> .global save_boot_params 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] |
1 2 3 4 5 6 7 8 | /*《PATH: /arch/arm/include/asm/arch-ti81xx/omap.h》 * Non-secure SRAM Addresses * Non-secure RAM starts at 0x40300000 for GP devices. But we keep SRAM_BASE * at 0x40304000(EMU base) so that our code works for both EMU and GP */ #define NON_SECURE_SRAM_START 0x40304000 #define NON_SECURE_SRAM_END 0x4030E000 #define LOW_LEVEL_SRAM_STACK 0x4030B7FC |
问题:这些参数是保存在哪里的?大概有哪些参数?
答:
这些参数保存的内存地址为 64 KB 的 OCM RAM 中:
![](https://i-blog.csdnimg.cn/blog_migrate/72a21e9bd9c83b57bb00f36d8ee244d4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/66e5f5822d46ac623e36700a6ec963fb.png)
注:Dowloaded Image 区域:是用来保存 MLO(SPL) 文件的,其最大可达到 109 KB
![](https://i-blog.csdnimg.cn/blog_migrate/7814b14a745fd53be3ad9405b75271d1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0cc20f5846beffc2b14b812af35a9244.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0ccf6fe20f0246ef87a3d52bdb26eb1a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c374e95f5cc74ad52c3859dc6507d21a.png)
@a2@ 设置 CPU 为 SVC32 模式
1 2 3 4 5 6 7 8 | <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 program statusregister),每一种处理器模式下都有一个状态寄存器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
详解:http://blog.chinaunix.net/uid-28458801-id-3487199.html
@a3@ CPU的初始化
1 2 3 4 5 | 《PATH : /arch/arm/cpu/armv7/start.S》 /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <PATH : /arch/arm/cpu/armv7/omap-common/lowlevel_init .S> .globl lowlevel_init lowlevel_init: /* * Setup a temporary stack */ ldr sp, =LOW_LEVEL_SRAM_STACK /* * 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} |
问题:CPU的初始化有哪些内容?
答:
@b1@ 首先要设置堆栈区,因为将会调用 C函数来实现CPU的初始化
问题:这个堆栈在什么位置,其内存大小是多少?
答
1 2 | 《PATH :/arch/arm/ include /asm/arch-ti81xx/omap.h》 #define LOW_LEVEL_SRAM_STACK 0x4030B7FC <strong></strong> |
![](https://i-blog.csdnimg.cn/blog_migrate/78547fe253d725e6e805a7590c1e775f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c127eff9a68c3d06bd6ec7d138439d47.png)
@b2@ 执行 s_init() 函数,实现 CPU 的初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | <PATH : /board/ti/am335x/evm.c> /* * early system init of muxing and clocks. */ void s_init( void ) { /* Can be removed as A8 comes up with L2 enabled */ l2_cache_enable(); /* WDT1 is already running when the bootloader gets control * Disable it to avoid "random" resets */ __raw_writel( 0xAAAA , WDT_WSPR); while (__raw_readl(WDT_WWPS) != 0x0 ); __raw_writel( 0x5555 , WDT_WSPR); while (__raw_readl(WDT_WWPS) != 0x0 ); #ifdef CONFIG_SPL_BUILD /* Setup the PLLs and the clocks for the peripherals */ pll_init(); /* Enable RTC32K clock */ rtc32k_enable(); /* UART softreset */ u32 regVal; u32 uart_base = DEFAULT_UART_BASE; enable_uart0_pin_mux(); /* IA Motor Control Board has default console on UART3*/ /* XXX: This is before we've probed / set board_id */ if (board_id == IA_BOARD) { uart_base = UART3_BASE; } regVal = __raw_readl(uart_base + UART_SYSCFG_OFFSET); regVal |= UART_RESET; __raw_writel(regVal, (uart_base + UART_SYSCFG_OFFSET) ); while ((__raw_readl(uart_base + UART_SYSSTS_OFFSET) & UART_CLK_RUNNING_MASK) != UART_CLK_RUNNING_MASK); /* Disable smart idle */ regVal = __raw_readl((uart_base + UART_SYSCFG_OFFSET)); regVal |= UART_SMART_IDLE_EN; __raw_writel(regVal, (uart_base + UART_SYSCFG_OFFSET)); /* Initialize the Timer */ init_timer(); preloader_console_init(); printf( "\nlocation /board/ti/am335x\n" ); //@@ /*@@*/ // led(); /*@@*/ config_am335x_ddr(); #endif } |
@c1@ 使能第二级缓冲区
1 2 3 4 5 6 7 8 9 10 | /* Can be removed as A8 comes up with L2 enabled */ l2_cache_enable(); <PATH : /arch/arm/cpu/armv7/ti81xx/cache.S> l2_cache_enable: push {r0, r1, r2, lr} mrc 15, 0, r3, cr1, cr0, 1 orr r3, r3, #2 mcr 15, 0, r3, cr1, cr0, 1 pop {r1, r2, r3, pc} |
![](https://i-blog.csdnimg.cn/blog_migrate/7c53558e7b715bd821a7b2f5fbda5b06.png)
@c2@ 关闭看门狗(WDT)
1 2 3 4 5 6 7 | /* WDT1 is already running when the bootloader gets control * Disable it to avoid "random" resets */ __raw_writel(0xAAAA, WDT_WSPR); while (__raw_readl(WDT_WWPS) != 0x0); __raw_writel(0x5555, WDT_WSPR); while (__raw_readl(WDT_WWPS) != 0x0); |
1 2 3 4 5 6 7 8 9 10 11 | <PATH : /arch/arm/ include /asm/arch-ti81xx/cpu.h> #define WDT_WSPR (WDT_BASE + 0x048 ) <PATH : /arch/arm/ include /asm/arch-ti81xx/hardware.h> /* Watchdog Timer */ #ifdef CONFIG_AM335X #define WDT_BASE 0x44E35000 # else #define WDT_BASE 0x480C2000 #endif |
![](https://i-blog.csdnimg.cn/blog_migrate/6b49c588787153c6f05958f810882a5e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f8db613d2514e03f9ad18b03e8770f82.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b9b935cab712801ee5cc9cfe79c8a27a.png)
@c3@ 给外设设置好 PLL 和 时钟频率等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* Setup the PLLs and the clocks for the peripherals */ pll_init(); <PATH : /board/ti/am335x/pll.c> /* * Configure the PLL/PRCM for necessary peripherals */ void pll_init() { mpu_pll_config(MPUPLL_M_500); core_pll_config(); per_pll_config(); ddr_pll_config(); /* Enable the required interconnect clocks */ interface_clocks_enable(); /* Enable power domain transition */ power_domain_transition_enable(); /* Enable the required peripherals */ per_clocks_enable(); } |
![](https://i-blog.csdnimg.cn/blog_migrate/7d0609e22fa4b175c87ffbae6b0405b4.png)
@c4@ 使能 32-KHz 频率的实时时钟
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /* Enable RTC32K clock */ rtc32k_enable(); 《PATH : /board/ti/am335x/evm.c》 static void rtc32k_enable( void ) { /* Unlock the rtc's registers */ __raw_writel( 0x83e70b13 , (AM335X_RTC_BASE + RTC_KICK0_REG)); __raw_writel( 0x95a4f1e0 , (AM335X_RTC_BASE + RTC_KICK1_REG)); /* Enable the RTC 32K OSC */ __raw_writel( 0x48 , (AM335X_RTC_BASE + RTC_OSC_REG)); } <PATH : /arch/arm/ include /asm/arch-ti81xx/hardware.h> /* RTC base address */ #define AM335X_RTC_BASE 0x44E3E000 <PATH : /board/ti/am335x/evm.c> #define RTC_KICK0_REG 0x6c #define RTC_KICK1_REG 0x70 #define RTC_OSC_REG 0x54 |
![](https://i-blog.csdnimg.cn/blog_migrate/bbd6649b09cf6515a397bdd91abb2bbc.png)
@c5@ 使能UART0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | /* UART softreset */ u32 regVal; u32 uart_base = DEFAULT_UART_BASE; enable_uart0_pin_mux(); /* IA Motor Control Board has default console on UART3*/ /* XXX: This is before we've probed / set board_id */ if (board_id == IA_BOARD) { uart_base = UART3_BASE; } regVal = __raw_readl(uart_base + UART_SYSCFG_OFFSET); regVal |= UART_RESET; __raw_writel(regVal, (uart_base + UART_SYSCFG_OFFSET) ); while ((__raw_readl(uart_base + UART_SYSSTS_OFFSET) & UART_CLK_RUNNING_MASK) != UART_CLK_RUNNING_MASK); /* Disable smart idle */ regVal = __raw_readl((uart_base + UART_SYSCFG_OFFSET)); regVal |= UART_SMART_IDLE_EN; __raw_writel(regVal, (uart_base + UART_SYSCFG_OFFSET)); <PATH : /arch/arm/include/asm/arch-ti81xx/cpu.h> #ifdef CONFIG_AM335X #define DEFAULT_UART_BASE UART0_BASE #endif <PATH : /arch/arm/include/asm/arch-ti81xx/hardware.h> #ifdef CONFIG_AM335X #define UART0_BASE 0x44E09000 #else #define UART0_BASE 0x48020000 #endif |
![](https://i-blog.csdnimg.cn/blog_migrate/0a4b0b79c045ce8cf64f0c01fbff13e8.png)
@c6@ 初始化 定时器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /* Initialize the Timer */ init_timer(); <PATH : /board/ti/am335x/evm.c> static void init_timer( void ) { /* Reset the Timer */ __raw_writel( 0x2 , (DM_TIMER2_BASE + TSICR_REG)); /* Wait until the reset is done */ while (__raw_readl(DM_TIMER2_BASE + TIOCP_CFG_REG) & 1 ); /* Start the Timer */ __raw_writel( 0x1 , (DM_TIMER2_BASE + TCLR_REG)); } <PATH : /arch/arm/ include /asm/arch-ti81xx/hardware.h> /* DM Timer base addresses */ #define DM_TIMER0_BASE 0x4802C000 #define DM_TIMER1_BASE 0x4802E000 #define DM_TIMER2_BASE 0x48040000 #define DM_TIMER3_BASE 0x48042000 #define DM_TIMER4_BASE 0x48044000 #define DM_TIMER5_BASE 0x48046000 #define DM_TIMER6_BASE 0x48048000 #define DM_TIMER7_BASE 0x4804A000 |
![](https://i-blog.csdnimg.cn/blog_migrate/8131b7b58ebeede344343b9b5694acf1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c884f537ff13a1603cfb76857758d1d1.png)
@c7@ 初始化控制台,通过UART可以查看相关信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | preloader_console_init(); 《PATH : /arch/arm/cpu/armv7/omap-common/spl.c》 /* This requires UART clocks to be enabled */ void preloader_console_init( void ) { const char *u_boot_rev = U_BOOT_VERSION; char rev_string_buffer[50]; gd = &gdata; gd->bd = &bdata; gd->flags |= GD_FLG_RELOC; gd->baudrate = CONFIG_BAUDRATE; serial_init(); /* serial communications setup */ /* Avoid a second "U-Boot" coming from this string */ u_boot_rev = &u_boot_rev[7]; printf ( "\nU-Boot SPL %s (%s - %s)\n" , u_boot_rev, U_BOOT_DATE, U_BOOT_TIME); omap_rev_string(rev_string_buffer); printf ( "Texas Instruments %s\n" , rev_string_buffer); } <span style= "font-size:14px;color:#003399;" ></span> |
@c8@ 配置 DDR
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | config_am335x_ddr(); 《PATH :》 /* void DDR2_EMIF_Config(void); */ static void config_am335x_ddr( void ) { int data_macro_0 = 0; int data_macro_1 = 1; enable_ddr_clocks(); config_vtp(); Cmd_Macro_Config(); Data_Macro_Config(data_macro_0); Data_Macro_Config(data_macro_1); __raw_writel(PHY_RANK0_DELAY, DATA0_RANK0_DELAYS_0); __raw_writel(PHY_RANK0_DELAY, DATA1_RANK0_DELAYS_0); __raw_writel(DDR_IOCTRL_VALUE, DDR_CMD0_IOCTRL); __raw_writel(DDR_IOCTRL_VALUE, DDR_CMD1_IOCTRL); __raw_writel(DDR_IOCTRL_VALUE, DDR_CMD2_IOCTRL); __raw_writel(DDR_IOCTRL_VALUE, DDR_DATA0_IOCTRL); __raw_writel(DDR_IOCTRL_VALUE, DDR_DATA1_IOCTRL); __raw_writel(__raw_readl(DDR_IO_CTRL) & 0xefffffff, DDR_IO_CTRL); __raw_writel(__raw_readl(DDR_CKE_CTRL) | 0x00000001, DDR_CKE_CTRL); config_emif_ddr2(); } 《PATH : /arm/include/asm/arch-ti81xx/cpu.h》 #define DATA0_RANK0_DELAYS_0 (DDR_PHY_BASE_ADDR + 0x134) #define DATA1_RANK0_DELAYS_0 (DDR_PHY_BASE_ADDR + 0x1D8) /* DDR offsets */ #define DDR_PHY_BASE_ADDR 0x44E12000 #define DDR_IO_CTRL 0x44E10E04 #define DDR_CKE_CTRL 0x44E1131C #define CONTROL_BASE_ADDR 0x44E10000 |
![](https://i-blog.csdnimg.cn/blog_migrate/1e3115a485adef85eac8956d0448cc82.png)
@c DONE@
@b DONE@
@a4@ 设置 internal RAM 内存空间的栈指针,调用 board_init_f()函数
1 2 3 4 5 6 | /* Set stackpointer in internal RAM to call board_init_f */ call_board_init_f: ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ ldr r0,=0x00000000 bl board_init_f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <PATH: include/configs/am335x_evm.h> #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_INIT_RAM_ADDR + \ CONFIG_SYS_INIT_RAM_SIZE - \ GENERATED_GBL_DATA_SIZE) #define CONFIG_SYS_INIT_RAM_ADDR SRAM0_START #define CONFIG_SYS_INIT_RAM_SIZE SRAM0_SIZE <PATH : /arch/arm/include/asm/arch-ti81xx/hardware.h : begin> #ifdef CONFIG_AM335X #define SRAM0_START 0x402F0400 #else #define SRAM0_START 0x40300000 #endif <PATH : /arch/arm/include/asm/arch-ti81xx/hardware.h : end> <PATH : /arch/arm/include/asm/arch-ti81xx/cpu.h : begin> #if defined(CONFIG_AM335X) || defined(CONFIG_TI814X) #define SRAM0_SIZE (0x1B400) /* 109 KB */ #define SRAM_GPMC_STACK_SIZE (0x40) #endif <PATH : /arch/arm/include/asm/arch-ti81xx/cpu.h : end> <PATH : /am335x/include/generated/generic-asm-offsets.h : begin> #define GENERATED_GBL_DATA_SIZE (128) /* (sizeof(struct global_data) + 15) & ~15 */ <PATH : /am335x/include/generated/generic-asm-offsets.h : end> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <PATH : /arch/arm/cpu/armv7/omap-common/spl.c'> void board_init_f(ulong dummy) { /* * We call relocate_code() with relocation target same as the * CONFIG_SYS_SPL_TEXT_BASE. This will result in relocation getting * skipped. Instead, only .bss initialization will happen. That's * all we need */ debug( ">>board_init_f()\n" ); relocate_code(CONFIG_SPL_STACK, &gdata, CONFIG_SPL_TEXT_BASE); } <PATH : /arch/arm/cpu/armv7/omap-common/spl.c : begin> #define CONFIG_SPL_TEXT_BASE 0x402F0400 #define CONFIG_SPL_MAX_SIZE (46 * 1024) #define CONFIG_SPL_STACK LOW_LEVEL_SRAM_STACK <PATH : /arch/arm/cpu/armv7/omap-common/spl.c : end> <PATH : /arch/arm/include/asm/arch-ti81xx/omap.h : begin> #define LOW_LEVEL_SRAM_STACK 0x4030B7FC <PATH : /arch/arm/include/asm/arch-ti81xx/omap.h : end> |
1 2 3 4 5 6 7 8 9 10 11 12 13 | <PATH : /arch/arm/cpu/armv7/start .S> /* * void relocate_code (addr_sp, gd, addr_moni) * * This "function" does not return , instead it continues in RAM * after relocating the monitor code. * */ .globl relocate_code relocate_code: mov r4, r0 /* save addr_sp */ mov r5, r1 /* save addr of gd */ mov r6, r2 /* save addr of destination 0x402F0400*/ |
@a5@ 代码重定位
代码重定向,它首先检测自己(MLO)是否已经在内存中:
如果是直接跳到下面的堆栈初始化代码 clear_bss。
如果不是就将自己从Nor Flash中拷贝到内存中。
Nor Flash 和Nand Flash 本质区别就在于是否进行代码拷贝,也就是下面代码所表述:无论
是Nor Flash 还是Nand Flash,核心思想就是将 uboot 代码搬运到内存中去运行,但是没有拷
贝bss 后面这段代码,只拷贝bss 前面的代码,bss 代码是放置全局变量的。Bss 段代码是为
了清零,拷贝过去再清零重复操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* Set up the stack */ stack_setup: mov sp, r4 adr r0, _start cmp r0, r6 moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */ beq clear_bss /* 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 |
@a6@ 清空 bss 段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | clear_bss: ldr r0, _bss_start_ofs ldr r1, _bss_end_ofs mov r4, r6 /* reloc addr */ add r0, r0, r4 add r1, r1, r4 mov r2, #0x00000000 /* clear */ clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 bne clbss_l /* * These are defined in the board-specific linker script. */ .globl _bss_start_ofs _bss_start_ofs: .word __bss_start - _start /* __bss_start = 0x80000000 */ |
@a7@ 调用函数 board_init_r,用以完成 MLO(SPI)阶段的所有初始化,并跳转到 uboot.img 阶段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* * We are done . Do not return , instead branch to second part of board * initialization, now running from RAM. */ jump_2_ram: /* * If I-cache is enabled invalidate it */ #ifndef CONFIG_SYS_ICACHE_OFF mcr p15, 0, r0, c7, c5, 0 @ invalidate icache mcr p15, 0, r0, c7, c10, 4 @ DSB mcr p15, 0, r0, c7, c5, 4 @ ISB #endif ldr r0, _board_init_r_ofs adr r1, _start add lr, r0, r1 add lr, lr, r9 /* setup parameters for board_init_r */ mov r0, r5 /* gd_t */ mov r1, r6 /* dest_addr */ /* jump to it ... */ mov pc, lr _board_init_r_ofs: .word board_init_r - _start |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | 《PATH : /arch/arm/cpu/armv7/omap-common/spl.c 》 void board_init_r(gd_t *id, ulong dummy) { u32 boot_device; debug( ">>spl:board_init_r()\n" ); timer_init(); i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); #ifdef CONFIG_SPL_BOARD_INIT spl_board_init(); #endif boot_device = omap_boot_device(); debug( "boot device - %d\n" , boot_device); switch (boot_device) { #ifdef CONFIG_SPL_MMC_SUPPORT case BOOT_DEVICE_MMC1: case BOOT_DEVICE_MMC2: spl_mmc_load_image(); break ; #endif #ifdef CONFIG_SPL_NAND_SUPPORT case BOOT_DEVICE_NAND: spl_nand_load_image(); break ; #endif #ifdef CONFIG_SPL_YMODEM_SUPPORT case BOOT_DEVICE_UART: spl_ymodem_load_image(); break ; #endif default : printf ( "SPL: Un-supported Boot Device - %d!!!\n" , boot_device); hang(); break ; } switch (spl_image.os) { case IH_OS_U_BOOT: debug( "Jumping to U-Boot\n" ); jump_to_image_no_args(); break ; default : puts ( "Unsupported OS image.. Jumping nevertheless..\n" ); jump_to_image_no_args(); } } |
@a DONE@
3,第三级 bootloader:uboot.img 做了哪些事情?
uboot.img 内存分布如下:
![](https://i-blog.csdnimg.cn/blog_migrate/cd910bffa2a5ff0cd112717f82f06a32.png)
![](https://i-blog.csdnimg.cn/blog_migrate/87887b002327f6fe57acbc442406ba85.png)
访问 /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 中。
其成员是开发板的相关参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | <PATH : /arch/arm/include/asm/global_data.h > /* * The following data structure is placed in some memory which is * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or * some locked parts of the data cache) to allow for a minimum set of * global variables during system initialization (until we have set * up the memory controller so that we can use RAM). * * Keep it *SMALL* and remember to set GENERATED_GBL_DATA_SIZE > sizeof(gd_t) */ typedef struct global_data { bd_t *bd; unsigned long flags; unsigned long baudrate; unsigned long have_console; /* serial_init() was called */ unsigned long env_addr; /* Address of Environment struct */ unsigned long env_valid; /* Checksum of Environment valid? */ unsigned long fb_base; /* base address of frame buffer */ #ifdef CONFIG_FSL_ESDHC unsigned long sdhc_clk; #endif #ifdef CONFIG_AT91FAMILY /* "static data" needed by at91's clock.c */ unsigned long cpu_clk_rate_hz; unsigned long main_clk_rate_hz; unsigned long mck_rate_hz; unsigned long plla_rate_hz; unsigned long pllb_rate_hz; unsigned long at91_pllb_usb_init; #endif #ifdef CONFIG_ARM /* "static data" needed by most of timer.c on ARM platforms */ unsigned long timer_rate_hz; unsigned long tbl; unsigned long tbu; unsigned long long timer_reset_value; unsigned long lastinc; #endif #ifdef CONFIG_IXP425 unsigned long timestamp; #endif unsigned long relocaddr; /* Start address of U-Boot in RAM */ phys_size_t ram_size; /* RAM size */ unsigned long mon_len; /* monitor len */ unsigned long irq_sp; /* irq stack pointer */ unsigned long start_addr_sp; /* start_addr_stackpointer */ unsigned long reloc_off; #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) unsigned long tlb_addr; #endif void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ } gd_t; #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8") <PATH : /arch/arm/include/asm/u-boot.h > typedef struct bd_info { int bi_baudrate; /* serial console baudrate */ unsigned long bi_ip_addr; /* IP Address */ ulong bi_arch_number; /* unique id for this board */ ulong bi_boot_params; /* where this board expects params */ struct /* RAM configuration */ { ulong start; ulong size; } bi_dram[CONFIG_NR_DRAM_BANKS]; } bd_t; |
其中 DECLARE_GLOBAL_DATA_PTR 宏定义在系统初始化过程中会被频繁调用,
其的作用是,声明gd这么一个全局的指针,这个指针指向gd_t结构体类型,并且这个gd指针是保存在ARM的r8这个寄存器里面的。
uboot.img 第一个运行的文件还是 start.o,其在运行访问的 board_init_f() 函数定义在 /arch/arm/lib/board.c 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <PATH : /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; /* Pointer is writable since we allocated a register for it */ gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); /* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__( "" : : : "memory" ); memset (( void *)gd, 0, sizeof (gd_t)); ... } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <PATH : /include/configs/am335x_evm> #define CONFIG_SYS_INIT_RAM_ADDR SRAM0_START #define CONFIG_SYS_INIT_RAM_SIZE SRAM0_SIZE #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_INIT_RAM_ADDR + \ CONFIG_SYS_INIT_RAM_SIZE - \ GENERATED_GBL_DATA_SIZE) <PATH : /arch/arm/include/asm/arch-ti81xx/hardware.h> #define SRAM0_START 0x402F0400 <PATH : /arch/arm/include/asm/arch-ti81xx/cpu.h> #define SRAM0_SIZE (0x1B400) /* 109 KB */ <PATH : /am335x/include/generated/generic-asm-offsets.h> #define GENERATED_GBL_DATA_SIZE (128) /* (sizeof(struct global_data) + 15) & ~15 */ |
因此,系统初始化参数将会被保存在 (保存 MLO(SPL)文件的内存空间的)末尾 2 KB 处。
通过计算的 gb 指针指向的内存空间地址为 gb = 0x4030B000
![](https://i-blog.csdnimg.cn/blog_migrate/4ec7c5091192a59191cee6a791e1e50b.png)
gb_t 结构体中某些元素的值是来自于 uboot.img's header,这个header的数据保存在内存的0x807FFFCO,大小为 64字节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <PATH : /include/image.h> /* * Legacy format image header, * all data in network byte order (aka natural aka bigendian). */ typedef struct image_header { uint32_t ih_magic; /* Image Header Magic Number */ uint32_t ih_hcrc; /* Image Header CRC Checksum */ uint32_t ih_time; /* Image Creation Timestamp */ uint32_t ih_size; /* Image Data Size */ uint32_t ih_load; /* Data Load Address */ uint32_t ih_ep; /* Entry Point Address */ uint32_t ih_dcrc; /* Image Data CRC Checksum */ uint8_t ih_os; /* Operating System */ uint8_t ih_arch; /* CPU architecture */ uint8_t ih_type; /* Image Type */ uint8_t ih_comp; /* Compression Type */ uint8_t ih_name[IH_NMLEN]; /* Image Name */ } image_header_t; |
1 2 3 4 5 6 7 8 | <PATH : / include /configs/am335x_evm.h> /* * 8MB into the SDRAM to allow for SPL's bss at the beginning of SDRAM. * 64 bytes before this address should be set aside for u-boot.img's * header. That is 0x807FFFC0--0x80800000 should not be used for any * other needs. */ #define CONFIG_SYS_TEXT_BASE 0x80800000 |
原文: http://blog.chinaunix.net/uid-28458801-id-3486399.html