以下内容源于朱有鹏嵌入式课程的学习,如有侵权,请告知删除。

一、内核移植初体验

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

  • 源码包来于三星的SMDKV210开发板附带的光盘资料,下载地址。

2、构建移植环境

(1)Windows下建立SI工程;

  • 建立前删除不必要的目录和文件:删除arch目录下非arm架构的目录文件;删除arch/arm目录下不是三星的mach-xxx、plat-xxx目录。

(2)ubuntu下解压;

3、配置、编译

(1)检查Makefile中ARCH =arm 和CROSS_COMPILE =/usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-;

(2)make xx_defconfig;(如何确定xx_defconfig?这里是smdkv210_android_defconfig)

  • 在arch/arm/configs下有很多,找到三星相关的。这里找一个最像的:smdkv210_android_defconfig

(3)make menuconfig;(目前还不知道修整哪些)

(4)make -j4(直接make,则会直接单线程编译;如果make -j4,则会4线程编译)。

4、将得到的zImage下载运行看结果

  • 编译完成后得到的内核镜像不在源码树的根目录下,而是在arch/arm/boot这个目录下。
  • 通过tftp将镜像下载到开发板,操作过程见博客http://blog.csdn.net/oqqhutu12345678/article/details/70537721。

5、现象分析

(1)结果

  • 只见到uboot打印出的下图信息,看不到linux自解压代码打印出的“Uncompressing Linux... done, booting the kernel.”

(2)分析

  • 说明zImage根本没有被解压成功,内核代码根本就没有被运行。因此问题出在解压相关的部分。
  • 问题出在解压后代码放置的内存地址处(该地址可以由内核配置的,如果不对,则内核不能运行)。
  • 内核配置的解压地址应该等于连接地址,否则自解压之后内核无法运行。
  • 现在问题变成:第一,内核的连接地址等于多少?第二,内核中配置的解压地址是多少
  • 这里面还有个问题:内核的连接地址是一个虚拟地址,而自解压代码解压内核时需要物理地址(此时mmu还没开,因为内核还没有开始运行),因此连接地址对应的物理地址等于自解压地址。
  • 连接地址和对应的物理地址在head.S中可以查到,经分析(在.config中查宏TEXT_OFFSET,合成)得知分别是0xC0008000和0x30008000,那么自解压代码配置的解压地址应该是30008000。

(3)修改操作

  • 自解压代码对应的自解压地址在mach-s5pv210/Makefile.boot文件中。
  • 在此文件后面追加入下面内容。
  • (实践中这样好像并没有预期的结果。因此我直接赋值不使用宏。问题是,为什么这里的宏不起作用?因为我写错了,尴尬……)
# override for SMDKV210
zreladdr-$(CONFIG_MACH_SMDKV210)    := 0x30008000
params_phys-$(CONFIG_MACH_SMDKV210) := 0x30000100

(4)同步、编译,重新下载运行查看结果

  • 因为只修改了一些链接参数,不需要重新配置编译,所以make很快就完成了。
  • 自解压代码解压打印信息已经出来;
  • 内核还是没有运行;

(5)问题分析

  • 因为我们在uboot中配置的空间为3000 0000开始(而不是2000 0000开始),因此这里定义的物理地址不对,从20000000改到30000000即可。

(6)重新编译,运行结果:内核打印出很多运行信息。

二、内核中机器码的确定(知识课程)

内核支持什么架构、支持哪款cpu是怎么确定的。
内核中保存了一份机器码,会和uboot传过来的机器码比对(在head.s中比对uboot传过来的机器码),比对成功说明uboot和内核是匹配的。
之前讲过uboot如何给内核传机器码。本课讲内核的机器码是怎么确定的,怎么来的。
每个mach-xx文件夹代表一种cpu,其内可能包含cpu相同的不同开发板。比如mach-s5pv210文件夹内有mach-smdkc110.c和mach-smdkv210.c等开发板。

1、MACHINE_START宏

  • 这个宏用来定义一个机器码的数据结构。
  • 即用来定义一个结构体类型为machine_desc类型的结构体变量,名为__mach_desc_SMDKV210。
  • 这个结构体变量会被定义到一个特定段.arch.info.init,因此这个结构体变量将来会被链接器链接到这个.arch.info.init段中。

比如 MACHINE_START(SMDKV210, "SMDKV210")解释为下面内容。

static const struct machine_desc __mach_desc_SMDKV210    \__used                         \__attribute__((__section__(".arch.info.init"))) = { \.nr        = MACH_TYPE_SMDKV210,//机器码2456     \.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结构体变量(一个机器码对应一个开发板,一个cpu可以对应多个开发板)

  • 这个结构体变量放到.arch.info.init段中后,表示当前内核可以支持这个机器码的开发板。

(2)我们所移植的目标开发板使用S5PV210的CPU,开发板名字叫X210。

  • 但在三星官方版本的内核中找不到mach-x210.c;
  • 但不想从零开始移植,因此在三星移植的mach-s5pv210目录下找一个mach-xx.c,使得该开发板和X210开发板最为接近(同一款cpu),然后以此为基础来移植。

(3)经过查看,发现mach-s5pc110.c和mach-s5pv210.c与X210开发板最为接近。

  • 寻找原则:开发板和三星官方的哪个开发板最为相似。
  • X210开发板抄的是三星的SMDKV210,因此要找SMDKV210对应的文件(按理应该是mach-smdkv210.c,但实际是吗?)

(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)内核启动中的错误信息的特征

  • 由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版本的内核移植时,一般都需要移植、更改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支持

  • 搜索/DM9000,找到所在路径。
  • menuconfig中选择Y。

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

(1)mach-smdkc110.c中的smdkc110_machine_init是整个开发板的所有硬件的初始化函数

  • 在这里被加载的硬件,在将来启动时就会被初始化;
  • 在这里没被加载的硬件,在将来启动时就不管。

(2)smdkc110_devices和smdkc110_dm9000_set()这两个地方是和DM9000有关的,要分别去做移植。

  • smdkc110_dm9000_set(),是和DM9000相关的SROM bank的寄存器设置;
  • 其相当于uboot中dm9000移植时的dm9000_pre_init函数,只是读写寄存器的函数名称不同了。
  • smdkc110_dm9000_set()函数的更改,直接拷贝九鼎的移植好的。

4、修改相应的配置参数

(1)DM9000相关的数据配置在arch/arm/plat-s5p/devs.c中;

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

  • 根据实际情况,将其改为0x8800 0300。

(3)+2改成+4

截图是x210的配置,可见里面是+4,因此下面要改成+4




(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。

4、典型led函数

//移植内核的led调试方法
led:// 第一步:把0x11111111写入0xE0200240(GPJ0CON)位置ldr r3, =0x11111111            // 从后面的=可以看出用的是ldr伪指令,因为需要编译器来判断这个数ldr r4, =0xE0200240         // 是合法立即数还是非法立即数。一般写代码都用ldr伪指令str r3, [r4]              // 寄存器间接寻址。功能是把r0中的数写入到r1中的数为地址的内存中去// 第二步:把0xff写入0xE0200244(GPJ0DAT)位置ldr r3, =0xff               //开发板启动时,led半亮,这里赋值为ff,则三颗全灭了,一颗半亮。ldr r4, =0xE0200244str r3, [r4]              // 把ff写入到GPJ0DAT寄存器中,引脚即输出高电平,LED熄灭mov pc,lr          //函数一定要返回
//r0,r1,r2在head.s中,是用来给内核传参的,类似于全局变量。
//因此不能用这些寄存器,我们可以使用那些没有被使用的寄存器。
//因此可以r0改为r3,r1改为r4

kernel移植——从三星官方内核开始移植相关推荐

  1. 《17.内核的移植1-从三星官方内核开始移植》

    转自 https://edu.csdn.net/lecturer/505 朱老师物联网大讲堂 <17.内核的移植1-从三星官方内核开始移植> 第一部分.章节目录 2.17.1.内核移植初体 ...

  2. 2.17.内核的移植1-从三星官方内核开始移植

    请移步到这里: http://note.youdao.com/noteshare?id=f857b3250ceaf78d81f68a4284093a66&sub=62409316D643480 ...

  3. 《11.uboot的移植-从三星官方uboot开始移植》

    转自 https://edu.csdn.net/lecturer/505 朱老师物联网大讲堂 <11.uboot的移植-从三星官方uboot开始移植> 第一部分.章节目录 2.11.1.移 ...

  4. 《11.uboot的移植1-从三星官方uboot开始移植》

    转自 https://edu.csdn.net/lecturer/505 朱老师物联网大讲堂 <11.uboot的移植1-从三星官方uboot开始移植> 第一部分.章节目录 2.11.1. ...

  5. ARM uboot 的移植0-从三星官方 uboot 开始移植的准备工作

    一.移植前的准备工作 1.三星移植过的uboot源代码准备 (1) 三星对于 S5PV210 的官方开发板为 SMDKV210,对应的移植过的 uboot 是:三星官方为210移植过的uboot和ke ...

  6. 嵌入式uboot移植之三星官方uboot开始移植

    注:以下内容来自朱老师物联网课件 1. 移植前的准备工作 1.1 获取三星移植过的uboot源代码 我们使用的是老师提供的android_uboot_smdkv210.tar.bz2,文件存储在课件2 ...

  7. linux中内核中machine_desc,Linux-内核-学习笔记(13):移植三星官方内核

    Linux-内核-学习笔记(13):移植三星官方内核 一.移植前的准备 当拿到源代码时,首先要在window下利用SourceInsight创建一个工程,并将uboot源代码加载到SI中,方便修改和查 ...

  8. s5pv210 linux内核移植,S5PV210-kernel-从三星官方的内核开始移植

    1.拿到三星官方移植过的内核 2.在Linux下解压一份在共享目录下,解压一份在Linux的源生目录下.一份编辑,一份编译 3.建立SI工程,添加kernel源代码 4.在Linux下先看makefi ...

  9. 三星官方smdkv210 uboot移植到我的s5pv210开发板

    北京 2020-7-26 19:44 周日 昨天外面闷热 今日凉快.空调一开啥事没有!O(∩_∩)O 用了差不多两个周末蹲家里移植的.进度比较慢,算是把uboot相关的一些东西基本了解了. uboot ...

最新文章

  1. 进程 线程 协程_进程,线程,协程那些事
  2. 书评 - 《展望敏捷软件测试》
  3. Thymeleaf引入公共片段方式
  4. 2022年全球及中国户外电源产品行业容量前景与运营动态分析报告
  5. 安卓系统PHP MYSQL服务器,安卓手机搭建网站服务器【Php+Mysql+Lighttpd】
  6. Arduino LiquidCrystal库函数中文对照
  7. Div+CSS 布局
  8. 数据中心不再有空调、风扇等冷却装置会怎样?
  9. 定积分华里士公式推广_数学分析第九章《定积分》备考指南
  10. java emf 转jpg_java – emf到jpg的转换
  11. 作为Java开发工程师,如何高效优雅地编写接口文档
  12. spring底层原理
  13. ip地址怎么设置才有效_如果想减肥,怎么拆解目标才是有效的?
  14. 恒星演化 —— 恒星的一生
  15. 走进AngularJs(一)angular基本概念的认识与实战
  16. Boring Old Menu Bar for Mac(Big Sur菜单栏优化工具)
  17. Linux wget下载方式
  18. 红帽linux卸载软件命令,好记性不如烂笔头- linux 下rpm软件的安装和卸载 rpm --force -ivh ......
  19. windows 11 锁屏壁纸路径
  20. 总结一下强大的ES6符号

热门文章

  1. HDU 3572 Task Schedule
  2. [dp]最长单调递增子序列LIS
  3. 23期PHP基础班第四天
  4. servlet中实现页面跳转return “r:”和return “f:
  5. Docker搭建便捷的开发者环境
  6. [hackerrank]Manasa and Stones
  7. Prepare for Mac App Store Submission--为提交到Mac 应用商店做准备
  8. 21OGNL与ValueStack(VS)-静态方法访问
  9. linux+apache+mysql+php
  10. 用STATSVN统计公司SVN代码修改