ARM kernel 内核的移植 - 从三星官方内核开始移植

一、内核移植初体验

1、三星官方移植版内核获取

(1) 从网盘下载源码包。

(2) 这个文件最初是来自于三星的 SMDKV210 开发板附带的光盘资料。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第1张图片


2、构建移植环境

(1) Windows下建立工程。
ARM kernel 内核的移植 - 从三星官方内核开始移植_第2张图片


(2) ubuntu下解压。

在这里插入图片描述


3、配置编译下载尝试

(1) 检查 Makefile 中 ARCH 和 CROSS_COMPILE。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第3张图片


(2) make smdkv210_android_defconfig。
(3) make menuconfig。
(4) make -j4。

默认情况下直接 make,则会直接单线程编译。但是如果 make -j4, 则会 4 线程编译。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第4张图片


4、后续要做的事情:

(1) 编译得到的 zImage 去下载运行,看结果。

(2) 根据结果去分析问题原因,然后去尝试解决这些问题。

注意:使用的 uboot,是 x210 的 uboot。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第5张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第6张图片


二、初步移植以看到启动信息

ARM kernel 内核的移植 - 从三星官方内核开始移植_第7张图片

1、分析问题

(1) 根据运行结果,分析发现:linux 内核的自解压代码都没有运行(因为没有看到:Uncompressing Linux… done, booting the kernel.

ARM kernel 内核的移植 - 从三星官方内核开始移植_第8张图片


(2) 说明 zImage 根本没有被解压成功,内核代码根本就没有被运行,当然没有输出信息了。所以问题出在解压相关的部分。

(3) 问题出在,内核配置的解压后代码放置的内存地址处。

(4) 内核配置的解压地址,应该等于链接地址,否则自解压之后内核无法运行。

现在问题变成:
第一,内核的链接地址等于多少?
第二,内核中配置的解压地址是多少?


(5) 这里面还有个问题:内核的链接地址是一个虚拟地址,而自解压代码解压内核时,需要物理地址;因此上面说的,其实是链接地址对应的物理地址等于自解压地址。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第9张图片


(6) 链接地址和它对应的物理地址在 head.S 中可以查到,分别是 0xC00080000x30008000 。那么自解压代码配置的解压地址应该是 0x30008000.

ARM kernel 内核的移植 - 从三星官方内核开始移植_第10张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第11张图片


(7) 自解压代码对应的自解压地址在 mach/Makefile.boot 文件中。在其中修改,加入两行:

# override for SMDKV210
zreladdr-$(CONFIG_MACH_SMDKV210)	:= 0x30008000
params_phys-$(CONFIG_MACH_SMDKV210)	:= 0x30000100

ARM kernel 内核的移植 - 从三星官方内核开始移植_第12张图片


(8) 同步代码,并且编译,得到的 zImage 复制到 tftp 服务器,然后重新下载运行查看结果。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第13张图片


三、内核中机器码的确定

1、MACHINE_START 宏

(1) 这个宏 用来定义一个机器码的数据结构的。这个宏的使用,其实是用来定义一个结构体类型为 machine_desc 类型的结构体变量,名为 __mach_desc__type 。这个结构体变量,会被定义到一个特定段 .arch.info.init,因此这个结构体变量将来会被链接器链接到这个 .arch.info.init 段中。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第14张图片


(2) 经过分析,发现一个 mach-xxx.c 文件中定义了一个机器码的开发板的machine_desc 结构体变量,这个结构体变量放到 .arch.info.init 段中后,那么就表示当前内核可以支持这个机器码的开发板。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第15张图片


(3) 落实到当前开发板和当前内核中来分析,当前我们移植的目标开发板使用 S5PV210 的 CPU,开发板名字叫 X210。

我们在三星官方版本的内核中是找不到 mach-x210.c 的,所以我们又不想从零开始去移植,因此我们的思路是,在三星移植的 mach-s5pv210 目录下找一个 mach-xx.c,这个开发板和我们的 X210 开发板最为接近,然后以此为基础来移植。


(4) 经过查看,发现 mach-s5pc110.c 和 mach-s5pv210.c 和我们的 X210 开发板最为接近。我们一般确定的一个原则是:看我们的开发板和三星官方的哪个开发板最为相似。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第16张图片

我们的 X210 开发板抄的是三星的 SMDKV210,因此要找这个对应的那个文件。


(5) 结合 mach-s5pv210 目录下的 Makefile 来分析,得知 .config 中定义了CONFIG_MACH_SMDKV210 后,实际绑定的是 mach-smdkc110.c 这个文件。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第17张图片

所以实际上,mach-smdkv210.c 这个文件根本没用到。启示就是:不要光看名字。


2、硬件驱动的加载和初始化函数执行

(1)

.init_machine	= smdkc110_machine_init,

ARM kernel 内核的移植 - 从三星官方内核开始移植_第18张图片


(2) 这个元素定义了一个机器硬件初始化函数,这个函数非常重要,这个函数中绑定了我们这个开发板 linux 内核启动过程中,会初始化的各种硬件的信息。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第19张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第20张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第21张图片


四、解决内核启动中的错误

1、认识内核启动OOPS

(1) 内核启动后会有打印信息,打印信息中隐藏了问题所在。认真的去分析这个打印信息,从中找到对的或者错误的一些信息片段,才能帮助我们找到问题,从而解决问题。


(2) 内核启动中的错误信息有一些特征:

ARM kernel 内核的移植 - 从三星官方内核开始移植_第22张图片


(3) 从以上错误信息中的 PC 和 LR 的值可以看出,程序是执行到 dev_driver_string 或者 max8698_pmic_probe(这两个是函数或者汇编中的标号)符号部分的时候出错了。我们就从这两个符号出发去寻找、思考可能出错的地方然后试图去解决。


2、错误追溯及问题解决

(1) max8698_pmic_probe 看名字是 max8698 这个电源管理 IC 的驱动安装函数部分出错了,应该是我们的开发板系统中配置了支持这个电源管理 IC,于是启动时,去加载它的驱动,结果驱动在加载执行的过程中出错了 OOPS 了。


(2) 我们为什么要配置支持这个驱动?这个驱动加载为什么要出错?


(3) 结合我们 X210 开发板的硬件实际情况来分析:我们 X210 开发板上根本就没有 max8698 这个电源管理 IC,既然硬件都没有,驱动执行了肯定会出错。


(4) 回忆当时从三星版本的 uboot 移植的时候,在 uboot 的 lowlevel_init.S 中也有调用过电源管理 IC 初始化函数(PMIC_init),后来解决的办法就是屏蔽掉了这个函数的调用,uboot 就成功运行下去了。


(5) 为什么我们的 uboot 和内核中默认都调用了这个电源管理 IC的初始化代码?

原因就是,三星的 SMDKV210 开发板中是用了 max8698 这个电源管理 IC 的,所以三星的 uboot 和 kernel 中都有默认支持这个。但是 X210 中是没用的,因此都需要去掉。


(6) 怎么解决?

在 uboot 中是直接改源代码,屏蔽掉那个初始化函数解决的;

在内核中不能这么干?因为 linux kernel 是高度模块化高度可配置的,内核中每一个模块都是被配置项条件编译了的,因此要去掉某个模块的支持,只需要重新配置去掉选项即可,不用改源代码。所以我们的关键就是要找它对应的配置项。


(7) 我们的做法:make menuconfig,然后斜杠搜索(/MAX8698)这几个关键字,然后看到这个配置项的路径,然后到路径下去按 N 键去掉这个模块的支持,保存,重新编译即可。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第23张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第24张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第25张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第26张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第27张图片


(8) 实践证明问题被解决了,而且内核再次启动后直接运行到挂载 rootfs 才出错。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第28张图片

在这里插入图片描述


3、分析及总结

(1) 分析:问题究竟是怎么被解决的?涉及哪几个方面。

根本原因在于 CONFIG_MFD_MAX8698 这个配置宏。这个配置宏决定了很多东西。

第一:这个配置宏 决定了 drivers 目录下的 max8698 对应的驱动程序源代码是否被编译。
第二:这个配置宏 决定了 kernel 启动过程中是否会调用一些 max8698 的相关的代码。

在这里插入图片描述


(2) 总结:kernel 是高度模块化和可配置化的,所以在内核中做任何事情(添加一个模块、更改一个模块、去掉一个模块)都必须按照内核设定的方案和流程来走。


五、iNand 的问题和安排

1、错误分析

(1) 得到的内核错误信息:

Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

从错误信息字面意思来分析,就是内核试图挂载根文件系统时失败,失败的原因是unknown-block(不能识别的块设备)。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第29张图片


(2) Backtrace 分析,可以得知错误信息的来源,再结合之前的内核启动流程分析,就更加确定了出错的地方。


(3) 下一个问题:分析这个错误出现的原因: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分区。


(4) 下一步问题:为什么没找到 mmc 设备 0 的第 2 分区。一定是因为 kernel 启动过程中加载 mmc 驱动的时候有问题,驱动没有发现 mmc 设备 0。问题定位在 MMC 相关的驱动方面。


(5) 对比九鼎版本的内核启动信息,即可发现:我们的内核启动并没有找到 MMC 设备(内置的 iNand 和外置的 SD 卡都没找到),没找到肯定是驱动的问题,这就要去移植 MMC 驱动了。

九鼎 kernel :

ARM kernel 内核的移植 - 从三星官方内核开始移植_第30张图片


三星官方 kernel:

ARM kernel 内核的移植 - 从三星官方内核开始移植_第31张图片


2、问题阐述

(1) SD/iNand 本身都是由一个一个的扇区组成的,回忆裸机中讲到的 210 的启动时,BL1 在 SD 卡的 1 扇区开始往后存放,SD 卡的 0 扇区是不用的。SD 卡的 0 扇区是用来放置 MBR 的

ARM kernel 内核的移植 - 从三星官方内核开始移植_第32张图片


(2) MBR 就是用来描述块设备的分区信息的, 事先定义了一个通用的数据结构来描述块设备的分区,我们只要按照这个标准,将分区信息写入 MBR 中,即可对该设备完成分区。MBR 默认就是在块设备的第 0 个扇区上存放的。


(3) 我们内核中读到 iNand 分 4 个分区,我们是在哪里分区的?uboot 中有一个命令 fdisk -c 0 时,就对 iNand 进行了分区。uboot 的 fdisk 命令内部已经写死了 iNand 的分区表,到内核中时,内核直接读取 MBR 就知道了分区。所以在 uboot 和内核之间 iNand 设备的分区信息是靠 iNand 自己传递的,所以 uboot 不用给内核传参时 传递分区表信息。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第33张图片

在这里插入图片描述


(4) 如果开发板用的是 nandFlash 的话,分区表一般是在内核中自己用代码构建的。所以 nand 版本的内核移植的时候,一般都需要去移植更改 nand 分区表。


3、解决安排

(1) 暂时解决不了这个问题。


4、后续课程安排

(1) 一节课搞定网卡驱动的移植,一节课讲述一些内核移植的小方法和技巧,然后课程结束。


(2) 整体移植的课程结束,进入根文件系统部分。


六、网卡驱动的移植和添加实验

1、移植标准

(1) 网卡驱动移植 ok 时,启动信息为:

[    1.452008] dm9000 Ethernet Driver, V1.31
[    1.455870] eth0: dm9000c at e08f4300,e08f8304 IRQ 42 MAC: 00:09:c0:ff:ec:48 (platform data)

在这里插入图片描述


(2) 当前内核中网卡驱动尚未移植,因此内核启动时有错误的打印信息:

ARM kernel 内核的移植 - 从三星官方内核开始移植_第34张图片


(3) 移植的目标就是,让我们的版本的内核可以打印出正确情况下的启动信息,那我们就相信内核启动后网卡是可以工作的。


2、make menuconfig 中添加 DM9000 支持

(1) menuconfig 中选择 Y。

(2) 其实这一步本来就是 Y,所以在我们这里是不用管的。但是你自己遇到的一个内核可能默认不是 Y,因此要设置。


3、mach-smdkc110.c 中逻辑分析

(1) mach-smdkc110.c 中的 smdkc110_machine_init 是整个开发板的所有硬件的初始化函数,在这里加载了的硬件, 将来启动时就会被初始化,在这里没有的,将来启动时就不管。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第35张图片


(2) smdkc110_devicessmdkc110_dm9000_set() 这两个地方是和 DM9000 有关的,要分别去做移植。
ARM kernel 内核的移植 - 从三星官方内核开始移植_第36张图片


(3) smdkc110_dm9000_set 这个函数就是 DM9000 相关的SROM bank 的寄存器设置,相当于 uboot 中 dm9000 移植时的 dm9000_pre_init 函数。只是读写寄存器的函数名称不同了。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第37张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第38张图片


4、修改相应的配置参数

(1) DM9000 相关的数据配置在 arch/arm/plat-s5p/devs.c 中更改。
ARM kernel 内核的移植 - 从三星官方内核开始移植_第39张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第40张图片

ARM kernel 内核的移植 - 从三星官方内核开始移植_第41张图片


(2) 在 arch/arm/mach-s5pv210/include/mach/map.h 中定义了 DM9000 的 IO 基地址,和 DM9000 接在哪个bank有关。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第42张图片


(3) 还有 +2 改成 +4,IRQ_EINT9 改成 10 即可。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第43张图片


5、代码实践

(1) 同步代码、编译生成 zImage。

(2) 下载启动后看启动信息。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第44张图片


七、内核启动第一阶段的调试方法

1、问题点描述

(1) 内核启动在 head.S 中首先进行了三个校验(CPU id 的校验、机器码的校验、tag 的校验),然后创建页表,然后做了一些不太会出错的事情,然后 b start_kernel。基本上能运行到 start_kernel ,内核移植就不太会出问题了。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第45张图片


(2) 有时候,移植的内核启动后的现象是:根本没有启动信息出来。

这时候,有可能是内核启动运行了,但是运行出错了没启动起来,所以没有打印信息;

也有可能是内核根本没得以运行。

都有可能但是没法确定。我们希望能有一种调试手段来确定问题所在。


2、调试方法和原理

(1) 调试方法就是,在内核启动的第一阶段添加汇编操作 led 点亮/熄灭的方法,来标明代码运行的轨迹。

(2) 我们找之前裸机中汇编操作 led 点亮/熄灭的代码过来,复制粘贴到 head.S 中合适位置。然后内核启动后,根据 led 的表现来标明代码有无运行。


3、动手测试

(1) 整理好 led 操作的代码段,在 head.S 中合适的地方添加 led 这个函数,然后在 head.S 的内核起始运行阶段添加调用 led 函数,然后重新编译内核,运行内核看这段代码有无被运行。

ARM kernel 内核的移植 - 从三星官方内核开始移植_第46张图片


(2) 如果被运行了,证明在这个调用 led 的步骤之前的部分,都是没问题的,那么如果有错,肯定错误在后边;

如果没有被运行,则证明错误在之前,那么就要去之前的部分debug。


4、实验验证


源自朱有鹏老师.

你可能感兴趣的:(ARM,S5PV210,朱有鹏,linux,内核驱动,ARM,linux,s5pv210,ubuntu,kernel)