U-BOOT小全(五):BootLoader源码(SPL-UBoot 2)
前面我们分析到了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)相关推荐
- 全云端万能小程序_万能门店全云端独立版微信小程序源码V4.0.10,全五端源码下载...
独立版万能门店全端云小程序是一款可以一键生成全端小程序(微信小程序.百度小程序.支付宝小程序.抖音/头条小程序.QQ 小程序)的平台, 平台集成了分销商城.会员卡券.知识付费.商户平台.同城论坛等功能 ...
- 最新ThinkPHP内核全行业小程序运营管理系统源码 DIY布局 一键生成小程序
介绍: ThinkPHP内核全行业小程序运营管理系统源码 自由DIY布局 一键生成小程序,内附安装说明 无需编程,各行业模版直接套用,一键生成,轻松搭建小程序 界面自由DIY,打造个性小程序 可拖拽式 ...
- [原创]jQuery推箱子小游戏(100关且可扩展可选关),休闲,对战,娱乐,小游戏,下载即用,兼容iPad移动端,代码注释全(附源码)
Sokoban 介绍 [原创]jQuery推箱子小游戏(100关且可扩展可选关),休闲,对战,娱乐,小游戏,下载即用,兼容iPad移动端,代码注释全(附源码) 游戏说明 经典的推箱子是一个来自日本的古 ...
- ThinkPHP内核全行业小程序运营管理系统源码免费分享下载
ThinkPHP内核全行业小程序运营管理系统源码 界面自由DIY,打造个性小程序 可拖拽式DIY布局,开启自定义功能新征程,无需繁琐操作,轻松拖拽即可实现界面布局:同步实时预览,可视化操作让您所见即所 ...
- 多端合一小程序商城制作系统源码 后台自由DIY+全开源可二开
分享一个多端合一小程序商城制作系统源码,系统后台自由DIY,含完整前后端+部署教程,源码全开源可二开. 系统支持7个终端+一个后台,省去一个平台开发一个小程序的麻烦,支持微信小程序.支付宝小程序.百度 ...
- Spring Boot 2.x 启动全过程源码分析(全)
上篇<Spring Boot 2.x 启动全过程源码分析(一)入口类剖析>我们分析了 Spring Boot 入口类 SpringApplication 的源码,并知道了其构造原理,这篇我 ...
- 【微信小程序控制硬件②】 开始微信小程序之旅,导入小程序Mqtt客户端源码,实现简单的验证和通讯于服务器.(附带源码)
文章目录 一.前言: 二.注册微信小程序: 三.本博文连接和微信物联有何区别: 四.微信小程序`MQTT`客户端源码导入注意事项: 五.下载: 微信物联网生态主要分在微信硬件开发平台与腾讯物联开发平台 ...
- Spring Boot Dubbo 应用启停源码分析
作者:张乎兴 来源:Dubbo官方博客 背景介绍 Dubbo Spring Boot 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发.同时也整合了 Spring Boo ...
- 微小区v11.1.1 (公众号+小程序模块版)源码安装教程
微小区微擎模块版很早以前使用过,非常合适物业公司或者集团式物业公司公众号应用,一个平台多个小区.多个物业公司管理,非常好的物业解决方案,每个小区都可以单独设置自己的小区主页,业主打开平台时会提示选择小 ...
- NXP BootLoader源码分析并改写SD卡启动
1 官方资料 NXP官方提供了MCUBoot SDK:NXP_Kinetis_Bootloader_2.0.0 package,里面包含了各种型号芯片的的BootLoader. BootLoader参 ...
最新文章
- 【工具类】JAVA POI 代码导出表格的两种办法(代码全注释,小白也不怕)
- redis管道pipeline的运用
- 你的工作是为了你自己!
- 在Vue中引入Bootstrap,Font-awesome
- mfc大观之三(创建对象)
- Linux实战教学笔记50:Zabbix监控平台3.2.4(二)深入理解zabbix
- oracle 的自增需要依靠序列和触发器共同实现
- 实用的 Python —— 使用虚拟环境 virtualenv(Linux)
- 4.1 tensorflow2实现Kruskal - Wallis 检验 ——python实战
- 进一步理解VC中的句柄
- 基于asp.net面向小商户的轻量级仓储管理系统
- 【SQL基础-4】SQL语句练习实例—在SQLzoo平台练习
- 算法竞赛进阶指南0x00基本算法 0x01位运算 例题起床困难综合征
- 使用学信网验证报告免费使用jetbrains全家桶
- MathType快捷键
- 解决第三方dll出现:找不到指定模块(非路径错误)
- 淘宝走过的大数据之路
- java 逗号分隔数字_java程序 输入n个数字,以逗号隔开,然后升序排列,再重新输出...
- mysql安装时损坏的图像_损坏的图像,详细教您提示损坏的图像该怎么解决
- 微软今天发布的紧急安全公告 MS08-067