以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。
一、移植总结
二、移植过程
1、获取三星官方版本的uboot
(1)由于X210开发板使用S5PV210这款CPU,因此我们应该寻找一块使用相同CPU的开发板,然后以这块开发板的uboot为基础进行移植,这样可以大大减少移植的工作量。X210开发板是根据三星的smdkv210开发板进行裁剪设计的,两者使用相同的CPU,因此我们以这个开发板的uboot为基础进行移植工作。
(2)三星smdkv210开发板使用的uboot是uboot_smdkv210.tar。
2、删除无关的文件
移植之前我们首先把不相关的cpu、board、lib目录删除,然后用sourceinsight生成project。
(1)/board目录:此目录下每个文件夹代表一个开发板。把不相关的开发板的文件全部删除,只保留samsung文件夹。samsung文件夹里只保留common文件夹、smdkc110文件夹。
(2)/cpu目录:每一个文件夹就是一个SoC系列。这个目录下只保留s5pc11x文件夹,其他全部删除。
(3)/include目录:asm-开头的文件夹只保留asm-arm文件夹。asm-arm文件夹下arch-开头的文件夹只保留arch-s5pc11x。/include目录下的其他文件与文件夹保留。
(4)/include/configs目录:每一个开发板对应一个.h文件。只保留smdkv210single.h文件。
(5)/lib_ 开头的文件夹保留lib_arm、lib_generic 。
3、直接编译三星移植版uboot尝试运行
(1)检查与修改Makefile中的交叉编译工具链。
ifeq ($(ARCH),arm) #CROSS_COMPILE = arm-linux- #CROSS_COMPILE = /usr/local/arm/4.4.1-eabi-cortex-a8/usr/bin/arm-linux- #CROSS_COMPILE = /usr/local/arm/4.2.2-eabi/usr/bin/arm-linux- CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
(2)先配置“make smdkv210single_config”,然后编译“make”,生成uboot.bin文件。
配置操作对应着/include/configs/smdkv210single.h这个头文件。
执行上述命令时可能遇到127权限问题,解决方法“chmod 777 -R uboot_smdkv210”。
root@ubuntu:/home/xjh/iot/embedded_basic/uboot/uboot_smdkv210# make smdkv210single_config make: execvp: /home/xjh/iot/embedded_basic/uboot/uboot_smdkv210/mkconfig: 权限不够 make: *** [smdkv210single_config] 错误 127 root@ubuntu:/home/xjh/iot/embedded_basic/uboot# chmod 777 -R uboot_smdkv210 root@ubuntu:/home/xjh/iot/embedded_basic/uboot/uboot_smdkv210# make smdkv210single_config Configuring for smdkv210single board... root@ubuntu:/home/xjh/iot/embedded_basic/uboot/uboot_smdkv210#
- 生成的uboot.bin文件位于uboot源码的根目录下。
(3)将sd卡接入虚拟机linux系统,然后在uboot源码的sd_fusing目录下执行“./sd_fusing.sh /dev/sdb”命令,把刚才生成的uboot.bin文件烧录到sd卡中。
注意sd_fusing.sh脚本中,烧写的内容是不是uboot.bin以及uboot.bin的路径对不对。
利用“file mkbl1”查看mkbl1是不是32bit的,如果不是则需要在sd_fusing目录下执行make clean,然后再make。
sd_fusing.sh文件中,“/mkbl1 ../u-boot.bin SD-bl1-8k.bin 8192”表示将uboot的前8k复制到SD-bl1-8k.bin文件中。
执行此sd_fusing.sh脚本即可把bl1、uboot.bin烧录到sd卡中。
(4)运行结果
1)现象
开发板启动时,电源置锁(电源指示灯常亮),显示“SD checksum Error”,没有打印“ok”。
2)分析
uboot中串口最早的输出“OK”,是在lowlevel_init.S中初始化串口时打印出来的,而现在串口没有输出"O"则说明在打印"O"之前代码已经死掉了。开发板供电锁存操作在lowlevel_init.S文件中完成,而现在开发板供电锁存成功,说明这个代码之前的部分是没问题的。错误发生在开发板供电锁存代码和串口初始化打印"O"代码之间。
3)纠错
在/board/samsung/smdkc110/lowlevel_init.S文件的lowlevel_init函数中,PMIC_InitIp函数是用来管理电源的。由于X210开发板没有PMIC模块,这个函数内部向PMIC模块发送I2C接口的命令时,会一直等待模块的响应,导致后面的代码不能运行。只要把此函数注释掉即可。
4、解决“NAND: ”后面没有显示信息的问题
(1)将PMIC_InitIp函数屏蔽后,执行make进行编译,然后将镜像文件烧写至sd卡,将sd卡插入开发板,然后重新启动时,uboot启动时输出如下:
朱有鹏的教程视频在这步显示的内容如下:
对比可知启动信息与朱有鹏的教程视频不同,这里的“NAND: ”之后就没有后续的输出。
(2)在linux系统中搜索“NAND:”(利用grep "NAND:" -nr ./)得知其定位是/lib_arm/board.c文件的start_armboot函数中,其下一行是nand_init函数,猜想问题出现在这个函数。
(3)在/drivers/mtd/nand/nand.c、/lib_arm/board.c文件中都定义了nand_init函数,我们在函数开始位置,分别在添加代码“puts("this is nand.c !\n");”与“puts("this is board.c !\n");”,根据重新编译与运行的显示信息,得知其调用的是/drivers/mtd/nand/nand.c这个文件。
(4)这里做的移植实验,为何“NAND: ”之后就没有后续的输出呢?在nand_init函数的下一行添加代码“puts("xjh is a handsome man! \n");”,如下所示。
然后重新编译与运行,发现没有输出这个信息,这说明uboot卡在了nand_init函数中。
(5)为什么会卡在这个函数呢?我一开始猜想这个函数写错了,但对比X210开发板的uboot的nand_init函数,发现两者是一样的,所以这个函数内部应该不会写错。其次想到X210开发板没有使用nand,因此uboot会卡在nand_init函数,于是我将nand_init函数注释掉,重新编译与运行,发现uboot显示完整的信息。
(6)为什么X210开发板的uboot不出现这个问题?这与是否定义了CONFIG_CMD_NAND这个宏有关。在X210开发板的uboot源码中搜索这个宏,在 ./include/configs/x210_nand.h 文件中定义了这个宏,而x210_sd.h文件中把这个宏注释掉了。由博文uboot源码——根目录下的mkconfig文件分析可知,X210开发板使用的是x210_sd.h文件,因此没有定义CONFIG_CMD_NAND这个宏,所以nand_init函数就不会执行。
(7)为什么移植的uboot出现了这个问题?因为在/include/configs/smdkv210single.h中定义了CONFIG_CMD_NAND这个宏。因此相比于注释掉nand_init函数,把这个宏注释掉是更加合乎规范的选择。但实际上把宏注释掉,执行make的时候有其他差错,因为别处用到这个宏。
5、uboot的版本号和时钟配置
上面的问题解决后,uboot启动的信息如下:
(1)由博文uboot源码——C阶段的start_armboot函数的第二节的第8点可知,uboot的版本号信息是由display_banner这个函数打印的。如果想要修改“for SMDKV210”这个信息,只需在/include/configs/smdkv210single.h文件中修改CONFIG_IDENT_STRING这个宏即可。
(2)由博文uboot源码——C阶段的start_armboot函数的第二节的第9点可知,时钟信息是在print_cpuinfo这个函数中打印出来的。在lowlevel_init.S文件中通过“bl system_clock_init”调用system_clock_init函数来完成时钟的初始化。如果想要更改时钟部分的信息,完全不需要改动system_clock_init这个函数,只有修改这个函数中有关的宏的宏定义即可。这是因为三星移植时已经把smdkv210常用的各种时钟的配置计算好,并且用宏来表示其中的参数,只要打开相应的宏开关就能将系统配置为各种不同的频率。注意,这里显示的时钟信息是对的,因此不需要任何修改。
(3) 由博文uboot源码——C阶段的start_armboot函数的第二节的第10点可知,“Board: SMDKV210”是/board/samsung/smdkc110/smdkc110.c文件中的checkboard函数打印的。
上述修改之后,uboot启动时显示的信息如下:
6、DDR的配置
uboot打印的这个信息“DRAM: 1GB”是错误的,移植时要根据实际情况进行修改。
(1)在/include/configs/smdkv210single.h中定义了DRAM中的配置信息,移植前的内容如下图。由此可知开发板有两块DRAM,大小均为512M,所以显示总的内容大小为1GB。
但是X210开发板实际只有512M内存,每块DRAM大小都是256M,因此圈出来的地方要修改为“#define SDRAM_BANK_SIZE 0x10000000 ”,表示256M大小。
(2)上图中的MEMORY_BASE_ADDRESS这个宏表示DRAM0的物理地址,它的值为0x20000000。SDRAM_BANK_SIZE这个宏表示DRAM0或DRAM1的大小,修改后的值为0x10000000。这意味着DRAM0的物理地址范围是0x20000000~0x2FFFFFFF(256MB),DRAM1的物理地址范围是0x40000000~0x4FFFFFFF(256MB)。
我们希望内存地址是连续的,为0x30000000~0x4FFFFFFF,因此需要配置DDR的地址。
步骤1:更改寄存器的值
DDR的初始化,是lowlevel_init.S文件中通过“bl mem_ctrl_asm_init”调用mem_ctrl_asm_init函数来完成的。
这个函数的内部通过一些宏对相应的寄存器做了相应的初始化,当我们需要更改寄存器的内容时,修改这些宏的值即可。
为了让uboot具有可移植性,这些宏都定义在/include/configs/smdkv210single.h文件中,我们需要根据实际情况来修改这个文件中有关的宏。
这里修改DMC0_MEMCONFIG_0这个宏的值为0x30F01323(原因要看mem_ctrl_asm_init函数代码),如下所示。
步骤2:软件配置值更改
更改寄存器的值只是相当于更改了硬件配置部分,还需要更改uboot中与DDR相关的一些软件配置值还需要更改。
在/include/configs/smdkv210single.h文件中,将MEMORY_BASE_ADDRESS这个宏的值由0x20000000更改为0x30000000。
步骤3:虚拟地址映射表中进行相应修改
uboot中开启MMU对内存进行了段式映射,其中有一张内存映射表,即/board/samsung/smdkc110/lowlevel_init.S文件中标签mmu_table下面的代码。
经过分析得知,这个内存映射只是把20000000开始的256MB映射到C0000000开头的256MB。
这里将“.set __base,0x200”修改成“.set __base,0x300”,如下图所示。
步骤4:修改虚拟地址到物理地址的映射函数
修改/board/samsung/smdkc110/smdkc110.c中的virt_to_phy_smdkc110函数,将其中的20000000改为30000000即可。
上述修改之后,uboot启动时显示的信息如下:
7、解决SD卡版本号问题
(1)SD/MMC提示错误如下:
在uboot源码目录下输入“grep "unrecognised EXT_CSD structure" -nr ./”,得知问题定位在/drivers/mmc/mmc.c文件的818行,mmc_read_ext_csd函数中。
(2)X210开发板有一个iNand接在SD0上,有一个外置SD卡接在SD2上。在uboot源码/drivers/mmc/mmc.c文件中的mmc_initialize函数中,有一行代码“mmc=find_mmc_device(0);”,参数0表示SD0(当然也可以改为1,此时表示SD2),所以uboot中初始化的是iNand而不是SD卡,也就是说uboot中实际用的是SD0而不是SD2。
(3)mmc_read_ext_csd这个函数是在读取SD或者iNand的ext_csd寄存器的值。通过浏览代码结合出错地方,可知从sd卡或者iNand中读取ext_csd寄存器是成功的,并且得到了SD 卡或者iNand的版本号信息,否则不会在比较版本号后输出错误提示。因为SD卡的版本号一般比较低而不会大于5,所以使用外置SD卡时不会提示这个错误;而iNand的版本号比较高而可能出现大于5的情形。又从(2)中得知uboot中初始化的是iNand,所以会出现这个错误提示。
(4)这里只要把 if 判断语句中的5修改为8,就能解决SD卡版本号的问题。
上述修改之后,uboot启动时显示的信息如下:
8、网卡驱动的移植
注意到上图中也提示找不到网卡芯片DM9000,但X210开发板实际上有这个网卡。之所以找不到网卡DM9000,这是因为还没有移植网卡的驱动。
(1)DM9000的网卡驱动主要体现在/drivers/net/dm9000x.c和dm9000x.h这两个文件,它们来源于linux内核,可以在uboot中直接使用,我们不需要改动。
(2)网卡移植的关键或者说主要工作在于初始化网卡,只要将网卡芯片正确地初始化则网卡芯片就能工作。
(3)注意“网卡的初始化”与“网卡驱动”不是同一个概念,网卡驱动dm9000x.c和dm9000x.h的工作依赖于网卡的初始化。
(4)uboot在第二阶段init_sequences中进行了一系列的初始化,其中board_init函数通过调用/board/samsung/smdkc110/smdkc110.c文件中的dm9000_pre_init函数对网卡芯片进行了初始化。
(5)三星版本的uboot中,dm9000_pre_init函数内容如下,主要是对网卡的GPIO和端口进行配置。
从中可知它操作的是bit20-bit23,再对照数据手册中寄存器定义,可以得知三星的开发板DM9000是接在Bank5上的。而X210开发板网卡DM9000是接在bank1上的,因此我们需要操作的bit位是bit4-bit7,将dm9000_pre_init函数修改如下(修改三个寄存器的内容)。
(6)在/include/configs/smdkv210single.h文件中,关于网卡部分的宏定义如下。
1)CONFIG_DM9000_BASE这个宏的值,是DM9000网卡通过SROM bank映射到SoC中地址空间中的地址。这个地址取决于网卡接到哪个bank。每个bank的基地址是SoC已经定义好而且不能更改的。X210开发板的网卡接到了bank1,而bank1的基地址是0x88000000,所以CONFIG_DM9000_BASE要修改成0x88000000。但实际要改为0x88000300,0x300跟DM9000网卡芯片型号版本有关,是DM9000网卡本身的问题,其内部寄存器就有一个0x300的一个偏移量。
2)DM9000_IO这个宏表示访问网卡芯片IO的基地址(网卡芯片中与IO有关的寄存器的开始地址),直接就是CONFIG_DM9000_BASE,不用修改。
3)DM9000_DATA这个表示我们访问数据时的基地址(网卡芯片中与数据有关的寄存器的开始地址),因为DM9000芯片的CMD引脚接到了ADDR2,因此这里要+4(0b100对应着ADDR2)。
更多具体说明,见博客uboot移植——DM9000的移植_天糊土的博客-CSDN博客 。
(7)如何验证网卡驱动已经正确移植呢?
如果能够使用ping命令ping通虚拟机的linux系统,则说明网卡驱动已经正确移植。
9、修改提示符“SMDKV210 #”(可选)
这个很简单,修改/include/configs/smdkv210single.h文件中的CFG_PROMPT这个宏即可。
10、使用sd卡中的uboot启动内核
uboot的意义在于启动内核。完成前面的移植工作后,是时候检验uboot能否启动内核了。
(1)将移植后的uboot编译得到的镜像烧写至sd卡。
(2)参考博客利用tftp将镜像下载到开发板,在uboot的控制台上,利用tftp命令将内核镜像下载至开发板的内存中(tftp 0x30008000 zImage),然后利用bootm命令启动内核(bootm 0x30008000)。
(3)成功启动内核之后,打印内容如下:
(4)如果没有成功启动内核(直观表现为:串口没有输出内核启动信息,或者启动打印的信息不对),则需要仔细检查以下几个内容:
1、uboot使用的输出串口与内核使用的输出串口是否一致。如果不一致,比如uboot使用串口0打印启动信息,而内核使用串口2打印启动信息,则uboot就算正确引导内核启动,SCRT也不会打印内核启动的信息。
2、uboot给开发板设定的机器码,是否与内核支持的机器码匹配。
1)uboot给开发板设定机器码,是在uboot源码/board/samsung/smdkc110/smdkc110.c文件中的board_init函数中,如下所示,给X210开发板赋予的机器码是2456。
三星内核中支持的smdkv210开发板,按理它对应着/arch/arm/mach-s5pv210/mach-smdkv210.c文件这个文件,但实际对应的是smdkc110.c这个文件。具体细节见kernel移植——从三星官方内核开始移植第二节的第1点。
在smdkc110.c这个文件中,给smdkv210开发板合成的机器码是“MACH_TYPE_SMDKV210”,这个宏在内核源码/include/generated/mach-types.h文件中定义,而这个mach-types.h文件是在内核配置的时候生成的,它维护着该版本内核所支持的全部机器码。在这个文件中搜索“MACH_TYPE_SMDKV210”得知其值为2456。
3、查看/include/configs/smdkv210single.h文件中是否定义了bootm传参需要的宏,如CONFIG_CMDLINE_TAG等。