转自迅为iTOP-4412开发板实战教程书籍:http://www.topeetboard.com 1. Bootloader简介在专用的嵌入式目标板上运行Linux系统已经变得越来越流行。一个嵌入式Linux系统从软件的角度,通常可以分为四个层次。 (1) 引导加载程序。包括固化在固件(firmware)中的boot代码(可选),和BootLoader两大部分。 (2) Linux内核。特定于目标板的定制内核以及内核的启动参数。 (3) 文件系统。包括根文件系统和建立于Flash内存设备之上的文件系统。 (4) 用户应用程序。特定于用户的应用程序。有时在用户应用程序和内核层之间可能还会包括一个嵌入式图形用户界面。 引导加载程序是系统加电后运行的第一段软件代码。例如没在PC机中的引导加载程序由BIOS(其本质就是一段固件程序)和位于硬盘MBR中的OS BootLoader(比如,LILO和GRUB等)一起组成。BIOS在完成硬件检测和资源分配后,将硬盘MBR中的BootLoader读到系统的RAM中,然后将控制权交给OS BootLoader。BootLoader的主要运行任务就是将内核映象从硬盘上读到 RAM 中,然后跳转到内核的入口点去运行,也即开始启动操作系统。 而在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。 简单地说,BootLoader就是在操作系统内核运行之前运行的一段小程序,功能类似BIOS。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。 注意由于BootLoader是严重地依赖于硬件而实现的,因此,在嵌入式世界里建立一个通用的BootLoader几乎是不可能的。 2. Bootloader的种类嵌入式系统世界已经有各种各样的Bootloader,种类划分也有多种方式。除了按照处理器体系结构不同划分以外,还有功能复杂程度的不同。 首先区分一下“Bootloader”和“Monitor”的概念。严格来说,“Bootloader”只是引导设备并且执行主程序的固件;而“Monitor”还提供了更多的命令行接口,可以进行调试、读写内存、烧写Flash、配置环境变量等。“Monitor”在嵌入式系统开发过程中可以提供很好的调试功能,开发完成以后,就完全设置成了一个“Bootloader”。所以,习惯上大家把它们统称为Bootloader。下表列出了Linux 的开放源码引导程序及其支持的体系结构。表中给出了X86 、ARM、PowerPC体系结构的常用引导程序。 如上表所示,可以看出对于每种体系结构,都有一系列开放源码Bootloader可以选用。 3. Uboot源码结构解压就可以得到全部Uboot源程序。在顶层目录下有18 个子目录,分别存放和管理不 同的源程序。这些目录中所要存放的文件有其规则,可以分为3类。 第1类目录与处理器体系结构或者开发板硬件直接相关; 第2类目录是一些通用的函数或者驱动程序; 第3类目录是Uboot 的应用程序、工具或者文档。 下表列出了Uboot顶层目录下各级目录存放原则。 目 录 | | | | | 存放电路板相关的目录文件,例如:cpu86、smdk210、samsung 等目录 | | | 存放CPU相关的目录文件,例如:mpc8xx、ppc4xx、arm720t、mips、xscale、i386等目录 | | | 存放对PowerPC 体系结构通用的文件,主要用于实现PowerPC 平台通用的函数 | | | 存放对ARM体系结构通用的文件,主要用于实现ARM平台通用的函数 | | | 存放对X86体系结构通用的文件,主要用于实现X86平台通用的函数 | | | 头文件和开发板配置文件,所有开发板的配置文件都在configs目录下 | | | | | | | | | | | | | | | | | | | | | | | | | | | 存放制作S-Record 或者U-Boot 格式的映像等工具,例如mkimage | | | |
Uboot的源代码包含对几十种处理器、数百种开发板的支持。对于特定的开发板,配置编译过程只需要其中部分程序。 4. Uboot分析本节从理论上分析关于Uboot启动的一些基础知识。 下图中可以看出,Uboot引导程序分为BL1、BL2、U-boot、TZSW四个部分。 另外可以看到Booting分区有严格的规定,BL1部分是15KB,BL2部分是16KB,U-boot是328K。例如,Uboot部分最终需要做补齐的,就是补齐到328K。Uboot不论怎么编译都是328K。 Uboot分段启动下图是exnoys4412 Datasheet中和启动相关的框图。 如上图所示,通过控制启动模式(OM),处理器上电以后,开始执行IROM中的代码,这段代码是原厂三星固化在芯片中,是无法修改的。 由上图可知,BL1可以放在4个引导设备中:NAND、SD/MMC、eMMC、USB。 IROM启动以后,会去找BL1,也就是bootloader1,BL1执行完以后会去找BL2。BL2会运行Uboot,Uboot引导Linux内核,内核启动完毕,Uboot的生命周期完全结束。运行顺序为:IROM--->BL1----->BL2---->Uboot---->Linux。 整个Uboot是分段启动。BL1运行在IRAM(不是IROM)中,不要将IRAM误解为DDR3内存,因为这个时候DDR3还没有被初始化,就是说DDR3的控制器还没有初始化。IRAM是可以上电就能使用的,可以这样理解,iRAM就是4412芯片内部特殊的RAM,不用初始化就能用。iRAM造价高,所以很小,总共才256K,所以不能把操作系统装进来,甚至无法将Uboot全部运行,要分段去启动,这样就分成了BL1和BL2。 BL1的安全性分析bootloader在系统启动的最前面运行,如果bootloader被攻破的话,危险级别几乎都会是致命的,所以这部分内容也一直是黑客们进行漏洞挖掘的领域。 智能机的安全问题越来越重要,手机中如果在Uboot中被“种”了病毒,病毒把通讯录或者私密视频到处网上发等,关键是“种”的病毒是无法杀掉的。可以这么说如果能够控制bootloader,那么iphone的越狱、Android的root以及在手机上安装第三方的ROM就都不成问题。bootloader是安全问题的重中之重,要保护bootloader的安全,防止bootloader被篡改。 出于安全性考虑,在上电启动执行IROM程序以后,加载BL1的时候,要对BL1进行多种安全检验,防止BL1是假的,然后BL1会对BL2进行各种检验,直至操作系统Linux ,这样保证整个系统是干净五毒的!下图是BL1的结构。 如下图所示,最上面的16个字节,number of sector(BL1 size) 也就是BL1的大小, Checksum Value也就是累加和校验。就是说把整个BL1主体部分的内容累加和放到这里,然后实际计算下整个累加和,做对比,如果不对,就说明BL1是假的或错误的,不予执行。 然后是BL1的第二部分,如下图所示。就算第一部分能作假,看下BL1的第二部分Encrypted BL1,实际就是经过加密的BL1代码,在Uboot中这部分没有源码,只有二进制文件,这段二进制文件由三星提供。 如下图所示,BL1的第三部分,这里放了客户和三星的公钥以及签名文件。 经过分析可知,在三星bootloader给用户提供了三重安全保障。 BL2分析BL2的结构如下图所示。BL2 Binary部分其实就是14K,然后下面是chechsum(校验),signature(签名),Padding(补齐)。可以看到BL2的主体部分没有加密,但下面有签名。 Padding(补齐),需要将BL2代码扩充到16K,补齐部分全部使用00000000。 在Uboot的makefile编译文件中,有下面几句编译命令: @./sdfuse_q/chksum @./sdfuse_q/add_padding @rm bl2a* 当Uboot生成以后,会通过add_padding进行后续处理,通过这个处理后,就补齐了。 需要注意的是,用户的BL1和BL2是没有经过secure boot的,也就是没有的安全认证内容,但是BL1和BL2本身还是安全的,三星提供了安全保障,BL1不再对BL2进行secure认证了。 在iTOP-4412开发板Uboot源码中,有两个文件需要注意。 E4412.TZ.SSCR.EVT1.1.bin 这个就是一个要求认证的BL1,它会对下级的BL2进行签名认证。 Exynos4412_V21.prv BL2 这个是私钥来签名,用于对BL2的签名 5. Uboot常用命令Uboot在硬件初始化完成之后,会进入一个无限循环,等待用户输入命令。U-boot发展到现在,它的命令行模式已经非常接近Linux下的shell了。 如下图所示,在控制台使用命令help可以查看所有默认支持的命令。 在iTOP-4412开发板中,命令如下表所示。 6. 裸机程序安装DNW驱动DNW驱动在网盘“iTOP-4412开发板视频教程及其相关”→“06-裸机程序实验文档以及工具文件”→”tools/USB驱动/dnw_driver“目录下面,我们提供了xp系统、win7-32位、win7-64位三种系统下面的驱动。下面我们以win7 64位系统的驱动为例来讲解一下DNW驱动的安装。 首先使用串口线连接iTOP-4412开发板的调试串口到PC机,使用USB连接线连接iTOP-4412开发板的USB OTG接口到PC机的USB口。 打开“tools”目录下的dnw.exe软件。 选择“Serial Port->Connect",连接到iTOP-4412开发板的串口,启动开发板,可以看到dnw软件会输出启动信息,在Uboot进入”倒数秒“的时候,按键盘的任意按键,将进入Uboot命令行。在Uboot的命令行输入“dnw”, 如下图所示。 在PC上右键点击“计算机”,选择“管理”, 打开”计算机管理“,然后鼠标点击上图左侧一栏里面的“设备管理器”,将会列出设备列表。 在中间一栏的“其他设备”里,可以看到“SEC S3C6400X Test B/D”这个未知设备,需要为这个设备安装DNW驱动,鼠标右键点击“SEC S3C6400X Test B/D”选择“更新驱动程序软件(P)...”,如下图所示。 打开“更新驱动程序软件”对话框,然后选择“浏览计算机以查找驱动程序软件(R)”,将会显示弹出如下图所示对话框。 点击上图的”浏览“,找到驱动,将会出现“windows安全”的对话框,然后点击的“始终安装此驱动程序软件”,开始安装驱动。 安装完成后,点击“关闭”按钮,关闭对话框。 在设备管理器里面看到DNW的设备了,如下图所示: 通过上面的操作,DNW的驱动已经安装完成了,现在可以使用DNW下载裸机程序到开发板。 下载裸机程序到开发板上重新启动我们的开发板,进入到Uboot命令行下面,然后输入“dnw 40008000”(这句的意思是启动DNW,设置程序下载到0x40008000地址), 如下图所示: 然后选择“USB Port->Transmit->Transmit”,如下图所示: 弹出传输文件对话框之后,选择裸机程序,这里选择的是”led.bin“程序。led.bin文件在在网盘“iTOP-4412开发板视频教程及其相关”→“06-裸机程序实验文档以及工具文件”→”tools/例程/led“目录下面。 然后点击“打开”按钮,这时我们在打开“USB Port->Transmit”,可以看到刚才选择的led.bin,如下图所示。 然后选择上图红色方框内的led.bin,下载到内存,下载完成后,串口输出如下图所示: 从上图我们可以看到led.bin下载到了内存0x40008000的地址,接下来在dnw软件上输入“go 40008000”使cpu从0x40008000地址开始运行,如下图所示。 运行上面的命令后,就可以看到iTOP-4412开发板上的两个led开始交替闪烁了。 裸机led的程序实现裸机led的程序实现需要启动文件start.S、代码文件led.c和编译文件Makefile。 start.S文件: .global _start _start: //disable watch dog ldr r0, =0x10060000 mov r1, #0 str r1, [r0] //turn on icache mrc p15, 0, r0, c1, c0, 0 //bic r0, r0, #0x00002300 /* clear bits 13, 9:8 (--V- --RS) */ //bic r0, r0, #0x00000087 /* clear bits 7, 2:0 (B--- -CAM) */ //orr r0, r0, #0x00000002 /* set bit 2 (A) Align */ //orr r0, r0, #0x00001000 /* set bit 12 (I) I-Cache */ #ifdef CONFIG_SYS_ICACHE_OFF // clear bit 12 (I) I-cache bic r0, r0, #0x00001000 #else // set bit 12 (I) I-cache orr r0, r0, #0x00001000 #endif mcr p15, 0, r0, c1, c0, 0 //mcr p15, 0, r0, c7, c5, 0 @ invalidate icache //set stack ldr sp, =0x02050000 bl led_blink halt: b halt led.c文件: #define GPL2CON (*(volatile unsigned long *) 0x11000100) #define GPL2DAT (*(volatile unsigned long *) 0x11000104) #define GPK1CON (*(volatile unsigned long *) 0x11000060) #define GPK1DAT (*(volatile unsigned long *) 0x11000064) //GPL2_0, GPK1_1 void delay(int r0) { volatile int count = r0; while (count--) ; } void led_blink() { GPL2CON = 0x00000001; GPK1CON = 0x00000010; while(1) { GPL2DAT = 1; GPK1DAT = 0; delay(0x80000); GPL2DAT = 0; GPK1DAT = 0x2; delay(0x80000); } } Makefile文件: led.bin: start.o led.o arm-none-linux-gnueabi-ld -Ttext 0x0 -o led.elf $^ arm-none-linux-gnueabi-objcopy -O binary led.elf led.bin arm-none-linux-gnueabi-objdump -D led.elf > led_elf.dis %.o : %.S arm-none-linux-gnueabi-gcc -o $@ $< -c -nostdlib %.o : %.c arm-none-linux-gnueabi-gcc -o $@ $< -c -nostdlib clean: rm *.o *.elf *.bin *.dis -f
|