0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

关于升级uboot遇到的两个问题

Q4MP_gh_c472c21 来源:qb杂货铺 作者:qb杂货铺 2020-09-21 11:41 次阅读

背景

之前做过一次uboot的升级,当时留下了一些记录,本文摘录其中比较有意思的两个问题。

启动失败问题

问题简述

uboot代码中用到了一个库,考虑到库本身跟uboot版本没什么关系,就直接把旧的库文件拷贝过来使用。结果编译链接是没问题,启动却会卡住。

消失的打印

为了明确卡住的位置,就去修改了库的源码,添加一些打印(此时还是在旧版本uboot下编译的),结果发现卡住的位置或随着添加打印的变化而变化,且有些打印语句,添加后未打印出来。 我决定先从这些神秘消失的打印入手。 分析下uboot中的printf实现,最底层就是写寄存器,是一个同步的函数,也没什么可疑的地方。 为了确认打印不出来的时候,到底有没有调用到printf,我决定给printf增加一个计数器,在gd结构体中,增加一个printf_count字段,初始化为0,每次打印时执行printf_count++并打印出值。 设计这个试验,本意是确认未打印出来时是否确实也调用到了printf,但却有了别的发现,实验结果中printf_count值会异常变化,不是按打印顺序递增,而是会突变成很大的异常值。 printf_count是gd结构体的成员,那就是gd的问题了。进一步将uboot全局结构体gd的地址打印出来。确认了原因是gd结构体的指针变化了。 这也可以解释部分打印消失的现象,原因是我们在gd中有另一个字段,用于控制打印等级。当gd被改动了,printf就可能解析出错,误以为打印等级为0而提前返回。

gd的实现

那么好端端的,gd为什么会被改了呢?这就要先看看gd到底是怎么实现的了。 uboot中维护了一个全局的结构体gd。在代码中加入

DECLARE_GLOBAL_DATA_PTR; 即可使用gd指针访问这个全局结构体,许多地方都会借助gd来保存传递信息。 进一步看看这个宏的定义旧版本uboot: #defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r8") 新版本uboot: #defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r9") 居然不一样,一个是将gd的值放到r8寄存器,一个是放在r9寄存器。 那么就可以猜测到,库是在旧版本uboot中编译出来的,可能使用了r9,那么放到新版本uboot中去,就会破坏r9寄存器中保存的gd值,导致一系列依赖gd的代码不能正常工作。

验证改动

为了求证,将库反汇编出来,发现确实避开了r8寄存器,但使用了r9寄存器。 说明uboot在指定gd寄存器的同时,还有某种方法让其他代码不使用这个寄存器。 那是不是把旧uboot中的这个r8改成r9,重新编译库就可以了呢?试一下,还是不行。 那么禁止其他代码使用r8寄存器肯定就是通过别的方式实现的了。简单粗暴地在旧版本uboot下搜索r8,去掉.c .h等类型后,很容易发现了

./arch/arm/cpu/armv7/config.mkPLATFORM_RELFLAGS+=-fno-common-ffixed-r8-msoft-floa 将-ffixed-r8修改为-ffixed-r9,重新编译出库,这回就可以正常工作了,打印正常,启动正常。反汇编出来也可以看到,新编译出来的库用了r8没有用r9。 当然更好的改法,是直接在新版本的uboot中编译,这是最可靠的。

追本溯源

话说回来,为什么两个版本的uboot,会使用不同的寄存器呢?难道有什么坑? 这就得去翻一下git记录了。

commitfe1378a961e508b31b1f29a2bb08ba1dac063155 Author:JeroenHofstee Date:SatSep2114412013+0200 ARM:user9forgd TobemoreEABIcompliantandasapreparationforbuilding withclang,usetheplatform-specificr9registerforgd insteadofr8. note:TheFIQisnotupdatedsinceitisnotusedinu-boot, andunderdiscussionforthetimebeing. Thefollowingcheckpatchwarningisignored: WARNING:Useofvolatileisusuallywrong:see Documentation/volatile-considered-harmful.txt Signed-off-by:JeroenHofstee cc:AlbertARIBAUD 从git记录中,也可以确认完整地将r8切换到r9,都需要做哪些修改diff--gita/arch/arm/config.mkb/arch/arm/config.mk index16c2e3d1e0..d0cf43ff41100644 ---a/arch/arm/config.mk +++b/arch/arm/config.mk @@-17,7+17,7@@endif LDFLAGS_FINAL+=--gc-sections PLATFORM_RELFLAGS+=-ffunction-sections-fdata-sections --fno-common-ffixed-r8-msoft-float +-fno-common-ffixed-r9-msoft-float #SupportgenericboardonARM __HAVE_ARCH_GENERIC_BOARD:=y diff--gita/arch/arm/cpu/armv7/lowlevel_init.Sb/arch/arm/cpu/armv7/lowlevel_init.S index82b2b86520..69e3053a42100644 ---a/arch/arm/cpu/armv7/lowlevel_init.S +++b/arch/arm/cpu/armv7/lowlevel_init.S @@-22,11+22,11@@ENTRY(lowlevel_init) ldrsp,=CONFIG_SYS_INIT_SP_ADDR bicsp,sp,#7/*8-bytealignmentforABIcompliance*/ #ifdefCONFIG_SPL_BUILD -ldrr8,=gdata +ldrr9,=gdata #else subsp,#GD_SIZE bicsp,sp,#7 -movr8,sp +movr9,sp #endif /* *Savetheoldlr(passedinip)andthecurrentlrtostack diff--gita/arch/arm/include/asm/global_data.hb/arch/arm/include/asm/global_data.h index79a9597419..e126436093100644 ---a/arch/arm/include/asm/global_data.h +++b/arch/arm/include/asm/global_data.h @@-47,6+47,6@@structarch_global_data{ #include -#defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r8") +#defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r9") #endif/*__ASM_GBL_DATA_H*/ diff--gita/arch/arm/lib/crt0.Sb/arch/arm/lib/crt0.S index960d12e732..ac54b9359a100644 ---a/arch/arm/lib/crt0.S +++b/arch/arm/lib/crt0.S @@-69,7+69,7@@ENTRY(_main) bicsp,sp,#7/*8-bytealignmentforABIcompliance*/ subsp,#GD_SIZE/*allocateoneGDaboveSP*/ bicsp,sp,#7/*8-bytealignmentforABIcompliance*/ -movr8,sp/*GDisaboveSP*/ +movr9,sp/*GDisaboveSP*/ movr0,#0 blboard_init_f @@-81,15+81,15@@ENTRY(_main) *'here'butrelocated. */ -ldrsp,[r8,#GD_START_ADDR_SP]/*sp=gd->start_addr_sp*/ +ldrsp,[r9,#GD_START_ADDR_SP]/*sp=gd->start_addr_sp*/ bicsp,sp,#7/*8-bytealignmentforABIcompliance*/ -ldrr8,[r8,#GD_BD]/*r8=gd->bd*/ -subr8,r8,#GD_SIZE/*newGDisbelowbd*/ +ldrr9,[r9,#GD_BD]/*r9=gd->bd*/ +subr9,r9,#GD_SIZE/*newGDisbelowbd*/ adrlr,here -ldrr0,[r8,#GD_RELOC_OFF]/*r0=gd->reloc_off*/ +ldrr0,[r9,#GD_RELOC_OFF]/*r0=gd->reloc_off*/ addlr,lr,r0 -ldrr0,[r8,#GD_RELOCADDR]/*r0=gd->relocaddr*/ +ldrr0,[r9,#GD_RELOCADDR]/*r0=gd->relocaddr*/ brelocate_code here: @@-111,8+111,8@@clbss_l:cmpr0,r1/*whilenotatendofBSS*/ blred_led_on /*callboard_init_r(gd_t*id,ulongdest_addr)*/ -movr0,r8/*gd_t*/ -ldrr1,[r8,#GD_RELOCADDR]/*dest_addr*/ +movr0,r9/*gd_t*/ +ldrr1,[r9,#GD_RELOCADDR]/*dest_addr*/ /*callboard_init_r*/ ldrpc,=board_init_r/*thisisauto-relocated!*/

启动慢问题

问题简述

填了几个坑之后,新的uboot可以启动到内核了,但发现启动速度非常慢,内核启动速度慢了接近10倍!明明是同一个内核,为什么差异这么大。

排查寄存器

初步排查了下设备树配置,以及uboot跳转内核前的一些关键寄存器,确实在两个版本的uboot中有所不同,但具体去看这些不同,发现都不会影响速度,将一些驱动对齐之后寄存器差异基本就消失了。

差异的分界

那再细看,kernel的速度有差异,uboot呢?在哪个时间点之后,速度开始产生差异? 尝试在两个版本的uboot中插入一些操作,对比时间戳,发现两个uboot在某个节点之后的速度确实有区别。 进一步排查,原来是在打开cache操作之后,旧uboot的速度就会比新uboot快。尝试将旧uboot的cache关掉,则二者基本一致。尝试将旧uboot操作cache的代码,移植到新uboot,未发生改变。 此时可确认新uboot的开cache有问题。但觉得这个跟kernel启动慢没关系。因为uboot进入kernel之前都会关cache,由kernel自己去重新打开。 也就是不管是用哪份uboot,也不管uboot中是否开了cache,对kernel阶段都应该没有影响才对。 于是记录下来uboot的这个问题,待后续修复。先继续找kernel启动慢的原因。(注:现在看来当时的做法是有问题的,这里的异常这么明显,应该设法追踪下去找出原因才对)

锁定uboot

uboot的嫌疑非常大,但还不能完全确认,因为uboot之前还有一级spl。是否会是spl的问题呢? 尝试改用新spl+旧uboot,启动速度正常。而新spl+新uboot的启动速度则很慢,其他因素都不变,说明问题确实出在uboot阶段。

多做or少做

当时到这一步就卡住了,直接比较两份uboot的代码不太现实,差异太大了。 后来我就给自己提了个问题,到底新uboot是多做了某件事情,还是少做了某件事情? 换个说法,目前已知

spl-->旧uboot-->kernel(速度快) spl-->新uboot-->kernel(速度快) 但到底是以下的情况A还是情况B呢?A:spl(速度慢)-->旧uboot(做了某个会提升速度的操作)-->kernel(速度快) spl(速度慢)-->新uboot(少做了某个会提升速度的操作)-->kernel(速度慢) B:spl(速度快)-->旧uboot(没做特殊操作)-->kernel(速度快) spl(速度快)-->新uboot(多做了某个会限制速度的操作)-->kernel(速度慢) 为了验证,我决定让spl直接启动内核,看看内核到底是快是慢。 支持过程碰到了一些小问题 1.spl没有能力加载这么大的kernel 解决:此时不需要kernel能完全启动,只需要能加载启动一段,足以体现出启动速度是否正常即可,于是裁剪出一个非常小kernel来辅助实验。 2.kernel需要dtb 解决:内核有一个CONFIG_BUILD_ARM_APPENDED_DTB_IMAGE选项。选上重新编译。编译后再用dd将kernel和dtb拼接到一起,作为新的kernel。这样,spl就只需要加载一个文件并跳转过去即可。 试验结果,spl启动的kernel和使用新uboot启动的kernel速度一致,均比旧uboot启动的kernel慢。 说明,旧uboot中做了某个关键操作,而新uboot没做。

找出关键操作

那接下来的任务就是,找出旧uboot中的这个关键操作了。 怎么找呢?有了上一步的成果,我们可以使用以下方法来排查

spl加载kernel和旧uboot

spl跳转到旧uboot,此时kernel其实已经在dram中准备好了,随时可以启动

在旧uboot的启动流程各个阶段,尝试直接跳转到kernel,观察启动速度

如果在旧uboot的A点跳转kernel启动慢,B点跳转启动快,则说明关键操作位于AB点之间。

方法有了,很快就锁定到start.S,进一步在start.S中揪出了这段代码

#ifdefined(CONFIG_ARM_A7) @setSMPbit mrcp15,0,r0,c1,c0,1 orrr0,r0,#(1<<6)     mcr        p15, 0, r0, c1, c0, 1 #endif 新uboot的start.S中没有这段代码,尝试在新uboot的start.S中添加此操作,速度立马恢复正常了。 再全局搜索下,原来这个新版本uboot中,套路是在board_init中进行此项设置的,而这个平台从旧版本移植过来,就没有设置 SMP bit, 补上即可。

SMP bit是什么

SMP 是指对称多处理器,看起来这个 bit 会影响多核的 cache一致性,此处没有再深入研究。 但可以知道,对于单处理器的情况,也需要设置这个bit才能正常使用cache。 贴下arm的图和描述:

[6]SMP SignalsiftheCortex-A9processoristakingpartincoherencyornot. Inuniprocessorconfigurations,ifthisbitisset,thenInnerCacheableSharedistreatedasCacheable.Theresetvalueiszero. 搜下kernel的代码,发现也是有地方调用了的。不过这个芯片是单核的,根本就没配置CONFIG_SMP。#ifdefCONFIG_SMP ALT_SMP(mrcp15,0,r0,c1,c0,1) ALT_UP(movr0,#(1<< 6))  @ fake it for UP  tst r0, #(1 << 6)   @ SMP/nAMP mode enabled?  orreq r0, r0, #(1 << 6)  @ Enable SMP/nAMP mode  orreq r0, r0, r10   @ Enable CPU-specific SMP bits  mcreq p15, 0, r0, c1, c0, 1 #endif

总结

整理出来一方面是记录这两个bug,另一方面也是想记录下当时的一些操作。 毕竟同样的bug可能以后都不会碰到了,但解bug的方法和思路却是可以积累复用的。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 编译
    +关注

    关注

    0

    文章

    657

    浏览量

    32854
  • Uboot
    +关注

    关注

    4

    文章

    125

    浏览量

    28219

原文标题:坑!uboot升级过程遇到的两个bug

文章出处:【微信号:gh_c472c2199c88,微信公众号:嵌入式微处理器】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    用AD8338做AGC遇到两个问题求解

    我们用AD8338做AGC,但是遇到两个问题,特此请教一下 1、C61 如果接电容,则终端信号没有输出,接电阻才可以,不知道为什么 2、我们如果把R59去掉,R57,R60 换成47R电阻,在C57
    发表于 12-19 06:50

    音频DSP TLV320AIC3254调试遇到两个问题求解答

    关于TI 音频DSP TLV320AIC3254的调试我们这边遇到两个问题谢谢帮忙看一下: AIC3254的差分Mic用AIC3254_C工具配置一直没有成功,现在我们只能使用单端的配置,帮忙
    发表于 10-24 07:21

    单相电机两个绕组都在定子上吗

    单相电机的两个绕组,即起动线圈(或称为辅助绕组、副绕组)和运行线圈(或称为主绕组),都位于定子上 。这两个绕组在电机中起着关键作用,共同协作以产生旋转磁场,从而使电机能够运转。 单相电机通常由一
    的头像 发表于 09-03 15:10 742次阅读

    ad如何设置两个元器件的距离

    在Altium Designer(简称AD)中设置两个元器件之间的距离,主要是通过设置元器件间的安全间距(Clearance)规则来实现的。这个规则定义了元器件之间、元器件与走线之间以及其他设计元素
    的头像 发表于 09-02 15:31 6675次阅读

    触发器的两个稳定状态分别是什么

    触发器作为数字电路中的基本逻辑单元,具有两个稳定状态,这两个状态通常用于表示二进制数码中的0和1。
    的头像 发表于 08-12 11:01 1061次阅读

    双稳态电路的两个稳定状态是什么

    双稳态电路是一种具有两个稳定状态的电子电路,广泛应用于数字电路、通信系统、存储器等领域。 双稳态电路的基本概念 双稳态电路是一种具有两个稳定状态的电路,即在没有外部输入信号的情况下,电路可以保持在
    的头像 发表于 08-11 15:00 1339次阅读

    双稳态触发器的两个基本性质是什么

    双稳态触发器(Bistable Trigger)是一种具有两个稳定状态的逻辑电路,广泛应用于数字电路设计中。它具有两个基本性质:记忆性和切换性。 一、双稳态触发器的基本概念 1.1 双稳态触发器
    的头像 发表于 08-11 10:08 669次阅读

    使用esp-iot-bridge遇到两个疑问求解

    AP了,点击扫描时,提示错误;log显示一直在连接AP,能不能设置重试多少次之后暂停? 2、打开了usb和ap共享网络模式,两个接口获取到的网络段都不一样,和无线路由器那边的IP段也不一样。我使用到的场景是固定IP通信的,设备端的IP和无线路由器不在同一网段,没办法通信。请问要如何解决?
    发表于 06-27 06:05

    请问ad9171的两个输出端口是否支持同时输出两个不同的频率?

    你好,关于AD9171芯片我有一问题 ,数据手册显示该芯片具有两个输出通道,芯片内部有DAC0和DAC1共两个DAC通道,那么这两个通道是
    发表于 05-28 06:20

    两个铜片可以形成原电池吗

    两个铜片本身不能形成原电池,因为原电池的工作原理依赖于两个不同电位的电极材料之间的氧化还原反应。
    的头像 发表于 05-21 16:23 945次阅读

    STM32 IAP升级,KEIL如何一份代码运行于两个APP区?

    校验。目前的问题是,没办法使用一份代码运行两个APP。两个APP,则IAP跳转不同的APP,跳转的地址不一样,则代码中需要设置的中断向量表及链接地址。这样,一份代码就不行了,而且份代码还需要注意当前到底应该
    发表于 03-26 07:20

    使用CYUSB3014-BZXI时遇到两个疑问求解

    在使用 CYUSB3014-BZXI 的时候出现如下两个问题,不知道你们是否有遇到?有没有相应的解决方案? 1.在使用cypress 过程中,发现有时候(极小概率),插入USB之后,Windows
    发表于 02-29 06:20

    关于PSDR和DSPR遇到两个问题求解

    PSPR 主要用途放置静态函数,提示高函数数执行效率 DSPR 主要用途于全局变量、场景保护的上下文管理与等数据 以上是我找到的关于 PSPR 和 DSPR 的解析,我有两个问题: 1。PSPR
    发表于 02-26 07:57

    arcgis中如何关联两个属性表

    在ArcGIS中,关联两个属性表是一重要的操作,可以通过此操作将两个表中的数据关联起来,以便进行分析和查询。下面是详细介绍如何在ArcGIS中实现属性表的关联。 首先,我们需要明确两个
    的头像 发表于 02-25 11:01 4194次阅读

    两个电位器地控制一变频器,如何接线?

    两个电位器地控制一变频器,如何接线? 接线方式如下: 1. 首先,明确需要使用的电器设备。在这个场景中,我们需要两个电位器(即可变电阻器)和一
    的头像 发表于 02-05 10:13 5232次阅读