Uboot在s3c2440移植笔记
虚拟机:VMWare - Ubuntu 14.04.1
开发板:s3c2440
编译器:arm-linux-gcc
U-boot:u-boot-2009.08.tar.bz2
一:下载适当的U-boot版本并解压并配置交叉编译工具链
1、在ftp://ftp.denx.de/pub/u-boot/网站上下载u-boot-2009.08.tar.bz2,拷贝至ubuntu下
2、运用test@ubuntu:~/Documents$ tar -jxvf u-boot-2009.08.tar.bz2 解压
二:uboot目录结构说明
Uboot移植思想:
由于本次实验使用了NandFlash启动Uboot,NandFlash传输方式为非总线式访问,在NandFlash上电后自动将4K复制到片内内存SRAM中,然后在将后续的NandFlash的数据拷贝到SDRAM中,故可将Uboot分为两个部分stage1 stage2
stage1 :代码通常放在cpu/xxxx/start.S文件中,用汇编语言写成;
Stage2 :代码通常放在lib_xxxx/board.c文件中,他用C语言写成。
三、U-boot源码分析
==================================stage1阶段===================================
1、由于u-boot官方并没有针对s3c2440的soc的直接可用的源码,故我们选择与之相近的S3c2410的soc作为分析
①分析首要工作是通过分析链接脚本lds文件来查找程序的入口 故在uboot\cpu\arm920t\u-boot.lds中可查看到内存分配结构
② 在链接脚本中我们可以清楚的看到uboot链接的第一个文件是start.S文件 故我们查看uboot\cpu\arm920t\start.S
(1)在start.S文件中 第一步:建立异常向量表
(2)在start.S文件中 第二步:保存各个内存段的入口地址
(3)在start.S文件中 第三步:关看门狗 屏蔽所有中断 设置时钟
(4)在start.S文件中 第四步:跳转至 使能icache 关mmu
(5)在start.S文件中 第五步:跳转 初始化SDRAM (uboot\board\samsung\smdk2410\lowlevel_init.c)
(6)在start.S文件中 第六步:重定位relocate 将flash后4k拷贝到sdram
1、uboot中默认为NORFLASH拷贝至SDRAM (我们需求nandflash 注释此段)
2、自写NANDFLASH拷贝至SDRAM代码 初始化nandflash控制寄存器
3、调用自写nandflash至sdram拷贝函数 uboot\board\samsung\smdk2410\nand_read.c)
4、*注:自写函数需要在makefile中加上函数文件以确保得到编译链接(修改:uboot\board\samsung\smdk2410\Makefile)
5、*注:自写函数nand_read.c在start.s文件重定位完成前需要用到 故需保证链接在前4k (修改:uboot\cpu\arm920t\u-boot.lds)
(7)在start.S文件中 第七步:重定位完成 设置栈指针
(8)在start.S文件中 第八步:重定位完成 清bss段 跳转start_armboot
==================================stage2阶段===================================
2、完成在SRAM中4k的基本初始化过后,跳转至SDRAM中执行第一个函数start_armboot,完成各个外设的初始化,最终进入main_loop死循环执行CMD
==========================进入start_armboot配置初始化项========================
①分析start_armboot, 地址:uboot\lib_arm\board.c
(1)在start_armboot.c文件中 第一步:清global_data和board_info
1、此时大致可以看出SDRAM中空间的分布图为:(gd_t后紧接着bd_t内存空间 图未标出)
①:分析gd_t结构体(uboot/include/asm-arm/global_data.h ====>arm架构通用的h文件)
②:分析bd_t结构体(uboot/include/asm-arm/u-boot.h ====>arm架构通用的h文件)
*注:通过上面分析,我们可以从gb_t结构体间接的得到 bd_t开发板信息内存, env 数据内存, 跳转表jump table内存
···可初步获得以下信息:CPU时钟
串口波特率
RAM位置大小信息
环境变量数据区
uboot传参数据区
IP地址
机器码......................................等
(2)在start_armboot.c文件中 第二步:调用初始化函数序列
①:分析初始化函数序列数组init_sequence(重点:设置了相关的gb_t中属性和bd_t中属性 uboot\lib_arm\board.c)
(3)在start_armboot.c文件中 第三步:清MALLOC堆区
(4)在start_armboot.c文件中 第四步:初始化基本的外设
①:*注:在上述初始化过程中对env环境变量进行了初始化与重定位
env_relocate函数:uboot/common/Env_nand.c(由于我们是重定位到nandflash 故只分析了env_nand.c文件中的env_relocate函数)
(5)在start_armboot.c文件中 第五步:后续初始化
(6)在start_armboot.c文件中 第六步:进入死循环main_loop处理CMD
==============================进入main_loop 处理CMD==========================
1、main_loop函数是在start_armboot函数执行完成后进入的第二个函数,死循环执行用于接收用户输入CMD并解析执行
①分析main_loop, 地址:uboot\common\main.c
(1)在main_loop.c文件中 第一步:设置UBOOT启动次数和限制次数
*注:启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统
(2)在main_loop.c文件中 第二步:初始化Modem功能
(3)在main_loop.c文件中 第三步:设置Uboot版本信息
(4)在main_loop.c文件中 第四步:初始化CMD自动完成和HUSH功能
*注:设置命令行自动完成功能,该功能与Linux的shell类似,当用户输入一部分命令后,可以通过按下键盘上的Tab键补全命令的剩余部分,如果为减少uboot编译后大小,可通过宏定义剪裁uboot补全功能
(5)在main_loop.c文件中 第五步:设置延时启动时间、检查启动次数
(5)在main_loop.c文件中 第五步:执行进入kernel或者菜单项
①:如果bootdelay中途未跳出 并且环境变量
*注:分析怎样判断是否跳出delay延时,通过调用abortboot函数得到的返回值来判断 1:跳出 0:未跳出
判断思路:在delay延时过程中,轮询查看标准输入设备stdin是否接收到字符,如若接收到,则判断用户按下某个键,中断延时,返回1,并将按键字符保存在menukey中 (按下空格键才会满足menukey == CONFIG_MENUKEY条件进入菜单项,否则进入死循环命令)
分析:abortboot函数 地址 uboot\common\main.c
①:如果bootdelay中途跳出,则将用户按键的字符与空格键(默认)对比,匹配则进入菜单项
(6)在main_loop.c文件中 第六步:接收执行用户输入命令
①:在接收到用户输入的命令后,就调用run_commmand函数执行 因此我们继续分析解析和执行命令的函数run_commmand 地址:uboot\common\main.c
(1.)解析命令 并分割字符串保存到argv数组中
(2、)在u_boot_cmd段中的命令结构体遍历匹配命令并执行
②:遍历匹配命令时,要通过在u_boot_cmd段中依次查找存储存储的命令结构体cmd_tbl_s ,故我们分析命令结构体的基本组成 地址:uboot\include\command.h
③:继续分析怎么将命令结构体存入到u_boot_cmd段中的,在uboot\common\command.h中我们可以发现U_BOOT_CMD宏定义
*注:##为token(标号)连接符, #将变量转换为字符(变量两边加“”)
__attribute__ ((unused,section (".u_boot_cmd"))) 表示将变量存在u_boot_cmd段中
④:自定义命令加入u_boot_cmd段中,思路就是用U_BOOT_CMD宏定义一个结构体变量,那么编译时就会将该结构体保存在u_boot_cmd段中,故命令在uboot\common文件夹下c文件定义:下面以uboot\common\cmd_cache.c为例分析
1、定义一个命令结构体并存储在u_boot_cmd段
2、定义命令所执行的函数do_icache,由结构体的属性定义所知 do_icache的函数类型必须满足int(*cmd)(struct cmd_tbl_s *, int, int, char *[]) 格式
3、在当前目录下的Makefile文件中添加目标文件cmd_cache.o确保该文件中变量能被编译链接
4、确保当前命令的宏定义开关为开启状态 所有命令的宏定义开关位于uboot\common\Config_cmd_all.h
总结:通过上述分析,我们可以初步的分析出执行命令的方式是死循环的从console中接受用户输入的CMD 并解析保存在argv数组中,然后在u_boot_cmd段中的结构体序列中遍历出所匹配的当前命令结构体,以便获得该命令要执行的函数指针,最终执行。
UBOOT移植JZ2440实战
1、解压uboot源码选择适当的uboot,由于2440并没有直接适配的uboot版本,故我们选择与之相近的samsung/smdk2410
2、进入/u-boot/board/samsung目录下 拷贝一份smdk2410并改名为my2440
3、进入my2440的目录下,修改必要的位置
①将my2440目录下的smdk2410.c文件名改为my2440.c
②更改对应的Makefile
更改:
4、建立2440的头文件(里面存放在开发板相应的寄存器定义)
①进入uboot目录下的include/configs文件夹下
②建立一份my2440.h文件 直接拷贝smdk2410的头文件
5、在uboot的根目录下的中Makefile中配置my2440的编译选项
说明:arm:CPU的架构
arm920t:CPU的类型
my2440:对应board目录下的开发板目录
Samsung:对应的board的厂商目录,如果没有厂商目录 写NULL
S3c24x0:cpu型号
注:编译选项第二行要Tab开始,因为去解释文档时要靠Tab字符定位,不写报错
6、测试编译新建的my2440 出现以下字符则表示编译成功
U-Boot第一阶段的代码包括:
(1) cpu/arm920t/start.S (平台无关,处理器架构相关,存放如u-boot.lds链接脚本文件)
(2) board/samsung/lowlevel_init.S (平台与处理器型号相关)
(3) board/samsung/config.mk (平台相关,设置TEXT_BASE)
(4) include/configs/my2440.h (平台相关,设置寄存器初值等)
四、根据uboot的stage1和stage2来具体分析启动流程
一般的ARM编译完整后 会调用外部的链接脚本进行相应的段分配,故我们可以通过lds链接文件来查看nandflash前4k的代码(stage1阶段代码)分布情况
①查看make的编译信息可以看到链接脚本所在位置
②进入相应的文件夹 打开u-boot.lds
③故通过u-boot.lds我们发现最先放置在0x0地址处被运行的是cpu/arm920t/start.s
④运用source insight打开cpu/arm920t/start.s文件进行分析