前面我们分析到了s_init函数,现在继续。

1、s_init函数

然后调用s_init来进行更多模块的初始化。函数s_init在arch/arm/cpu/armv7/sunxi/board.c中定义,代码如下。

87 void s_init(void)88 {89 #if !defined CONFIG_SPL_BUILD && defined CONFIG_SUN7I90         /* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */91         asm volatile(92                 "mrc p15, 0, r0, c1, c0, 1\n"93                 "orr r0, r0, #1 << 6\n"94                 "mcr p15, 0, r0, c1, c0, 1\n");95 #endif  96 97         watchdog_init();98         clock_init();99         timer_init();
100         gpio_init();
101
102 #ifdef CONFIG_SPL_BUILD
103         gd = &gdata;
104         preloader_console_init();
105
106 #ifdef CONFIG_SPL_I2C_SUPPORT
107         /* Needed early by sunxi_board_init if PMU is enabled */
108         i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
109 #endif
110
111         sunxi_board_init();
112 #endif
113 }

在该函数内部,**对watchdog、时钟、定时器timer和gpio进行初始化,并对console终端进行初始化,然后进行i2c的初始化,**最后调用sunxi_board_init函数,该函数在board/sunxi/board.c中定义,代码如下。

2、sunxi_board_init

80 void sunxi_board_init(void)81 {82         int power_failed = 0;83         unsigned long ramsize;84 85         printf("DRAM:");86         ramsize = sunxi_dram_init();87         printf(" %lu MiB\n", ramsize >> 20);88         if (!ramsize)89                 hang();90 91 #ifdef CONFIG_AXP152_POWER92         power_failed = axp152_init();93         power_failed |= axp152_set_dcdc2(1400);94         power_failed |= axp152_set_dcdc3(1500);95         power_failed |= axp152_set_dcdc4(1250);96         power_failed |= axp152_set_ldo2(3000);97 #endif98 #ifdef CONFIG_AXP209_POWER99         power_failed |= axp209_init();
100         power_failed |= axp209_set_dcdc2(1400);
101 #ifdef CONFIG_FAST_MBUS
102         power_failed |= axp209_set_dcdc3(1300);
103 #else
104         power_failed |= axp209_set_dcdc3(1250);
105 #endif
106         power_failed |= axp209_set_ldo2(3000);
107         power_failed |= axp209_set_ldo3(2800);
108         power_failed |= axp209_set_ldo4(2800);
109 #endif
110
111         /*
112          * Only clock up the CPU to full speed if we are reasonably
113          * assured it's being powered with suitable core voltage
114          */
115         if (!power_failed)
116 #ifdef CONFIG_SUN7I
117                 clock_set_pll1(912000000);
118 #else
119                 clock_set_pll1(1008000000);
120 #endif
121         else
122                 printf("Failed to set core voltage! Can't set CPU frequency\n");

该函数首先对DRAM进行初始化,然后利用前面初始化的i2c来对axp209电源管理芯片进行配置。

现在回到start.S当中,reset复位异常向量处理的最后一步是调用_main函数。_main函数存在于arch/arm/lib/crt0.S中,在分析代码之前,可以先看看这个函数的注释,注释写的非常详细。

3、_main函数

 13 /*14  * This file handles the target-independent stages of the U-Boot15  * start-up where a C runtime environment is needed. Its entry point16  * is _main and is branched into from the target's start.S file.17  *18  * _main execution sequence is:19  *20  * 1. Set up initial environment for calling board_init_f().21  *    This environment only provides a stack and a place to store22  *    the GD ('global data') structure, both located in some readily23  *    available RAM (SRAM, locked cache...). In this context, VARIABLE24  *    global data, initialized or not (BSS), are UNAVAILABLE; only25  *    CONSTANT initialized data are available.26  *27  * 2. Call board_init_f(). This function prepares the hardware for28  *    execution from system RAM (DRAM, DDR...) As system RAM may not29  *    be available yet, , board_init_f() must use the current GD to30  *    store any data which must be passed on to later stages. These31  *    data include the relocation destination, the future stack, and32  *    the future GD location.33  *34  * (the following applies only to non-SPL builds)35  *36  * 3. Set up intermediate environment where the stack and GD are the37  *    ones allocated by board_init_f() in system RAM, but BSS and38  *    initialized non-const data are still not available.39  *40  * 4. Call relocate_code(). This function relocates U-Boot from its41  *    current location into the relocation destination computed by42  *    board_init_f().43  *44  * 5. Set up final environment for calling board_init_r(). This45  *    environment has BSS (initialized to 0), initialized non-const46  *    data (initialized to their intended value), and stack in system47  *    RAM. GD has retained values set by board_init_f(). Some CPUs48  *    have some work left to do at this point regarding memory, so49  *    call c_runtime_cpu_setup.50  *51  * 6. Branch to board_init_r().52  */

我们对照注释先整理一下_main函数的执行顺序:

1)为调用board_init_f()函数准备最初的环境。

该环境仅仅提供一个栈和存储GD结构体(全局数据)的空间,它们都位于一些可用的RAM中(比如SRAM和锁定的缓存中)。在这样的环境下,可变的全局变量,包括已初始化的和未初始化的(BSS),都是不可用的只有已初始化的常量才是可用的。

2)调用board_init_f()函数。

**该函数为后续在系统RAM(DRAM、DDR)中执行的代码准备好硬件环境。**由于此时系统RAM可能还不可用,board_init_f()函数必须使用当前的GD来存储任何必须传递到后续步骤的数据。这些数据包括重定位目的地址、重新定义的栈和重新定义的GD位置。

下面的步骤只有在non-SPL下才可用。
(这里我觉得其实对于我们实验室使用了安全启动的系统是不具备SPL的。因为加载uboot的东西是其他的image)

3)创建一个中间的环境:

栈和GD都在系统RAM中由board_init_f()函数分配,但是BSS和已初始化的非常量数据仍然不可用。

4)调用relocate_code()。

该函数将U-Boot从当前的位置重定位到由board_init_f()函数计算得出的重定位目的地址处。

5)为调用board_init_r()函数创建最终的环境。

该环境包括BSS(已经初始化为0)、已初始化的非常量数据(已初始化为预期的值)和系统RAM中的栈。GD保存着被board_init_f()函数设定的值。 某些CPU还有一些关于存储的工作要做,所以会调用c_runtime_cpu_setup函数。

6)跳转到board_init_r()函数。

阅览完代码注释后,再来分析一下代码。

 54 /*55  * entry point of crt0 sequence56  */57 58 ENTRY(_main)59 60 /*61  * Set up initial C runtime environment and call board_init_f(0).62  */63 64 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)65         ldr     sp, =(CONFIG_SPL_STACK)66 #else67         ldr     sp, =(CONFIG_SYS_INIT_SP_ADDR)68 #endif69         bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */70         sub     sp, sp, #GD_SIZE        /* allocate one GD above SP */71         bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */72         mov     r9, sp          /* GD is above SP */73         mov     r0, #074         bl      board_init_f75 76 #if ! defined(CONFIG_SPL_BUILD)77 78 /*79  * Set up intermediate environment (new sp and gd) and call80  * relocate_code(addr_moni). Trick here is that we'll return81  * 'here' but relocated.82  */83 84         ldr     sp, [r9, #GD_START_ADDR_SP]/* sp = gd->start_addr_sp */85         bic     sp, sp, #7      /* 8-byte alignment for ABI compliance */86         ldr     r9, [r9, #GD_BD]                /* r9 = gd->bd */87         sub     r9, r9, #GD_SIZE                /* new GD is below bd */88 89         adr     lr, here90         ldr     r0, [r9, #GD_RELOC_OFF]         /* r0 = gd->reloc_off */91         add     lr, lr, r092         ldr     r0, [r9, #GD_RELOCADDR]         /* r0 = gd->relocaddr */93         b       relocate_code94 here:95 96 /* Set up final (full) environment */97 98         bl      c_runtime_cpu_setup     /* we still call old routine here */99
100         ldr     r0, =__bss_start        /* this is auto-relocated! */
101         ldr     r1, =__bss_end          /* this is auto-relocated! */
102
103         mov     r2, #0x00000000         /* prepare zero to clear BSS */
104
105 clbss_l:cmp     r0, r1        /* while not at end of BSS */
106         strlo   r2, [r0]      /* clear 32-bit BSS word */
107         addlo   r0, r0, #4    /* move to next */
108         blo     clbss_l
109
110         bl coloured_LED_init
111         bl red_led_on
112
113         /* call board_init_r(gd_t *id, ulong dest_addr) */
114         mov     r0, r9                  /* gd_t */
115         ldr     r1, [r9, #GD_RELOCADDR] /* dest_addr */
116         /* call board_init_r */
117         ldr     pc, =board_init_r       /* this is auto-relocated! */
118
119         /* we should not return here. */
120
121 #endif
122
123 ENDPROC(_main)

因为我们采用了SPL框架,所以后面的步骤36都不会执行,也就是代码行76121不会执行。第64~68行代码设定栈指针,然后保证栈指针的8字节对齐,接着在栈的顶部为GD结构体分配存储空间,由于栈指针在分配空间时往底部移动了,所以要重新保证栈指针的8字节对齐。在设定栈指针的字节对齐后,有一句mov r9,sp,因为前面的操作中r9保存的是GD的地址,所以这里表明GD位置位于栈指针之上。

最后调用board_init_f(0)函数,注意这个函数是带有参数的。参数为赋值为0的r0。具体的调用规则可以查看ATPCS(ARM-THUMB procedure call standard)。

关于ATPCS,ARM官网上有一篇名为《The ARM-THUMB Procedure Call Standard》的文档对其做了全面的介绍,网址是http://infocenter.arm.com/help/topic/com.arm.doc.espc0002/ATPCS.pdf。这里,我们简单了解一下ATPCS,ATPCS统一了APCS(ARM Procedure Call Standard)和TPCS(Thumb Procedure Call Standard)两种标准。ATPCS标准提出的目的是:1)同时支持ARM态和Thumb态。
2)支持ARM态和Thumb态的联合工作。
3)支持更小的代码体积、嵌入式应用的合适功能、高性能。
4)支持可选的浮点运算架构和指令集。
5)二进制兼容APCS和TPCS。ATPCS规定了一些子程序之间的基本调用规则。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。为适应一些特定的需要,对这些基本的调用规则进行一些修改,可得到几种不同的子程序调用规则,这些特定的调用规则包括:支持数据栈限制检查的ATPCS、支持只读段位置无关的ATPCS、支持可读写段位置无关的ATPCS、支持ARM程序和Thumb程序混合使用的ATPCS、处理浮点运算的ATPCS。

在这里,board_init_f函数在arch/arm/lib里的board.c和spl.c中都有定义,因为这里还是SPL的启动阶段,所以应该用的是spl.c中的board_init_f函数。这可以从当前目录中的Makefile来确认一下:

ifndef CONFIG_SPL_BUILD
ifdef CONFIG_ARM64
obj-y += relocate_64.o
else
obj-y += relocate.o
endif
ifndef CONFIG_SYS_GENERIC_BOARD
obj-y += board.o
endif
obj-$(CONFIG_CPU_V7M) += cmd_boot.o
obj-$(CONFIG_OF_LIBFDT) += bootm-fdt.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o
obj-$(CONFIG_SYS_L2_PL310) += cache-pl310.o
obj-$(CONFIG_USE_ARCH_MEMSET) += memset.o
obj-$(CONFIG_USE_ARCH_MEMCPY) += memcpy.o
else
obj-$(CONFIG_SPL_FRAMEWORK) += spl.o
endif

在定义了CONFIG_SPL_BUILD的条件下,我们链接的是spl.o中的board_init_f函数。

在arch/arm/lib/spl.c中定义的board_init_f函数有如下的注释:

/** In the context of SPL, board_init_f must ensure that any clocks/etc for* DDR are enabled, ensure that the stack pointer is valid, clear the BSS* and call board_init_f.  We provide this version by default but mark it* as __weak to allow for platforms to do this in their own way if needed.*/

注释中说,在SPL启动阶段,board_init_f函数必须保证用于DDR的时钟等配置完成,并且保证栈指针是有效的,而且清除了BSS。这里也将它标记为__weak属性,如果需要的话,就可以重写该函数。

4、board_init_f

在arch/arm/lib/spl.c中定义的board_init_f函数如下。

 26 void __weak board_init_f(ulong dummy)27 {     28         /* Clear the BSS. */29         memset(__bss_start, 0, __bss_end - __bss_start);30 31         /* Set global data pointer. */32         gd = &gdata;33 34         board_init_r(NULL, 0);35 }

该函数对BSS进行清零操作,然后调用common/spl/spl.c中的board_init_r函数,该函数首先判断从哪种存储设备启动,这里给出RAM、MMC和NAND的代码。

5、board_init_r

155         boot_device = spl_boot_device();
156         debug("boot device - %d\n", boot_device);
157         switch (boot_device) {
158 #ifdef CONFIG_SPL_RAM_DEVICE
159         case BOOT_DEVICE_RAM:
160                 spl_ram_load_image();
161                 break;
162 #endif
163 #ifdef CONFIG_SPL_MMC_SUPPORT
164         case BOOT_DEVICE_MMC1:
165         case BOOT_DEVICE_MMC2:
166         case BOOT_DEVICE_MMC2_2:
167                 spl_mmc_load_image();
168                 break;
169 #endif
170 #ifdef CONFIG_SPL_NAND_SUPPORT
171         case BOOT_DEVICE_NAND:
172                 spl_nand_load_image();
173                 break;
174 #endif

第158~162行:如果定义了CONFIG_SPL_RAM_DEVICE,并且设备是RAM,则执行spl_ram_load_image(),也就是将image下载到RAM中。

第163~169行:如果定义了CONFIG_SPL_MMC_SUPPORT,并且设备是MMC/SD,则执行spl_mmc_load_image(),也就是将image从mmc/sd里面读取到RAM中。

第170~174行:如果定义了CONFIG_SPL_NAND_SUPPORT,并且设备是Nand Flash,则执行spl_nand_load_image(),也就是将image从Nand Flash中读取到RAM中。

这里我们用的是common/spl/spl_mmc.c中的spl_mmc_load_image()函数,该函数首先初始化MMC接口。

spl_mmc_load_image()

 80         mmc_initialize(gd->bd);81         /* We register only one device. So, the dev id is always 0 */82         mmc = find_mmc_device(0);83         printf("@@ debug by baikal      use the only one device\n");84         if (!mmc) {85 #ifdef CONFIG_SPL_LIBCOMMON_SUPPORT86                 puts("spl: mmc device not found!!\n");87 #endif88                 hang();89         }90         printf("@@  debug by baikal    here we mmc_init\n");91         err = mmc_init(mmc);92         if (err) {93 #ifdef CONFIG_SPL_LIBCOMMON_SUPPORT94                 printf("spl: mmc init failed: err - %d\n", err);95 #endif96                 hang();97         }

然后根据boot_mode将U-Boot加载到内存RAM中。

99         boot_mode = spl_boot_mode();
100         if (boot_mode == MMCSD_MODE_RAW) {
101                 debug("boot mode - RAW\n");
102 #ifdef CONFIG_SPL_OS_BOOT
103                 if (spl_start_uboot() || mmc_load_image_raw_os(mmc))
104 #endif
105                 err = mmc_load_image_raw(mmc,
106                 CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);
107 #ifdef CONFIG_SPL_FAT_SUPPORT
108         } else if (boot_mode == MMCSD_MODE_FAT) {

其中,CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR的定义如下:

#define CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR 80      /* 40KiB */

这个表明U-Boot放置在MMC的40KB偏移处,因为是在8KB偏移处放置32KB大小的SPL,所以40KB=8KB+32KB。其实SPL用不了32KB大小,但是因为SPL是放置在内部的32KB的SRAM中运行,所以给SPL预留32KB的空间。

在mmc_load_image_raw函数中解析镜像头,根据镜像头的结构体数据将U-Boot加载到合适的DDR地址。

我们可以看一下u-boot.img的头:

    bash-4.2# tools/mkimage -l u-boot.img
Image Name:   U-Boot 2014.04-rc2-10390-g96510e
Created:      Sun Jun 29 16:51:19 2014
Image Type:   ARM U-Boot Firmware (uncompressed)
Data Size:    240744 Bytes = 235.10 kB = 0.23 MB
Load Address: 4a000000
Entry Point:  00000000

根据该头部信息,我们得知SPL会将U-Boot加载到0x4a000000地址处运行。当要启动的image位于RAM中后,我们就可以启动它。

然后接着

224         switch (spl_image.os) {
225         case IH_OS_U_BOOT:
226                debug("Jumping to U-Boot\n");
227                break;
228 #ifdef CONFIG_SPL_OS_BOOT
229         case IH_OS_LINUX:
230                debug("Jumping to Linux\n");
231                spl_board_prepare_for_linux();
232                jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
233 #endif
234         default:
235                debug("Unsupported OS image.. Jumping nevertheless..\n");
236         }
237         jump_to_image_no_args(&spl_image);

其中,第224行:判断image的类型。

第225~227行:如果是U-Boot,则直接到237行去运行U-Boot。

第228~233行:如果是Linux,则到232行去启动Linux。

至此,SPL结束它的生命,控制权交于U-Boot或Linux。

(这玩意说明这个board_init_r也是有多个存在,或者是复用,不然这个玩意怎么能跳过UBoot自己去加载Linux。或者是说其实SPL本身也可以去加载Linux?因为他的功能不就是加载Uboot,然后Uboot去加载Kernel,现在我不要你了,我自己去加载kernel,省事。说起这个,对哦?为什么不直接加载Kernel,而要通过UBoot,通过我的认识,应该是SPL就那么大点,干不了那么对事情。通过分级加载来省了内存。其实要解决这个疑惑,可以后面来看看这个下一个阶段做了什么工作,是SPL不能完成的,)

在这里,整个过程是SPL→U-Boot→Linux。我们分析了**SPL调用U-Boot的过程,**接下来再分析一下U-Boot调用Linux的过程。

参考资料:
《深入理解BootLoader》

U-BOOT小全(五):BootLoader源码(SPL-UBoot 2)相关推荐

  1. 全云端万能小程序_万能门店全云端独立版微信小程序源码V4.0.10,全五端源码下载...

    独立版万能门店全端云小程序是一款可以一键生成全端小程序(微信小程序.百度小程序.支付宝小程序.抖音/头条小程序.QQ 小程序)的平台, 平台集成了分销商城.会员卡券.知识付费.商户平台.同城论坛等功能 ...

  2. 最新ThinkPHP内核全行业小程序运营管理系统源码 DIY布局 一键生成小程序

    介绍: ThinkPHP内核全行业小程序运营管理系统源码 自由DIY布局 一键生成小程序,内附安装说明 无需编程,各行业模版直接套用,一键生成,轻松搭建小程序 界面自由DIY,打造个性小程序 可拖拽式 ...

  3. [原创]jQuery推箱子小游戏(100关且可扩展可选关),休闲,对战,娱乐,小游戏,下载即用,兼容iPad移动端,代码注释全(附源码)

    Sokoban 介绍 [原创]jQuery推箱子小游戏(100关且可扩展可选关),休闲,对战,娱乐,小游戏,下载即用,兼容iPad移动端,代码注释全(附源码) 游戏说明 经典的推箱子是一个来自日本的古 ...

  4. ThinkPHP内核全行业小程序运营管理系统源码免费分享下载

    ThinkPHP内核全行业小程序运营管理系统源码 界面自由DIY,打造个性小程序 可拖拽式DIY布局,开启自定义功能新征程,无需繁琐操作,轻松拖拽即可实现界面布局:同步实时预览,可视化操作让您所见即所 ...

  5. 多端合一小程序商城制作系统源码 后台自由DIY+全开源可二开

    分享一个多端合一小程序商城制作系统源码,系统后台自由DIY,含完整前后端+部署教程,源码全开源可二开. 系统支持7个终端+一个后台,省去一个平台开发一个小程序的麻烦,支持微信小程序.支付宝小程序.百度 ...

  6. Spring Boot 2.x 启动全过程源码分析(全)

    上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入口类 SpringApplication 的源码,并知道了其构造原理,这篇我 ...

  7. 【微信小程序控制硬件②】 开始微信小程序之旅,导入小程序Mqtt客户端源码,实现简单的验证和通讯于服务器.(附带源码)

    文章目录 一.前言: 二.注册微信小程序: 三.本博文连接和微信物联有何区别: 四.微信小程序`MQTT`客户端源码导入注意事项: 五.下载: 微信物联网生态主要分在微信硬件开发平台与腾讯物联开发平台 ...

  8. Spring Boot Dubbo 应用启停源码分析

    作者:张乎兴 来源:Dubbo官方博客 背景介绍 Dubbo Spring Boot 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发.同时也整合了 Spring Boo ...

  9. 微小区v11.1.1 (公众号+小程序模块版)源码安装教程

    微小区微擎模块版很早以前使用过,非常合适物业公司或者集团式物业公司公众号应用,一个平台多个小区.多个物业公司管理,非常好的物业解决方案,每个小区都可以单独设置自己的小区主页,业主打开平台时会提示选择小 ...

  10. NXP BootLoader源码分析并改写SD卡启动

    1 官方资料 NXP官方提供了MCUBoot SDK:NXP_Kinetis_Bootloader_2.0.0 package,里面包含了各种型号芯片的的BootLoader. BootLoader参 ...

最新文章

  1. 【工具类】JAVA POI 代码导出表格的两种办法(代码全注释,小白也不怕)
  2. redis管道pipeline的运用
  3. 你的工作是为了你自己!
  4. 在Vue中引入Bootstrap,Font-awesome
  5. mfc大观之三(创建对象)
  6. Linux实战教学笔记50:Zabbix监控平台3.2.4(二)深入理解zabbix
  7. oracle 的自增需要依靠序列和触发器共同实现
  8. 实用的 Python —— 使用虚拟环境 virtualenv(Linux)
  9. 4.1 tensorflow2实现Kruskal - Wallis 检验 ——python实战
  10. 进一步理解VC中的句柄
  11. 基于asp.net面向小商户的轻量级仓储管理系统
  12. 【SQL基础-4】SQL语句练习实例—在SQLzoo平台练习
  13. 算法竞赛进阶指南0x00基本算法 0x01位运算 例题起床困难综合征
  14. 使用学信网验证报告免费使用jetbrains全家桶
  15. MathType快捷键
  16. 解决第三方dll出现:找不到指定模块(非路径错误)
  17. 淘宝走过的大数据之路
  18. java 逗号分隔数字_java程序 输入n个数字,以逗号隔开,然后升序排列,再重新输出...
  19. mysql安装时损坏的图像_损坏的图像,详细教您提示损坏的图像该怎么解决
  20. 微软今天发布的紧急安全公告 MS08-067

热门文章

  1. 2021蓝牙耳机种草推荐,五款出差通勤必备高音质蓝牙耳机
  2. 教大家如何批量采集义乌购上的图片和保存方法
  3. 新东方人工智能中台建设和AI部门管理经验分享
  4. Docker 镜像部署服务器流程
  5. Lazada开店最全介绍,开店入驻只需三步
  6. 手把手教你入门R语言(1)——R软件初识与安装
  7. 【R语言】-核密度估计图绘制
  8. pywin32 python3.6_pywin32 py3.6下载
  9. 现有改善肠道菌群的药物有哪些?
  10. 卡洛斯·鲁依斯·萨丰的《风之影》