本文以MPC8308(powerpc架构),测试板HX软件包为依据,详细内容可参考HX源码
一、嵌入式系统
一个嵌入式 Linux 系统从软件的角度看通常可以分为四个层次:
1.引导加载程序。包括固化在固件(firmware)中的boot 代码(可选),和 Boot Loader 两大部分。
2. Linux 内核。特定于嵌入式板子的定制内核以及内核的启动参数。
3. 文件系统。包括根文件系统和建立于 Flash 内存设备之上文件系统。通常用 ram disk 来作为 root fs。
4. 用户应用程序。
二、BootLoader的作用
Boot Loader 就是在操作系统内核运行之前运行的一段小程序。通过这段小程序,我们可以初始化硬件设备、建立内存空间的映射图,
从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
Boot Loader 主要依赖于CPU的体系结构和具体的嵌入式板级设备的配置
三、结合代码具体分析
1.上电
CPU 在复位时通常都从地址 0x00000000 取它的第一条
指令。而基于 CPU 构建的嵌入式系统通常都有某种类型的固态存储设备(比如: ROM、 EEPROM
或 FLASH 等)被映射到这个预先安排的地址上。
以下摘自mpc8308芯片手册:
4.3.2.2.1 Boot Memory Space (BMS)
The device defines the default boot ROMmemory space to be 8 Mbytes at addresses 0x0000_0000 to 0x007F_FFFF or0xFF80_0000 to 0xFFFF_FFFF. When the core comes out of reset, if it isenabled to boot, it fetches boot code from one of two addresses, 0x0000_0100or 0xFFF0_0100, and exceptions are vectored to the physical addresses,0x000n_nnnn or 0xFFFn_nnnn appropriately.
就是CPU会设置8Mbytes的启动空间,启动地址可能是从0x0000_0000开始或者0xFF80_0000。而取指令的地址也因空间不同而不同,第一种地址是从0x0000_0100开始,另外一种是从0xFFF0_0100开始。(详细见:/学习资料/u-boot分析/PowerPC上电复位的过程描述)
/cpu/mpc83xx/u-boot.lds中定义了整个img的入口函数/cpu/mpc83xx/start.S
/mpc83xx.h
通过调用系统复位中断从System reset偏移向量0x100来获取指令
system.map中_start的线性地址
2.空间结构图
同时装有 Boot Loader、内核的启动参数、内核映像和根文件系统映像
的固态存储设备的典型空间分配结构图。
HX flash分区如下:
定义在MPC8308EDD.h
#define MTDPARTS_DEFAULT "mtdparts=nand_mtd:0x00080000@0x00000000(uboot),"\
"0x00020000@0x00080000(uenv),"\
"0x3FF60000@0x000A0000(ubifs)"
3.两个阶段
stage1:依赖于 CPU 体系结构的代码,比如设备初始化代码等,通常都放在其中,而且通常都用汇编语言来实现,以达到短小精悍的目的。
stage2 则通常用C 语言来实现,这样可以实现给复杂的功能,而且代码会具有更好的可读性和可移植性。
4.u-boot环境变量和ubifs
MPC8308EDD.h中的宏
#define CONFIG_EXTRA_ENV_SETTINGS (自定义的)
刚烧入u-boot(flash)时,环境变量是
uchar default_environment[] (/common/env_common.c 默认环境变量+自定义变量)
+CONFIG_EXTRA_ENV_SETTINGS
"bootcmd=run dnalli\0"
然后执行bootcmd后会下载set_env.uscr脚本并按脚本添加和修改部分环境变量
其中:
set rt 'run ramargs addip; ubi part ubifs; ubifsmount kernel; ubifsload $loadaddredd_uImage; ubifsload $ramdiskaddr edd_uRamdisk; ubifsload $fdtaddr edd.dtb;bootm $loadaddr $ramdiskaddr $fdtaddr'
set bootcmd 'run rt'
这两条将设置下次启动后要执行的bootcmd,下面着重解析 run rt
run ramargs主要设置终端设备/ttyS0,波特率115200,指定根文件系统root=/dev/ram
run addip 主要设置localip,serverip,gateway,netmask,hostname
这里主要讲一下关于ubi的配置,配置u-boot支持ubi系统,参考Uboot201106的Ubifs文件系统移植
在MPC8308EDD.h中添加UBI宏和MTD分区信息宏,重新编译烧录后可以看到如下命令行:
ubi - ubi commands
ubifsload- load file from an UBIFS filesystem
ubifsls - list files in a directory
ubifsmount- mount UBIFS volume
version - print monitor version
1.ubi part ubifs(/common/cmd_ubi.c)
激活分区ubifs,在boot下执行
=> mtdpart
device nand0<nand_mtd>, # parts = 3
#: name size offset mask_flags
0: uboot 0x00080000 0x00000000 0
1: uenv 0x00020000 0x00080000 0
2: ubifs 0x3ff60000 0x000a0000 0
可以看到nand分区uboot uenv ubifs,从代码可以看出对这一分区做了一些初始化操作
do_ubi-->ubi_dev_scan-->ubi_init-->
ubi_msg("attaching mtd%d to ubi%d", mtd->index,ubi_num);-->
ubi_attach_mtd_dev-->io_init-->
ubi_msg("physical eraseblock size: %d bytes (%d KiB)", ubi->peb_size, ubi->peb_size >>10);
ubi_msg("logical eraseblock size: %dbytes", ubi->leb_size);
ubi_msg("smallest flash I/O unit: %d",ubi->min_io_size);
此时可通过ubi part命令查看已激活分区
=> ubi part
Device 0: nand0, partition ubifs
2.ubifsmountkernel
挂载分区,名称为“kernel”,打印信息如下:
UBIFS: mounted UBI device 0, volume 0, name "kernel"
这时可以通过ubifsls命令查看该文件系统内的文件
=> ubifsls
2006232 Tue May 19 12:10:47 2015 edd_uImage
7481360 Wed May 20 04:00:20 2015 edd_uRamdisk
5667 Tue May 19 12:10:47 2015 edd.dtb
三个文件的由来:
当内核、ramdisk编译完成后,将edd_uImage edd.dtb edd_uRamdisk拷贝到相对路径bin下,并按照ubifs的格式组合成目标文件kernel.img
mkfs.ubifs -r ./bin -m 2048 -e 129024 -c 300-o kernel.img
kernel升级时run dnk如下
set dnk 'tftp 0x3000000 kernel.img; ubi partubifs; ubi write 0x3000000 kernel $kernel_img'
将kernel_img写入ubifs分区
3.ubifsload $loadaddr edd_uImage; ubifsload $ramdiskaddredd_uRamdisk; ubifsload $fdtaddr edd.dtb;
loadaddr=1000000 edd_uImage
ramdiskaddr=2000000 edd_uRamdisk
fdtaddr=C00000 edd.dtb
将三个文件load到已规划好的内存空间中,最后执行bootm引导内核启动