Análisis del proceso del kernel de arranque IMX6ULL-uboot (4)

uboot versión 2016.03

Comando Bootz cargando el análisis del proceso del kernel de Linux

El comando bootz siempre estará acompañado de una variable global images durante el proceso de inicio del kernel.images es una estructura en forma de bootm_headers_t . La definición de la estructura está en el archivo /include/image.h:

typedef struct bootm_headers {
    /*
     * Legacy os image header, if it is a multi component image
     * then boot_get_ramdisk() and get_fdt() will attempt to get
     * data from second and third component accordingly.
     */
    image_header_t    *legacy_hdr_os;        /* image header pointer */
    image_header_t    legacy_hdr_os_copy;    /* header copy */
    ulong        legacy_hdr_valid;

#if defined(CONFIG_FIT)
    const char    *fit_uname_cfg;    /* configuration node unit name */

    void        *fit_hdr_os;    /* os FIT image header */
    const char    *fit_uname_os;    /* os subimage node unit name */
    int        fit_noffset_os;    /* os subimage node offset */

    void        *fit_hdr_rd;    /* init ramdisk FIT image header */
    const char    *fit_uname_rd;    /* init ramdisk subimage node unit name */
    int        fit_noffset_rd;    /* init ramdisk subimage node offset */

    void        *fit_hdr_fdt;    /* FDT blob FIT image header */
    const char    *fit_uname_fdt;    /* FDT blob subimage node unit name */
    int        fit_noffset_fdt;/* FDT blob subimage node offset */

    void        *fit_hdr_setup;    /* x86 setup FIT image header */
    const char    *fit_uname_setup; /* x86 setup subimage node name */
    int        fit_noffset_setup;/* x86 setup subimage node offset */
#endif

#ifndef USE_HOSTCC
    image_info_t    os;        /* os image info */
    ulong        ep;        /* entry point of OS */

    ulong        rd_start, rd_end;/* ramdisk start/end */

    char        *ft_addr;    /* flat dev tree address */
    ulong        ft_len;        /* length of flat device tree */

    ulong        initrd_start;
    ulong        initrd_end;
    ulong        cmdline_start;
    ulong        cmdline_end;
    bd_t        *kbd;
#endif

    int        verify;        /* getenv("verify")[0] != 'n' */

#define    BOOTM_STATE_START    (0x00000001)
#define    BOOTM_STATE_FINDOS    (0x00000002)
#define    BOOTM_STATE_FINDOTHER    (0x00000004)
#define    BOOTM_STATE_LOADOS    (0x00000008)
#define    BOOTM_STATE_RAMDISK    (0x00000010)
#define    BOOTM_STATE_FDT        (0x00000020)
#define    BOOTM_STATE_OS_CMDLINE    (0x00000040)
#define    BOOTM_STATE_OS_BD_T    (0x00000080)
#define    BOOTM_STATE_OS_PREP    (0x00000100)
#define    BOOTM_STATE_OS_FAKE_GO    (0x00000200)    /* 'Almost' run the OS */
#define    BOOTM_STATE_OS_GO    (0x00000400)
    int        state;

#ifdef CONFIG_LMB
    struct lmb    lmb;        /* for memory mgmt */
#endif
} bootm_headers_t;

La línea 32 define una variable miembro os de tipo image_info_t , que describe la información de imagen del sistema a través de esta variable. Se define de la siguiente manera:

typedef struct image_info {
    ulong start, end; /* blob 开始和结束位置*/
    ulong image_start, image_len; /* 镜像起始地址(包括 blob)和长度 */
    ulong load; /* 系统镜像加载地址*/
    uint8_t comp, type, os; /* 镜像压缩、类型, OS 类型 */
    uint8_t arch; /* CPU 架构 */
} image_info_t;

Líneas 49~59, la definición de macro se usa para describir las diferentes etapas de uboot al iniciar el kernel.

función do_bootz

La función a la que llama el comando bootz es la función do_bootz, que se define en /cmd/bootm.c, de la siguiente manera:

int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
    int ret;

    /* Consume 'bootz' */
    argc--; argv++;

    if (bootz_start(cmdtp, flag, argc, argv, &images))
        return 1;

    /*
     * We are doing the BOOTM_STATE_LOADOS state ourselves, so must
     * disable interrupts ourselves
     */
    bootm_disable_interrupts();

    images.os.os = IH_OS_LINUX;
    ret = do_bootm_states(cmdtp, flag, argc, argv,
                  BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
                  BOOTM_STATE_OS_GO,
                  &images, 1);

    return ret;
}

La línea 6 equivale a eliminar el comando de arranque que pasa los parámetros del comando.

En la línea 8, se llama a la función bootz_start .

En la línea 15, se llama a la función bootm_disable_interrupts para desactivar la interrupción.

En la línea 17, el tipo de kernel que se iniciará se establece en LINUX.

En la línea 18, se llama a la función do_bootm_states para ejecutar diferentes etapas de arranque.

función bootz_start

/*
 * zImage booting support
 */
static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc,
            char * const argv[], bootm_headers_t *images)
{
    int ret;
    ulong zi_start, zi_end;

    ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START,
                  images, 1);

    /* Setup Linux kernel zImage entry point */
    if (!argc) {
        images->ep = load_addr;
        debug("*  kernel: default image load address = 0x%08lx\n",
                load_addr);
    } else {
        images->ep = simple_strtoul(argv[0], NULL, 16);
        debug("*  kernel: cmdline image address = 0x%08lx\n",
            images->ep);
    }

    ret = bootz_setup(images->ep, &zi_start, &zi_end);
    if (ret != 0)
        return 1;

    lmb_reserve(&images->lmb, images->ep, zi_end - zi_start);

    /*
     * Handle the BOOTM_STATE_FINDOTHER state ourselves as we do not
     * have a header that provide this informaiton.
     */
    if (bootm_find_images(flag, argc, argv))
        return 1;

#ifdef CONFIG_SECURE_BOOT
    extern uint32_t authenticate_image(
            uint32_t ddr_start, uint32_t image_size);
    if (authenticate_image(images->ep, zi_end - zi_start) == 0) {
        printf("Authenticate zImage Fail, Please check\n");
        return 1;
    }
#endif
    return 0;
}

El rol de la función bootz_start es completar el trabajo de soporte para que uboot inicie el archivo de imagen zImage.

En la línea 10, llame a la función do_bootm_states para completar la etapa BOOTM_STATE_START .

Líneas 18-22, configure la variable miembro ep de imágenes, que es la dirección de entrada de la imagen del sistema, e imprima la dirección.

La línea 24 llama a la función bootz_setup , que es relativamente simple.En esta función , se obtiene el encabezado del archivo del tipo zimage_header del archivo de imagen , y se juzga si el archivo de imagen actual es una imagen ARM LINUX. Y guarde la dirección de entrada y la dirección final del archivo de imagen en las variables zi_start y zi_end respectivamente .

En la línea 34, se llama a la función bootm_find_images y se configuran images->ftaddr (la dirección inicial del árbol de dispositivos) e images->ft_len (la longitud del árbol de dispositivos) .

función do_bootm_states

int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
            int states, bootm_headers_t *images, int boot_progress)
{
    boot_os_fn *boot_fn;
    ulong iflag = 0;
    int ret = 0, need_boot_fn;

    images->state |= states;

    /*
     * Work through the states and see how far we get. We stop on
     * any error.
     */
    if (states & BOOTM_STATE_START)
        ret = bootm_start(cmdtp, flag, argc, argv);


    /*****省略部分代码*****/


    /* From now on, we need the OS boot function */
    if (ret)
        return ret;
    boot_fn = bootm_os_get_boot_func(images->os.os);
    need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
            BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
            BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
    if (boot_fn == NULL && need_boot_fn) {
        if (iflag)
            enable_interrupts();
        printf("ERROR: booting os '%s' (%d) is not supported\n",
               genimg_get_os_name(images->os.os), images->os.os);
        bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);
        return 1;
    }

    if (!ret && (states & BOOTM_STATE_OS_PREP))
        ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);

#ifdef CONFIG_TRACE
    /* Pretend to run the OS, then run a user command */
    if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {
        char *cmd_list = getenv("fakegocmd");

        ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,
                images, boot_fn);
        if (!ret && cmd_list)
            ret = run_command_list(cmd_list, -1, flag);
    }
#endif

    /* Check for unsupported subcommand. */
    if (ret) {
        puts("subcommand not supported\n");
        return ret;
    }

    /* Now run the OS! We hope this doesn't return */
    if (!ret && (states & BOOTM_STATE_OS_GO))
        ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
                images, boot_fn);

    /* Deal with any fallout */
err:
    if (iflag)
        enable_interrupts();

    if (ret == BOOTM_ERR_UNIMPLEMENTED)
        bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL);
    else if (ret == BOOTM_ERR_RESET)
        do_reset(cmdtp, flag, argc, argv);

    return ret;
}

La función do_bootm_states implementará el código de las diferentes etapas del kernel de arranque de uboot de acuerdo con los estados de los parámetros.

La línea 14 maneja la fase BOOTM_STATE_START . En esta etapa, se llama a la función bootm_start, se borra la memoria de imágenes y se establece la variable de miembro images.verify , y images.state se establece en BOOTM_STATE_START .

Línea 24, a través de la función bootm_os_get_boot_func para encontrar la función de inicio del sistema, el parámetro imágenes->os.os es el IH_OS_LINUX establecido en la función do_bootz. Hasta ahora, el puntero de función de boot_fn apunta a la función de inicio do_bootm_linux de LINUX.

Línea 37, llame a la función do_bootm_linux para procesar el estado BOOTM_STATE_OS_PREP, la función do_bootm_linux es la siguiente:

int do_bootm_linux(int flag, int argc, char * const argv[],
           bootm_headers_t *images)
{
    /* No need for those on ARM */
    if (flag & BOOTM_STATE_OS_BD_T || flag & BOOTM_STATE_OS_CMDLINE)
        return -1;

    if (flag & BOOTM_STATE_OS_PREP) {
        boot_prep_linux(images);
        return 0;
    }

    if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
        boot_jump_linux(images, flag);
        return 0;
    }

    boot_prep_linux(images);
    boot_jump_linux(images, flag);
    return 0;
}

Llame a boot_prep_linux en esta función para manejar el estado BOOTM_STATE_OS_PREP. En la función boot_prep_linux, se procesa principalmente la variable de entorno bootargs utilizada por uboot para iniciar Linux.

La línea 42 es para procesar el estado BOOTM_STATE_OS_FAKE_GO, que no se ejecutará porque no se ha definido ninguna macro.

La línea 59 es para procesar el estado BOOTM_STATE_OS_GO, es decir, iniciar el núcleo, llamar a la función boot_selected_os -> llamar a la función boot_fn(do_bootm_linux) -> función boot_jump_linux, la función se define de la siguiente manera:

static void boot_jump_linux(bootm_headers_t *images, int flag)
{
    /*省略64位代码*/

    unsigned long machid = gd->bd->bi_arch_number;
    char *s;
    void (*kernel_entry)(int zero, int arch, uint params);
    unsigned long r2;
    int fake = (flag & BOOTM_STATE_OS_FAKE_GO);

    kernel_entry = (void (*)(int, int, uint))images->ep;

    s = getenv("machid");
    if (s) {
        if (strict_strtoul(s, 16, &machid) < 0) {
            debug("strict_strtoul failed!\n");
            return;
        }
        printf("Using machid 0x%lx from environment\n", machid);
    }

    debug("## Transferring control to Linux (at address %08lx)" \
        "...\n", (ulong) kernel_entry);
    bootstage_mark(BOOTSTAGE_ID_RUN_OS);
    announce_and_cleanup(fake);

    if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
        r2 = (unsigned long)images->ft_addr;
    else
        r2 = gd->bd->bi_boot_params;

    if (!fake) {
#ifdef CONFIG_ARMV7_NONSEC
        if (armv7_boot_nonsec()) {
            armv7_init_nonsec();
            secure_ram_addr(_do_nonsec_entry)(kernel_entry,
                              0, machid, r2);
        } else
#endif
            kernel_entry(0, machid, r2);
    }
#endif
}

En la línea 5, se define la variable macid.Si no se usa el árbol de dispositivos, esta variable uboot se pasará al kernel de Linux, y el kernel verificará si la máquina es compatible con la tabla de ID de máquina. Esta variable no tiene efecto si se utiliza el árbol de dispositivos.

La línea 7 define un puntero de función kernel_entry, que tiene 3 parámetros, el primer parámetro es 0, el segundo parámetro es la ID de la máquina y el tercer parámetro es la primera dirección de ATAGS o árbol de dispositivos (DTB).

La línea 11 obtiene la función kernel_entry, que está definida por el kernel de Linux y es la primera línea de código en la imagen de Linux.

En la línea 27, si se utiliza el árbol de dispositivos, el valor del registro r2 se asigna como la primera dirección del árbol de dispositivos.

La línea 30 no se aplica al árbol de dispositivos y r2 es la primera dirección del parámetro bootargs.

La última línea 40 llama a la función kernel_entry para iniciar el kernel de Linux.

¡Hasta ahora, todo el trabajo de uboot se ha completado!

Supongo que te gusta

Origin blog.csdn.net/qq_42174306/article/details/128857389
Recomendado
Clasificación