Uboot中 TEXT_BASE的理解

本文深入探讨U-Boot启动过程中的关键问题,包括代码如何从Flash复制到SDRAM,以及链接器如何处理地址映射等问题。通过分析,揭示了U-Boot第一阶段的运行机制及其内部地址转换细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载

都知道U-BOOT分为两个阶段,第一 T阶段是(~/cpu/arm920t/start.S中)在FLASH上运行(一般情况下),完成对硬件的初始化,包括看门狗,中断缓存等,并且负责把代码搬移到SDRAM中(在搬移的时候检查自身代码是否在SDRAM中),然后完成C程序运行所需要环境的建立,包括堆栈的初始化等,最后执行一句跳转指令:

        ldr pc, _start_armboot

        _start_armboot: .word start_armboot,

进入到/lib_arm/board.c中的函数void start_armboot (void),从此就进入了第二阶段。这是在很多资料上都有讲述的,所以勿需多言了。

    现在对于第一阶段有几个问题,以前我一直是没有搞明白的,既然在FLASH中的代码是把自己拷贝到SDRAM中,那么在S3C2410的内存地址空间,就有两份的启动代码,第一份就是在FLASH中,第二份就是在SDRAM中。根据链接脚本文件(~/board/smdk2410/u-boot.lds)

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;   

. = ALIGN(4);
.text      :
{
   cpu/arm920t/start.o (.text)
   *(.text)
}

. = ALIGN(4);
.rodata : { *(.rodata) }

. = ALIGN(4);
.data : { *(.data) }

. = ALIGN(4);
.got : { *(.got) }

. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;

. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;

}
    其中的链接命令 . = 0x00000000;表示地址计数器从0地址开始计数,而且_start 是程序代码段的入口,那么*.text中的所有地址标号(cpu/arm920t/start.S中定义的)就应该从0地址开始计数,那么标号start_armboot(就是void start_armboot (void)函数的入口地址)应该在FLASH中才对啊,所以按照上边的分析,

        ldr pc, _start_armboot

        _start_armboot: .word start_armboot

此条语句后,并没有跳转到SDRAM中的void start_armboot (void),而是跳转到了FLASH中的void start_armboot (void)中。

所以就出现了这样的矛盾,在FLASH中有一段代码把自己拷贝到SDRAM中,产生了两份UBOOT可执行的指令流,但是最后却没有跳转到SDRAM中去运行以提高指令执行的速度。

产生以上的认识是基于以下几个认识(肯定是错误的):

1.*.text中的所有地址标号(在链接时确定)是从0地址开始生成的。

      实际上在arm-linux-ld 执行时,原来定义的0x0地址被更新为TEXT_BASE定义的地址。

2.relocate:   
   adr r0, _start
   ldr r1, _TEXT_BASE
   cmp     r0, r1                 
   beq     stack_setup

   ldr r2, _armboot_start
   ldr r3, _bss_start
   sub r2, r3, r2
   add r2, r0, r2

如果不是出于调试阶段,这段搬移代码中的r0和r1肯定不相等的,r0=#0,r1=#TEXT_BASE: 0x33F80000(在./board/smdk2410/config.mk中),所以执行代码的自身拷贝与搬移。

注意:在GNU中:adr r0, _start 作用是获得 _start 的实际运行所在的地址值,而ldr r1, _TEXT_BASE 为获得地址_TEXT_BASE中所存放的数据,其中adr r0, _start翻译成 add r0,(PC+#offset),offset 就是 adr r0, _start 指令到_start 的偏移量,在链接时确定,这个偏移量是地址无关的。而 ldr r1, _TEXT_BASE 指令表示以程序相对偏移的方式加载数据,是索引偏移加载的另外一种形式,等同于ldr r1,[PC+#offset],offset 是 ldr r1, _TEXT_BASE 到 _TEXT_BASE 的偏移量。注意这种用法并不是伪指令,伪指令的特征是 ldr r1, =expr/lable_expr。对于LDR伪指令,ADS的情况有些不一样(细微差别),在ADS中的情况可以参考杜春雷144页。


比较一下:

add r0,(PC+#offset):(PC+#offset)是相对地址,表示把本指令上溯或下溯offset处的地址加载到 r0;

ldr r1,[PC+#offset]:[PC+#offset]也是相对地址,表示把偏移offset处的地址上的数据加载到 r1;

现在继续:

    刚才分析所得到的矛盾,肯定是在认识上存在的偏差,经过把U-BOOT进行make后,从所生成的两个.map文件来看(~/u-boot.map和Systen.map),所有的地址标号都是从0x33f80000开始的,就是从SDRAM的高地址开始,等于TEXT_BASE的值,也就是说,链接器是从0x33F80000开始来链接所编译生成的目标文件的,而不是从0地址开始,经过查看,start_armboot=0x33f80d9c,就是说void start_armboot (void)函数的入口地址在SDRAM中(链接器决定),所以执行

        ldr pc, _start_armboot

        _start_armboot: .word start_armboot,

PC指针肯定就指向了SDRAM中,换句话就是说进入到SDRAM中了,对于ldr pc, _start_armboot,其仍然是GNU中使用程序相对偏移的方式加载数据,翻译一下就是ldr pc, [pc+pc到_start_armboot的偏移值],结果就把_start_armboot地址中的数start_armboot放入pc中完成了跳转,而 start_armboot 的值(函数地址)是在链接时就确定了,是相对于 TEXT_BASE 的。因为在整个UBOOT的阶段1中所有的寻址都是相对位置的寻址(虽然链接器认为是阶段1的代码是从地址0x3ff80000中开始链接的),把阶段1的代码放在0地址开始的FLASH中也是可以正确的运行的,如果ARM的复位向量是在0x00000001(假设),那么把代码烧写到从0x00000004处开始的地方,上电时也可以正确的运行(假设ARM的复位向量是在0x00000004成立),当然ARM的复位向量不在这里,只是以此假设来说明以上的对于阶段1的分析。

    现在最后一个矛盾就是链接脚本(~/board/smdk2410/u-boot.lds)所描述的链接地址与实际的链接地址不相同的问题,因为根据链接脚本,所有的地址标号应该从0地址开始计数的,然而不是。经过查找Makefile文件,在顶层的Makefile文件中,在166行中链接是的链接命令:

$(LD) $(LDFLAGS) $$UNDEF_SYM $(OBJS) /,

其中的LDFLAGS在定义在顶层的config.mk中的145行:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS),

最关键的就是 -Ttext $(TEXT_BASE)命令了,他的含义就是说,起始地址在TEXT_BASE,而TEXT_BASE在~/board/smdk2410/config.mk中TEXT_BASE = 0x3FF80000;

到此就弄清楚为什么链接从0x3ff80000开始的了,至于链接脚本,其主要作用是用来指明各个*.o文件的顺序,如入口地址标号(_start)等,以及使两个地址标号得到当前的地址

    __u_boot_cmd_start = .;    *.u_boot_cmd段的起始地址

    .u_boot_cmd : { *(.u_boot_cmd) }
    __u_boot_cmd_end = .;       *.u_boot_cmd段的结束地址

以供C程序使用。 __u_boot_cmd_start和__u_boot_cmd_end可以作为全局的一个常数使用。

 

总结:

    因为-Ttext $(TEXT_BASE)命令的使用,链接器把UBOOT从地址0x3ff80000开始连接,在第一阶段中,所有使用的目标地址寻址都是使用当前PC值加减偏移量的方法,所以把UBOOT烧写到0地址开始的FLASH中,不影响第一阶段的正确执行。

https://blog.youkuaiyun.com/weixin_43245753/article/details/123914980现在已经配置了charge-animation { compatible = "rockchip,uboot-charge"; rockchip,uboot-charge-on = <1>; //启用uboot的关机充电模式:1:开启,0:关闭 rockchip,android-charge-on = <0>;//启用android的关机充电模式:1:开启,0:关闭,这里默认是关闭,使用的是uboot的关机充电 rockchip,uboot-low-power-voltage = <6800>; //uboot低电预充的开机电压 rockchip,screen-on-voltage = <6900>;// uboot低电预充的亮屏电压 rockchip,uboot-exit-charge-level = <2>; // 退出uboot充电的电量百分比 rockchip,uboot-exit-charge-auto = <0>; //是否开启自动退出uboot充电,与uboot-exit-charge-level 配合使用 rockchip,system-suspend = <1>; //配置uboot关机充电时支持深度休眠 //uboot关机充电时支持深度休眠时如下电源不关闭 regulator-on-in-mem = <&vdd_log_s0>, <&vcc_2v0_pldo_s3>, <&vdd2_ddr_s3>, <&vcc_1v1_nldo_s3>, <&vdd1_1v8_ddr_s3>, <&vcc_1v8_s3>, <&master_pldo6_s3>, <&vdd_0v75_s3>, <&vdd2l_0v9_ddr_s3>, <&vdd_1v8_pll_s0>, <&pldo6_s3>; //uboot关机充电时支持深度休眠时如下电源关闭 regulator-off-in-mem = <&vdd_gpu_s0>, <&vdd_npu_s0>, <&vdd_vdenc_s0>, <&vdd_gpu_mem_s0>, <&vdd_npu_mem_s0>, <&vdd_vdenc_mem_s0>, <&avcc_1v8_s0>, <&vcc_3v3_s0>, <&vccio_sd_s0>, <&master_nldo3>, <&avdd_0v75_s0>, <&vdd_0v85_s0>, <&vdd_cpu_big1_s0>, <&vdd_cpu_big0_s0>, <&vdd_cpu_lit_s0>, <&vdd_cpu_big1_mem_s0>, <&vdd_cpu_big0_mem_s0>, <&vcc_1v8_s0>, <&vdd_cpu_lit_mem_s0>, <&vddq_ddr_s0>, <&vdd_ddr_s0>, <&vcc_1v8_cam_s0>, <&avdd1v8_ddr_pll_s0>, <&vcc_3v3_sd_s0>, <&vcc_2v8_cam_s0>, <&vdd_0v75_pll_s0>, <&vdd_ddr_pll_s0>, <&slave_nldo3>, <&avdd_1v2_cam_s0>, <&avdd_1v2_s0>, <&vcc_3v3_s3>; status = "okay"; }; dts的属性已经被uboot驱动检测到了,但是插入电源适配器还是自动开机
最新发布
07-11
<think>我们正在处理一个关于Rockchip平台Uboot充电配置的问题。用户已经配置了Uboot的关机充电模式,但插入电源适配器时设备仍然自动开机,而不是进入充电模式。我们需要分析可能的原因并提供解决方案。 首先,我们需要理解Rockchip平台的关机充电流程。在Rockchip平台上,关机充电功能通常由Uboot和内核共同支持。Uboot负责检测充电状态并显示充电界面,而内核则负责实际的充电管理。当设备关机并插入充电器时,Uboot应检测到充电器插入,并进入充电模式(显示充电界面,不启动内核)。如果设备直接开机,说明Uboot没有正确进入充电模式。 可能的原因: 1. **硬件检测问题**:Uboot未能正确检测到充电器插入。 2. **软件配置问题**:Uboot的充电配置(如设备树或Kconfig)未正确启用或设置。 3. **电源管理相关驱动问题**:Uboot中与充电相关的驱动(如PMIC驱动)存在问题。 4. **固件问题**:Uboot本身或相关固件(如rkbin)存在问题。 根据引用[1]和[2],我们可以参考Rockchip Uboot的编译和配置,以及如何修改开机LOGO(充电界面可能涉及LOGO显示)。但用户的问题集中在充电模式未按预期工作。 解决步骤: 1. **确认硬件连接和充电器**:确保使用的充电器是设备支持的,并且连接正常。有些设备可能只支持特定电压/电流的充电器。 2. **检查Uboot配置**: - 确认Uboot已配置支持充电模式。在Uboot的配置文件(如`configs/<board>_defconfig`)中,应包含以下配置: ``` CONFIG_DM_CHARGER=y CONFIG_CHARGER_RK817=y # 根据实际PMIC型号选择 CONFIG_POWER_GPIO=y CONFIG_DM_PMIC=y CONFIG_PMIC_RK8XX=y ``` - 检查设备树(dts)配置。在设备树中,需要正确配置充电相关的节点,例如: ```dts charger { compatible = "rockchip,rt5036-charger"; // 根据实际PMIC型号修改 status = "okay"; }; ``` 以及电源键的配置(因为长按电源键会触发开机): ```dts keys { power { gpios = <&gpio0 RK_PA5 GPIO_ACTIVE_LOW>; linux,code = <KEY_POWER>; label = "power"; wakeup-source; }; }; ``` 3. **检查PMIC(电源管理集成电路)驱动**:确保Uboot中对应的PMIC驱动已正确初始化,并能正确读取充电状态。 4. **调试Uboot**:通过串口查看Uboot启动日志,观察插入充电器时的检测信息。可能需要修改Uboot代码,添加调试信息,例如在充电检测函数中打印日志。 5. **检查rkbin**:引用[1]提到rkbin,这是Rockchip提供的二进制文件,包括DDR初始化、miniloader等。确保使用的rkbin版本与Uboot和硬件匹配。 6. **检查关机充电的流程**:在Rockchip平台上,关机充电模式需要Uboot和内核的配合。内核关机时需要将系统状态保存,并在下次启动时由Uboot读取。如果内核关机时没有正确设置标志,Uboot可能会忽略充电状态而直接启动内核。 7. **检查内核配置**:虽然问题可能主要在Uboot,但内核的配置也可能影响。确保内核中配置了相应的充电驱动,并且在关机时能够正确设置充电标志。 8. **参考已知问题**:查阅Rockchip官方文档或社区,看是否有类似问题及解决方案。 具体操作建议: 1. **获取调试信息**:通过串口连接设备,观察插入充电器时Uboot的输出。正常进入充电模式时,Uboot会显示充电界面(可能是一个LOGO或电池图标)。如果直接进入内核启动流程,则说明检测失败。 2. **修改Uboot代码添加调试信息**:在Uboot中,与充电检测相关的代码可能在`drivers/power/`目录下。例如,在`charger_rk817.c`(或其他对应PMIC的驱动)的`charger_get_status`函数中添加打印,查看检测到的充电状态。 3. **测试充电器检测**:在Uboot命令行中,尝试使用命令读取PMIC寄存器,例如: ``` rk8xx read 0x4a # 读取充电状态寄存器(具体寄存器地址需参考PMIC手册) ``` 4. **检查电源键状态**:如果电源键被按下(或短路),设备会开机。因此,需要检查电源键是否正常,以及在插入充电器时是否误触发了电源键。 5. **更新固件**:尝试更新Uboot和rkbin到最新版本,以排除已知问题。 6. **检查设备树**:确认设备树中充电器节点和电源键节点的配置正确,特别是GPIO引脚配置。 7. **检查内核关机流程**:确保内核在关机时正确设置了充电标志,并且Uboot能够读取该标志。 如果以上步骤无法解决问题,可能需要更深入地分析Uboot的电源管理流程。 相关问题: 1. 如何调试Rockchip Uboot的充电检测功能? 2. Rockchip平台关机充电的完整流程是怎样的? 3. 如何配置Uboot的设备树以支持关机充电? 4. 在Rockchip平台上,PMIC驱动常见问题有哪些? 5. 如何更新rkbin以及它与Uboot的兼容性如何?</think>### Rockchip Uboot 充电配置问题分析 根据您的描述,设备在配置 Uboot 关机充电模式后,插入电源适配器时仍自动开机而非进入充电模式。以下是可能原因及解决方案: #### 一、核心原因分析 1. **电源检测逻辑错误** Uboot 可能未正确检测到充电器插入事件,导致直接触发开机流程而非充电模式。Rockchip 平台需通过 `drivers/power/` 下的 PMIC 驱动(如 RK808/RK817)检测电源状态[^1]。 2. **设备树配置缺失** 关键配置项未正确启用: ```dts charger { compatible = "rockchip,rt5036-charger"; // 需匹配实际PMIC型号 rockchip,dc-det-gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>; // DC检测GPIO status = "okay"; // 必须启用 }; ``` 3. **固件兼容性问题** 使用的 rkbin 版本与硬件不兼容(常见于 RK3399/RK3588),导致充电流程初始化失败[^1]。 4. **硬件触发信号干扰** 电源按钮 GPIO 被错误触发(如电压波动),强制启动系统。 #### 二、解决方案步骤 1. **验证硬件检测** 在 Uboot 命令行执行 PMIC 寄存器检测: ```bash => i2c dev 0 # 切换到PMIC所在I2C总线 => i2c read 0x40 0x4a 1 # 读取RK817充电状态寄存器 ``` 正常充电状态应返回 `0x01`,否则需检查硬件连接。 2. **检查关键配置** - 确认 `CONFIG_POWER_CHARGER=y` 已加入 **defconfig** 文件 - 在设备树中确保: ```dts / { chosen { rockchip,uboot-charge-on = <1>; // 启用Uboot充电 }; }; ``` 3. **更新 rkbin 固件** 从官方仓库获取最新版本: ```bash git clone https://github.com/rockchip-linux/rkbin.git cp rkbin/bin/rk33/rk3399_bl31_v1.35.elf ./ # 以RK3399为例 make BL31=./rk3399_bl31_v1.35.elf ``` 4. **添加充电模式调试** 在 Uboot 源码中添加调试信息(`drivers/power/charge_display.c`): ```c printf("Charger status: %s\n", charger_is_connected() ? "Connected" : "Disconnected"); ``` 5. **检查电源键防抖配置** 在设备树中增加防抖参数: ```dts gpio-keys { power { debounce-interval = <100>; // 100ms防抖 }; }; ``` #### 三、验证流程 1. 编译烧写更新后的 Uboot: ```bash make rk3399_defconfig make CROSS_COMPILE=aarch64-linux-gnu- rkdeveloptool db rk3399_loader_v2.24.126.bin rkdeveloptool wl 0x40 uboot.img ``` 2. 通过串口监控启动日志: ```log U-Boot 2023.07 (Oct 10 2023 - 16:20:35 +0800) DRAM: 4 GiB Charger status: Connected // 关键调试信息 Entering charge mode... // 成功进入充电模式 ``` > **注意**:若仍无法解决,请提供以下信息: > - 具体芯片型号(如 RK3399/RK3588) > - Uboot 版本及 rkbin 版本 > - 串口输出的完整启动日志
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值