uboot主要是用于启动操作系统内核,部署整个计算机系统,操作主板上的驱动,提供命令行界面供操作员操作等工作。
计算机系统通常有CPU,外部存储区,内部存储区三部分构成,典型的PC机在上电时先启动固化在主板上NorFlash的BIOS程序,由BIOS程序初始化DDR内存,硬盘,从硬盘将操作系统镜像读取到DDR中,然后跳转到DDR中执行操作系统,直到操作系统启动完成,BIOS退出。
嵌入式系统的启动过程也是参考PC机的,上电后,由部署在能作为启动设备的Flash上的uboot程序首先启动,由于嵌入式系统没有硬盘,所以操作系统部署在充当硬盘的Flash(NandFlash | iNand)上,uboot负责启动和初始化DDR和Flash,然后将操作系统镜像读取到DDR中,再启动操作系统,操作系统启动后,uboot退出。
Android系统和典型的嵌入式linux系统的启动过程基本上是一样的,只是在内核启动后加载根文件系统后才会不同。可以分为两个阶段:
Android启动的差别主要在第二个阶段。
uboot期初是SourceForge上的开源项目,经过多年的发展,已经成为事实上的标准,大部分都会默认使用uboot作为bootloader。早期的uboot使用三位数作为版本号,在后来,版本号使用年份和月份来表示。但是核心部分没什么变化,越新版本支持的开发板越多。
uboot具有可移植性,具有在源代码级别的移植能力,可以针对开发板进行移植使用。
uboot本质上是一个裸机程序,一旦开始整个Soc就会单纯运行uboot,独占CPU,一旦结束后就无法再回到uboot,uboot在开机的时候自动启动,在启动内核之后就结束了。
uboot是一个裸机程序,一般的uboot.bin的文件大小在190K左右,这个镜像文件被烧录到启动介质中由Soc来执行,运行时候被加载到内存中执行每一条指令。
在一些需要和人进行交互的程序中,需要实现一个命令行的Shell界面,这个Shell和linux的终端Shell很类似,只不过可以执行的命令集不一样,例如运行fastboot可以进行系统部署。
uboot的环境变量和操作系统的环境变量的原理基本上完全相同,在设计之初就借鉴了操作系统的理念,环境变量可以认为是系统的全局变量,变量名都是系统内置的,在运行时通过配置环境变量,可以使程序更灵活。
打印当前系统中所有的环境变量,不需要参数,当前系统中的程序都可以根据需要去添加,删除,获取,修改环境变量。
设置系统中的环境变量,如果环境变量的value为空,则表示删除该环境变量
将内存中的环境变量同步保存到flash中,内存中所有的环境变量将会覆盖flash中原来的环境变量,序列化后的环境变量在uboot重启之后仍然有效
测试开发板和主机之间的网络连接,需要主机和开发板有电缆连接并配置正确的IP。
uboot通常需要将内核镜像从主机下载下来,并烧录到flash中,主流方式是fastboot和tftp,tftp通常将镜像下载到指定的内存地址上,然后就可以使用movi指令组进行镜像烧写。
开发板如果用SD卡,EMMC,或者是iNand作为Flash的时候,则在uboot中操作flash的命令为movi或者mmc,movi有很多子命令,read和write组合成一组,用于在iNand和DDR之间数据的交互。
DDR是没有分区的,但是在使用的时候注意不能越界访问,尤其是uboot这种裸机程序,很有可能会把自己的数据覆盖掉:
uboot的最终目标是用于启动内核,在命令行中执行该指令就可以启动内核,该命令会启动内核,并退出uboot,bootm启动内核同时给内核传递参数,go指令不传递参数,一般情况下都是用bootm。而go内部是一个函数指针,直接调用该函数,实际上是直接跳转到一个内存地址去运行,可以用来在uboot中执行裸机程序。
环境变量在uboot中是以字符串的形式存在的,所以uboot是以字符串匹配的方式寻找环境变量的。
自动运行命令的设置,uboot在启动后会开机自动倒数bootdelay秒,如果没有按下回车打断启动,则uboot会自动执行启动命令来启动内核,实际上就是执行了bootcmd环境变量的命令集, 该环境变量的值可以是索个命令组成的集合。
Linux内核在启动的时候可以接受uboot给它传递的启动参数,这些参数是提前约定好的,Linux在这些启动参数的指导下完成启动过程,使得内核启动方式更加灵活,只要该环境变量有值,则bootm在启动时会自动将这些环境变量值传递给内核,举一个常见的bootargs命令来分析:console=ttySAC2,115200 root=/dev/mmcblk0p2 rw init=/linuxrc rootfstype=ext3,意思是控制台使用的是ttySAC2(串口2),波特率为115200,root表示根文件系统,使用 MMCblock0(SD卡端口0的设备,也就是板载的iNand)其中的第二分区作为根文件系统路径,rw表示根文件系统可读写。init表示linux进程1的路径。rootfstype表示根文件系统的类型为ext3.
内核传参是非常重要的步骤,一定要保证给内核传正确的参数。
Flash中使用分区表对空间进行分区:
分区方式不是固定的,是可以变动的,但是必须在移植之前事先设计好并固定,一般在设计系统移植时的标准是:
每个分区收尾相连接,整个Flash可以被充分被利用,uboot一般必须在Flash的开头,其他分区原则上是可以变动的。分区大小一般根据需要来定。在系统移植前就需要确定分区表,在uboot和内核中都是用的是同一个分区表。在系统部署的运行的时候分区方式也必须一样。
DDR的分区和Flash不同,两者的存储原理不一样。uboot的内存管理任务主要在linux内核启动之前,Linux内核启动之后,整个DDR会被内核接管,就不需要uboot来管理了。uboot阶段DDR管理只需要避免内存被多个任务同时使用即可。