• 基于AR9331(MIPS架构)分析系统启动过程(uboot)


    前提:

    1.AR9331是基于MIPS 24K CPU的一款WIFI1X1芯片,其SDK采用uboot作为引导。AR9331中定义的基地址是:0x9f00,0000

    2.MIPS24K芯片,将固定的起始地址,规定为0xBF00,0000(见http://blog.csdn.net/phunxm/article/details/9393021 和http://www.cnblogs.com/xmphoenix/archive/2011/11/02/2233397.html有提到

    此地址属于MIPS的KSEG1的地址范围内(见http://blog.csdn.net/phunxm/article/details/9393021),其实际的物理地址是:0x1F00,0000(=0xBF00,0000 & 0x1FFF,FFFF)

    A.  uboot在编译时,会经历如下动作:

    bootstrap: depend version $(SUBDIRS) $(OBJS_BOOTSTRAP) $(LIBS_BOOTSTRAP) $(LDSCRIPT_BOOTSTRAP)

           UNDEF_SYM=`$(OBJDUMP) -x $(LIBS_BOOTSTRAP) |sed  -n -e 's/.*(__u_boot_cmd_.*)/-u1/p'|sort|uniq`;

           $(LD) $(LDFLAGS_BOOTSTRAP[xxx1] ) $$UNDEF_SYM $(OBJS_BOOTSTRAP)

               --start-group $(LIBS_BOOTSTRAP) --end-group $(PLATFORM_LIBS)

               -Map bootstrap.map -o bootstrap

    u-boot:         depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)

           UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed  -n -e 's/.*(__u_boot_cmd_.*)/-u1/p'|sort|uniq`;

           $(LD) $(LDFLAGS)[xxx2]  $$UNDEF_SYM $(OBJS) $(BOARD_EXTRA_OBJS)

               --start-group $(LIBS) --end-group $(PLATFORM_LIBS)

               -Map u-boot.map -o u-boot

    以及

    u-boot.lzimg: $(obj)u-boot.bin System.map

           @$(LZMA) e $(obj)u-boot.bin u-boot.bin.lzma

           @./tools/mkimage -A mips -T firmware -C lzma

           -a 0x$(shell grep "T _start" $(TOPDIR)/System.map | awk '{ printf "%s", $$1 }')

           -e 0x$(shell grep "T _start" $(TOPDIR)/System.map | awk '{ printf "%s", $$1 }')

    [xxx3]       -n 'u-boot image' -d $(obj)u-boot.bin.lzma $@

    也就是说,在编译的时候,就决定了tuboot需要在0x9f000000处被引导启动,也就是说,烧写tuboot时,需要烧到0x9f000000[xxx4] 处。

    而0x9F00,0000属于KSEG0范围,其实际对应的物理地址也是0x1F00,0000[xxx5] (=0x9F00,0000&0x7FFF,FFFF)

    B. Uboot编译连接脚本文件,在ap121上,就是/boot/u-boot/board/ar7240/ap121/u-boot.lds

    此文件的作用:

    ² 连接脚本是用来描述输出文件的内存布局;源代码经过编译器编译后包含如下段:

    正文段text:包含程序的指令代码;

    数据段data:包含固定的数据,如常量和字符串;

    未初始化数据段:包含未初始化的变量、数组等。

    连接器的任务是将多个编译后的文件的textdatabass等段连接在一起;而连接脚本文件就是告诉连接器从什么地址(运行时地址)开始放置这些段

    ² 此文件中,最要关注的是.text字段。一切从这里开始

    C. 先运行bootstrap,然后再运行uboot

    在ootu-bootoardar7240ap121u-boot-bootstrap.lds,有定义:ENTRY(_start_bootstrap)

    在ootu-bootoardar7240ap121u-boot.lds,有定义:ENTRY(_start)

    而在bootu-bootoardar7240ap121config.mk,有定义:

    # ROM version

    ifeq ($(COMPRESSED_UBOOT),1)

    TEXT_BASE = 0x80010000    #对应uboot的TEXT正文地址,见u-boot.map的_start

    BOOTSTRAP_TEXT_BASE = 0x9f000000  #对应bootstrap的TEXT正文地址,见bootstrap.map的_start_bootstrap

    [xxx6] else

    TEXT_BASE = 0x9f000000

    Endif

    所以,先执行_start_bootstrap,再执行_start。那么,这两个在哪儿?

    D. 在bootstrap.map中,可以看到:

    .text           0x000000009f000000     0x3aa0

     *(.text)

     .text      0x000000009f000000      0x8b0 cpu/mips/start_bootstrap.o

                    0x000000009f000000                _start_bootstrap

         Address of section .text set to 0x9f000000

    在u-boot.map中,可以看到:

    .text           0x0000000080010000    0x17da0

    *(.text)

    .text          0x0000000080010000     0x3350 cpu/mips/start.o

    0x0000000080010030                relocate_code

    0x0000000080010000                _start

               Address of section .text set to 0x80010000

           然后,在boot/u-boot/cpu/mips中,可以找到:start_bootstrap.S和start.S

    这两个,就是真正执行_start_boostrap和_start的地方

           在__start_bootstrap中,可以看到:bootstrap_board_init_f和bootstrap_board_init_r。最后,在bootstrap_board_init_r中,有:

    addr = (char *)(BOOTSTRAP_CFG_MONITOR_BASE + ((ulong)&uboot_end_data_bootstrap - dest_addr));

            memmove (&header, (char *)addr, sizeof(image_header_t));

            以及:

            data = addr + sizeof(image_header_t);/*越过uboot的头,定位到uboot的净荷开始*/

            fn = ntohl(hdr->ih_load);/*定位位于hdr->ih_load位置的起止程序,并执行之。这个程序就是__start*/

    (*fn)(gd->ram_size);

        可见,bootstrap中会定位并剥掉uboot的image_header_t的头,这样就会调位到__start。

           下面,分析_start

    E. 在start.S中,可以看到:board_init_f和board_init_r(boot/u-boot/lib_mips/board.c)。这就是从汇编进入C的两个入口。先board_init_f,再board_init_r;

    最终,在board_init_r中,调用无限循环:

    for (;;) {

         main_loop ();

    }

    F. main_loop(boot/u-boot/common/main.c)中,最终会调用:run_command (lastcommand, flag);

    G. 在run_command(boot/u-boot/common/main.c)中,利用find_cmd找到合适的cmd_tbl_t *cmdtp对象,最后执行((cmdtp->cmd) (cmdtp, flag, argc, argv)

    并且,在cmd_bootm.c中,有定义:

    U_BOOT_CMD(

         bootm, CFG_MAXARGS,  1,  do_bootm,

         "bootm   - boot application image from memory ",

         "[addr [arg ...]]     - boot application image stored in memory "

         " passing arguments 'arg ...'; when booting a Linux kernel, "

         " 'arg' can be the address of an initrd image "

    );

    在boot/u-boot/include/command.h中,有定义:

    #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help)

    cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}

    那么,最初的((cmdtp->cmd) (cmdtp, flag, argc, argv),就会执行到do_bootm

    H. 在boot模式下,敲入printenv,可以看到uboot所用到的环境变量的值:

    ar7240> printenv

    bootargs=console=ttyS0,115200 root=31:02 rootfstype=squashfs init=/sbin/init mtdparts=ar7240-nor0:256k(u-boot),64k(u-boot-env),2752k(rootfs),896k(uImage),64k(NVRAM),64k(ART)[xxx7] 

    bootcmd=bootm 0x9f300000[xxx8] 

    bootdelay=4[xxx9] 

    baudrate=115200

    ethaddr=0x00:0xaa:0xbb:0xcc:0xdd:0xee

    ipaddr=192.168.1.2[xxx10] 

    serverip=192.168.1.10[xxx11] 

    stdin=serial

    stdout=serial

    stderr=serial

    ethact=eth0

    这些环境变量的定义,是在boot/u-boot/common/environment.c中赋值的;而具体的来源,则大部分在文件boot/u-boot/include/configs/ap121.h中定义;并且这些宏定义,是通过boot/u-boot/common/env_nowhere.c中的env_init引导的。

    I. 在编译内核镜像时,有如下命令:

    /home/xxx/140703_AR9331_Dev/u11_OnlyBasicAndWLAN_AP121-4MB/build/../boot/u-boot/tools/mkimage -A mips -O linux -T kernel -C gzip -a 0x80002000 -e 0x8019bd60 -n Linux Kernel Image -d /home/xxx/140703_AR9331_Dev/u11_OnlyBasicAndWLAN_AP121-4MB/build/../linux/kernels/mips-linux-2.6.31/arch/mips/boot/vmlinux.bin.gz /home/xxx/140703_AR9331_Dev/u11_OnlyBasicAndWLAN_AP121-4MB/build/../images/ap121-2.6.31/vmlinux.gz.uImage[xxx12] 

    J. Uboot启动内核,是调用cmd_bootm.c中的do_bootm函数,其传入的命令参数就是:

    bootm 0x9f300000[xxx13] 

    然后,可以看到如下的启动信息:

    ## Booting image at 9f300000 ... [xxx14] 

    Image Name:   Linux Kernel Image

    Created:      2013-02-06  22:27:48 UTC

    Image Type:   MIPS Linux Kernel Image (lzma compressed)

    Data Size:    771996 Bytes = 753.9 kB

    Load Address: 80002000 [xxx15] 

    Entry Point:  8019bd60[xxx16] 

    Verifying Checksum at 0x9f300040 ...OK

       Uncompressing Kernel Image ... OK

    上述这些信息,都是do_bootm函数中,读取镜像文件头image_header_t信息后得出的

    然后,利用gunzip ((void *)ntohl(hdr->ih_load), unc_len, (uchar *)data, &len) != 0),将压缩的镜像文件解压缩到hdr->ih_load[xxx17] 指向的地址。

    最后,调用do_bootm_linux  (cmdtp, flag, argc, argv,addr, len_ptr, verify); 开始内核启动过程

    K. do_bootm_linux(boot/u-boot/lib_mips/mips_linux.c)  :

    ² 获得内核镜像的启动地址:

    theKernel =

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

    ² [xxx18] 解析boot_args字段,得到:

    linux_params_init (UNCACHED_SDRAM (gd->bd->bi_boot_params), commandline);

    ² 最后,直接运行内核镜像的启动地址:

    flash_size_mbytes = gd->bd->bi_flashsize/(1024 * 1024);

    theKernel (linux_argc, linux_argv, linux_env, flash_size_mbytes);[xxx19] 

    L. entry: 0x8019bf90地址上的程序,是什么呢?

    看一下:linux/kernels/mips-linux-2.6.31/System.map,搜索8019bf9,会发现:

    ffffffff8019bf90 T kernel_entry

    哈哈,原来该地址上的程序是:kernel_entry

    M. kernel_entry在arch/mips/kernel/head.S中定义;并且最终会跳转到start_kernel函数,从而进入C代码

    N. 这里就调用了start_kernel(linux/kernels/mips-linux-2.6.31/init/main.c) 

    O. Start_kernel->rest_init->kernel_thread([xxx20]kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);->kernel_init->init_post->run_init_process("/sbin/init"); --> 进入busybox的init流程


     [xxx1]## LDFLAGS_BOOTSTRAP 中,含有-Bstatic -T $(LDSCRIPT_BOOTSTRAP) -Ttext $(BOOTSTRAP_TEXT_BASE) $(PLATFORM_LDFLAGS)

    ## BOOTSTRAP_TEXT_BASE,在bootu-bootoardar7240ap121config.mk中有定义,指明了BOOTSTRAP_TEXT_BASE = 0x9f000000,bootstrap的报文段会从此地址开始。

    ## 那么,也就是要求:需要将tuboot放到0x9f000000处。这样tuboot启动时,才能找到这里的正确位置

     [xxx2]## LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)

    ## 其中的$(TEXT_BASE)bootu-bootoardar7240ap121config.mk中有定义,指明了TEXT_BASE = 0x80010000,uboot的报文段会从此地址开始        

     [xxx3]           

    -a 0xffffffff80010000

    -e 0xffffffff80010000

     [xxx4]0x9f000000位于KSEG0地址段,其距离KSEG0地址段上限(0x9fffffff)还有0x1000000,即16M的空间。也就是说,设置0x9f000000作为基地址,也就意味着AR9331可以支持最多16MB Flash ---我的猜测,不知道是否正确

     [xxx5]MIPS24K的固定起始地址是一样的。这就是为何要规定基地址是0x9F00,0000的缘故

     [xxx6]这是u-bootbootstrap在内存中的运行域

     [xxx7]ap121.h中的CONFIG_BOOTARGS定义

    表示传递给内核的启动参数

     [xxx8]ap121.h中的CONFIG_BOOTCOMMAND定义

    表示自动启动时执行的命令

    这里的0x9f300000就是linux内核的TEXT_BASE地址;这也正是uboot下的cp.b命令烧写Linux内核的目的地址

     [xxx9]执行自动启动的等候秒数

     [xxx10]ap121.h中的CONFIG_IPADDR定义,表示uboot模式下,单板的IP地址

     [xxx11]ap121.h中的CONFIG_SERVERIP定义,表示uboot升级时,所认可的TFTP ServerIP地址

     [xxx12]参数说明:

      -a ==> set load address to 'addr' (hex)  -- 表示内核的运行地址

     -e ==> set entry point to 'ep' (hex)

    是入口地址

     [xxx13]这就是在使用uboot烧写内核文件时,给出的基地址

     [xxx14]对应bootm的参数

     [xxx15]对应编译时的-a参数

     [xxx16]对应编译时的-e参数

     [xxx17]就是mkimage –a选项指定的地址

     [xxx18]ap121上,这个值就是entry: 0x8019bf90

     [xxx19]也就是直接运行位于

    entry: 0x8019bf90地址上的程序

     [xxx20]创建内核线程

  • 相关阅读:
    在Docker中启动Nacos-Server
    maven配置阿里云公共仓库
    Centos7动态IP改静态后SSH很慢
    Vue+NodeJS的跨域请求Session不同
    一款非常简洁漂亮方便调用的jQuery前端分页
    springmvc后台接收List参数的几种办法
    net use命令详解(转)
    c#开发windows服务
    c# base64转字符串
    关于web api 验证
  • 原文地址:https://www.cnblogs.com/lagujw/p/3996576.html
Copyright © 2020-2023  润新知