以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
参考内容
uboot——官网下载直接移植(一) - biaohc - 博客园
uboot——官网下载直接移植(二) - biaohc - 博客园
uboot移植(一)配置过程分析_LouisGou的博客-CSDN博客
获取官方uboot
(1)下载地址:ftp://ftp.denx.de/pub/u-boot/
(2)下载版本:u-boot-2013.10.tar.bz2
(3)这版本的uboot还没有引入kernel的配置体系,新一点的版本就引入了这个配置体系。
分析uboot目录
1、比较版本的不同之处
(1)九鼎用的是1.3.4版本,这里用的是2013.10版本。
(2)九鼎1.3.4版本的uboot的/cpu文件夹,对应着2013.10版本的uboot的/arch文件夹。
(3)九鼎1.3.4版本的uboot,它的主Makefile文件中包含着与board有关的配置信息。2013.10版本的uboot,将与board有关的配置信息从主Makefile文件中抽离出来,写在/board.cfg文件中。后续将查找board.cfg文件中的配置信息来确定我们参照哪个开发板。
2、选取参照开发板
(1)参照开发板的cpu要和x210开发板的cpu相同。x210开发板的cpu是S5PV210,因此要在uboot中寻找使用S5PV210或者S5PC110进行移植的例子作为参考。
(2)打开board.cfg文件,搜索s5pc1xx,发现有两个相关的开发板,即goni和smdk100。
(3)我们选择goni开发板,该开发板对应的头文件是/include/configs/s5p_goni.h,对应的board在/board/samsung/goni这个目录,对应的cpu在/arch/arm/cpu/armv7/s5pc1xx。
移植过程思维导图
一、删除无关文件后建立SI项目
1、删除/arch目录中无关文件
- /arch目录下只保留arm文件夹。
- /arch/arm/cpu目录下除了armv7文件夹,其他的文件夹删除。
- /arch/arm/cpu/armv7目录除了s5pc1xx 以及s5p_common这两个文件夹,其他的文件夹删除。
2、删除/board目录中无关文件
- /board目录下只保留samsung文件夹。
- /board/samsung/目录下只保留goni、common文件夹。
3、使用sourceinsight创建项目
二、主Makefile的修改
1、新版本uboot的主Makefile文件
(1)以前配置x210开发板的uboot时,直接make x210_sd_config,对应的主Makefile目标、依赖性、操作如下所示。从中可以看住,这里给出了明确的目标“x210_sd_config”,以及参数“x210_sd、arm、s5pc11x、x210、samsung、s5pc110”。
x210_sd_config : unconfig@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
(2)此版本的uboot的主Makefile中:
1)对目标使用了通配符%,即“%_config”(Makefile中的%表示通配符,而*表示任意字符。举例:%.o表示匹配所有的.o文件,注意是用于匹配的,*.o是表示所有的.o文件。前者一般用作目标,后者一般用作删除)。
2)将以前版本的主Makefile中各种开发板的配置操作抽象出来,把具体的配置信息部分写到一个独立的文件boards.cfg。这样在make xxx_config时,无论xxx是什么都会执行相同的操作。现版本的uboot的主Makefile只有将近1000行的代码,以前版本的uboot的主Makefile有几千行,就是因为以前版本的主Makefile对于目标没有使用通配符,而是为每个板子列了一个目标以及具体的操作。
%_config:: unconfig@$(MKCONFIG) -A $(@:_config=)sinclude $(obj).boards.depend $(obj).boards.depend: boards.cfg //参数信息要在这个文件中寻找@awk '(NF && $$1 !~ /^#/) { print $$7 ": " $$7 "_config; $$(MAKE)" }' $< > $@
3)每个板子的具体配置参数信息,要到/boards.cfg文件中查找,该文件内容如下。
//……省略部分 #Status, Arch, CPU:SPLCPU, SoC, Vendor, Board name, Target, Options, Maintainers // 板子名称 配置时xxx_config中的xxx //……省略部分Active arm armv7 s5pc1xx samsung goni s5p_goni - Minkyu Kang <mk7.kang@samsung.com> Active arm armv7 s5pc1xx samsung smdkc100 smdkc100 - Minkyu Kang <mk7.kang@samsung.com> //……省略部分
2、明确本次移植参照的板子信息
(1)其对应的board文件夹:u-boot-2013.10/board/samsung/goni。
(2)其对应的cpu文件夹:u-boot-2013.10/arch/arm/cpu/armv7。
(3)其对应的头文件:u-boot-2013.10/include/configs/s5p_goni.h
3、在主Makefile中添加交叉编译工具链
(1)官方原版的uboot中CROSS_COMPLIE没有定义,需要自己定义。
(2)如果没有定义而直接编译,则使用GCC进行编译。
(3)我们在Makefile中添加一行代码,如下。
CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
三、/mkconfig脚本的分析
1、在命令行配置uboot,即执行“make s5p_goni_config”时,对应Makefile中的这个目标:
%_config:: unconfig@$(MKCONFIG) -A $(@:_config=)
- $(MKCONFIG)表示uboot根目录下的/mkconfig脚本。
- 该脚本接受两个参数,即-A和s5p_goni。
- 该脚本完成一些符号连接的创建,以及创建头文件include/config.h。
- include/config.h文件中仅有一行代码,即“ #include<include/configs/s5p_goni.h> ”。
- 配置前,configs文件夹就已经位于/include文件夹下,它的每个文件对应一个开发板的头文件。这些头文件都是一些宏定义配置文件,是移植时最主要的文件。
2、分析/mkconfig脚本。
if [ \( $# -eq 2 \) -a \( "$1" = "-A" \) ] ; then# Automatic modeline=`awk '($0 !~ /^#/ && $7 ~ /^'"$2"'$/) { print $1, $2, $3, $4, $5, $6, $7, $8 }' boards.cfg`if [ -z "$line" ] ; thenecho "make: *** No rule to make target \`$2_config'. Stop." >&2exit 1fiset ${line}# add default board name if needed[ $# = 3 ] && set ${line} ${1} fi
- 上面是mkconfig脚本24至35行代码。
- 其使用awk正则表达式,将boards.cfg文件中与刚才$2(s5p_goni)能够匹配上的那一行截取出来赋值给变量line,然后将line的内容以空格为间隔依次分开,分别赋值给$1、$2…$7、$8。
- 在解析完boards.cfg之后,$1到$8重新赋值如下:$1 = Active,$2 = arm,$3 = armv7,$4 = s5pc1xx,$5 = samsung,$6 = goni,$7 = s5p_goni,$8 = -;
arch="$2" cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $1}'` spl_cpu=`echo $3 | awk 'BEGIN {FS = ":"} ; {print $2}'` if [ "$6" = "-" ] ; thenboard=${BOARD_NAME} elseboard="$6" fi [ "$5" != "-" ] && vendor="$5" [ "$4" != "-" ] && soc="$4"
- 上面是mkconfig脚本55至64行代码。
- 从此段代码得到arch=arm,cpu=armv7,vendor=samsung,soc=s5pc1xx。
# # Create link to architecture specific headers # if [ "$SRCTREE" != "$OBJTREE" ] ; thenmkdir -p ${OBJTREE}/includemkdir -p ${OBJTREE}/include2cd ${OBJTREE}/include2rm -f asmln -s ${SRCTREE}/arch/${arch}/include/asm asmLNPREFIX=${SRCTREE}/arch/${arch}/include/asm/cd ../includemkdir -p asm elsecd ./includerm -f asmln -s ../arch/${arch}/include/asm asm firm -f asm/archif [ -z "${soc}" ] ; thenln -s ${LNPREFIX}arch-${cpu} asm/arch elseln -s ${LNPREFIX}arch-${soc} asm/arch fiif [ "${arch}" = "arm" ] ; thenrm -f asm/procln -s ${LNPREFIX}proc-armv asm/proc fi
上面是mkconfig脚本94至123行代码。
此处在创建符号连接,即:include/asm—>arch/arm/include/asm,include/asm/arch—>include/asm/arch-s5pc1xx,include/asm/proc —> include/asm/proc-armv。
# # Create board specific header file # if [ "$APPEND" = "yes" ] # Append to existing config file thenecho >> config.h else> config.h # Create new config file fi echo "/* Automatically generated - do not edit */" >>config.hfor i in ${TARGETS} ; doi="`echo ${i} | sed '/=/ {s/=/ /;q; } ; { s/$/ 1/; }'`"echo "#define CONFIG_${i}" >>config.h ; doneecho "#define CONFIG_SYS_ARCH \"${arch}\"" >> config.h echo "#define CONFIG_SYS_CPU \"${cpu}\"" >> config.h echo "#define CONFIG_SYS_BOARD \"${board}\"" >> config.h[ "${vendor}" ] && echo "#define CONFIG_SYS_VENDOR \"${vendor}\"" >> config.h[ "${soc}" ] && echo "#define CONFIG_SYS_SOC \"${soc}\"" >> config.hcat << EOF >> config.h #define CONFIG_BOARDDIR board/$BOARDDIR #include <config_cmd_defaults.h> #include <config_defaults.h> #include <configs/${CONFIG_NAME}.h> #include <asm/config.h> #include <config_fallbacks.h> #include <config_uncmd_spl.h> EOFexit 0
- 上面是mkconfig脚本151至185行代码。
- 此处创建include/config.h文件。
四、配置编译测试
依次输入下列指令,得到u-boot.bin。
- make distclean
- make s5p_goni_config
- make
五、start.S最前面添加16字节填充
将x210开发板的sd_fusing文件夹复制到新版uboot的根目录中,然后烧录sd卡。sd_fusing文件夹是三星官方另外提供的烧录sd卡的工具,所以x210开发版的uboot以及三星官方uboot目录中都有这个文件夹。现在我们使用新版本uboot进行移植,所以uboot中没有这个文件夹。
烧写sd卡后重启,输出信息如下。
分析:
- 接的是串口2,串口有输出。但是这个串口输出不是uboot输出的,而是内部iROM中的BL0运行时输出的。
- 第一个SD checksum Error:是第一顺序启动设备SD0(iNand)启动时校验和失败打印出来的(正常,因为已经破坏inand,想从SD卡启动);
- 第二个SD checksum Error:是第二顺序启动设备SD2(外部SD卡)启动时校验和失败打印出来的;
- 第三个uart negotiation error:是串口启动失败打印出来的;
- 第四个Insert an OTG cable into the connector:是usb启动失败打印出来的。
总结:
- 从两个SD checksum Error可以看出,外部SD卡校验和失败了。
- 经过分析和查找(用winhex工具对比正常的uboot和出错的uboot),发现是mkbl1程序和start.S中前16个字节校验和的处理不匹配造成的。
- 新的uboot没有占位,但sd_fusing里的mkbl1还是按有16字节占位的情况进行校验和,所以会失败。
解决方法:
- 法一:在start.S(目录层次arch/arm/cpu/armv7/start.S)最前面加上16个字节的占位。
- 法二:参见博客uboot——官网下载直接移植(二) - biaohc - 博客园
重新编译烧写运行,发现结果只显示一个SD checksum Error。这是内部SD0通道的inand启动校验和失败打印出来的,说明外部SD卡校验和成功,只是SD卡上的uboot是错误的,没有串口输出内容,因此没有输出。
六、start.S文件的分析和移植
1、start.S流程分析
2、添加“开发板制锁”和“串口输出“O””的代码
(1)添加代码的目的
- 为了调试uboot的第一阶段(调试的目的是确认它可以顺利运行,完成相应的工作),就要看到现象。
- 为了看到现象,向lowlevel_init函数中添加2个程序段,一个是开发板制锁,一个是串口初始化后打印"O"。
(2)实践添加
- 开发板制锁代码
- 串口打印"O"代码
(3)结果及分析
- 结果:没看到开发板制锁,串口也没有输出任何东西,实验失败。
- 结论:开发板制锁没有成功,因此判定在开发板制锁代码运行之前uboot就已经挂掉了。因此要追踪代码运行,可通过添加LED点亮代码跟踪程序运行。
3、添加LED点亮代码跟踪程序运行
(1)使用LED点亮的方式来调试程序
- 在基础代码阶段,串口还没有运行,串口调试工具还无法使用。
- 可以从程序的基本运行路径端出发,隔一段添加一个LED点亮代码,根据运行时的现象来观察,判定哪里执行了哪里没执行,从而去定位问题。
- 以前做实验时发现,uboot运行时按住电源开关时所有4颗LED都是亮的,因此做实验时点亮LED是判断不了的,应该熄灭某些LED来判断。
- 也可以用Jlink等调试工具来调试这种基础代码。
(2)可用的示例代码
- 注意不同代码处亮灭情况的修改;
- 注意示例代码不是函数,因此没有必要添加函数返回语句mov pc,lr;
ldr r0, =0x11111111ldr r1, =0xE0200240str r0, [r1]ldr r0, =((1<<3) | (0<<4) | (1<<5)) // 1是灭,0是亮ldr r1, =0xE0200244str r0, [r1]ldr r2, =9000000ldr r3, =0x0 delay_loop: sub r2, r2, #1 cmp r2, r3 bne delay_loop//mov pc,lr 不要添加这句,以前在裸机中是作为延时函数使用,因此需要函数返回;现在是直接使用,不需要!
(3)实践操作
(4)结果和分析
- 结果:点亮LED灯的代码一旦放到lowlevel_init.S中,就不工作。
- 结论:b lowlevel_init这句代码出现问题。
4、修改u-boot.lds,将lowlevel_init.S放到前部
(1)首先,分析知道问题出在代码的链接上
- 三星S5PV210要求BL1大小为8KB,因此uboot第一阶段的代码必须在前8KB内,否则跳转不到。
(2)其次,明确三星官方uboot的u-boot.lds、官方版本uboot的u-boot.lds的区别
- 这两个版本的uboot的链接脚本的目录位置是不同的;
- lowlevel_init.S代码段没有被放在前面,即lowlevel_init.S不在前8KB内,所以b lowlevel_init执行失败。
(3)然后,修改操作
- 在u-boot.lds中start.o后面添加board/samsung/goni/lowlevel_init.o (.text*),即可保证lowlevel_init函数被连接到前面8kb中。
(4)最后重新编译
- 报错,提示lowlevel_init重复定义。
5、修改board/samsung/goni/Makefile解决编译问题
(1)为什么会重复定义?
- lowlevel_init这个函数被链接两次。一次是在board/samsung/goni目录下生成libgoni.o时,另一次是链接脚本在链接生成u-boot时。
- 单纯地将代码注释掉,将导致不会编译.s(因为没有.o的需要,根据自动推导原则,将不会编译)。我们只是不需要链接它,编译还是需要的。
(2)如何解决此错误?
- 解决思路:完成编译,但在libgoni.o中不被连接,在最终连接u-boot时才被链接。
- 参考当前版本(即待移植)的uboot的start.S文件的处理技巧,解决此这个问题。
(3)实验结果
- 开发板制锁,串口输出'O'。
6、目前已修改内容
(1)针对“lowlevel_init.S不在前8KB内,因此b lowlevel_init执行失败”的问题
- 在u-boot.lds中start.o后面添加board/samsung/goni/lowlevel_init.o (.text*),即可保证lowlevel_init函数被连接到前面8kb中。
(2)针对“lowlevel_init重复定义”的问题
- 见上面5的操作细节。
七、添加DDR初始化
1、分析下一步移植路线
(1)在2013.10版本的uboot中
- 第一阶段就是cpu_init_crit函数(因此要在cpu_init_crit函数中添加DDR初始化和uboot的重定位),cpu_init_crit函数成功初始化串口、时钟后,转入_main函数。
- 第二阶段在arch/arm/lib/crt0.S文件中,入口是_main函数。
(2)在2013.10版本的uboot中,把以前uboot的第二阶段start_armboot函数分成2部分:board_init_f和board_init_r。
- board_init_f中是板级的初始化;
- board_init_r中进入了uboot的命令行。
(3)在crt0.S中首先设置栈,将sp指向DDR中的栈地址;然后调用board_init_f函数进行板级初始化。
- board_init_f函数在arch/arm/lib/board.c中。
(4)下一步移植工作方向
- 先在cpu_init_crit函数中添加DDR初始化;
- 然后在start.S中bl _main之前添加uboot的重定位;
- 然后将bl _main改成ldr pc, __main(__main: .word _main)长跳转;
- 然后在crt0.S中board_init_f后删除那些重定位代码;
- 至此uboot的第二阶段就应该能启动起来。
2、DDR初始化代码移植
(1)2013.10版本的uboot中根本没有DDR初始化,需要另外添加DDR初始化代码。
- 可以从三星版本的uboot中直接移植DDR初始化代码。
- 三星版本的uboot中DDR初始化函数在cpu/s5pc11x/s5pc110/cpu_init.S文件中。
(2)动手移植
a、将三星版本的cpu/s5pc11x/s5pc110/cpu_init.S复制到uboot2013.10中。
- 必须保证cpu_init.S有关的代码在前8kb内,因此类似于lowlevel_init.S文件一样的链接处理;
- 链接处理主要是在board/samsung/goni/Makefile中、arch/arm/cpu/u-boot.lds文件中做修改添加。
b、添加头文件s5pc110.h到include目录下。
c、对cpu_init.S文件代码进行修整。
- 把一些无用的代码去掉,把一些相关的条件编译人工处理一下。
d、在SourceInsigt工程中添加cpu_init.S文件、s5pc110.h文件。
- 重新解析一遍。
- 对新添加的代码进行分析修整,把里面一些明显的宏定义缺失给补上。
e、移植必要的宏定义
- DDR配置参数,从三星版本的smdkv210single.h中复制到s5p_goni.h中。
- s5pc110.h中的修整。
f、代码同步、编译、再修整
g、添加调试信息,验证DDR初始化完成。
- 调试信息有LED点亮和串口输出两种,优先选用串口调试的方法。因为这里已经将串口初始化了。
- 在DDR初始化完成后,添加串口输出字符"K",这样启动时如果看到了"OK"就说明DDR已经被成功初始化了。
h、结果
- 看到了"OK"标志,说明DDR添加实验成功。
八、添加uboot第二阶段重定位
1、在重定位代码前加调试信息定位
(1)逻辑上来说,重定位部分代码应该在DDR初始化之后和uboot第二阶段来临前之间。
(2)uboot的第一阶段和第二阶段的划分并不是绝对的,但第一阶段必须不能大于8KB。
- uboot的第一阶段至少要完成DDR初始化和重定位,且最多不能超过8KB。
- 在满足这些条件时,第一阶段和第二阶段的接点可以随便挑。
(3)找到合适的地方来写重定位代码,重定位之后远跳转到第二阶段的入口。
2、重定位代码移植
3、清bss段移植
4、movi_bl2_copy函数移植
(1)从三星版本的uboot中复制movi.c和movi.h到uboot2013.10中;
(2)改makefile和u-boot.lds。
5、_mian函数中基本处理
- 主要就是把里面的重定位代码部分给删除掉。剩下就是:设置栈、调用board_init_f函数和board_init_r函数。
6、代码同步及编译
- 主要是crt0.S和movi.h。
7、编译中出现问题解决
(1)movi.h中宏定义出错
- 在s5p_goni.h中添加了 CONFIG_EVT1
(2)连接错误:u-boot contains relocations other than R_ARM_RELATIVE
- 在uboot下用grep "R_ARM_RELATIVE" -nR *搜索,发现Makefile中有一个检查重定位的规则,屏蔽掉这个规则后编译连接成功。
8、编译,下载,结果分析
uboot启动打印出来一系列信息,但是uboot没有进入命令行。这说明uboot中的DDR初始化和重定位功能都已经实现。
九、CPU时钟信息显示移植
1、banner信息补全
2、CPU ID的确定
3、CPU各种频率的自动计算
4、代码实践
- arch/arm/include/asm/arch-s5pc1xx/cpu.h,和arch/arm/cpu/armv7/s5p-common/cpu_info.c文件同步一下。
5、问题分析
(1)时钟显示ARMCLK是400MHz。
(2)调试,把m、p、s和apll_ratio打印出来后,发现这几个值的设置和之前的uboot的设置是不同的。
- 原因是当前版本的uboot并未对SoC的时钟进行设置,当前uboot中的时钟是iROM代码默认设置的。
(3)我一直认为iROM中已经把210的时钟设置为1000MHz,因此uboot中是否有时钟设置并不重要,但实际并非如此。
- 内部iROM中设置的时钟APLL输出的是800MHz,ARMCLK是400MHz。
- 因此如果uboot中不做时钟的设置,实际得到的就是这个时钟,所以得到的结果是400MHz。
- 所以要解决这个时钟不对的问题,在lowlevel_init.S中添加上时钟初始化的代码即可。
6、时钟初始化函数的添加
- 在lowlevel_init.S中移植system_clock_init函数,并且在s5p_goni.h中添加相关的宏定义参数,然后在lowlevel_init函数中调用system_clock_init函数。
十、board和DDR配置显示移植
1、board名称更改
2、DDR配置值修改
3、MACH_TYPE定义
4、DDR打印信息更改
5、代码实践
6、关于MACH_TYPE的定义问题
(1)uboot2013.10、uboot1.3.4的设计有所不同
- uboot1.3.4中,MACH_TYPE分散定义在各个配置头文件中;
- uboot2013.10中,MACH_TYPE集中定义在arch/arm/include/asm/mach-types.h中。
- 集中定义是uboot从linux内核中学来的,在linux kernel中,MACH_TYPE在文件中集中定义。
- 集中定义的好处是方便查阅,不容易定义重复。
(2)MACH_TYPE和开发板绑定
- 原则上每一个开发板型号都有一个MACH_TYPE,这个机器码由linux内核管理者来分配的,如果需要应申请。
十一、board_init_r移植
1、去掉oneNand支持
2、添加SD/MMC支持
十二、uboot2013.10中SD/MMC驱动浏览
1、从初始化代码开始浏览
2、相关函数和文件
- drivers/mmc/mmc.c
- drivers/mmc/sdhci.c
- board/samsung/goni/goni.c
- arch/arm/include/asm/arch-s5pc1xx/mmc.h
3、当前错误定位及解决方案分析
(1)错误发生路径定位
- board_init_r
- mmc_initialize
- do_preinit
- mmc_start_init
- mmc_go_idle
- mmc_send_cmd
- sdhci_send_command
- sdhci_transfer_data:错误在这个函数中
(2)错误原因分析
- sdhic.c中的所有函数构成了三星210CPU的SD/MMC控制器的驱动。
- 这里面的函数是三星公司的工程师写的,内容就是用来控制210CPU的内部的SD/MMC控制器和外部的SD卡通信的。这就是所谓的驱动。
- sdhci_transfer_data函数出错,说明是SoC的SD/MMC控制器和外部SD卡(其实现在用的是SD0的iNand)的数据传输出了问题。
- 细节分析发现是控制器内部有一个中断状态错误标志被置位了。
(3)三种解决方案
- 第一种,逐行分析SD卡驱动实现(分析中要对SD卡通信协议、SD控制器非常熟悉),发现错误所在,然后修改代码解决问题;
- 第二种,把原来三星移植版本的uboot中的SD/MMC驱动整个移植过来替换掉uboot2013.10中的MMC驱动;
- 第三种,综合第一种和第二种,譬如参考三星移植版本的uboot中的驱动实现来修补uboot2013.10中的驱动实现。
十三、SD卡驱动移植
1、分析两个版本的uboot中SD卡驱动差异
(1)uboot2013.10中,驱动相关的文件主要有:
- drivers/mmc/mmc.c
- drivers/mmc/sdhci.c
- drivers/mmc/s5p_sdhci.c
- board/samsung/goni/goni.c
(2)三星移植版本中,驱动相关的文件主要有:
- drivers/mmc/mmc.c
- drivers/mmc/s3c_hsmmc.c
- cpu/s5pc11x/cpu.c
- cpu/s5pc11x/setup_hsmmc.c
(3)SD卡驱动要工作要包含2部分内容
- 一部分是drivers/mmc目录下的是驱动;
- 一部分是uboot自己提供的初始化代码(譬如GPIO初始化、时钟初始化)
2、复制必要的文件并修改相应Makefile
(1)首先解决drivers/mmc目录下的文件替换。
(2)修改初始化代码。
3、代码浏览及修补
按照代码运行时的流程来逐步浏览代码,看哪里需要修补。
4、继续修补驱动代码
(1)include/mmc.h
(2)include/s3c_hsmmc.h
5、同步及编译、问题解决
(1)出错1:cmd_mmc.c中出错
- 原因是cmd_mmc.c和mmc驱动密切相关,所以改了驱动后这个实现文件也要跟着改。
- 解决方法是从三星版本的直接同名文件复制过来替换
(2)出错2:drivers/mmc/mmc_write.c编译出错
- 原因是这个文件和本来版本中的mmc.c文件相关,但是mmc.c被替换掉了所以这个文件编译报错。
- 解决方案就是修改makefile去掉这个文件的依赖,让他不被编译。
(3)出错3:#include<regs.h>注释掉,然后添加#include <s5pc110.h>
6、解决每次编译时间都很长的问题
(1)本次移植实验中,每次编译脚本cp.sh执行时都会先cp同步代码,然后make distclean……所以每次都会清空后从头编译,很费时间。
(2)实际上有时候不用make distclean,只需要先cp然后直接make即可。
- 当更改没有涉及到配置头文件s5p_goni.h,没有涉及到makefile文件,或者其他项目配置文件,
- 即更改只是发生在普通代码文件的更改时;
7、效果测试
- 读写测试均成功
十四、环境变量的代码浏览
1、检查iNand分区表,弄清楚ENV究竟应该放在哪
(1)测试环境变量是否可以保存,通过开机set设置环境变量然后save,然后关机后重启来测试环境变量的保存是否成功。
(2)环境变量究竟保存到哪里去了?这个要分析代码中的分区表。
(3)环境变量应该被放在哪里?
- 虽然无法确定ENV一定要放在哪里,但是有一些地方肯定是不能放的,否则将来会出问题。原则是同一个SD卡扇区只能放一种东西,不能叠加,否则就会被覆盖掉。
- uboot烧录时使用的扇区数是:SD2的扇区1~16和49~x(x-49大于等于uboot的大小)。
(4)从uboot的烧录情况来看
- SD2的扇区0空闲;扇区1-16被uboot的BL1占用;扇区17-48空闲;扇区49-x被uboot的BL2占用。再往后是内核、rootfs等镜像的分区。系统移植工程师可以根据kernel镜像大小、rootfs大小等给SD分区。
- 根据uboot的分区情况,ENV不能放置在扇区1~16或者49~x,其他地方都可以商量。
- ENV的大小是16K字节,也就是32个扇区。
2、环境变量相关代码浏览
(1)目前情况是uboot在SD2中(SD卡),而ENV在SD0中(iNand),所以现在ENV不管放在SD卡哪个扇区都能工作,因为用的是SD0中的ENV。但是我们还是得找到ENV分区在SD卡的扇区位置,并且修改,使其不会和SD卡中的uboot冲突。而且将来部署系统时我们会将uboot和kernel、rootfs等都烧录到iNand中去,那时候也要确保不会冲突。
(2)write_env()函数
static inline int write_env(struct mmc *mmc, unsigned long size,unsigned long offset, const void *buffer)
- mmc表示要写的mmc设备,size表示要写的大小,offset表示要写到SD卡的哪个扇区去,buffer是要写的内容。
(3)CONFIG_ENV_OFFSET
- 决定ENV在SD卡中相对SD卡扇区0的偏移量,也就是ENV写到SD卡的位置。
- 经过分析发现这个宏的值为0,因此ENV被写到了0扇区开始的32个扇区中。
- 写到这里肯定不行,因为和uboot的BL1冲突了;解决方案是改变这个CONFIG_ENV_OFFSET的值,将ENV写到别的空闲扇区去。
(4)ENV位置
#define MOVI_BL2_POS ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_BL1_BLKCNT + MOVI_ENV_BLKCNT)
- 后面三个分别是1+16+32=49;其中的1就是扇区0(空闲的),16是就是扇区1-16(uboot的BL1),32就是扇区17-48(存放ENV的),49是uboot的BL2开始扇区。
- 这种安排是三星移植的uboot版本中推荐的SD卡的分区方式,不一定是唯一的。我们参考这个设计,即可实现环境变量不冲突。所以只要将ENV放到17扇区起始的地方即可。
十五、环境变量的测试和配置移植
1、如何测试环境变量的保存是否正确?
(1)程序修改重新编译后启动,启动后要注意iNand中本来有没有环境变量。
- 为了保险起见,对iNand的前49个扇区进行擦除,然后就可以确保里面没有之前保存过的环境变量了。
- 使用命令:mmc write 0 30000000 0# 49,来擦除SD0的扇区0-48,保证以前的环境变量都没有了。
(2)重新开机后,先set随便改一个环境变量作为标记,然后saveenv,接着重启。
(3)测试方法
- 使用mmc read 0 30000000 17# 32命令,将iNand的17开始的32个扇区读出来到内存30000000处,然后md查看。
- 找到显示区域里面的各个环境变量,看读出来的和自己刚才修改的值是否一样。
2、常用环境变量的配置移植
- 常用的环境变量就是网络相关的那几个,和CONFIG_BOOTCOMMAND、CONFIG_BOOTARGS等。
十六、网卡驱动的移植
1、添加网络支持
(1)uboot通过条件编译来实现可配置可裁剪。默认情况下,uboot没有选择支持网络。
(2)在配置头文件中添加一行 #define CONFIG_CMD_NET
(3)添加了网络支持宏之后,在uboot初始化时就会执行eth_initialize函数,从而网络相关代码初始化就会被执行,将来网络就有可能能用。
2、添加ping和tftp命令
(1)在linux系统中,网络底层驱动被上层应用调用的接口是socket,是一个典型的分层结构,底层和上层是完全被socket接口隔离的。
(2)但是在uboot中,网络底层驱动和上层应用是不分层的,意思就是上层网络的每一个应用都是自己去调用底层驱动中的操作硬件的代码来实现的。
(3)uboot中有很多预先设计好的需要用到网络的命令,和我们直接相关的就是ping和tftp这两个命令。这两个命令在uboot中也是需要用相应的宏开关来打开或者关闭的。
(4)经过代码检查,发现ping命令开关宏为CONFIG_CMD_PING,而tftp命令的开关为CONFIG_CMD_NET,确认添加。
3、代码实践
- 结果是ping和tftp命令都被识别了,但是都提示no ethernet found……网络不通。
- 为什么不通?因为还没做初始化等移植。
4、移植网卡初始化代码
5、实验现象分析
(1)因为没有自定义的网卡初始化函数(board_eth_init或者cpu_eth_init),所以uboot启动时初始化网卡时打印:Net: Net Initialization Skipped。
(2)eth.c中有2个很重要的全局变量
- eth_devices:用来指向一个链表,这个链表中保存了当前系统中所有的网卡信息;
- eth_current:eth_current指针指向当前我们正在操作的那个网卡。
(3)在linux的网卡驱动体系中,有一个数据结构(struct eth_device)用来表示(封装)一个网卡的所有信息
- 注册一个网卡,就是要建立一个这个结构体的实例,然后填充这个实例中的各个元素,最后将这个结构体实例加入到eth_devices这个链表上,就完成了注册。
- 网卡驱动在初始化时,必须将自己注册到系统的网卡驱动体系中(即把自己的eth_device结构体实例添加到eth_devices链表中),否则会出现“No ethernet found.”。
(4)分析当前的问题是:在305行判断eth_devices是否为NULL之前,没有去做网卡驱动的注册,所以这里为NULL,所以打印出了“No ethernet found.”
6、DM9000驱动浏览
(1)因此要在305行之前去注册网卡驱动
- 注册网卡驱动的代码不能随便乱写,一定要遵守linux网卡驱动架构的要求。
- 这一块的代码一般属于网卡驱动的一部分,像这里就在dm9000x.c中。
(2)dm9000x.c中的最后一个函数int dm9000_initialize(bd_t *bis),这个函数就是用来注册dm9000网卡驱动的。
7、问题修复
- 根据之前分析uboot函数,发现前面有2个函数预留的可以用来放网卡初始化函数的,经过对比感觉board_eth_init函数稍微合适点,于是乎去添加。
8、对比和总结
十七、uboot启动内核的移植
待写。