UBoot源码分析1
- 格式:pdf
- 大小:1.03 MB
- 文档页数:28
uboot代码详细分析目录u-boot-1.1.6之cpu/arm920t/start.s分析 (2)u-boot中.lds连接脚本文件的分析 (12)分享一篇我总结的uboot学习笔记(转) (15)U-BOOT内存布局及启动过程浅析 (22)u-boot中的命令实现 (25)U-BOOT环境变量实现 (28)1.相关文件 (28)2.数据结构 (28)3.ENV的初始化 (30)3.1env_init (30)3.2env_relocate (30)3.3env_relocate_spec (31)4.ENV的保存 (31)U-Boot环境变量 (32)u-boot代码链接的问题 (35)ldr和adr在使用标号表达式作为操作数的区别 (40)start_armboot浅析 (42)1.全局数据结构的初始化 (42)2.调用通用初始化函数 (43)3.初始化具体设备 (44)4.初始化环境变量 (44)5.进入主循环 (44)u-boot编译过程 (44)mkconfig文件的分析 (47)从NAND闪存中启动U-BOOT的设计 (50)引言 (50)NAND闪存工作原理 (51)从NAND闪存启动U-BOOT的设计思路 (51)具体设计 (51)支持NAND闪存的启动程序设计 (51)结语 (53)参考文献 (53)U-boot给kernel传参数和kernel读取参数—structtag(以及补充) (53)1、u-boot给kernel传RAM参数 (54)2、Kernel读取U-boot传递的相关参数 (56)3、关于U-boot中的bd和gd (59)U-BOOT源码分析及移植 (60)一、u-boot工程的总体结构: (61)1、源代码组织 (61)2.makefile简要分析 (61)3、u-boot的通用目录是怎么做到与平台无关的? (63)4、smkd2410其余重要的文件: (63)二、u-boot的流程、主要的数据结构、内存分配 (64)1、u-boot的启动流程: (64)2、u-boot主要的数据结构 (66)3、u-boot重定位后的内存分布: (68)三、u-boot的重要细节。
UBOOT源码分析UBOOT是一种开放源码的引导加载程序。
作为嵌入式系统启动的第一阶段,它负责初始化硬件设备、设置系统环境变量、加载内核镜像以及跳转到内核开始执行。
Uboot的源码是开放的,让我们可以深入了解其内部工作机制和自定义一些功能。
Uboot源码的文件组织结构非常清晰,主要分为三个大类:目录、文件和配置。
其中目录包含了一系列相关的文件,文件存放具体的源码实现代码,配置文件包含了针对特定硬件平台的配置选项。
Uboot源码的核心部分是启动代码,位于arch目录下的CPU架构相关目录中。
不同的CPU架构拥有不同的启动代码实现,如arm、x86等。
这些启动代码主要包括以下几个关键功能:1. 初始化硬件设备:Uboot首先需要初始化硬件设备,例如设置时钟、中断控制器、串口等设备。
这些初始化操作是在启动代码中完成的。
通过查看该部分代码,我们可以了解硬件的初始化过程,以及如何配置相关寄存器。
2. 设置启动参数:Uboot启动参数存储在一个称为"bd_info"的数据结构中,它包含了一些关键的设备和内存信息,例如DRAM大小、Flash 大小等。
这些参数是在启动代码中设置的,以便内核启动时能够正确识别硬件情况。
3. 加载内核镜像:Uboot负责加载内核镜像到内存中,以便内核可以正确执行。
在启动代码中,会通过读取Flash设备或者网络等方式,将内核镜像加载到指定的内存地址处。
加载过程中,可能会进行一些校验和修正操作,以确保内核数据的完整性。
4. 启动内核:在内核镜像加载完成后,Uboot会设置一些寄存器的值,并执行一个汇编指令,跳转到内核开始执行。
此时,Uboot的使命即结束,控制权交由内核处理。
除了启动代码,Uboot源码中还包含了许多其他功能模块,如命令行解析器、存储设备驱动、网络协议栈等。
这些功能模块可以根据需求进行配置和编译,以满足不同平台的需求。
例如,可以通过配置文件选择启用一些功能模块,或者自定义一些新的功能。
uboot笔记uboot命令分析+实现uboot笔记:uboot命令分析+实现Ubootuboot命令分析+实现先贴⼀个重要结构,位于uboot/include/command.h,这个结构代表每个uboot命令struct cmd_tbl_s {char *name; /* Command Name */int maxargs; /* maximum number of arguments*/int repeatable;/* autorepeat allowed? *//* Implementation function */int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);char *usage; /* Usage message (short)简短⽤法信息*/#ifdef CFG_LONGHELPchar *help; /* Help message (long) 长的帮助信息*/#endif#ifdef CONFIG_AUTO_COMPLETE/* do auto completion on the arguments */ int (*complete)(intargc, char *argv[], charlast_char, intmaxv, char *cmdv[]); #endif};typedefstruct cmd_tbl_s cmd_tbl_t;============================================================uboot的第⼀阶段:硬件相关初始化0.reset执⾏arm920t/start.s 过程如下1.设置cpu svc管理模式2.关看门狗中断,mmu等3.设置时钟,sdram,外部总线4.代码重定位,搬运代码,从flash到sdram5.设置栈,bss段清零, bss⽤于未初始化的全局变量和静态变量6.ldr pc, _start_armboot即进⼊uboot启动的第⼆阶段,调⽤c函数start_armboot()从start_armboot开始经过⼀系列外设初始化⽐如falsh_initnand_init...最后循环调⽤mian_loop()main_loop主要流程{1. ⽣成环境变量mtdparts, 调⽤mtdparts_init2. 在启动过程中若⽆空格键按下则boot_zImage,即run_command(getenv("bootcmd"),0)有空格键按下则run_command("menu",0)3. shell过程,读取⽤户的输⼊并执⾏相应的命令{从控制台获得命令,保存在全局变量comsole_buffer中解析命令⾏字符串,分割命令与参数,最后执⾏run_command(...); }}也就是说在mian_loop中,是处理环境变量和控制台⼈机交互,mian_loop调⽤readline ()读取命令⾏到console_buffer,再把console_buffer复制到lastcommand中去,还要设置flag,最后调⽤run_command (lastcommand, flag)函数,run_command (lastcommand, flag)函数中,⾸先定义cmd_tbl_t *cmdtp,再解析命令⾏。
u_boot移植(五)之分析uboot源码中nand flash操作一、OneNand 和Nand Flash我们已经能从Nand Flash启动了,启动之后,大家会看到如下效果:可以看出,我们的uboot默认使用的是OneNand。
需要注意的是我们的FSC100上面是没有OneNand的,有的是K9F2G08U0B型号的NAND FLASH。
前面我们了解过Nor Flash 和Nand Flash,那OneNand Flash又是什么呢?二、uboot 源码中Nand Flash部分代码分析我们从Nand Flash初始化看起,打开lib_arm/board.c文件,为了紧抓主线,以下代码只列举出了主线代码。
可以看出,我们可以通过CONFIG_CMD_NAND和CONFIG_CMD_ONENAND两个宏来选择NAND FLASH初始化还是 ONENAND FLASH初始化。
uboot 中默认定义了宏CONFIG_CMD_ONENAND,所以选择的是ONENAND FLASH初始化。
我们的FSC100上面使用的是NAND FLASH,所以我们要定义CONFIG_CMD_NAND宏,取消CONFIG_CMD_ONENAND宏的定义。
嗯!先做个记录:修改include/configs/fsc100.h,定义宏CONFIG_CMD_NAND,取消宏CONFIG_CMD_ONENAND。
好了,接下我们看看nand_init()函数时如何实现的。
看以看出,这段代码调用根据CONFIG_SYS_MAX_NAND_DEVICE宏[默认没有定义]的值来决定系统中Nand Flash设备的个数。
接着调用nand_init_chip()函数完成Nand Flash初始化,然后计算出每块Nand Flash的大小。
最终会输出Nand Flash总的容量。
嗯!做个记录:修改include/configs/fsc100.h,定义宏CONFIG_SYS_MAX_NAND_DEVICE,值为1没有看明白的地方是给nand_init_chip()函数传递的参数,接下来我们来看看他们是如何定义的。
uboot源码分析(2)uboot环境变量实现简析uboot 环境变量实现简析----------基于u-boot-2010.03u-boot的环境变量是使⽤u-boot的关键,它可以由你⾃⼰定义的,但是其中有⼀些也是⼤家经常使⽤,约定熟成的,有⼀些是u-boot⾃⼰定义的,更改这些名字会出现错误,下⾯的表中我们列出了⼀些常⽤的环境变量:bootdelay 执⾏⾃动启动的等候秒数baudrate 串⼝控制台的波特率netmask 以太⽹接⼝的掩码ethaddr 以太⽹卡的⽹卡物理地址bootfile 缺省的下载⽂件bootargs 传递给内核的启动参数bootcmd ⾃动启动时执⾏的命令serverip 服务器端的ip地址ipaddr 本地ip 地址stdin 标准输⼊设备stdout 标准输出设备stderr 标准出错设备上⾯只是⼀些最基本的环境变量,请注意,板⼦⾥原本是没有环境变量的,u-boot的缺省情况下会有⼀些基本的环境变量,在你执⾏了saveenv之后,环境变量会第⼀次保存到flash或者eeprom中,之后你对环境变量的修改,保存都是基于保存在flash中的环境变量的操作。
环境变量可以通过printenv命令查看环境变量的设置描述,通过setenv 命令进⾏重新设置,设置完成后可以通过saveenv将新的设置保存在⾮易失的存储设备中(nor flash 、nand flash 、eeprom)。
例如:setenv bootcmd "nand read 0x30008000 0x80000 0x500000;bootm 0x30008000"saveenv通过这两条命令就完成了环境变量bootcmd的重新设置,并讲其保存在固态存储器中。
下⾯简单分析下uboot中环境变量的实现流程。
uboot启动后,执⾏玩start.S中的汇编程序,将跳⼊board.c 中定义的start_arm_boot()函数中,在该函数中,uboot讲完成板⼦外设和相关系统环境的初始化,然后进⼊main_loop循环中进⾏系统启动或者等待与⽤户交互,这其中就包括环境变量的初始化和重定位。
1 U-Boot简介U-Boot,全称Universal BootLoader,是遵循GPL条款的开放源码项目。
从FADSROM、8xxROM、PPCBOOT逐步发展演化而来。
其源码目录、编译形式与Linux内核很相似,事实上,不少U-Boot源码就是相应的Linux内核源程序的简化,尤其是一些设备的驱动程序,这从U-Boot源码的注释中能体现这一点。
但是U-Boot不仅仅支持嵌入式Linux系统的引导,当前,它还支持NetBSD,VxWorks, QNX, RTEMS, ARTOS,LynxOS嵌入式操作系统。
其目前要支持的目标操作系统是OpenBSD, NetBSD,FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR,VxWorks, LynxOS, pSOS, QNX, RTEMS,ARTOS。
这是U-Boot中Universal的一层含义,另外一层含义则是U-Boot除了支持PowerPC系列的处理器外,还能支持MIPS、x86、ARM、NIOS、XScale等诸多常用系列的处理器。
这两个特点正是U-Boot 项目的开发目标,即支持尽可能多的嵌入式处理器和嵌入式操作系统。
就目前来看,U-Boot 对PowerPC系列处理器支持最为丰富,对Linux的支持最完善。
其它系列的处理器和操作系统基本是在2002年11月PPCBOOT改名为U-Boot后逐步扩充的。
从PPCBOOT向U-Boot 的顺利过渡,很大程度上归功于U-Boot的维护人德国DENX软件工程中心WolfgangDenk[以下简称W.D]本人精湛专业水平和持着不懈的努力。
当前,U-Boot项目正在他的领军之下,众多有志于开放源码BOOTLOADER移植工作的嵌入式开发人员正如火如荼地将各个不同系列嵌入式处理器的移植工作不断展开和深入,以支持更多的嵌入式操作系统的装载与引导。
am335xu-boot启动过程分析 u-boot属于两阶段的bootloader,第⼀阶段的⽂件为 arch/arm/cpu/armv7/start.S 和 arch/arm/cpu/armv7/lowlevel_init.S,前者是平台相关的,后者是开发板相关的。
1. u-boot第⼀阶段代码分析 (1)硬件设备初始化 将CPU的⼯作模式设为管理模式(SVC); 关闭中断; 禁⽤MMU,TLB ; 板级初始化; (2)为加载Bootloader的第⼆阶段代码准备RAM空间 加载u-boot.img,跳转到u-boot.img; 上述⼯作,也就是uboot-spl代码流程的核⼼。
代码如下:arch/arm/cpu/armv7/start.S1/*2 * the actual reset code3*/4reset:5 bl save_boot_params6/*7 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,8 * except if in HYP mode already9*/10 mrs r0, cpsr11 and r1, r0, #0x1f @ mask mode bits12 teq r1, #0x1a @ test for HYP mode13 bicne r0, r0, #0x1f @ clear all mode bits14 orrne r0, r0, #0x13 @ set SVC mode15 orr r0, r0, #0xc0 @ disable FIQ and IRQ16 msr cpsr,r017@@ 以上通过设置CPSR寄存器⾥设置CPU为SVC模式,禁⽌中断18@@ 具体操作可以参考《[kernel 启动流程] (第⼆章)第⼀阶段之——设置SVC、关闭中断》的分析1920/* the mask ROM code should have PLL and others stable */21#ifndef CONFIG_SKIP_LOWLEVEL_INIT22 bl cpu_init_cp1523@@ 调⽤cpu_init_cp15,初始化协处理器CP15,从⽽禁⽤MMU和TLB。
uboot分析之bootmbootm命令执⾏过程中调⽤了bootm_start函数,这个函数⽐较重要,所以先分析它。
mon/cmd_bootm.cCpp代码1. static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])2. {3. void *os_hdr;4. int ret;5. memset ((void *)&images, 0, sizeof (images));//images是⼀个bootm_headers_t类型的全局变量。
见下⾯的分析。
6. images.verify = getenv_yesno ("verify");//从环境变量中检查是否要对镜像的数据(不是镜像头)进⾏校验。
7. bootm_start_lmb();//不做任何有意义的⼯作,除了定义# define lmb_reserve(lmb, base, size)8. /* get kernel image header, start address and length */寻找可⽤的内核镜像,见下⾯的分析。
主要根据传⼊的参数检查镜像的合法性并获取信息。
9. os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,10. &images, &images.os.image_start, &images.os.image_len);//返回指向内存中镜像头的指针11. if (images.os.image_len == 0) {12. puts ("ERROR: can't get kernel image!\n");13. return 1;14. }15. /* get image parameters */16. switch (genimg_get_format (os_hdr)) {//根据镜像魔数获取镜像类型17. case IMAGE_FORMAT_LEGACY:18. images.os.type = image_get_type (os_hdr);//镜像类型19. p = image_get_comp (os_hdr);//压缩类型20. images.os.os = image_get_os (os_hdr);//操作系统类型21. images.os.end = image_get_image_end (os_hdr);//当前镜像的尾地址22. images.os.load = image_get_load (os_hdr);//镜像数据的载⼊地址23. break;24. default:25. puts ("ERROR: unknown image format type!\n");26. return 1;27. }28. /* find kernel entry point */29. if (images.legacy_hdr_valid) {//如果镜像已经通过验证30. images.ep = image_get_ep (&images.legacy_hdr_os_copy);//获取⼊⼝地址,填充images.ep 。
uboot一、uboot是ppcboot和armboot合并而成,现在主流的bootloader为uboot和redboot二、bootm addr_kernel addr_initrd三、移植uboot时最好(一定)要找到一个自己板子的原形(即自己的板子是在这个板子上做一些修改而来的)的版本,这样就可以事半功倍。
这样要修改的地方就比较少,也比较容易了。
uboot支持很多平台,与一个具体平台相关的主要有三个地方:1、./include/configs/xxxxx.h, 主要定义了flash、sdram的起始地址等信息,一般要修改flash的起始地址、大小,有时候会有位宽等。
2、./board/xxxxx/*,这个目录下主要有两三个.c文件,主要为该平台的初始化和flash操作的函数。
有时候flash的操作需要修改,不过一般都是找一个现有的支持该flash的驱动,一般情况在uboot 别的./board/平台下就会有现成的,拷贝过了就可以了。
3、./cpu/xxxxxx/arch_xxx/xxxxxx/*, 一般是此cpu的初始等函数。
四、具体移植的时候最多涉及到的会是./include/configs/xxxx.h,如果有现成的平台(uboot现在支持绝大部分我们常用的平台),可能只需要对着原来的xxxx.h文件,修改几个我们在硬件上修改了的地方,一般会是flash的起始地址、大小;内存大小(内存的起始地址应该都是0);uboot设置信息保存的地址和长度;console 口和它的波特率;默认的设置;uboot的入口地址等(具体情况可能会有一些变化),如果不是从相同的平台移植,可能会比较麻烦,因为这时候要修改一些和此cpu相关的一些寄存器、频率和内存等硬件方面的东西了(也在这个xxxx.h中),虽然这时改动的地方也不多,但是会很痛苦,因为经常不知道要改哪里或者改为多少。
所以可能需要参考cpu的datasheet和到网上找一些资料了并且慢慢试了。
uboot 代码运行流程U-Boot代码运行流程U-Boot(Universal Bootloader)是一个开源的引导加载程序,广泛应用于嵌入式系统中。
它负责在系统上电后初始化硬件并加载操作系统内核,是系统启动的重要一环。
下面将从U-Boot代码的运行流程方面进行介绍。
1. 启动阶段当系统上电后,处理器会从预定义的存储器地址开始运行代码。
U-Boot的启动代码通常存放在ROM中,处理器会从ROM的起始地址开始执行。
启动代码负责初始化处理器和一些外设,然后跳转到U-Boot的入口点。
2. 入口点U-Boot的入口点是指U-Boot的main()函数。
在启动代码的最后,会调用main()函数,从而进入U-Boot的主循环。
U-Boot的主循环负责处理用户输入的命令,并根据命令执行相应的操作。
3. 硬件初始化在main()函数中,首先会进行硬件的初始化工作。
这包括初始化串口、初始化存储器控制器、初始化网络接口等。
硬件初始化的目的是为了确保系统能够正常运行,并为后续的操作做好准备。
4. 系统启动硬件初始化完成后,U-Boot会尝试从存储设备(如闪存、SD卡)中加载操作系统内核镜像。
U-Boot会根据预定义的启动命令(例如bootcmd)来确定从哪个设备加载内核镜像,并执行相应的加载操作。
加载完成后,U-Boot会将控制权交给操作系统内核,进入操作系统的启动阶段。
5. 用户交互一般情况下,U-Boot会在系统启动后进入命令行界面,等待用户输入命令。
用户可以通过串口、网络等方式与U-Boot进行交互,执行各种操作,例如烧写固件、修改配置等。
U-Boot提供了丰富的命令集,可以满足不同的需求。
6. 系统重启当用户输入重启命令或系统发生异常时,U-Boot会执行系统重启操作。
重启操作包括重新初始化硬件、重新加载内核镜像等步骤,以重新启动系统。
U-Boot会将控制权交给重新加载的内核,然后进入内核的启动流程。
总结:U-Boot代码的运行流程包括启动阶段、入口点、硬件初始化、系统启动、用户交互和系统重启等几个关键步骤。
瑞芯微rk3399-uboot简单分析使⽤的配置⽂件是:configs/rk3399_box_defconfigCONFIG_RKCHIP_RK3399=yCONFIG_PRODUCT_BOX=yCONFIG_NORMAL_WORLD=yCONFIG_SECOND_LEVEL_BOOTLOADER=yCONFIG_BAUDRATE=1500000CONFIG_ARM=yCONFIG_ROCKCHIP_ARCH64=yCONFIG_PLAT_RK33XX=y下载uboot原⽣的代码和瑞芯微提供的源码进⾏对⽐,⾸先肯定对⽐⼀下Makefile发现差异如下:ifeq ($(ARCHV),aarch64)ifneq ($(wildcard ../toolchain/aarch64-linux-android-4.9),)CROSS_COMPILE ?= $(shell pwd)/../toolchain/aarch64-linux-android-4.9/bin/aarch64-linux-android-endififneq ($(wildcard ../prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin),)CROSS_COMPILE ?= $(shell pwd)/../prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin/aarch64-linux-android-endifelseifneq ($(wildcard ../toolchain/arm-eabi-4.8),)CROSS_COMPILE ?= $(shell pwd)/../toolchain/arm-eabi-4.8/bin/arm-eabi-endififneq ($(wildcard ../toolchain/arm-eabi-4.7),)CROSS_COMPILE ?= $(shell pwd)/../toolchain/arm-eabi-4.7/bin/arm-eabi-endififneq ($(wildcard ../toolchain/arm-eabi-4.6),)CROSS_COMPILE ?= $(shell pwd)/../toolchain/arm-eabi-4.6/bin/arm-eabi-endififneq ($(wildcard ../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin),)CROSS_COMPILE ?= $(shell pwd)/../prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-endififneq ($(wildcard ../prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin),)CROSS_COMPILE ?= $(shell pwd)/../prebuilts/gcc/linux-x86/arm/arm-eabi-4.7/bin/arm-eabi-endififneq ($(wildcard ../prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin),)CROSS_COMPILE ?= $(shell pwd)/../prebuilts/gcc/linux-x86/arm/arm-eabi-4.6/bin/arm-eabi-endifendif # ARCHV=aarch64这⼀段其实只是指定交叉编译⼯具链没什么好解释的。
U-boot中SPL功能和源码流程分析 在U-boot⽬录下,有个⽐较重要的⽬录就是SPL的,SPL到底是什么呢?为什么要⽤它呢? SPL(Secondary programloader)是uboot第⼀阶段执⾏的代码。
主要负责搬移uboot第⼆阶段的代码到系统内存(System Ram,也叫⽚外内存)中运⾏。
SPL是由固化在芯⽚内部的ROM引导的。
我们知道很多芯⽚⼚商固化的ROM⽀持从nandflash、SDCARD等外部介质启动。
所谓启动,就是从这些外部介质中搬移⼀段固定⼤⼩(4K/8K/16K等)的代码到内部RAM中运⾏。
这⾥搬移的就是SPL。
在最新版本的uboot中,可以看到SPL也⽀持nandflash,SDCARD等多种启动⽅式。
当SPL本⾝被搬移到内部RAM中运⾏时,它会从nandflash、SDCARD等外部介质中搬移uboot第⼆阶段的代码到系统内存中。
SPL复⽤的是uboot⾥⾯的代码. SPL的主要功能就是衔接系统的硬件SRAM和u-boot之间的纽带。
1.BasicArm Initialization2.UART console initialization3.Clocks and DPLL Locking(minimal)4.SDRAM initialization5.Mux(minimal)6.Boot Device Initialization, based on where we are booting from MMC1, or MMC2,or Nand, or Onenand7.Bootloading real u-boot from the Boot Device and passing control to it. 怎么编译SPL呢? 上⽂中说道“SPL复⽤的是uboot⾥⾯的代码”,那要⽣成我们所需要的SPL⽬标⽂件,我们⼜该如何下⼿呢?很容易想到,通过编译选项便可以将SPL和uboot代码分离、复⽤。
Bootloader代码分析代码分析报告(一)Bootloader代码分析1.第一部分功能:关闭中断,进入ARM状态,切换到SVC模式(复位异常进入SVC 模式)。
MRS r0, cpsr把状态寄存器CPSR中数据读入r0 寄存器。
BIC r0, r0, #MASK_MODE(MASK_MODE = 0x0000003F)把 r0 寄存器的低6位清零。
(把Thumb状态为清零,回到ARM状态)ORR r0, r0, #MODE_SVC32(MODE_SVC32 = 0x00000013)把r0 寄存器的低5置为 10011(SVC模式)。
ORR r0, r0, #I_BIT(I_BIT = 0x80)把r0 寄存器的第8位置1(关闭中断状态位)。
ORR r0, r0, F_BIT(F_BIT = 0x40 )把r0 寄存器的第7位置1(关闭快速中断状态位)。
MSR cpsr_c, r0把r0 寄存器的低8位存储进CPSR寄存器的低8位。
自此进入ARM状态,切换到SVC模式,关闭中断和快速中断。
LDR r2, = ARM7_INTMASKASIC_BASE EQU 0x03ff0000ARM7_INTMASK EQU (ASIC_BASE+0x4008)ARM7_INTMASK = 0x03ff4008把0x03ff4008存入r2 寄存器。
ASIC_BASE 是SYSCFG寄存器的第16位到第25位,是指S3C4510b中特殊寄存器的启始地址。
SYSCFG寄存器复位时缺省值是0x37ff ff91。
SYSCFG寄存器的第16位到第25位的值是0x 3ff。
ARM7_INTMASK EQU (ASIC_BASE+0x4008)中断模式寄存器:控制中断源的掩码。
偏移地址0x4008。
ARM7_INTMASK 就是中断模式寄存器的寻址地址。
ARM7_INTMASK = 0x03ff4008第21位是全局模式位:置1时断开所有中断源。
以下内容源于朱友鹏老师《物联网大讲堂》视频的学习整理。
如有侵权,请告知删除。
一、基本的概念∙阶段的定义:1、第一阶段,即在内部SRAM运行的阶段,简单地理解为汇编阶段,此阶段主要涉及start.S文件,在cpu/s5pc11x/目录下。
第一阶段以ldr pc _start_armboot为结束。
2、第二阶段,即在DDR中运行的阶段,简单地理解为C语言阶段,此阶段主要涉及start_armboot函数,在uboot/lib_arm/board.c文件的444~908行。
∙第一阶段主要完成的任务有:1、异常向量表的实现;2、设置进入特权模式,即SVC模式;3、检查恢复状态;4、IO状态恢复;5、关看门狗;6、一些与SRAM、SROM相关的GPIO设置;7、开发板的供电锁存;8、时钟的初始化;9、DDR的初始化;10、重新设置栈空间;11、uboot的重定位。
12、转换表的建立;13、使能MMU。
可见,uboot的第一阶段初始化了SoC内部的一些部件,初始化DDR并且重定位。
一、start.S的引入1、uboot中整个程序的入口取决于链接脚本中ENTRY声明的地方。
因为链接脚本(即board\samsung\x210\u-boot.lds)中有ENTRY(_start),因此_start符号所在的文件就是起始文件,所处的位置就是起始位置。
2、SI工具的使用:search—>lookup reference.二、start.S解析1(1)config.h文件在mkconfig脚本中生成,此文件内容为#include <configs/x210.h>;(2)version.h文件中的内容是#include "version_autogenerated.h"。
version_autogenerated.h文件是在主Makefile中自动生成的。
生成代码为:生成内容(即version_autogenerated.h文件中内容为):#define U_BOOT_VERSION "U-Boot 1.3.4"(3)由于定义了宏CONFIG_ENABLE_MMU,因此包含asm/proc/domain.h。
4.3.1U-Boot代码结构分析4.3 Bootloader之U-BootU-Boot是在PPC-Boot的基础上进化⽽来的⼀个开放源码的嵌⼊式BootROM程序,本⼩节将使⽤1.1.4版本的代码来分析U-Boot的代码结构,以及如何将它移植到基于S3C2410X的开发板上来。
4.3.1 U-Boot代码结构分析U-Boot4-9所⽰。
board:这个⽬录存放了所有U-Boot⽀持的⽬标板的⼦⽬录,如board/smdk2410/*就和我们的开发平台fs2410相类似。
要将U-Boot移植到⾃⼰的S3C2410X⽬标板上,必须参考这个⽬录下的内容,⽐如对Flash 以及Flash宽度和⼤⼩的定制等就要修改其中的flash.c。
cpu:这个⽬录存放了U-Boot⽀持的CPU类型,因为我们的开发平台是S3C2410X,所以只关⼼cpu/arm920t,CPU相关的⽂件主要是初始化⼀个执⾏环境,包括中断的初始化;start.S是整个u-boot.bin ⽬标可执⾏代码的第⼀段代码,它们是从Flash开始运⾏的,其主要⼯作就是对整个U-Boot⽬标代码的重定位,即将U-Boot转移到内存中去运⾏。
common:这个⽬录存放了U-Boot的⼀些公共命令的实现,像那些以cmd_*.c为名字的⽂件就是对应U-Boot的每个命令的实现代码,我们通常关⼼cmd_boot.c和cmd_bootm.c(它们和内核的引导相关)。
drivers:这个⽬录中存放了各种外设接⼝的驱动程序。
fs:这个⽬录中存放了U-Boot⽀持的⽂件系统。
lib_arm:这个⽬录存放了ARM平台公共的接⼝代码。
include:这个⽬录存放头⽂件的公共⽬录,其中的include/configs/smdk2410.h定义了所有和S3C2410X 相关的资源的配置参数,我们往往只需修改这个⽂件就可以配置⽬标板的参数,如波特率、引导参数、物理内存映射等。
•UBoot源码解析(一)主要内容•分析UBoot是如何引导Linux内核•UBoot源码的一阶段解析BootLoader概念•Boot Loader 就是在操作系统内核运行之前运行的一段小程序。
通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境•通常,Boot Loader 是严重地依赖于硬件而实现的,特别是在嵌入式世界。
因此,在嵌入式世界里建立一个通用的Boot Loader 几乎是不可能的。
尽管如此,我们仍然可以对Boot Loader 归纳出一些通用的概念来,以指导用户特定的Boot Loader 设计与实现。
UBoot来源•U-Boot 是 Das U-Boot 的简称,其含义是 Universal Boot Loader,是遵循 GPL 条款的开放源码项目。
最早德国DENX 软件工程中心的 Wolfgang Denk 基于 8xxROM 和FADSROM 的源码创建了 PPCBoot 工程项目,此后不断添加处理器的支持。
而后,Sysgo Gmbh 把 PPCBoot 移植到 ARM 平台上,创建了 ARMBoot 工程项目。
最终,以 PPCBoot 工程和 ARMBoot 工程为基础,创建了 U-Boot 工程。
•而今,U-Boot 作为一个主流、通用的 BootLoader,成功地被移植到包括 PowerPC、ARM、X86 、MIPS、NIOS、XScale 等主流体系结构上的百种开发板,成为功能最多、灵活性最强,并且开发最积极的开源 BootLoader。
目前。
U-Boot 仍然由 DENX 的 Wolfgang Denk 维护UBoot存储空间分布•UBoot是用来引导OS系统启动,那么它是如何引导OS启动的呢?启动参数内核根文件系统bootloaderUBoot和内核的交互•UBoot如何调用Linux内核?–UBoot通过命令把Linux内核镜像文件从Flash中读取到内存的某一位置,然后设置PC寄存器执向该位置UBoot调用Linux内核的前提条件是?–R0 =0–R1=适当的机器码机器码的位置存放在linux/arch/arm/mach-type 文件中–R2 =启动参数标记列表在内存中的位置–CPU必须设置为SVC模式并关闭中断–MMU必须关闭•UBoot如何给内核传递参数?–UBoot和内核交互是单向的,两个程序不能同时运行,那么要实现参数传递只能通过把参数存放到一个固定内存位置然后调用theKernel函数给对R0,R1,R2寄存器赋值。
Linux启动调用start_kernel函数读取内容即可。
do_bootm_linux函数•do_bootm_linux是UBoot真正启动Linux的函数(位于UBoot所在目录/lib_arm/bootm.c)int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images){bd_t*bd = gd->bd;char*s;int machid = bd->bi_arch_number;void(*theKernel)(int zero, int arch, uint params);..................theKernel = (void (*)(int, int, uint))images->ep; //传递Linux内核首地址theKernel (0, machid, bd->bi_boot_params); //启动Linux内核theKernel函数•参数 zero:设置为0 R0寄存器为0•参数:arch 表示机器存储于R1寄存器中•参数:params用于传递给Linux的参数地址(包括命令行参数信息)(*theKernel)(int zero, int arch, uint params);启动参数•Linux内核启动的时候并不会自己去扫描硬件的基本信息,因为它启动时都已经假设硬件都已经准备好了,但是使用过程中不可避免的要用到硬件信息,这些信息包括内存大小,串口配置信息等等。
那么这信息内核是如何获的呢?–编译Linux内核时可以通过make menuconfig命令进行命令行参数的配置–Uboot引导的时候传递启动参数参数地址给内核。
命令行参数参数信息•u-boot要传递给内核的参包含哪些内容?–MACH_TYPE(即我们所说的机器码)、–命令行参数CommandLine–系统根设备信息(标志,页面大小)、–内存信息(起始地址,大小)、–RAMDISK信息(起始地址,大小)、压缩的RAMDISK根文件系统信息(起始地址,大小)。
由此可见要传递的参数很多tag_list参数列表•UBoot如何组织这么多的参数信息呢?–UBoot使用struct tage 和struct tag_head来描述这些参数信息。
存放位置(include/asm-arm/setup.h)–相对应的在Linux内核源代码中也有完全相同的结构体定义存放位置(linux/arch/arm/include/asm/setup.h)–#define ATAG_CORE0x54410001–#define ATAG_MEM0x54410002–#define ATAG_VIDEOTEXT0x54410003–#define ATAG_RAMDISK0x54410004–#define ATAG_INITRD0x54410005–#define ATAG_SERIAL0x54410006–#define ATAG_REVISION0x54410007–#define ATAG_CMDLINE0x54410009tag结构体struct tag {struct tag_header hdr;union {struct tag_core core;//列表的开始struct tag_mem32mem;//描述内存信息//videotext; //struct tag_videotext videotext;struct tag_ramdisk ramdisk;struct tag_initrd initrd;//串口信息struct tag_serialnr serialnr;serialnr; ////版本信息revision; //struct tag_revision revision;struct tag_videolfb videolfb;//命令行cmdline; //struct tag_cmdline cmdline;。
//内存时钟memclk; //struct tag_memclk memclk;} u;};struct tag_header {u32 size;//每个参数的大小u32 tag;//每个参数的类型};do_bootm_linux流程•在do_bootm_linux函数(Uboot/lib_arm/bootm.c)中定义函数指针theKernel函数指针,并通过读取Linux的内核镜像文件uImage前面的40字节内容获取Linux内核的入口地址–theKernel = (void (*)(int, int, uint))images->ep;•通过getenv函数分别获取机器码和命令行参数信息信息•通过setup_start_tag函数开始参数列表信息•分别通过setup_xxx_tag函数把命令行信息,内存信息,串口信息分别写入参数列表中。
•最后调用cleanup_before_linux函数实现调用Linux前的准备工作•通过调用theKernel函数实现启动Linux内核。
bootm代码argv[], bootm_headers_t **images)int do_bootm_linux(int flag, int argc, char **argv[], bootm_headers_tint do_bootm_linux(int flag, int argc, char{bd_t*bd = gd->bd;char*s;int machid = bd->bi_arch_number;void(*theKernel)(int zero, int arch, uint params);char **commandline = getenv ("bootargs");char。
theKernel = (void (theKernel = (void (**)(int, int, uint))images->ep;s = getenv ("machid");。
setup_start_tag (bd);setup_serial_tag (¶ms);setup_revision_tag (¶ms);setup_memory_tags (bd);setup_commandline_tag (bd, commandline);if (images->rd_start && images->rd_end)setup_initrd_tag (bd, images->rd_start, images->rd_end);setup_videolfb_tag ((gd_tsetup_videolfb_tag ((gd_t **) gd);setup_end_tag (bd);cleanup_before_linux ();theKernel (0, machid, bd->bi_boot_params);return 1;}Linux内核的入口地址•搞清楚UBoot传递给Linux参数机制后,我们又有个新疑问,UBoot是如何知道Linux内核的入口地址呢?毕竟这个时候Linux内核还没启动•启动参数的位于内存具体哪个位置或者说谁决定了启动参数存放的位置?–UBoot工具mkimage的具体作用是什么?–UBoot是不能够直接引导Linux内核镜像文件,必须给内核镜像文件添加一个0x40字节的头部信息这样UBoot才能够引导Linux内核–那么这0x40字节的信息包含哪些内容?mkimage 结构体信息struct mkimage_params {int dflag;int eflag;int fflag;int lflag;int vflag;int xflag;int os;int arch;int type;int comp;char char **dtc;unsigned int addr;unsigned int ep;unsigned int ep; //Linux//Linux 内核镜像的入口地址char char **imagename;char char **datafile;char char **imagefile;char char **cmdname;};•mkimage 程序通过-e 参数写入Linux 内核的镜像入口地址,而这入口地址被以结构体的形式写入镜像文件的头部0x40字节的位置中,UBoot 利用image_get_ep 函数从内核文件中读取入口地址UBoot 源码结构开发使用文档文档Doc存放制作S-Record 或者 U-Boot 格式的映像等工具,例如mkimage 工具tools 一些独立运行的应用程序的例子,例如helloworld应用例程examples 数字温度测量器或者传感器的驱动通用Dtt RTC 的驱动程序通用Rtc 硬盘接口程序通用Disk 通用的设备驱动程序,主要有以太网接口的驱动通用drivers 存放上电自检程序通用Post 存放文件系统的程序通用Fs 存放网络的程序通用Net 通用库函数的实现通用lib_generic 通用的多功能函数实现通用common 头文件和开发板配置文件,所有开发板的配置文件都在configs 目录下通用include 存放对X86体系结构通用的文件,主要用于实现X86平台通用的函数平台依赖lib_i386存放对ARM 体系结构通用的文件,主要用于实现ARM 平台通用的函数平台依赖lib_arm Uboot 头文件平台依赖include 存放CPU 相关的目录文件,例如:mpc8xx 、ppc4xx 、arm720t 、arm920t 、 xscale 、i386等目录平台依赖cpu 存放电路板相关的目录文件,例如:RPXlite(mpc8xx)、smdk2410(arm920t)、sc520_cdp(x86) 等目录平台依赖board 解 释 说 明特 性目 录UBoot入口位置•UBoot是如何对编译好的二进制文件进行组合链接成一个可执行文件的?–lds链接文件用于描述如何组织和编译这些.o文件。