S5PV210 Uboot开发与移植01:Uboot概述

目录

1. uboot概述

1.1 uboot的版本

1.1.1 官方版本

1.1.2 SoC厂商版本

1.1.3 开发板厂商版本

1.2 uboot功能框架

1.3 uboot生命周期

2. uboot编译流程

2.1 配置

2.2 设置交叉工具链

2.3 编译

3. x210对uboot.bin的使用

3.1 配置原因

3.2 sd_fusing目录文件分析

3.2.1 目录结构

3.2.2 sd_fdisk.c文件分析

3.2.3 C110-EVT-mkbl1.c文件分析

3.2.4 sd_fusing.sh脚本分析

3.2.5 烧写验证

4. x210 iNand分区及uboot信息验证

4.1 iNand分区验证

4.2 iNand如何分区

4.2.1 fdisk命令

4.2.2 iNand分区流程

4.2.3 使用GPT分区表的讨论

4.3 uboot信息验证

5. uboot常用命令

5.1 帮助命令

5.2 环境变量类命令

5.2.1 查看环境变量printenv

5.2.2 添加/修改/删除环境变量setenv

5.2.3 保存环境变量saveenv

5.3 内存操作类命令

5.3.1 md(memory display)命令

5.3.2 mw(memory write)命令

5.3.3 mm(memory modify)命令

5.4 SD/MMC操作类命令

5.5 网络命令

5.5.1 网络设置

5.5.2 ping命令

5.5.3 tftp命令

5.6 启动内核命令

6. Linux启动参数解析


1. uboot概述

1.1 uboot的版本

1.1.1 官方版本

ftp://ftp.denx.de/pub/u-boot/

1.1.2 SoC厂商版本

各SoC厂商(e.g. 三星)在推出一款SoC的同时会提供官方的开发套件,其中就包含SoC厂商提供的uboot

大型SoC厂商还会将官方开发板的BSP提交到uboot官方版本中

1.1.3 开发板厂商版本

目前世面上基于S5PV210 SoC的开发板都是在三星官方开发板的基础上修改而来,一般会对硬件接口进行裁剪,同时替换为更廉价的配件(e.g. DDR)。此时开发板厂商需要在SoC厂商提供的uboot基础上进行修改,以适配自身的产品

说明:我们后续分析的就是基于x210开发板的uboot,属于开发板厂商版本

1.2 uboot功能框架

S5PV210 Uboot开发与移植01:Uboot概述_第1张图片

uboot一般具有以下功能:

① 引导系统(完成硬件的初始化)

iROM中仅会针对SoC进行简单初始化,后续针对各款开发板的初始化需要uboot完成

② 运行操作系统(加载Linux内核并运行)

这是uboot最主要的功能,之前的初始化也是为Linux内核的加载运行准备环境

③ Flash烧写(更新系统Flash中的内容)

Flash烧写功能用于实现整个系统的部署功能,所谓系统部署就是完成整个系统(包括uboot、Linux kernel、rootfs等镜像)在Flash上的下载和烧录

④ 通信(串口、网络、USB)

⑤ 人机交互界面(基于命令行的交互功能)

1.3 uboot生命周期

以S5PV210芯片为例,

开始:iROM中的BL0从SD卡 / iNand中拷贝BL1并运行

结束:启动内核,交出控制权

2. uboot编译流程

以x210开发板的uboot为例,

2.1 配置

make  x210_sd_config

 

2.2 设置交叉工具链

修改uboot顶级目录下主Makefile中arm体系架构的CROSS_COMPILE变量,指定交叉工具链前缀

说明1:如果已将交叉工具链路径加入PATH环境变量,此处可以不用绝对路径

说明2:也可以不修改Makefile中的CROSS_COMPILE变量,而是在make时传递ARCH和CROSS_COMPILE变量的值

2.3 编译

make [-j4]

说明:uboot清理流程

所谓清理,就是删除uboot之前配置和编译生成的文件,uboot中的make clean / make mrproper / make distclean可以实现不同层次的清理

make clean:Remove most generated files but keep the config and enough build support to build external modules

make mrproper:Remove all generated files + config  + various backup files

make distclean:mrproper + remove editor backup and patch files

可以在配置编译uboot前执行make distclean,以清除可能存在的编译残留文件

3. x210对uboot.bin的使用

3.1 配置原因

x210对uboot.bin的处理是为了适应S5PV210的启动流程,在此简要复述

S5PV210 Uboot开发与移植01:Uboot概述_第2张图片

① S5PV210建议启动流程:

a. 将bootloader划分为BL1和BL2,iROM加载BL1(最大16KB)至iRAM中运行

b. BL1将BL2从存储设备也拷贝到iRAM运行

c. 在BL2中进行内存初始化等操作,然后将Linux kernel拷贝到内存中,并跳转到内存启动内核

② x210使用的启动流程:

a. 从uboot.bin中截取前8KB作为BL1,将整个uboot.bin作为BL2

b. iROM加载BL1到iRAM中运行

c. BL1完成内存初始化并将BL2,即整个uboot.bin从iNand拷贝到uboot.bin的内存链接地址处(使用iROM提供的存储设备拷贝函数)

d. 跳转到内存中接着运行BL2

e. BL2完成进一步初始化,将Linux kernel拷贝到内存中,并启动内核

说明1:适配S5PV210的各版本uboot基本都未使用三星推荐的启动流程,这是因为uboot.bin的镜像大小普遍超过96KB,iRAM根本无法容纳其运行

S5PV210之所以选择这种启动方式,是因为在前一代产品S3C6410中三星引入了iROM + iRAM的启动方式,但当时的Stepping Stone只有8KB,所以只能容纳BL1运行,BL2只能加载到内存运行

因此在S5PV210中增加了iRAM容量,希望能够将BL2也纳入其中运行(额~~最后只能是希望了~~)

S5PV210 Uboot开发与移植01:Uboot概述_第3张图片

说明2:BL1的生成方式

x210是从整个uboot.bin中截取8KB并校验加头构成BL1,这种方法的好处是只需要维护一份代码,使用一种编译方式生成一个镜像

其实官方uboot中也支持单独生成BL1镜像,这就需要用到CONFIG_SPL_BUILD配置项

S5PV210 Uboot开发与移植01:Uboot概述_第4张图片

3.2 sd_fusing目录文件分析

3.2.1 目录结构

S5PV210 Uboot开发与移植01:Uboot概述_第5张图片

根据Makefile,

① mkbl1由C110-EVT1-mkbl1.c编译而来,用于生成BL1

② sd_fdisk由sd_fdisk.c编译而来,用于生成分区表

3.2.2 sd_fdisk.c文件分析

3.2.2.1 struct SDInfo

S5PV210 Uboot开发与移植01:Uboot概述_第6张图片

该结构用于描述一张SD卡的信息,首先要注意的是addr_mode字段,该字段用于标识当前SD卡的地址模式是CHS_MODE还是LBA_MODE

CHS_MODE使用磁头-柱面-扇区号来寻址一个block;LBA_MODE使用逻辑块地址来寻址一个block

代码中对地址模式的判断标准为SD卡的block总数是否超过1023 * 255 * 63

3.2.2.2 struct PartitionInfo

S5PV210 Uboot开发与移植01:Uboot概述_第7张图片

该结构用于描述一个DPT分区表表项,此处介绍下DPT分区表及其相关概念

① Boot Sector(引导扇区)

Boot Sector(引导扇区)通常指设备的第一个扇区(是整个磁盘的第一个扇区,需要区别于分区引导扇区),在PC上BIOS会读取该扇区,并将控制权转交给Boot Sector上的MBR

Boot Sector包含3部分内容:

MBR(Master Boot Record,主引导记录),446B

DPT(Disk Partition Table,磁盘分区表), 64B

BRID(Boot Record ID,引导记录标识),2B,且必须是0x55AA

S5PV210 Uboot开发与移植01:Uboot概述_第8张图片

个人:在实际使用中,很多教材或文档以MBR指代Boot Sector,需要根据上下文区分

② DPT分区表结构

S5PV210 Uboot开发与移植01:Uboot概述_第9张图片

DPT分区表的每个表项如上表所示,从中可以总结出,

a. 每个表项占用16B,所以DPT最多只能容纳4个分区,因此传统磁盘上才引入了扩展分区和逻辑分区的概念

b. 分区总扇区数为4B,即一个分区最多容纳2^32个扇区,即2TB

注意:这种分区表格式可以同时支持CHS和LBA地址模式的存储设备(LBA地址模式可以使用分区起始相对扇区号和分区总扇区数,同时将CHS相关的参数填写为最大值)

3.2.2.3 函数流程

① 获取SD卡信息并填充SDInfo结构

其中get_sd_block_count函数是通过读取/sys/block/sdb/size获得当前SD的block总数,总数为15976448 ≈ 7.62GB,而且 < (1023 * 254 * 63),所以是CHS_MODE

② 划分FAT分区

S5PV210 Uboot开发与移植01:Uboot概述_第10张图片

首先空出SD卡的前10MB空间,然后设置该分区的2个属性,

bootable:设置为0,即非活动分区

partitionId = 0x0C,即W95 FAT32(LBA)分区

S5PV210 Uboot开发与移植01:Uboot概述_第11张图片

而该分区的大小则一直延伸到SD卡结束(以宏BLOCK_END标识),即将剩余空间全部划分为一个分区

注意:将剩余空间全部划分为一个分区时,会空出末尾的10MB空间

S5PV210 Uboot开发与移植01:Uboot概述_第12张图片

③ 构造分区表

S5PV210 Uboot开发与移植01:Uboot概述_第13张图片

分区表中仅有一个FAT32分区,从SD卡10MB处至SD卡末尾10MB处

注意:0x55AA为MBR标识

最终生成的分区表如下,符合上述分析,

S5PV210 Uboot开发与移植01:Uboot概述_第14张图片

3.2.3 C110-EVT-mkbl1.c文件分析

该文件与mkv210_image.c功能一致,用于读取uboot.bin的前8KB并计算其校验和,构成BL1

此处需要注意以下2点:

① uboot代码中预留BL1头部信息位置

S5PV210 Uboot开发与移植01:Uboot概述_第15张图片

在x210的uboot最前端,为BL1的头信息预留了16B,对照Header Info格式,此处指定的BL1长度为8KB

S5PV210 Uboot开发与移植01:Uboot概述_第16张图片

② 校验范围

BL1长度包含16B的头部信息,但是校验时并不包含头部信息(这个其实是很显然的)

S5PV210 Uboot开发与移植01:Uboot概述_第17张图片

3.2.4 sd_fusing.sh脚本分析

sd_fusing.sh脚本调用方式如下,

./sd_fusing.sh  /dev/sdb # 设备文件名根据实际情况确定

3.2.4.1 参数验证

S5PV210 Uboot开发与移植01:Uboot概述_第18张图片

此处需要注意的是,-b用于判断$1标识的文件是否存在且是否是块设备

注意:x210提供的脚本有一处错误!!!

S5PV210 Uboot开发与移植01:Uboot概述_第19张图片

定义变量partition的正确方式如下,

partition1="${1}1"

按原先脚本中partition1="$11"的定义,partition1的值为空,因为shell会认为11是一个变量,而11是没有定义的

说明:shell脚本对变量的引用

${变量名}称作对变量的引用,在部分情况下可以省略为$变量名,如果要引用的变量名之后还有其他内容,则需要使用{}

3.2.4.2 分区 + 格式化

S5PV210 Uboot开发与移植01:Uboot概述_第20张图片

说明1:如之前分析,调用sd_fdisk创建分区表文件sd_mbr.dat,然后使用dd命令将分区表文件烧写到SD卡的第0扇区,也就实现了对SD卡的分区

说明2:通过 2> /dev/null重定位stderr是因为如果卸载不存在的分区,可以免去终端的错误打印信息

说明3:格式化/dev/sdb1分区时指定-F 32也是与分区时指定的FAT32格式匹配

补充:上述步骤体现出了分区和格式化的关系

① 分区体现在对分区表的设置

② 一旦分区表设置完成,Linux的设备驱动将会自动生成partition1变量标识的分区,e.g. /dev/sdb1

③ 此处的格式化基于分区实现,即在partition1变量标识的分区中构建FAT32文件系统。而在嵌入式环境中,一般从目录构建文件系统,以实际项目为例,

其中-r选项表示,

# -r: build file system from directory DIR

3.2.4.3 生成并烧写BL1

S5PV210 Uboot开发与移植01:Uboot概述_第21张图片

从uboot.bin开头截取8KB构造BL1(SD-bl1-8k.bin),然后烧写到SD卡第1扇区

3.2.4.4 烧写BL2

将整个uboot.bin作为BL2烧写到SD的第49扇区(该扇区与uboot代码的拷贝位置匹配,详见后续文档)

3.2.5 烧写验证

S5PV210 Uboot开发与移植01:Uboot概述_第22张图片

启动时先打印出了SD checksum Error,然后有uboot的启动信息,说明在iNand启动失败后,从SD卡的uboot启动成功

此时再验证一下SD卡中烧写的内容,需要注意的是导出SD扇区的命令,

# 获取MBRsudo dd if=/dev/sdb of=0_sector.bin bs=512 count=1

MBR中只有DPT分区表和MBR标识

# 获取MBR
sudo dd if=/dev/sdb of=0_sector.bin bs=512 count=1

这里需要特别注意的是skip选项(同时说明3个与读写数据相关的选项),

skip=BLOCKS:skip BLOCKS ibs-sized blocks at start of input

seek=BLOCKS:skip BLOCKS obs-sized blocks at start of output

bs=BYTES:read and write up to BYTES bytes at a time

ibs=BYTES:read up to BYTES bytes at a time (default: 512)

obs=BYTES:write BYTES bytes at a time (default: 512)

与写入SD不同,此处读取SD是input file需要偏移,所以应该使用skip选项

根据BL1前16B的头信息,BL1长度为8KB,且带有校验和

# 获取BL2的第1扇区
sudo dd if=/dev/sdb of=49_sector skip=49 bs=512 count=1

BL2的前16B就是代码中预留的头部信息位置,且没有改动

4. x210 iNand分区及uboot信息验证

4.1 iNand分区验证

x210分区情况如下,

S5PV210 Uboot开发与移植01:Uboot概述_第23张图片

可见iNand被划分为4个分区,且均为主分区(没有使用扩展分区),分区类型均为Linux分区(在实际使用中就对应了ext2/3/4的Linux系统默认的分区类型)。

/dev/mmcblk0p2为根分区,/dev/mmcblk0p4为用户分区,且这4个分区均非活动分区

对照iNand第0扇区中的内容与上述信息匹配(当然匹配,上述信息就是解析MBR得来的~~)。需要注意的是,fdisk -l显示信息中的Blocks是1K块的个数

S5PV210 Uboot开发与移植01:Uboot概述_第24张图片

4.2 iNand如何分区

根据上文分析,SD卡的分区是sd_fdisk.c文件实现的,那么iNand的分区是谁实现的呢 ? 即iNand第0扇区的MBR信息从何而来 ?

4.2.1 fdisk命令

此处说明的是uboot中的fdisk命令

uboot中的fdisk命令可以创建 / 打印分区表

S5PV210 Uboot开发与移植01:Uboot概述_第25张图片

4.2.2 iNand分区流程

fdisk命令会调用do_fdisk函数实现,其中创建分区表会调用create_mmc_fdisk函数

S5PV210 Uboot开发与移植01:Uboot概述_第26张图片

与sd_fsik.c相同,构造分区表的方式就是构造MBR数组,并将该数组写入iNand的第0扇区

S5PV210 Uboot开发与移植01:Uboot概述_第27张图片

而构造分区表的方式则和之前sd_fdisk.c的方式完全相同(其实sd_fdisk.c就是移植了uboot的fdisk命令)

S5PV210 Uboot开发与移植01:Uboot概述_第28张图片

此处需要注意的是uboot中的分区安排(按iNand中先后顺序排列)

分区大小

分区用途

10MB

空出iNand的前10MB空间(目前这部分有MBR、uboot[BL1 & BL2]、Linux kernel)

256MB

CONFIG_PART_SIZE(p1分区)

120MB

SYSTEM_PART_SIZE(p2分区)

100MB

CACHE_PART_SIZE(p3分区)

剩余空间

用户空间(p4分区)

说明:结合上文分析,p1和p3分区并未挂载,目前尚不知是否有其他组件使用

注意:system分区(p2分区)大小探讨

代码中的SYSTEM_PART_SIZE为120MB,但实际分区结果为256MB

该分区在Linux系统启动后的分区文件名为/dev/mmcblk0p2,存储的是根文件系统,在该分区烧写的文件系统镜像为rootfs_qt4.ext3,该文件大小为256MB,与目前的分区大小是匹配的

S5PV210 Uboot开发与移植01:Uboot概述_第29张图片

导致这一问题的原因是x210提供的uboot.bin与源代码并不一致,目前源码并未同步官方镜像对分区表大小的修改~~

4.2.3 使用GPT分区表的讨论

目前讨论的都是DPT分区表,如果要改用GPT分区表是否可行呢 ?

S5PV210 Uboot开发与移植01:Uboot概述_第30张图片

GPT分区表的结构如上图所示,扇区0需要写入Protective MBR(其中会设置一个类型为0xEE的分区),然后从第1到第34扇区写入GPT分区表,最后倒数33个扇区写入备份的GPT分区表

但是根据S5PV210的启动流程,BL1必须放在第1扇区,因此就无法在此处部署GPT分区表

因此是否可以使用GPT分区表,却决于SoC的启动方式,更准确的说是SoC从SD / MMC的何处取得BL1/BL2

注意:之所以分区表要写入固定的位置,是因为Linux驱动会到该位置读取并解析分区表

4.3 uboot信息验证

关于iNand中uboot的信息,通过比较第1扇区和第49扇区的内容,二者完全相同。考虑到iNand中的uboot是通过fastboot或SD量产卡实现烧写的,而二者在烧写时使用的就是一份已加头检验的uboot.bin,所以推测是将同一份uboot.bin先截取8KB烧写到第1扇区,然后再将整个uboot.bin烧写到第49扇区(具体验证见fastboot原理分析文档)

5. uboot常用命令

5.1 帮助命令

help命令有2种使用方式,

① 单独使用help

可以查看当前uboot支持的所有命令

S5PV210 Uboot开发与移植01:Uboot概述_第31张图片

② help + 命令名

可以查看一个uboot命令的具体用法

5.2 环境变量类命令

5.2.1 查看环境变量printenv

使用printenv可以查看当前uboot中的环境变量,print为该命令的简化版

S5PV210 Uboot开发与移植01:Uboot概述_第32张图片

5.2.2 添加/修改/删除环境变量setenv

① 添加/修改

setenv name value

② 删除

setenv name

注意:设置含有特殊符号的环境变量值

如上文截图,uboot中的环境变量bootargs中包含分号,在设置时有2种处理方式

① 使用单引号

S5PV210 Uboot开发与移植01:Uboot概述_第33张图片

② 使用转义字符

S5PV210 Uboot开发与移植01:Uboot概述_第34张图片

5.2.3 保存环境变量saveenv

在uboot启动过程中,会将Flash上存储的环境变量读入内存中,使用setenv修改的就是内存中的副本

调用saveenv才会将当前定义的所有变量及其值写入Flash

说明:环境变量的保存是整体覆盖,即不能够单独保存某个环境变量的key-value

5.3 内存操作类命令

5.3.1 md(memory display)命令

md采用十六进制和ASCII码两种形式来显示内存的内容,其中b为单字节 / w为2字节 / l 为4字节,同时还可以指定一次显示的字节数

S5PV210 Uboot开发与移植01:Uboot概述_第35张图片

说明:此处显示的地址为0x33e00000,对应uboot的链接地址(0xc3e00000),从内容对比可知uboot.bin确实被加载到此处

注意:uboot命令行中的所有数字默认作为十六进制处理

5.3.2 mw(memory write)命令

mw命令用于修改指定内存的内容

配合[count]字段还可以实现连续操作,下图即每次改写4B,连续修改16(0x10)次

5.3.3 mm(memory modify)命令

mm命令是一种互动修改内存的方法,他会显示地址和当前值,然后提示用户输入。如果输入一个合法的十六进制数,这个新值就会被写入该地址。然后地址自动递增,提示下一次修改。想终止修改时,只要输入空格,然后回车即可

说明:此时选择的地址是uboot的链接地址

5.4 SD/MMC操作类命令

在x210的uboot中,使用movi命令族实现对iNand的操作

在环境变量bootcmd中就是使用

movi read kernel 30008000

将Linux kernel从iNand加载到内存中

补充:NandFlash操作命令

对于使用NandFlash的开发板,一般会提供另一套操作命令

# 擦除start处开始的,长度为len的区域
nand erase 起始地址start 长度len


# 将内存地址起始处,长度为len的数据,写入flash起始地址处
nand write 内存起始地址 flash起始地址 长度len


# 将flash起始地址处,长度为len的数据,读到内存起始地址处
nand read 内存起始地址 flash起始地址 长度len

5.5 网络命令

5.5.1 网络设置

如果要使用uboot的网络功能,需要正确设置如下的环境变量

ipaddr:开发板IP地址

gateway:开发板网关

netmask:子网掩码

serverip:开发板通过tftp命令下载文件时对应的tftp服务器地址

ethaddr:开发板网卡MAC地址(一般不用设置,除非与局域网中其他MAC地址冲突)

5.5.2 ping命令

uboot的ping命令一般不实现回显机制,所以都是用开发板ping主机

5.5.3 tftp命令

一般使用tftp命令从主机下载Linux kernel

5.6 启动内核命令

bootm命令的作用是执行固定格式的二进制文件,一般用于启动Linux内核,在启动内核时还可以传递initrd的地址,x210开发板启动内核时就附带了initrd的地址

补充:uboot中的go命令也可以用于启动二进制文件,但是go命令只是简单修改PC指针跳转到指定地址执行,并传递所有命令行参数,但不能指定initrd等信息

这里简要说明下go命令的实现方式,

① do_go函数会将目标地址和剩余的命令行参数(去掉的正式"go"本身)传递给do_go_exec函数

② do_go_exec函数以传入的addr为entry入口点,直接跳转执行

③ do_go_exec函数被定义为弱类型(weak),可以重定义该函数并覆盖默认行为

6. Linux启动参数解析

Linux内核启动时可以接收uboot给他传递的启动参数,这些参数由Linux内核规定。Linux内核在这些启动参数的指导下完成启动过程。这种设计使得可以在不重新编译内核的情况下,以不同的方式启动内核

下面分析一下x210的uboot传递给Linux内核的参数,

console=ttySAC2,115200:控制台使用串口2,波特率为115200

root=/dev/mmcblk0p2  rw:根文件系统在/dev/mmcblk0p2分区,以读写方式挂载

init=/linuxrc:此处需要使用绝对路径,用于指定init进程(默认运行/sbin/init进程)

rootfstype=ext3:根文件系统类型为ext3

Linux内核参数详细介绍可参考Linux内核源代码Documentation/kernel-parameters.txt

注意:启动过程中的一处错误

此处传递给Linux内核的参数中指定了根文件系统所在分区,但是在调用bootm启动内核时又指定了initrd在内存中的位置,而initrd在启动过程中作为虚拟根文件系统供Linux内核挂载

在uboot的bootm命令实现中,会判断出该虚拟文件系统格式错误~~

S5PV210 Uboot开发与移植01:Uboot概述_第36张图片

此处增补一个疑问:uboot / Linux kernel / initrd在内存中的布局

根据相关文档分析,x210 uboot的链接地址为0xc3e00000,根据uboot中的MMU映射规则,该地址对应的物理地址为0x33e00000

但是bootcmd中将Linux kernel加载到0x30008000处,将initrd加载到0x30B00000处,这些内存地址之间会存在地址覆盖

但实际并没有出现问题,后续需要能够解释~~其实X210目前并未真正使用initrd启动

更正:此处内存分配是没有覆盖问题的,只是并没有有效的initrd

S5PV210 Uboot开发与移植01:Uboot概述_第37张图片

只要内核镜像大小不超过11MB - 32KB,就不会发生内存覆盖

你可能感兴趣的:(Linux嵌入式开发,linux,运维,服务器)