zoukankan      html  css  js  c++  java
  • Tiny4412 u-boot分析(3)u-boot 引导内核流程

    在u-boot中,通过bootm命令启动内核。bootm命令的作用是将内核加载到指定的内存地址,然后通过R0、R1、R2寄存器传递启动参数之后启动内核。在启动内核之前需要对环境做一些初始化工作,主要有如下几个方面:

    (1)、cpu 寄存器设置

        * R0 = 0

        * R1 = 板级 id

        * R2 = 启动参数在内存中的起始地址

    (2)、cpu 模式

        * 禁止所有中断

        * 必须为SVC(超级用户)模式

    (3)、缓存、MMU

        * 关闭 MMU

        * 指令缓存可以开启或者关闭

        * 数据缓存必须关闭并且不能包含任何脏数据

    (4)、设备

        * DMA 设备应当停止工作

    (5)、boot loader 需要跳转到内核镜像的第一条指令处

    这些需求都由 boot loader 实现,在常用的 uboot 中完成一系列的初始化后最后通过 bootm 命令加载 linux 内核。bootm 将内核镜像从各种媒介中读出,存放在指定的位置;然后设置标记列表给内核传递参数;最后跳到内核的入口点去执行。

    在分析u-boot源码之前,我们首先来分析一下u-boot中的命令格式。u-boot中每个命令都是通过 U_BOOT_CMD 宏来定义的,格式如下:

     U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")

    各项参数的意义如下:

    (1) -- name:命令的名字,注意,它不是一个字符串(不要用双引号括起来);

    (2)-- maxargs:最大的参数个数;

    (3)-- repeatable:命令是否可以重复,可重复是指运行一个命令后,下次敲回车即可再次运行;

    (4)-- command:对应的函数指针,类型为(*cmd)(struct cmd_tbl_s *, int, int, char *[]);

    (5) -- usage:简单的使用说明,这是个字符串;

    (6)-- help:较详细的使用说明,这是个字符串。

    下面就来具体分析一下bootm命令。bootm命令的源码路径为:u-boot源码路径/common/cmd_bootm.c

    我们通过

    U_BOOT_CMD(
        bootm,    CONFIG_SYS_MAXARGS,    1,    do_bootm, ...)

    可以看出bootm命令的入口函数为d_bootm,下面我们就去看一下它的庐山真面目。

    /*******************************************************************/
    /* bootm - boot application image from image in memory */
    /*******************************************************************/
    int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    {
    #ifdef CONFIG_ZIMAGE_BOOT
    #define LINUX_ZIMAGE_MAGIC    0x016f2818
        image_header_t    *hdr;
        ulong        addr;
       //找到内核镜像的地址
        /* find out kernel image address */
        if (argc < 2) {
            addr = load_addr;
            debug ("*  kernel: default image load address = 0x%08lx
    ",
                    load_addr);
        } else {
            addr = simple_strtoul(argv[1], NULL, 16);
        }
      //检查内核是否为zImage格式
        if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
            u32 val;
            printf("Boot with zImage
    ");
        //将内核地址转换为物理地址
            //addr = virt_to_phys(addr);
            hdr = (image_header_t *)addr;
            hdr->ih_os = IH_OS_LINUX;
            hdr->ih_ep = ntohl(addr);
            //提取内核镜像的头信息
            memmove (&images.legacy_hdr_os_copy, hdr, sizeof(image_header_t));
        //保存头信息
            /* save pointer to image header */
            images.legacy_hdr_os = hdr;
            images.legacy_hdr_valid = 1;
            goto after_header_check;
        }
    #endif
    #ifdef CONFIG_NEEDS_MANUAL_RELOC
        static int relocated = 0;
        //重定位启动函数表
        /* relocate boot function table */
        if (!relocated) {
            int i;
            for (i = 0; i < ARRAY_SIZE(boot_os); i++)
                if (boot_os[i] != NULL)
                    boot_os[i] += gd->reloc_off;
            relocated = 1;
        }
    #endif
         //判断是否有子命令
        /* determine if we have a sub command */
        if (argc > 1) {
            char *endp;
            simple_strtoul(argv[1], &endp, 16);
            /* endp pointing to NULL means that argv[1] was just a
             * valid number, pass it along to the normal bootm processing
             *
             * If endp is ':' or '#' assume a FIT identifier so pass
             * along for normal processing.
             *
             * Right now we assume the first arg should never be '-'
             */
            if ((*endp != 0) && (*endp != ':') && (*endp != '#'))
                return do_bootm_subcommand(cmdtp, flag, argc, argv);
        }
       //获取内核相关信息
        if (bootm_start(cmdtp, flag, argc, argv))
            return 1;
        /*
         * We have reached the point of no return: we are going to
         * overwrite all exception vector code, so we cannot easily
         * recover from any failures any more...
         */
        //关闭中断
        iflag = disable_interrupts();
    #if defined(CONFIG_CMD_USB)
        /*
         * turn off USB to prevent the host controller from writing to the
         * SDRAM while Linux is booting. This could happen (at least for OHCI
         * controller), because the HCCA (Host Controller Communication Area)
         * lies within the SDRAM and the host controller writes continously to
         * this area (as busmaster!). The HccaFrameNumber is for example
         * updated every 1 ms within the HCCA structure in SDRAM! For more
         * details see the OpenHCI specification.
         */
         //关闭USB
        usb_stop();
    #endif
      //加载内核
        ret = bootm_load_os(images.os, &load_end, 1);
        if (ret < 0) {
            if (ret == BOOTM_ERR_RESET)
                do_reset (cmdtp, flag, argc, argv);
            if (ret == BOOTM_ERR_OVERLAP) {
                if (images.legacy_hdr_valid) {
                    if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)
                        puts ("WARNING: legacy format multi component "
                            "image overwritten
    ");
                } else {
                    puts ("ERROR: new format image overwritten - "
                        "must RESET the board to recover
    ");
                    show_boot_progress (-113);
                    do_reset (cmdtp, flag, argc, argv);
                }
            }
            if (ret == BOOTM_ERR_UNIMPLEMENTED) {
                if (iflag)
                    enable_interrupts();
                show_boot_progress (-7);
                return 1;
            }
        }
        lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));
        if (images.os.type == IH_TYPE_STANDALONE) {
            if (iflag)
                enable_interrupts();
            /* This may return when 'autostart' is 'no' */
            bootm_start_standalone(iflag, argc, argv);
            return 0;
        }
        show_boot_progress (8);
    #if defined(CONFIG_ZIMAGE_BOOT)
    after_header_check:
        images.os.os = hdr->ih_os;
        images.ep = image_get_ep (&images.legacy_hdr_os_copy);
    #endif
    #ifdef CONFIG_SILENT_CONSOLE
        if (images.os.os == IH_OS_LINUX)
            fixup_silent_linux();
    #endif
      //获取内核启动参数
        boot_fn = boot_os[images.os.os];
        if (boot_fn == NULL) {
            if (iflag)
                enable_interrupts();
            printf ("ERROR: booting os '%s' (%d) is not supported
    ",
                genimg_get_os_name(images.os.os), images.os.os);
            show_boot_progress (-8);
            return 1;
        }
      //内核启动前的准备
        arch_preboot_os();
      //启动内核,不返回
        boot_fn(0, argc, argv, &images);
        show_boot_progress (-9);
    #ifdef DEBUG
        puts ("
    ## Control returned to monitor - resetting...
    ");
    #endif
        do_reset (cmdtp, flag, argc, argv);
        return 1;
    }

    该函数主要的工作流程是,通过bootm_start来获取内核镜像文件的信息,然后通过bootm_load_os函数来加载内核,最后通过boot_fn来启动内核。

    首先看一下bootm_start,该函数主要进行镜像的有效性判定、校验、计算入口地址等操作,大部分工作通过 boot_get_kernel -> image_get_kernel 完成。

    static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    {
        void        *os_hdr;
        int        ret;
        memset ((void *)&images, 0, sizeof (images));
        //读取环境变量,从环境变量中检查是否要对镜像的数据(不是镜像头)进行校验
        images.verify = getenv_yesno ("verify");
        //不做任何有意义的工作,除了定义# define lmb_reserve(lmb, base, size)  
        bootm_start_lmb();
        //获取镜像头,加载地址,长度,返回指向内存中镜像头的指针
        /* get kernel image header, start address and length */
        os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
                &images, &images.os.image_start, &images.os.image_len);
        if (images.os.image_len == 0) {
            puts ("ERROR: can't get kernel image!
    ");
            return 1;
        }
        //根据镜像魔数获取镜像类型  
        /* get image parameters */
        switch (genimg_get_format (os_hdr)) {
        case IMAGE_FORMAT_LEGACY:
            images.os.type = image_get_type (os_hdr);//镜像类型  
            images.os.comp = image_get_comp (os_hdr);//压缩类型  
            images.os.os = image_get_os (os_hdr);//操作系统类型 
            images.os.end = image_get_image_end (os_hdr);//当前镜像的尾地址
            images.os.load = image_get_load (os_hdr);//镜像数据的载入地址 
            break;
    #if defined(CONFIG_FIT)
        case IMAGE_FORMAT_FIT:
            if (fit_image_get_type (images.fit_hdr_os,
                        images.fit_noffset_os, &images.os.type)) {
                puts ("Can't get image type!
    ");
                show_boot_progress (-109);
                return 1;
            }
            if (fit_image_get_comp (images.fit_hdr_os,
                        images.fit_noffset_os, &images.os.comp)) {
                puts ("Can't get image compression!
    ");
                show_boot_progress (-110);
                return 1;
            }
            if (fit_image_get_os (images.fit_hdr_os,
                        images.fit_noffset_os, &images.os.os)) {
                puts ("Can't get image OS!
    ");
                show_boot_progress (-111);
                return 1;
            }
            images.os.end = fit_get_end (images.fit_hdr_os);
            if (fit_image_get_load (images.fit_hdr_os, images.fit_noffset_os,
                        &images.os.load)) {
                puts ("Can't get image load address!
    ");
                show_boot_progress (-112);
                return 1;
            }
            break;
    #endif
        default:
            puts ("ERROR: unknown image format type!
    ");
            return 1;
        }
         //获取内核入口地址
        /* find kernel entry point */
        if (images.legacy_hdr_valid) {
            images.ep = image_get_ep (&images.legacy_hdr_os_copy);
    #if defined(CONFIG_FIT)
        } else if (images.fit_uname_os) {
            ret = fit_image_get_entry (images.fit_hdr_os,
                    images.fit_noffset_os, &images.ep);
            if (ret) {
                puts ("Can't get entry point property!
    ");
                return 1;
            }
    #endif
        } else {
            puts ("Could not find kernel entry point!
    ");
            return 1;
        }
        if (((images.os.type == IH_TYPE_KERNEL) ||
             (images.os.type == IH_TYPE_MULTI)) &&
            (images.os.os == IH_OS_LINUX)) {
            //获取虚拟磁盘
            /* find ramdisk */
            ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH,
                    &images.rd_start, &images.rd_end);
            if (ret) {
                puts ("Ramdisk image is corrupt or invalid
    ");
                return 1;
            }
            
    #if defined(CONFIG_OF_LIBFDT)
             //获取设备树,设备树是linux 3.XX版本特有的
            /* find flattened device tree */
            ret = boot_get_fdt (flag, argc, argv, &images,
                        &images.ft_addr, &images.ft_len);
            if (ret) {
                puts ("Could not find a valid device tree
    ");
                return 1;
            }
            set_working_fdt_addr(images.ft_addr);
    #endif
        }
        //将内核加载地址赋值给images.os.start
        images.os.start = (ulong)os_hdr;
        //更新镜像状态
        images.state = BOOTM_STATE_START;
        return 0;
    }

    接着看一下bootm_load_os函数,它的主要工作是解压内核镜像文件,并且将它移动到内核加载地址。

    首先看一下两个重要的结构体

    //include/image.h 
    typedef struct image_header {
            uint32_t        ih_magic;       /* Image Header Magic Number    */
            uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
            uint32_t        ih_time;        /* Image Creation Timestamp     */
            uint32_t        ih_size;        /* Image Data Size              */
            uint32_t        ih_load;        /* Data  Load  Address          */
            uint32_t        ih_ep;          /* Entry Point Address          */
            uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
            uint8_t         ih_os;          /* Operating System             */
            uint8_t         ih_arch;        /* CPU architecture             */
            uint8_t         ih_type;        /* Image Type                   */
            uint8_t         ih_comp;        /* Compression Type             */
            uint8_t         ih_name[IH_NMLEN];      /* Image Name           */
    } image_header_t;
    typedef struct image_info {
            ulong           start, end;             /* start/end of blob */
            ulong           image_start, image_len; /* start of image within blob, len of image */
            ulong           load;                   /* load addr for the image */
            uint8_t         comp, type, os;         /* compression, type of image, os type */
    } image_info_t;
    static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
    {
        void        *os_hdr;
        int        ret;
        memset ((void *)&images, 0, sizeof (images));
        //读取环境变量,从环境变量中检查是否要对镜像的数据(不是镜像头)进行校验
        images.verify = getenv_yesno ("verify");
        //不做任何有意义的工作,除了定义# define lmb_reserve(lmb, base, size)  
        bootm_start_lmb();
        //获取镜像头,加载地址,长度,返回指向内存中镜像头的指针
        /* get kernel image header, start address and length */
        os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,
                &images, &images.os.image_start, &images.os.image_len);
        if (images.os.image_len == 0) {
            puts ("ERROR: can't get kernel image!
    ");
            return 1;
        }
        //根据镜像魔数获取镜像类型  
        /* get image parameters */
        switch (genimg_get_format (os_hdr)) {
        case IMAGE_FORMAT_LEGACY:
            images.os.type = image_get_type (os_hdr);//镜像类型  
            images.os.comp = image_get_comp (os_hdr);//压缩类型  
            images.os.os = image_get_os (os_hdr);//操作系统类型 
            images.os.end = image_get_image_end (os_hdr);//当前镜像的尾地址
            images.os.load = image_get_load (os_hdr);//镜像数据的载入地址 
            break;
    #if defined(CONFIG_FIT)
        case IMAGE_FORMAT_FIT:
            if (fit_image_get_type (images.fit_hdr_os,
                        images.fit_noffset_os, &images.os.type)) {
                puts ("Can't get image type!
    ");
                show_boot_progress (-109);
                return 1;
            }
            if (fit_image_get_comp (images.fit_hdr_os,
                        images.fit_noffset_os, &images.os.comp)) {
                puts ("Can't get image compression!
    ");
                show_boot_progress (-110);
                return 1;
            }
            if (fit_image_get_os (images.fit_hdr_os,
                        images.fit_noffset_os, &images.os.os)) {
                puts ("Can't get image OS!
    ");
                show_boot_progress (-111);
                return 1;
            }
            images.os.end = fit_get_end (images.fit_hdr_os);
            if (fit_image_get_load (images.fit_hdr_os, images.fit_noffset_os,
                        &images.os.load)) {
                puts ("Can't get image load address!
    ");
                show_boot_progress (-112);
                return 1;
            }
            break;
    #endif
        default:
            puts ("ERROR: unknown image format type!
    ");
            return 1;
        }
         //获取内核入口地址
        /* find kernel entry point */
        if (images.legacy_hdr_valid) {
            images.ep = image_get_ep (&images.legacy_hdr_os_copy);
    #if defined(CONFIG_FIT)
        } else if (images.fit_uname_os) {
            ret = fit_image_get_entry (images.fit_hdr_os,
                    images.fit_noffset_os, &images.ep);
            if (ret) {
                puts ("Can't get entry point property!
    ");
                return 1;
            }
    #endif
        } else {
            puts ("Could not find kernel entry point!
    ");
            return 1;
        }
        if (((images.os.type == IH_TYPE_KERNEL) ||
             (images.os.type == IH_TYPE_MULTI)) &&
            (images.os.os == IH_OS_LINUX)) {
            //获取虚拟磁盘
            /* find ramdisk */
            ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH,
                    &images.rd_start, &images.rd_end);
            if (ret) {
                puts ("Ramdisk image is corrupt or invalid
    ");
                return 1;
            }
            
    #if defined(CONFIG_OF_LIBFDT)
             //获取设备树,设备树是linux 3.XX版本特有的
            /* find flattened device tree */
            ret = boot_get_fdt (flag, argc, argv, &images,
                        &images.ft_addr, &images.ft_len);
            if (ret) {
                puts ("Could not find a valid device tree
    ");
                return 1;
            }
            set_working_fdt_addr(images.ft_addr);
    #endif
        }
        //将内核加载地址赋值给images.os.start
        images.os.start = (ulong)os_hdr;
        //更新镜像状态
        images.state = BOOTM_STATE_START;
        return 0;
    }
    #define BOOTM_ERR_RESET        -1
    #define BOOTM_ERR_OVERLAP    -2
    #define BOOTM_ERR_UNIMPLEMENTED    -3
    static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress)
    {
        uint8_t comp = os.comp;//压缩格式
        ulong load = os.load;//加载地址
        ulong blob_start = os.start;//系统起始地址
        ulong blob_end = os.end;//系统结束地址
        ulong image_start = os.image_start;//镜像起始地址
        ulong image_len = os.image_len;//镜像大小
        uint unc_len = CONFIG_SYS_BOOTM_LEN;//镜像最大长度
    #if defined(CONFIG_LZMA) || defined(CONFIG_LZO)
        int ret;
    #endif /* defined(CONFIG_LZMA) || defined(CONFIG_LZO) */
        //获取镜像类型
        const char *type_name = genimg_get_type_name (os.type);
        switch (comp) {
        case IH_COMP_NONE://镜像没有压缩过
            if (load == blob_start) {//判断是否需要移动镜像
                printf ("   XIP %s ... ", type_name);
            } else {
                printf ("   Loading %s ... ", type_name);
                memmove_wd ((void *)load, (void *)image_start,
                        image_len, CHUNKSZ);
            }
            *load_end = load + image_len;
            puts("OK
    ");
            break;
    #ifdef CONFIG_GZIP
        case IH_COMP_GZIP://镜像使用gzip压缩
            printf ("   Uncompressing %s ... ", type_name);
            //解压镜像文件
            if (gunzip ((void *)load, unc_len,
                        (uchar *)image_start, &image_len) != 0) {
                puts ("GUNZIP: uncompress, out-of-mem or overwrite error "
                    "- must RESET board to recover
    ");
                if (boot_progress)
                    show_boot_progress (-6);
                return BOOTM_ERR_RESET;
            }
            *load_end = load + image_len;
            break;
    #endif /* CONFIG_GZIP */
    ......
        return 0;
    }

    最后看一下boot_fn函数,boot_fn的定义为

    boot_os_fn *boot_fn;

    可以看出它是一个boot_os_fn类型的函数指针。它的定义为

    //  common/cmd_bootm.c
    typedef int boot_os_fn (int flag, int argc, char * const argv[],
                            bootm_headers_t *images); /* pointers to os/initrd/fdt */
    #ifdef CONFIG_BOOTM_LINUX
    extern boot_os_fn do_bootm_linux;
    #endif
    ......

    然后boot_fn在do_bootm函数中被赋值为

    boot_fn = boot_os[images.os.os];

    boot_os是一个函数指针数组

    //  common/cmd_bootm.c
    static boot_os_fn *boot_os[] = {
    #ifdef CONFIG_BOOTM_LINUX
        [IH_OS_LINUX] = do_bootm_linux,
    #endif
    #ifdef CONFIG_BOOTM_NETBSD
        [IH_OS_NETBSD] = do_bootm_netbsd,
    #endif
    #ifdef CONFIG_LYNXKDI
        [IH_OS_LYNXOS] = do_bootm_lynxkdi,
    #endif
    #ifdef CONFIG_BOOTM_RTEMS
        [IH_OS_RTEMS] = do_bootm_rtems,
    #endif
    #if defined(CONFIG_BOOTM_OSE)
        [IH_OS_OSE] = do_bootm_ose,
    #endif
    #if defined(CONFIG_CMD_ELF)
        [IH_OS_VXWORKS] = do_bootm_vxworks,
        [IH_OS_QNX] = do_bootm_qnxelf,
    #endif
    #ifdef CONFIG_INTEGRITY
        [IH_OS_INTEGRITY] = do_bootm_integrity,
    #endif
    };

    可以看出 boot_fn 函数指针最后指向的函数是位于 arch/arm/lib/bootm.c的 do_bootm_linux,这是内核启动前最后的一个函数,该函数主要完成启动参数的初始化,并将板子设定为满足内核启动的环境。

    int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)
    {
        //从全局变量结构体中获取串口参数
        bd_t    *bd = gd->bd;
        char    *s;
        //获取机器码
        int    machid = bd->bi_arch_number;
        //内核入口函数
        void    (*kernel_entry)(int zero, int arch, uint params);
        int    ret;
        //获取启动参数
    #ifdef CONFIG_CMDLINE_TAG
        char *commandline = getenv ("bootargs");
    #endif
        if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
            return 1;
        //从环境变量中获取机器码
        s = getenv ("machid");
        if (s) {
            machid = simple_strtoul (s, NULL, 16);
            printf ("Using machid 0x%x from environment
    ", machid);
        }
        //获取ramdisk
        ret = boot_get_ramdisk(argc, argv, images, IH_ARCH_ARM, 
                &(images->rd_start), &(images->rd_end));
        if(ret)
            printf("[err] boot_get_ramdisk
    ");
        show_boot_progress (15);
    #ifdef CONFIG_OF_LIBFDT
        if (images->ft_len)
            return bootm_linux_fdt(machid, images);
    #endif
        kernel_entry = (void (*)(int, int, uint))images->ep;
        debug ("## Transferring control to Linux (at address %08lx) ...
    ",
               (ulong) kernel_entry);
    #if defined (CONFIG_SETUP_MEMORY_TAGS) || 
        defined (CONFIG_CMDLINE_TAG) || 
        defined (CONFIG_INITRD_TAG) || 
        defined (CONFIG_SERIAL_TAG) || 
        defined (CONFIG_REVISION_TAG)
        setup_start_tag (bd);
    #ifdef CONFIG_SERIAL_TAG
        setup_serial_tag (params);
    #endif
    #ifdef CONFIG_REVISION_TAG
        setup_revision_tag (params);
    #endif
    #ifdef CONFIG_SETUP_MEMORY_TAGS
        setup_memory_tags (bd);
    #endif
    #ifdef CONFIG_CMDLINE_TAG
        setup_commandline_tag (bd, commandline);
    #endif
    #ifdef CONFIG_INITRD_TAG
        if (images->rd_start && images->rd_end)
            setup_initrd_tag (bd, images->rd_start, images->rd_end);
    #endif
        setup_end_tag(bd);
    #endif
        announce_and_cleanup();
    #ifdef CONFIG_ENABLE_MMU
        theLastJump((void *)virt_to_phys(kernel_entry), machid, bd->bi_boot_params);
    #else
        kernel_entry(0, machid, bd->bi_boot_params);
        /* does not return */
    #endif
        return 1;
    }

    kernel_entry(0, machid, r2) 

    真正将控制权交给内核, 启动内核;

    满足arm架构linux内核启动时的寄存器设置条件:第一个参数为0 ;第二个参数为板子id需与内核中的id匹配,第三个参数为启动参数地址bi_boot_params 。

    (1)首先取出环境变量bootargs,这就是要传递给内核的参数。

    (2)调用setup_XXX_tag

    static void setup_start_tag (bd_t *bd)
    {
           //将tags的首地址也就是bi_boot_params传给kernel
            params = (struct tag *) bd->bi_boot_params;
            params->hdr.tag = ATAG_CORE;
            params->hdr.size = tag_size (tag_core);
            params->u.core.flags = 0;
            params->u.core.pagesize = 0;
            params->u.core.rootdev = 0;
            params = tag_next (params);
    }
    params是一个用来存储要传给kernel的参数的静态全局变量。

     u-boot 是通过标记列表向内核传递参数,标记在源代码中定义为tag,是一个结构体,在 arch/arm/include/asm/setup.h 中定义。

    struct tag {                                                                                                                                                              
            struct tag_header hdr;
            union {
                    struct tag_core         core;
                    struct tag_mem32        mem;
                    struct tag_videotext    videotext;
                    struct tag_ramdisk      ramdisk;
                    struct tag_initrd       initrd;
                    struct tag_serialnr     serialnr;
                    struct tag_revision     revision;
                    struct tag_videolfb     videolfb;
                    struct tag_cmdline      cmdline;
                    /*
                     * Acorn specific
                     */
                    struct tag_acorn        acorn;
                    /*
                     * DC21285 specific
                     */
                    struct tag_memclk       memclk;
            } u;

    tag包括hdr和各种类型的tag_*,hdr来标志当前的tag是哪种类型的tag。setup_start_tag是初始化了第一个tag,是tag_core类型的tag。最后调用tag_next跳到第一个tag末尾,为下一个tag做准备。

    tag_next是一个宏定义,被定义在arch/arm/include/asm/setup.h中

    #define tag_next(t)     ((struct tag *)((u32 *)(t) + (t)->hdr.size))
    struct tag_header {
            u32 size;
            u32 tag;
    };

    最后调用setup_end_tag,将末尾的tag设置为ATAG_NONE,标志tag列表结束。

    static void setup_end_tag (bd_t *bd)                                                                                                                                    
    {
            params->hdr.tag = ATAG_NONE;
            params->hdr.size = 0;
    }
    u-boot将参数以tag数组的形式布局在内存的某一个地址,每个tag代表一种类型的参数,首尾tag标志开始和结束,首地址传给kernel供其解析

    通过上面的分析,我们可以尝试自己写一个bootm来引导内核(代码与4412无关,是学6410时的笔记)

    //atag.h
    #define ATAG_CORE    0x54410001
    #define ATAG_MEM    0x54410002
    #define ATAG_CMDLINE    0x54410009
    #define ATAG_NONE    0x00000000
    struct tag_header {
        unsigned int size;
        unsigned int tag;
    };
    struct tag_core {
        unsigned int flags;        
        unsigned int pagesize;
        unsigned int rootdev;
    };
    struct tag_mem32 {
        unsigned int    size;
        unsigned int    start;    
    };
    struct tag_cmdline {
        char    cmdline[1];    
    };
    struct tag {
        struct tag_header hdr;
        union {
            struct tag_core        core;
            struct tag_mem32    mem;
            struct tag_cmdline    cmdline;
        } u;
    };
    #define tag_size(type)    ((sizeof(struct tag_header) + sizeof(struct type)) >> 2)
    #define tag_next(t)    ((struct tag *)((unsigned int *)(t) + (t)->hdr.size))
    //boot.c
    #include "atag.h"
    #include "string.h"
    void (*theKernel)(int , int , unsigned int );
    #define SDRAM_KERNEL_START 0x51000000
    #define SDRAM_TAGS_START   0x50000100
    #define SDRAM_ADDR_START   0x50000000
    #define SDRAM_TOTAL_SIZE   0x16000000
    struct tag *pCurTag;
    const char *cmdline = "console=ttySAC0,115200 init=/init";
    void setup_core_tag()
    {
         pCurTag = (struct tag *)SDRAM_TAGS_START;
         
         pCurTag->hdr.tag = ATAG_CORE;
         pCurTag->hdr.size = tag_size(tag_core); 
         
         pCurTag->u.core.flags = 0;
         pCurTag->u.core.pagesize = 4096;
         pCurTag->u.core.rootdev = 0;
         
         pCurTag = tag_next(pCurTag);
    }
    void setup_mem_tag()
    {
         pCurTag->hdr.tag = ATAG_MEM;
         pCurTag->hdr.size = tag_size(tag_mem32); 
         
         pCurTag->u.mem.start = SDRAM_ADDR_START;
         pCurTag->u.mem.size = SDRAM_TOTAL_SIZE;
         
         pCurTag = tag_next(pCurTag);
    }
    void setup_cmdline_tag()
    {
         int linelen = strlen(cmdline);
         
         pCurTag->hdr.tag = ATAG_CMDLINE;
         pCurTag->hdr.size = (sizeof(struct tag_header)+linelen+1+4)>>2;
         
         strcpy(pCurTag->u.cmdline.cmdline,cmdline);
         
         pCurTag = tag_next(pCurTag);
    }
    void setup_end_tag()
    {
        pCurTag->hdr.tag = ATAG_NONE;
        pCurTag->hdr.size = 0;
    }
    void boot_linux(){
        
        //1.获取Linux启动地址
        theKernel = (void (*)(int , int , unsigned int ))SDRAM_KERNEL_START;
        printf("huo qu linux qi dong di zhi");
        //2.设置启动参数
        //2.1.设置核心启动参数
        setup_core_tag();
        //2.2.设置内存参数
        setup_mem_tag();
        //2.3.设置命令行参数
        setup_cmdline_tag();
        //2.4.设置结束标志
        setup_end_tag();
        
        //4.启动Linux内核
        theKernel(0,1626,SDRAM_TAGS_START);
        printf("qi dong linux nei he");
        
        }
  • 相关阅读:
    用 Python 带你看各国 GDP 变迁
    Fluent Interface(流式接口)
    probing privatePath如何作用于ASP.NET MVC View
    Word插入htm文件导致文本域动态增加的一个问题
    Visual Studio 2013附加进程调试IE加载的ActiveX Control无效解决方法
    Ubuntu下Chrome运行Silverlight程序
    Windows Phone Bing lock screen doesn't change解决方法
    SPClaimsUtility.AuthenticateFormsUser的证书验证问题
    Web Service Client使用Microsoft WSE 2.0
    Visual Studio 2013安装Update 3启动crash的解决方法
  • 原文地址:https://www.cnblogs.com/CoderTian/p/6006400.html
Copyright © 2011-2022 走看看