1.内核中对boot loader描述


(源码位于kernel中的Documentation/arm/booting)

4. Setup boot data
------------------

Existing boot loaders:        OPTIONAL, HIGHLY RECOMMENDED
New boot loaders:        MANDATORY

The boot loader must provide either a tagged list or a dtb image for
passing configuration data to the kernel.  The physical address of the
boot data is passed to the kernel in register r2.

4a. Setup the kernel tagged list
--------------------------------

The boot loader must create and initialise the kernel tagged list.
A valid tagged list starts with ATAG_CORE and ends with ATAG_NONE.
The ATAG_CORE tag may or may not be empty.  An empty ATAG_CORE tag
has the size field set to '2' (0x00000002).  The ATAG_NONE must set
the size field to zero.

Any number of tags can be placed in the list.  It is undefined
whether a repeated tag appends to the information carried by the
previous tag, or whether it replaces the information in its
entirety; some tags behave as the former, others the latter.

The boot loader must pass at a minimum the size and location of
the system memory, and root filesystem location(bootloader必须传递一个系统内存的位置和最小值,以及根文件系统位置).  
Therefore, the minimum tagged list should look:
+-----------+ base -> | ATAG_CORE | | +-----------+ | | ATAG_MEM | | increasing address +-----------+ | | ATAG_NONE | | +-----------+ v The tagged list should be stored in system RAM. The tagged list must be placed in a region of memory where neither the kernel decompressor nor initrd 'bootp' program will overwrite it. The recommended placement is in the first 16KiB of RAM.

  linux2.4x以后的内核都期望以标记列表(tagged list)的形式来传递启动参数,标记是一种数据结构;标记列表就是挨着存放的多个标记。标记列表以标记ATAG_CORE开始,以标记ATAG_NONE结束。uboot会给Linux Kernel传递很多参数,如:串口,RAM,videofb、MAC地址等。而Linux kernel也会读取和处理这些参数。两者之间通过struct tag来传递参数uboot把要传递给kernel的东西保存在struct tag数据结构中,启动kernel时,把这个结构体的物理地址传给kernel;Linux kernel通过这个地址,用parse_tags分析出传递过来的参数。

  标记的数据结构为tag,它由一个tag_header结构和一个联合(union)组成Tag_header结构表示标记的类型及长度。对于不同类型的标记使用不同的联合,比如表示内存时使用tag_mem32,表示命令行时使用tag_cmdline。

数据结构tag和tag_header定义在linux(uboot)源码的arch/arm/include/asm/setup.h, uboot的定义是从内核中拷贝过来的,要和内核一致的。

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; //command line字符串标签,我们平时设置的启动参数cmdline字符串(uboot中为bootargs环境变量)就放在这个标签中。 /* * Acorn specific */ struct tag_acorn acorn; /* * DC21285 specific */ struct tag_memclk memclk; } u; };

struct tag_header {
 u32 size; //标签的大小,包括header本身
 u32 tag;  //tag的类型
};

 

2.参数从uboot到特定的内存地址

2.1 使用uboot来启动一个Linux内核,通常情况下我们会按照如下步骤执行:

  •  设置内核启动的command line,也就是设置uboot的环境变量“bootargs”(非必须,如果你要传递给内核cmdline才要设置)
  •  加载内核映像文到内存指定位置(从SD卡、u盘、网络或flash),linux内核一定要加载到内存的。
  •  使用“bootm (内核映像基址)”命令来启动内核

  而uboot将参数按照协议处理好并放入指定内存地址的过程就发生在“bootm”命令中。

/*******************************************************************/
/* bootm - boot application image from image in memory */
/*******************************************************************/

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    ulong        iflag;
    ulong        load_end = 0;
    int        ret;
    boot_os_fn    *boot_fn;

#ifdef CONFIG_SECURE_BOOT
#ifndef CONFIG_SECURE_BL1_ONLY
    security_check();
#endif
#endif

#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\n",
                load_addr);
    } else {
        addr = simple_strtoul(argv[1], NULL, 16);
    }

    if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) {
        u32 val;
        printf("Boot with zImage\n");

        //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_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\n");
            } else {
                puts ("ERROR: new format image overwritten - "
                    "must RESET the board to recover\n");
                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\n",
            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 ("\n## Control returned to monitor - resetting...\n");
#endif
    do_reset (cmdtp, flag, argc, argv);

    return 1;
}
do_bootm()源码

相关文章: