reset命令不见了!!调试u-boot遇到的怪问题

命令玩起了捉迷藏

调试一款高通AR7240的AP,仿照高通AP91的代码,一通改,很顺利的u-boot就搞定了,正当自己感慨这种“搬砖式”调试已经日趋娴熟的时候,忽然发现reset命令没有了。。。。
执行reset命令,一直报找不到该命令的错误:
这里写图片描述
敲help命令,reset命令明明就在呀:
这里写图片描述
真是“你见与不见,我都在这里,但是不给你用。”一头雾水。。。。。
reset命令是MIPS CPU通用命令,代码与具体的板子型号完全无关,而新添的代码只有在board目录和configs目录有修改,况且这边的改动也不会影响到其他地方的代码。

于是仿照reset命令,复制他的命令注册代码,自己在其他地方做了一个treset命令:
这里写图片描述
执行treset命令是可以正常重启的,由此可以断定肯定不是reset命令相关的代码不适用于当前这块板子。
观察了下help命令输出,记得在其他板子上,一般都是?(help)这个命令排第一个,而现在是reset命令在第一个,应该也是一种不正常的表现。
于是我干脆把reset命令的注册代码注释掉,这个时候排第一个的命令变成了bootm,运行下bootm命令,同样是跟reset类似,报找不到:
这里写图片描述
真是妖孽呀!!!

连续做了两次relocate

在没法得到更进一步线索的情况下,只好开始代码跟踪调试了:
执行命令,会报“Unknown command xxx - try ‘help’”是因为在run_command中对命令解析时会进行一个find_cmd的判断,而在find_cmd中需要满足输入命令与当前全局变量中的命令名全部或部分相符。
命令相关的信息都存储在u_boot_cmd_start开始到u_boot_cmd_end结束的这部分区间内。
再看help命令的输出过程,同样也是去u_boot_cmd_start-u_boot_cmd_end这段区间获取命令信息,然后对各个命令名称按照组成字母的ASCII码大小进行递增排序,所以正常情况下都是“?”这个命令排第一。而现在比较奇怪的就是排第一的变成了reset或是bootm这种,显然不应该在“?”前面。
综上,导致这些奇怪现象的嫌疑犯按理说就是u_boot_cmd_start-u_boot_cmd_end这段区间了。
仔细跟了下代码,并对cmd命令的一些域做了调试输出,发现与命令相关的全局变量,在board_init_f的时候还是正常的,
但是到了board_init_r的时候不对了:

在board_init_f的最后,
cmdtp-cmd bootm, cmdtp-cmd len 5, cmdtp addr 0x800322e8, name addr 0x8002e46c
cmdtp-cmd boot, cmdtp-cmd len 4, cmdtp addr 0x80032300, name addr 0x8002ed40
cmdtp-cmd bootd, cmdtp-cmd len 5, cmdtp addr 0x80032318, name addr 0x8002e57c

在board_init_r最开始的地方再做输出:
cmdtp-cmd bootm, cmdtp-cmd len 5, cmdtp addr 0x83ff62e8, name addr 0x83ff246c
cmdtp-cmd boot, cmdtp-cmd len 4, cmdtp addr 0x83ff6300, name addr 0x8002ed40
cmdtp-cmd bootd, cmdtp-cmd len 5, cmdtp addr 0x83ff6318, name addr 0x8002e57c

可以看到,正常的命令如boot和bootd,他们的name字段,在这两个调试输出中是一样的,而出问题的bootm命令的那个字段前后发生了改变,而改变的偏移恰好等于relocate的offset。
由于这些name cmd 等命令结构中的字段,在board_init_r开始后,还要做一个手动relocate:

    /*
     * We have to relocate the command table manually
     */
    for (cmdtp = &__u_boot_cmd_start; cmdtp !=  &__u_boot_cmd_end; cmdtp++) {
        ulong addr;

        addr = (ulong) (cmdtp->cmd) + gd->reloc_off;
        cmdtp->cmd =
            (int (*)(struct cmd_tbl_s *, int, int, char *[]))addr;
....
....

所以对于这个bootm命令,其相应的那些字段就会做两次relocate,这也是bootm或是reset命令找不到的,或是排在?命令前面的原因。
在board_init_f和board_init_r之间只有relocate这段汇编有过执行,但是里面的代码也很简单且应该是MIPS架构很通用、成熟的代码,难道它针对某款特定CPU会有问题?好诡异呀。。。。

Relocate和GOT、LDS

观察了下reset、bootm这种问题命令在map中的位置,发现出问题都是u_boot_cmd段的第一个命令,并且紧挨着got_end,这样看来似乎跟.got有莫大的关联,莫非u_boot_cmd_xxx这段空间是无辜的?

80031e34 A __got_start
80031e40 A _gp
80031e40 D _GLOBAL_OFFSET_TABLE_
800322e8 A __got_end
800322e8 A __u_boot_cmd_start
800322e8 D __u_boot_cmd_bootm /* reset出问题时,这个位置是u_boot_cmd_reset */
80032300 D __u_boot_cmd_boot

start.s中除了relocate_code这段有针对.got的操作,在一开始初始化的时候,还有一段:

    /* Initialize GOT pointer.*/
    bal     1f
    nop
    .word   _GLOBAL_OFFSET_TABLE_
    1:
    move    gp, ra
    lw      t1, 0(ra)
    move    gp, t1

这段是把GLOBAL_OFFSET_TABLE_的值存到$gp寄存器里面,而在relocate_code中则有:

    /*
     * Fix GOT pointer:
     *
     * New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address
     */
    move    t6, gp
    sub gp, CFG_MONITOR_BASE
    add gp, a2          /* gp now adjusted      */
    sub t6, gp, t6      /* t6 <-- relocation offset */
...
...
    .word   uboot_end_data
    .word   uboot_end
    .word   num_got_entries

in_ram:
    /* Now we want to update GOT.
     */
    lw  t3, -4(t0)  /* t3 <-- num_got_entries   */
    addi    t4, gp, 8   /* Skipping first two entries.  */
    li  t2, 2
1:
    lw  t1, 0(t4)
    beqz    t1, 2f
    add t1, t6
    sw  t1, 0(t4)
2:
    addi    t2, 1
    blt t2, t3, 1b
    addi    t4, 4       /* delay slot           */
...
...

这段应该是计算出relocate的偏移数值,然后将整个gp寄存器和.got段都做了重定向操作,而重定向的范围,起始值是$gp寄存器决定,大小则是由一个全局变量num_got_entries决定的。
找了下,num_got_entries这个值原来是在.lds里面定义的:

    num_got_entries = (__got_end - __got_start) >> 2;

got_end和got_start也在同一个lds文件中定义:

    _gp = ALIGN(16);
    __got_start = .;
    .got  : { *(.got) }
    __got_end = .;

从上面的map中可以发现,GLOBAL_OFFSET_TABLE_与_gp是等价的,那么“真相”终于被发现了:
基于map,可以发现got_start(0x80031e34)比gp(0x80031e40)小,那么.got段的范围就比正常大了0xC Byte,而代码中起始地址是_gp决定的,所以在做.got重定向的时候,会一直操作到0x800322F4(__got_end + 0xC Byte)才结束,于是刚好把__u_boot_cmd_bootm相关的内容都囊括了进去,导致它做了两次relocate。
搞清楚了这个,修改就变得很简单了,只需要把对应lds文件修改为:

    . = ALIGN(16);
    _gp = .;
    __got_start = .;
    .got  : { *(.got) }
    __got_end = .;

得到的map如下:

80031d30 A __got_start
80031d30 A _gp
80031d30 D _GLOBAL_OFFSET_TABLE_
800321d4 A __got_end
800321d4 A __u_boot_cmd_start
800321d4 D __u_boot_cmd_reset

此时gp==__got_start==_GLOBAL_OFFSET_TABLE,对应的命令就都正常了。
再去对比了下其他没问题机器的相关lds代码,它们也是如此这般设置。

看来,“搬砖式”的调试态度要不得呀,更不能迷信官方发布的代码,是人就会出bug。有些貌似很熟练的事情如果出了古怪,那说明你对它其实还没有真正搞清楚。不清楚系统架构,就很容易被这种很奇怪的问题给搞得昏头转向。
当然,也只有在实战中遇到这些怪问题,才会倒逼自己深入思考,所以多调试、多练习是王道呀。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值