kernel移植——从三星官方内核开始移植
来源:互联网 发布:换头型软件 编辑:程序博客网 时间:2024/05/20 11:52
以下内容源于朱有鹏《物联网大讲坛》课程的学习,如有侵权,请告知删除。
一、内核移植初体验
见博客http://blog.csdn.net/oqqhutu12345678/article/details/70526678
1、三星官方移植版内核获取
- 源码包下载;
- 源码包来于三星的SMDKV210开发板附带的光盘资料;
2、构建移植环境
- Windows下建立SI工程;
- ubuntu下解压;
- 检查Makefile中ARCH和CROSS_COMPILE;
- make xx_defconfig;
- make menuconfig;
- make -j4(直接make,则会直接单线程编译;如果make -j4,则会4线程编译)。
4、将得到的zImage下载运行,看结果
- 编译完成后得到的内核镜像不在源码树的根目录下,而是在arch/arm/boot这个目录下。
- 通过tftp将镜像下载到开发板,见博客http://blog.csdn.net/oqqhutu12345678/article/details/70537721。
5、现象分析
(1)结果
- 看不到 Uncompressing Linux... done, booting the kernel.
- 说明zImage根本没有被解压成功,内核代码根本就没有被运行。因此问题出在解压相关的部分。
- 问题出在内核配置的解压后代码放置的内存地址处。
- 内核配置的解压地址应该等于连接地址,否则自解压之后内核无法运行。现在问题变成:第一,内核的连接地址等于多少?第二,内核中配置的解压地址是多少?
- 这里面还有个问题:内核的连接地址是一个虚拟地址,而自解压代码解压内核时需要物理地址,因此连接地址对应的物理地址等于自解压地址。
- 连接地址和对应的物理地址在head.S中可以查到,分别是0xC0008000和0x30008000,那么自解压代码配置的解压地址应该是30008000。
(3)修改操作
- 自解压代码对应的自解压地址在mach/Makefile.boot文件中。
- 在其中加入下面内容
# override for SMDKV210zreladdr-$(CONFIG_MACH_SMDKV210):= 0x30008000params_phys-$(CONFIG_MACH_SMDKV210):= 0x30000100
(4)同步、编译,重新下载运行查看结果
- 自解压代码解压打印信息已经出来;
- 内核还是没有运行;
(5)问题分析
- 定义的物理地址不对,从20000000改到30000000即可。
二、内核中机器码的确定
1、MACHINE_START宏
- 这个宏用来定义一个机器码的数据结构。
- 这个宏的使用其实是用来定义一个结构体类型为machine_desc类型的结构体变量,名为__mach_desc_SMDKV210。
- 这个结构体变量会被定义到一个特定段.arch.info.init,因此这个结构体变量将来会被链接器链接到这个.arch.info.init段中。
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,};
(1)mach-xxx.c文件定义了一个机器码的开发板的machine_desc结构体变量
- 这个结构体变量放到.arch.info.init段中后,表示当前内核可以支持这个机器码的开发板。
(2)落实到当前开发板和当前内核中来分析,当前我们移植的目标开发板使用S5PV210的CPU,开发板名字叫X210。
- 在三星官方版本的内核中找不到mach-x210.c;
- 但不想从零开始移植,因此在三星移植的mach-s5pv210目录下找一个mach-xx.c,使得该开发板和X210开发板最为接近,然后以此为基础来移植。
(3)经过查看,发现mach-s5pc110.c和mach-s5pv210.c与X210开发板最为接近。
- 寻找原则:开发板和三星官方的哪个开发板最为相似。
- X210开发板抄的是三星的SMDKV210,因此要找SMDKV210对应的文件。
(4)结合mach-s5pv210目录下的Makefile来分析,得知.config中定义CONFIG_MACH_SMDKV210后,实际绑定的是mach-smdkc110.c这个文件。
- 因此实际上mach-smdkv210.c这个文件根本没用到。
2、硬件驱动的加载和初始化函数执行
.init_machine= smdkc110_machine_init,
- 定义了一个机器硬件初始化函数;
- 这个函数非常重要,这个函数中绑定了我们这个开发板linux内核启动过程中会初始化的各种硬件的信息。
三、解决内核启动中的错误
1、认识内核启动OOPS
(1)内核启动后会有打印信息,打印信息中隐藏了问题所在。
- 认真的去分析这个打印信息,从中找到对的或者错误的一些信息片段,才能帮助我们找到问题,从而解决问题。
(2)内核启动中的错误信息的特征
Unable to handle kernel NULL pointer dereference at virtual address 00000060Internal error: Oops: 5 [#1] PREEMPTPC is at dev_driver_string+0xc/0x44LR is at max8698_pmic_probe+0x150/0x32c
- 由PC和LR的值可以看出,程序是执行到dev_driver_string或者max8698_pmic_probe(这两个是函数或者汇编中的标号)符号部分的时候出错了。
- 我们从这两个符号出发去寻找、思考可能出错的地方然后试图去解决。
2、错误追溯及问题解决
(1)max8698_pmic_probe
- max8698这个电源管理IC的驱动安装函数部分出错;
- 开发板系统中配置了支持这个电源管理IC,于是启动时去加载它的驱动,结果驱动在加载执行的过程中出错。
(2)这个驱动加载时为什么会出错?
- 结合X210开发板的硬件实际情况来分析:X210开发板上根本就没有max8698这个电源管理IC,既然硬件都没有驱动执行了肯定会出错。
- 之前移植三星版本的uboot时,在uboot的lowlevel_init.S中也有调用电源管理IC初始化函数(PMIC_init),结果会报错,屏蔽掉该函数的调用,uboot就可以成功运行下去。
(5)为什么uboot和内核中,都默认调用这个电源管理IC的初始化代码?
- 因为三星的SMDKV210开发板中用了max8698这个电源管理IC,因此三星的uboot和kernel中都默认支持这个。
- 但是X210中是没用的,因此uboot和内核中都需要去掉该代码模块。
(6)怎么解决?
- 在uboot中,是直接改源代码,即屏蔽掉那个初始化函数解决的;
- 在kernel中,不能直接修改源代码。
- 因为linux kernel是高度模块化高度可配置的,内核中每一个模块都被配置项条件编译;
- 因此要去掉对某个模块的支持,需要重新配置,配置时去掉选项即可,不用改源代码。
- 因此关键就是要找它对应的配置项。
(7)操作
- 先make menuconfig;
- 然后/搜索"MAX8698"这几个关键字,然后看到这个配置项的路径,然后到路径下去按N键去掉这个模块的支持,保存,重新编译即可。
(8)重新编译、下载运行
- 此问题被解决了;
- 内核再次启动后直接运行到挂载rootfs才出错。
3、分析及总结
分析:根本原因在于CONFIG_MFD_MAX8698这个配置宏。这个配置宏决定了很多东西
- 第一:这个配置宏决定了drivers目录下的max8698对应的驱动程序源代码是否被编译;
- 第二:这个配置宏决定了kernel启动过程中是否会调用一些max8698的相关的代码;
总结:kernel是高度模块化和可配置化的,所以在内核中做任何事情(添加一个模块、更改一个模块、去掉一个模块)都必须按照内核设定的方案和流程。
四、iNand的问题和安排
1、错误分析
(1)内核错误信息:Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)。
- 内核试图挂载根文件系统时失败,失败的原因是unknown-block(不能识别的块设备)
(2)backstrace分析,可以得知错误信息的来源,再结合之前的内核启动流程分析,就更加确定了出错的地方。
(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驱动。
2、问题阐述
(1)SD/iNand都是由一个一个的扇区组成的
- BL1从SD卡的1扇区开始往后存放;
- SD卡的0扇区是不用的,SD卡的0扇区用来放置MBR(主引导记录)。
(2)MBR用来描述块设备的分区信息
- 事先定义了一个通用的数据结构来描述块设备的分区;
- 只要将分区信息写入MBR中,即可完成对该设备的分区;
- MBR默认存放在块设备的第0个扇区。
(3)内核如何知道iNand分了4个分区?哪里对inand进行了分区?
- uboot中有一个命令fdisk,fdisk -c 0时,对iNand进行分区;
- fdisk命令对iNand的分区已经写死,内核通过读取MBR,就可以知道分区信息了;
- 由iNand本身通过MBR给内核传递分区信息,因此uboot给内核传参时,不用传递分区表信息。
(4)如果开发板使用的是nandFlash,分区表一般是在内核中用代码构建的。
- 因此内核移植的时候,一般都需要移植、更改nand分区表。
3、解决安排
暂时解决不了这个问题。
五、网卡驱动的移植和添加实验
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)当前内核中网卡驱动尚未移植,因此内核启动时有错误的打印信息:
[ 1.130308] dm9000 Ethernet Driver, V1.31[ 1.133113] ERROR : resetting [ 1.135700] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.140915] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.145941] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.150963] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.155992] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.161018] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.166041] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.171070] dm9000 dm9000.0: read wrong id 0x2b2a2928[ 1.176092] dm9000 dm9000.0: wrong id: 0x2b2a2928[ 1.180774] dm9000 dm9000.0: not found (-19).
(3)移植的目标
- 让此版本的内核可以打印出正确情况下的启动信息
2、make menuconfig中添加DM9000支持
- menuconfig中选择Y;
3、mach-smdkc110.c中逻辑分析
(1)mach-smdkc110.c中的smdkc110_machine_init是整个开发板的所有硬件的初始化函数
- 在这里被加载的硬件,在将来启动时就会被初始化;
- 在这里没被加载的硬件,在将来启动时就不管。
- smdkc110_dm9000_set(),是和DM9000相关的SROM bank的寄存器设置;
- 其相当于uboot中dm9000移植时的dm9000_pre_init函数,只是读写寄存器的函数名称不同了。
4、修改相应的配置参数
(1)DM9000相关的数据配置在arch/arm/plat-s5p/devs.c中;
(2)在arch/arm/mach-s5pv210/include/mach/map.h中定义了DM9000的IO基地址,和DM9000接在哪个bank有关。
(3)+2改成+4,IRQ_EINT9改成10即可。
5、同步、编译、下载,查看启动信息
六、内核启动第一阶段的调试方法
1、调试的原因
(1)内核启动在head.S中首先进行三个校验(CPU id的校验、机器码的校验、tag的校验),然后创建页表,然后做了一些不太会出错的事情,然后b start_kernel。基本上能运行到start_kernel,内核移植就不太会出问题了。
(2)有时候移植的内核启动后的现象是:根本没有启动信息出来。
- 有可能是内核启动运行了,但是运行出错了没启动起来所以没有打印信息;
- 有可能是内核根本没运行;
- 希望能有一种调试手段来确定问题所在。
2、调试方法和原理
(1)调试方法就是在内核启动的第一阶段,添加汇编操作led点亮/熄灭的方法来标明代码运行的轨迹。
(2)将led点亮和熄灭的代码,复制粘贴到head.S中合适位置,然后内核启动后根据led的表现来标明代码有无运行。
3、动手测试
(1)在head.S中合适的地方添加led这个函数,然后在head.S的内核起始运行阶段添加调用led函数,然后重新编译内核,运行内核看这段代码有无被运行。
(3)分析思路
- 如果被运行,证明在调用led的步骤之前的部分都是没问题的,如果有错,错误肯定在后边;
- 如果没有被运行则证明错误在之前,那么就要去之前的部分debug。
- kernel移植——从三星官方内核开始移植
- uboot移植-从三星官方源码开始移植过程总结
- 从三星官方移植uboot
- uboot移植-从uboot官方源码开始移植过程总结
- 嵌入式linux开发uboot移植(七)——三星官方uboot的移植
- 8、uboot移植——使用三星官方的uboot进行移植
- 内核移植(kernel)-3
- 三星官方uboot移植实战1
- 三星官方uboot移植实战2
- 嵌入式 Linux开发Kernel移植(一)——kernel内核简介
- 嵌入式 Linux开发Kernel移植(二)——kernel内核配置和编译
- uboot的移植2-从uboot官方标准uboot开始移植
- 准备开始移植内核实践
- Linux内核移植 part3:Exynos4412 Linux Kernel移植
- kernel移植
- kernel移植之linux stage1:内核引导
- kernel移植之linux stage2:启动内核
- Linux驱动移植(三)——从Linux内核2.4移植到linux内核2.6(待完善)
- ROS常用命令
- android的XML连续动画
- 蓝桥杯基础练习 龟兔赛跑预测
- ffmpeg解决H.264原始数据包去隔行的问题(上半场/顶场与下半场/底场合并) 待续。。。
- LeetCode -- 70. Climbing Stairs
- kernel移植——从三星官方内核开始移植
- 爱测未来移动-iTest特色功能介绍
- Struts2+Spring+Mybatis 配置讲解
- 项目day01-1
- 事务知识点补充(ACID和数据库隔离级别)
- 导出数据库
- Variable has existed/does not exist ,Did you mean to set reuse=True/None?
- Sass使用for循环
- 如何使用retrofit2网络框架