当拿到源代码时,首先要在window下利用SourceInsight创建一个工程,并将uboot源代码加载到SI中,方便修改和查看函数调用。
项目->新项目->设置项目名、工程路径->选择整个uboot文件->添加树 项目->同步文件(全打钩)
接着要在linux下创建一份,因为配置和编译过程都需要在Linux下进行。将压缩包通过winshare复制到linux下,并在/root/porting_x210/kernel下解压开,得到samsung-kernel。
问题分析: 对于待移植的内核文件,不一定都是按照你的要求进行配置的,所以在移植前一定要保证符合你的配置,比如架构是否对应你的开发板,交叉编译工具链是否对应你ubuntu下的那个。
解决方法: 打开Makefile文件,修改其中的ARCH和CROSS_COMPILE使他们对应的正确的配置。
修改完成后,需要对内核进行配置和编译,那么配置的命令怎么获得?
答:我们可以通过ls arch/arm/configs查看能够配置的开发板,找到一个最适合自己开发版的内容,然后用make xxx的方式编译它。
最后对内核进行编译:make -j4。这里-j4表示用4线程进行编译。
编译完成后即可得到内核镜像zImage,我们通过tftp的方式将内核镜像下载到开发板上:
(1)首先将内核镜像拷贝到tftp传输文件夹下:cp arch/arm/boot/zImage /tftpboot/ -f
(2)在uboot下通过tftp下载:tftp 30008000 zImage
(3)启动内核:bootm 30008000
问题描述: 启动内核后可以发现,打印信息停留在Starting kernel ···,这句话是在uboot端打印出来的,说明内核没有启动。并且linux内核的自解压代码都没有运行(因为没有看到:Uncompressing Linux… done, booting the kernel.)
问题分析: 由于自解压代码的信息没有打印出来,说明zImage根本没有被解压成功,内核代码根本就没有被运行,当然没有输出信息了。所以问题出在解压相关的部分。通过分析可能的原因是内核配置的解压后代码放置的内存地址位置有问题。
因为内核配置的解压地址应该等于链接地址,否则自解压之后内核无法运行。所以我们要知道内核的链接地址等于多少,配置的解压地址等于多少?由于内核的链接地址是一个虚拟地址,而自解压内核时需要物理地址,所以链接地址对应的物理地址应该等于自解压地址。通过head.S中可以得到,内核的物理地址为0x30008000,所以自解压代码配置的解压地址为0x30008000。
解决方法: 查看mach-s5pv210下的Makefile.boot文件,发现自解压地址不对,于是在该文件中添加一个条件编译选项,使得在定义了CONFIG_MACH_SMDKV210时,能够对应到正确的自解压地址。
添加完成后创建cp.sh脚本文件,将对应的文件同步:
结果: 还是没运行,但是有效果。自解压代码解压打印信息已经出来了。但是内核还没运行。
问题分析: 自解压代码信息打印出来,但是内核还没有启动,我们需要检查一下定义的内核启动地址对不对,会不会是内核找不到到哪里启动,还有没有可能是机器码不对。
解决方法: 检查memory.h文件,发现代码为在定义了CONFIG_MACH_SMDKV210之后的物理地址为0x20000000,这显然不对,我们的物理地址是0x30000000,所以我们需要修改物理地址。
紧接着再去检查机器码:我们使用的是SMDKV210开发板的内核,所以对应的开发板信息都在mach-smdkc110.c文件中定义。所有的基本信息通过宏定义为方式决定,下面用图分析一下宏定义解析过程:
将相应的位置用宏定义替换,那么这个结构体为:
static const struct machine_desc __mach_desc_SMDKV210 \
__used \
__attribute__((__section__(".arch.info.init"))) = {
\
.nr = MACH_TYPE_SMDKV210, \
.name = "SMDKV210",
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,
.timer = &s5p_systimer,
};
这个宏用来定义一个包含机器码的数据结构。这个宏的使用其实是用来定义一个结构体类型为machine_desc类型的结构体变量,名为__mach_desc_SMDKV210。这个结构体变量会被定义到一个特定段.arch.info.init,因此这个结构体变量将来会被链接器链接到这个.arch.info.init段中。
总结:这样的做法就是通过一个mach-xxx.c文件来定义了一个机器码的开发板的machine_desc结构体变量,这个结构体变量放到.arch.info.init段中后,那么就表示当前内核可以支持这个机器码的开发板。从而实现一个机器码对应一个开发板,那么我也可以通过include/generated/mach-types.h中用名字查看对应的机器码。落实到当前开发板和当前内核中来分析,当前我们移植的目标开发板使用S5PV210的CPU,对应三星官方开发板中最适合的是SMDKV210,它的架构信息保存在arch-smdkc110.c文件中,我们通过打开宏CONFIG_MACH_SMDKV210即可实现一系列的创建任务。
在结构体__mach_desc_SMDKV210中有一个元素 .init_machine = smdkc110_machine_init,这个元素定义了一个机器硬件初始化函数,这个函数非常重要,这个函数中绑定了我们这个开发板linux内核启动过程中会初始化的各种硬件的信息,也是驱动移植重点要看的函数。
结果: 通过修改物理地址和检查机器码是否正确,可以发现内核运行了,但是打印出一些错误信息。
问题分析: 从以上错误信息中的PC和LR的值可以看出,程序是执行到dev_driver_string或者max8698_pmic_probe(这两个是函数或者汇编中的标号)符号部分的时候出错了。我们就从这两个符号出发去寻找、思考可能出错的地方然后试图去解决。
max8698_pmic_probe看名字是max8698这个电源管理IC的驱动安装函数部分出错了,应该是我们的开发板系统中配置了支持这个电源管理IC,于是乎启动时去加载他的驱动,结果驱动在加载执行的过程中出错了OOPS了。我们X210开发板上根本就没有max8698这个电源管理IC,既然硬件都没有驱动执行了肯定会出错。
解决方法: 在uboot中是直接改源代码屏蔽掉那个初始化函数解决的。但是在内核中不能这么干,因为linux kernel是高度模块化高度可配置的,内核中每一个模块都是被配置项条件编译了的,因此要去掉某个模块的支持,只需要重新配置去掉选项即可,不用改源代码。所以我们的关键就是要找它对应的配置项。
通过menuconfig,搜索MAX8698对应的配置项,看看它是否打开了,如果打开则给它关闭:
结果: 通过去掉配置项,max8698报错内容消失。紧接着出现新的问题,显示VFS: Cannot open root device “mmcblk0p2” or unknown-block(0,0)。
问题分析: 得到的内核错误信息:Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)。从错误信息字面意思来分析,就是内核试图挂载根文件系统时失败,失败的原因是unknown-block(不能识别的块设备)。
unknown-block(0,0)。在kernel启动时uboot会传给内核一个cmdline,其中用root=xx来指定了rootfs在哪个设备上,内核就会到相应的地方去挂载rootfs。譬如我们传参中:root=/dev/mmcblk0p2,这里的/dev/mmcblk0p2就是rootfs的设备地址,这个设备文件编号的含义就是mmc设备0的第2个分区(设备0就是在SD0通道上的设备,也就是iNand),这里的问题就是没找到mmc设备0的第2分区。
为什么没找到mmc设备0的第2分区。一定是因为kernel启动过程中加载mmc驱动的时候有问题,驱动没有发现mmc设备0。问题定位在MMC相关的驱动方面。则需要去移植驱动。
max8698_pmic_probe看名字是max8698这个电源管理IC的驱动安装函数部分出错了,应该是我们的开发板系统中配置了支持这个电源管理IC,于是乎启动时去加载他的驱动,结果驱动在加载执行的过程中出错了OOPS了。我们X210开发板上根本就没有max8698这个电源管理IC,既然硬件都没有驱动执行了肯定会出错。
解决方法: 该问题涉及到驱动问题,留着放在学习驱动后补充……
问题分析: 通过内核打印网卡部分的驱动信息dm9000 dm9000.0: read wrong id 0x2b2a2928,可以得知当前网卡驱动尚未移植。
解决方法: 首先我们要检查.config中的配置项是否正确,通过menuconfig中查看DM9000确实配置为了y,那么这一步完成。
紧接着查看DM9000有关的源代码部分,mach-smdkc110.c中的smdkc110_machine_init是整个开发板的所有硬件的初始化函数,在这里加载了的硬件将来启动时就会被初始化,在这里没有的将来启动时就不管,所以重点对这里进行修改。
然后将修改后的文件同步到ubuntu下:
结果: 启动内核后发现,网卡驱动成功。
内核启动在head.S中首先进行了三个校验(CPU id的校验、机器码的校验、tag的校验),然后创建页表,然后做了一些不太会出错的事情,然后b start_kernel。基本上能运行到start_kernel内核移植就不太会出问题了。
(1)如果打印信息仅停留在Starting kernel …,那么需要检查解压代码之前的那部分。
(2)如果打印信息停留在Uncompressing Linux… done, booting the kernel.,说明解压过去了,但是内核没启动。那么需要检查与内核启动前校验有关信息,可能某部分校验没通过。
(3)如果内核启动打印了好多信息,但是到最后显示要重启,说明内核仍然没有启动成功,但是到这步内核基本功能已经启动,所以关注的重点应该在驱动是否移植好,根文件系统挂载是否成功上。