zoukankan      html  css  js  c++  java
  • openwrt u-boot_mod 代码分析

    u-boot_mod 是具有web 浏览器的uboot,也就是传说中的不死uboot,这里的不死指的是不管怎么刷firmware

    都可以方便更换firmware,而不是uboot本身就是不死的。

    这里将其的代码分析一下。

    代码的网址是:https://github.com/pepe2k/u-boot_mod

    uboot的代码在openwrt上是最底层的,就像PCBIOS

    整个uboot最开始的入口,是一段汇编语言(MIPS)代码


    要证明这段代码是一开始执行的,首先要看链接器脚本(u-boot-bootstrap.lds):


    链接器脚本

    OUTPUT_FORMAT("elf32-tradbigmips", "elf32-tradbigmips", "elf32-tradbigmips")
    OUTPUT_ARCH(mips)
    ENTRY(_start_bootstrap)
    SECTIONS
    {
        . = 0x00000000;
    
        . = ALIGN(4);
        .text       :
        {
          *(.text*)
        }
    
        . = ALIGN(4);
        .rodata  : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
    
        . = ALIGN(4);
        .data  : { *(.data*) }
    
        . = .;
        _gp = ALIGN(16);
        
        .got  : {
        __got_start_bootstrap = .;
            *(.got)
        __got_end_bootstrap = .;
        }
    
        . = ALIGN(4);
        .sdata  : { *(.sdata*) }
    
        uboot_end_data_bootstrap = .;
        num_got_entries = (__got_end_bootstrap - __got_start_bootstrap) >> 2;
    
        . = ALIGN(4);
        .sbss  : { *(.sbss*) }
        .bss  : { *(.bss*) . = ALIGN(4); }
        uboot_end_bootstrap = .;
    }

    这里可以看到,程序入口是_start_bootstrap,程序的入口代码段(.text)地址是 0x00000000



    那么,接下来分析最开始执行的代码(MIPS)


    _start_bootstrap


    start_bootstrap.s这个文件的入口处是_start_bootstrap

    最终会调用到以下代码:

    la t9, bootstrap_board_init_r
    
    j t9

    它的作用就是将bootstrap_board_init_r的地址loadt9寄存器,然后根据t9寄存器的内容跳转。




    bootstrap_board_init_r


    bootstrap_board_init_r里面,会调用


    fn = (void *)ntohl(hdr->ih_load);
    
    (*fn)(gd->ram_size);


    这里的hdr是根据image来设定的,这里不仔细分析了,得知其跳转的地址是start.s_start 函数



    _start


    最终会调用到以下代码:

    la t9, board_init_r
    
    j t9

    它的作用就是将board_init_r的地址loadt9寄存器,然后根据t9寄存器的内容跳转。



    board_init_r


    从这里开始,进入c语言的地盘了,下面逐个分析一下它们的作用。


    debug消息来看,这里已经开始在RAM上跑了。一开始是从硬件的0地址开始跑的,一开始的汇编中一般有将代码copyRAM的过程(参考http://www.linuxidc.com/Linux/2012-08/68707.htm)。


    printf("Now running in RAM - U-Boot at: %08lX
    ", dest_addr);

    接下来重新定位CMD table

    因为已经得到了 gd->reloc_off,那么将CMD table往后移动 gd->reloc_off



        /*
         * 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;
    
            addr = (ulong)(cmdtp->name) + gd->reloc_off;
            cmdtp->name = (char *)addr;
    
            if(cmdtp->usage){
                addr = (ulong)(cmdtp->usage) + gd->reloc_off;
                cmdtp->usage = (char *)addr;
            }
    #ifdef    CFG_LONGHELP
            if(cmdtp->help){
                addr = (ulong)(cmdtp->help) + gd->reloc_off;
                cmdtp->help = (char *)addr;
            }
    #endif
        }



    那么什么是CMD table呢?这里举个例子:


    Cmd_boot.c (common):U_BOOT_CMD(go, CFG_MAXARGS, 1, do_go, "start application at address 'addr'
    ",
    
    Cmd_boot.c (common):U_BOOT_CMD(reset, 1, 0, do_reset, "perform RESET of the CPU
    ", NULL);
    
    Cmd_bootm.c (common):U_BOOT_CMD(bootm, 2, 1, do_bootm, "boot application image from memory
    ", "[addr]
    "
    
    Cmd_bootm.c (common):U_BOOT_CMD(boot, 1, 1, do_bootd, "boot default, i.e., run 'bootcmd'
    ", NULL);
    
    Cmd_bootm.c (common):U_BOOT_CMD(bootd, 1, 1, do_bootd, "boot default, i.e., run 'bootcmd'
    ", NULL);



    U_BOOT_CMD这个宏来看,已经将CMD table的每一个entry和地址绑定了。


    #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) 
    
    cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}


    其中开始和结束的范围就是 __u_boot_cmd_start __u_boot_cmd_end


    从链接脚本可以看到:

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

    除了CMD table 调整之外,这里对基本的硬件做了初始化:

    /* 从硬件读取mac地址 */
    
    memcpy(buffer, (void *)(CFG_FLASH_BASE + OFFSET_MAC_DATA_BLOCK + OFFSET_MAC_ADDRESS), 6);
    
    /*get CPU/RAM/AHB clocks */
    
    ar7240_sys_frequency(&cpu_freq, &ddr_freq, &ahb_freq);
    
    /* configure available FLASH banks */
    
    size = flash_init();
    
    /* initialize malloc() area */
    
    mem_malloc_init();
    
    /* std in/out 初始化 */
    
    devices_init();
    
    console_init_r();
    
    /* ethernet 初始化 */
    
    eth_initialize(gd->bd);




    当这些东西初始化完之后,执行main_loop


    /* main_loop() can return to retry autoboot, if so just run it again. */
    for(;;){
        main_loop();
    }


    main_loop

    在这里做了一些主要的分支工作,

     

    uboot 里面集成了一个hush shell,可以将其看成一个精简的bash,可以处理用户的命令和一些脚本。


    在这个main loop里面,决定了从以下几种方式选择启动:

    1.web failsafe

    2.U-Boot console

    3.U-Boot netconsole

    4.boot command


    用下图概括:




    reset_button_status 指的是读取reset按键的状态,检测其是否按下。

    httpd则实现了用web页面来更新firmware的操作。

    关于网页更新firmware的http接口这个特色功能后面再分析,下面关注一下do_bootm后面的流程。

     

    do_bootm

    这个函数里面做了下面几个事情:

    1.print_image_hdr   //打印出firmware的头部,此时已经知道firmware所处的地址
    
    2.eth_halt //停止ethernet的协议栈,之前可能被开起来了,这里先关闭
    
    3.lzma_inflate //解压linux kernel 到内存
    
    4.do_bootm_linux //进入此函数,如果进入失败,就调用do_reset复位板子

    do_bootm_linux

     

    这里是uboot最后一个过程的函数了,它做了下面的事情:

    1.linux_params_init/linux_env_set  //生成即将传入linux的一些参数

    2.theKernel(linux_argc, linux_argv, linux_env, 0); //启动linux kernel

    其中,theKernel的地址是这样来的:

        theKernel = (void (*)(int, char **, char **, int *))ntohl(hdr->ih_ep);

    这里的ih_ep指针的数据可以通过mkimage工具来指定,详见mkimage.c。

     

     

     

    到这里为止。uboot的大体流程就分析好了,这个uboot本来就是定制过的,通过汉化、修改网页的

    手段可以进一步进行定制,其中已经有很多做好的版本了,如果不是要深入研究uboot,建议拿现成的

    来使用,毕竟openwrt主要关注的是firmware部分。

     

     

     

    参考资料:http://www.cnblogs.com/lagujw/p/3996576.html

     

     

  • 相关阅读:
    worker.properties配置
    uriworkermap.properties配置
    Apache Tomcat连接器-Web服务器操作方法
    x01.os.14: 时间都去哪儿了
    x01.os.13: 文件系统
    x01.os.12: 在 windows 中写 OS
    x01.os.11: IPC 路线图
    x01.os.10: 输入输出
    x01.os.9: 进程切换
    x01.os.8: 加载内核
  • 原文地址:https://www.cnblogs.com/tanhangbo/p/4456868.html
Copyright © 2011-2022 走看看