initrd_start地址是如何确定的?

作者 by adtxl / 2022-05-18 / 暂无评论 / 298 个足迹

使用版本:uboot2021,kernel arm64, Android boot.img

1. 问题描述

在bootm_headers结构体中,有一个initrd_start成员变量,本文主要描述该变量的值是如何确定的。

// include/image.h

/*
 * Legacy and FIT format headers used by do_bootm() and do_bootm_<os>()
 * routines.
 */
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 IMAGE_ENABLE_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;
    struct bd_info      *kbd;
#endif

    int     verify;     /* env_get("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;

extern bootm_headers_t images;

2. 问题说明

通过打log,能确定initrd_start的值是在do_bootm_states()函数中确定的,do_bootm()-->do_bootm_states()

// cmd/bootm.c
/*******************************************************************/
/* bootm - boot application image from image in memory */
/*******************************************************************/

int do_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
    static int relocated = 0;

    if (!relocated) {
        int i;

        /* relocate names of sub-command table */
        for (i = 0; i < ARRAY_SIZE(cmd_bootm_sub); i++)
            cmd_bootm_sub[i].name += gd->reloc_off;

        relocated = 1;
    }
#endif

    /* determine if we have a sub command */
    argc--; argv++;
    if (argc > 0) {
        char *endp;

        simple_strtoul(argv[0], &endp, 16);
        /* endp pointing to NULL means that argv[0] 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);
    }

    return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
        BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
        BOOTM_STATE_LOADOS |
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
        BOOTM_STATE_RAMDISK |
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_MIPS)
        BOOTM_STATE_OS_CMDLINE |
#endif
        BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
        BOOTM_STATE_OS_GO, &images, 1);
}

do_bootm_states()根据states进行处理,

/**
 * Execute selected states of the bootm command.
 *
 * Note the arguments to this state must be the first argument, Any 'bootm'
 * or sub-command arguments must have already been taken.
 *
 * Note that if states contains more than one flag it MUST contain
 * BOOTM_STATE_START, since this handles and consumes the command line args.
 *
 * Also note that aside from boot_os_fn functions and bootm_load_os no other
 * functions we store the return value of in 'ret' may use a negative return
 * value, without special handling.
 *
 * @param cmdtp     Pointer to bootm command table entry
 * @param flag      Command flags (CMD_FLAG_...)
 * @param argc      Number of subcommand arguments (0 = no arguments)
 * @param argv      Arguments
 * @param states    Mask containing states to run (BOOTM_STATE_...)
 * @param images    Image header information
 * @param boot_progress 1 to show boot progress, 0 to not do this
 * @return 0 if ok, something else on error. Some errors will cause this
 *  function to perform a reboot! If states contains BOOTM_STATE_OS_GO
 *  then the intent is to boot an OS, so this function will not return
 *  unless the image type is standalone.
 */
int do_bootm_states(struct cmd_tbl *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);

    if (!ret && (states & BOOTM_STATE_FINDOS))
        ret = bootm_find_os(cmdtp, flag, argc, argv);

    if (!ret && (states & BOOTM_STATE_FINDOTHER))
        ret = bootm_find_other(cmdtp, flag, argc, argv);

    /* Load the OS */
    if (!ret && (states & BOOTM_STATE_LOADOS)) {
        iflag = bootm_disable_interrupts();
        ret = bootm_load_os(images, 0);
        if (ret && ret != BOOTM_ERR_OVERLAP)
            goto err;
        else if (ret == BOOTM_ERR_OVERLAP)
            ret = 0;
    }

    /* Relocate the ramdisk */
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
    if (!ret && (states & BOOTM_STATE_RAMDISK)) {
        ulong rd_len = images->rd_end - images->rd_start;

        ret = boot_ramdisk_high(&images->lmb, images->rd_start,
            rd_len, &images->initrd_start, &images->initrd_end);
        if (!ret) {
            env_set_hex("initrd_start", images->initrd_start);
            env_set_hex("initrd_end", images->initrd_end);
        }
    }
#endif
#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB)
    if (!ret && (states & BOOTM_STATE_FDT)) {
        boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);
        ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,
                    &images->ft_len);
    }
#endif

    /* 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;
    }


    /* Call various other states that are not generally used */
    if (!ret && (states & BOOTM_STATE_OS_CMDLINE))
        ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
    if (!ret && (states & BOOTM_STATE_OS_BD_T))
        ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
    if (!ret && (states & BOOTM_STATE_OS_PREP)) {
#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY)
        if (images->os.os == IH_OS_LINUX)
            fixup_silent_linux();
#endif
        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 = env_get("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;
}

其中,initrd_start的值由上面代码中的boot_ramdisk_high()函数确定。

    /* Relocate the ramdisk */
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
    if (!ret && (states & BOOTM_STATE_RAMDISK)) {
        ulong rd_len = images->rd_end - images->rd_start;

        ret = boot_ramdisk_high(&images->lmb, images->rd_start,
            rd_len, &images->initrd_start, &images->initrd_end);
        if (!ret) {
            env_set_hex("initrd_start", images->initrd_start);
            env_set_hex("initrd_end", images->initrd_end);
        }
    }
#endif

CONFIG_SYS_BOOT_RAMDISK_HIGH宏有在arch/arm/include/asm/config.h中定义,所以下面的判断会走到。
从注释看,boot_ramdisk_high()函数的作用是重定位 init ramdisk的位置,传入的参数有

  • lmb类型的指针
  • ramdisk的开始地址,以及大小,这两个值应该是在前面的函数中,通过处理已经可以获得的,通过log发现,这两个值如下
the images->rd_start is e1156800.
the images->rd_end is e129370b.
  • 然后就是initrd_start,initrd_end这两个指针,这两个变量会最终确定ramdisk在ram中relocate的位置。

打印initrd_high变量的值,为initrd_high=0xffffffffffffffff,所以,下面的判断,会使initrd_copy_to_ram的值最终为0.

// common/image.c
#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH
/**
 * boot_ramdisk_high - relocate init ramdisk
 * @lmb: pointer to lmb handle, will be used for memory mgmt
 * @rd_data: ramdisk data start address
 * @rd_len: ramdisk data length
 * @initrd_start: pointer to a ulong variable, will hold final init ramdisk
 *      start address (after possible relocation)
 * @initrd_end: pointer to a ulong variable, will hold final init ramdisk
 *      end address (after possible relocation)
 *
 * boot_ramdisk_high() takes a relocation hint from "initrd_high" environment
 * variable and if requested ramdisk data is moved to a specified location.
 *
 * Initrd_start and initrd_end are set to final (after relocation) ramdisk
 * start/end addresses if ramdisk image start and len were provided,
 * otherwise set initrd_start and initrd_end set to zeros.
 *
 * returns:
 *      0 - success
 *     -1 - failure
 */
int boot_ramdisk_high(struct lmb *lmb, ulong rd_data, ulong rd_len,
          ulong *initrd_start, ulong *initrd_end)
{
    char    *s;
    ulong   initrd_high;
    int initrd_copy_to_ram = 1;

    s = env_get("initrd_high");
    if (s) {
        /* a value of "no" or a similar string will act like 0,
         * turning the "load high" feature off. This is intentional.
         */
        initrd_high = simple_strtoul(s, NULL, 16);
        if (initrd_high == ~0)
            initrd_copy_to_ram = 0;
    } else {
        initrd_high = env_get_bootm_mapsize() + env_get_bootm_low();
    }


    debug("## initrd_high = 0x%08lx, copy_to_ram = %d\n",
            initrd_high, initrd_copy_to_ram);

    if (rd_data) {
        if (!initrd_copy_to_ram) {  /* zero-copy ramdisk support */
            debug("   in-place initrd\n");
            /* 在我们的开发平台,判断会走到这里,所以这个initrd_start的值就是rd_data的值 */
            *initrd_start = rd_data;
            *initrd_end = rd_data + rd_len;
            lmb_reserve(lmb, rd_data, rd_len);
        } else {
            if (initrd_high)
                *initrd_start = (ulong)lmb_alloc_base(lmb,
                        rd_len, 0x1000, initrd_high);
            else
                *initrd_start = (ulong)lmb_alloc(lmb, rd_len,
                                 0x1000);

            if (*initrd_start == 0) {
                puts("ramdisk - allocation error\n");
                goto error;
            }
            bootstage_mark(BOOTSTAGE_ID_COPY_RAMDISK);

            *initrd_end = *initrd_start + rd_len;
            printf("   Loading Ramdisk to %08lx, end %08lx ... ",
                    *initrd_start, *initrd_end);

            memmove_wd((void *)*initrd_start,
                    (void *)rd_data, rd_len, CHUNKSZ);

#ifdef CONFIG_MP
            /*
             * Ensure the image is flushed to memory to handle
             * AMP boot scenarios in which we might not be
             * HW cache coherent
             */
            flush_cache((unsigned long)*initrd_start,
                    ALIGN(rd_len, ARCH_DMA_MINALIGN));
#endif
            puts("OK\n");
        }
    } else {
        *initrd_start = 0;
        *initrd_end = 0;
    }
    debug("   ramdisk load start = 0x%08lx, ramdisk load end = 0x%08lx\n",
            *initrd_start, *initrd_end);

    return 0;

error:
    return -1;
}
#endif /* CONFIG_SYS_BOOT_RAMDISK_HIGH */

所以,还是看下rd_start的值是怎么确定的,流程如下:

do_bootm()-->do_bootm_states()-->bootm_find_other()-->bootm_find_images()-->boot_get_ramdisk()-->android_image_get_ramdisk()

boot_get_ramdisk()函数中,

/**
 * boot_get_ramdisk - main ramdisk handling routine
 * @argc: command argument count
 * @argv: command argument list
 * @images: pointer to the bootm images structure
 * @arch: expected ramdisk architecture
 * @rd_start: pointer to a ulong variable, will hold ramdisk start address
 * @rd_end: pointer to a ulong variable, will hold ramdisk end
 *
 * boot_get_ramdisk() is responsible for finding a valid ramdisk image.
 * Curently supported are the following ramdisk sources:
 *      - multicomponent kernel/ramdisk image,
 *      - commandline provided address of decicated ramdisk image.
 *
 * returns:
 *     0, if ramdisk image was found and valid, or skiped
 *     rd_start and rd_end are set to ramdisk start/end addresses if
 *     ramdisk image is found and valid
 *
 *     1, if ramdisk image is found but corrupted, or invalid
 *     rd_start and rd_end are set to 0 if no ramdisk exists
 */
int boot_get_ramdisk(int argc, char *const argv[], bootm_headers_t *images,
             uint8_t arch, ulong *rd_start, ulong *rd_end)
{
    ulong rd_addr, rd_load;
    ulong rd_data, rd_len;
#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
    const image_header_t *rd_hdr;
#endif
    void *buf;
#ifdef CONFIG_SUPPORT_RAW_INITRD
    char *end;
#endif
#if IMAGE_ENABLE_FIT
    const char  *fit_uname_config = images->fit_uname_cfg;
    const char  *fit_uname_ramdisk = NULL;
    ulong       default_addr;
    int     rd_noffset;
#endif
    const char *select = NULL;

    *rd_start = 0;
    *rd_end = 0;

#ifdef CONFIG_ANDROID_BOOT_IMAGE
    /*
     * Look for an Android boot image.
     */
    buf = map_sysmem(images->os.start, 0);
    if (buf && genimg_get_format(buf) == IMAGE_FORMAT_ANDROID)
        select = (argc == 0) ? env_get("loadaddr") : argv[0];
#endif

    if (argc >= 2)
        select = argv[1];

    /*
     * Look for a '-' which indicates to ignore the
     * ramdisk argument
     */
    if (select && strcmp(select, "-") ==  0) {
        debug("## Skipping init Ramdisk\n");
        rd_len = rd_data = 0;
    } else if (select || genimg_has_config(images)) {
#if IMAGE_ENABLE_FIT
        if (select) {
            /*
             * If the init ramdisk comes from the FIT image and
             * the FIT image address is omitted in the command
             * line argument, try to use os FIT image address or
             * default load address.
             */
            if (images->fit_uname_os)
                default_addr = (ulong)images->fit_hdr_os;
            else
                default_addr = image_load_addr;

            if (fit_parse_conf(select, default_addr,
                       &rd_addr, &fit_uname_config)) {
                debug("*  ramdisk: config '%s' from image at "
                        "0x%08lx\n",
                        fit_uname_config, rd_addr);
            } else if (fit_parse_subimage(select, default_addr,
                        &rd_addr, &fit_uname_ramdisk)) {
                debug("*  ramdisk: subimage '%s' from image at "
                        "0x%08lx\n",
                        fit_uname_ramdisk, rd_addr);
            } else
#endif
            {
                rd_addr = simple_strtoul(select, NULL, 16);
                debug("*  ramdisk: cmdline image address = "
                        "0x%08lx\n",
                        rd_addr);
            }
#if IMAGE_ENABLE_FIT
        } else {
            /* use FIT configuration provided in first bootm
             * command argument. If the property is not defined,
             * quit silently.
             */
            rd_addr = map_to_sysmem(images->fit_hdr_os);
            rd_noffset = fit_get_node_from_config(images,
                    FIT_RAMDISK_PROP, rd_addr);
            if (rd_noffset == -ENOENT)
                return 0;
            else if (rd_noffset < 0)
                return 1;
        }
#endif

        /*
         * Check if there is an initrd image at the
         * address provided in the second bootm argument
         * check image type, for FIT images get FIT node.
         */
        buf = map_sysmem(rd_addr, 0);
        switch (genimg_get_format(buf)) {
#if CONFIG_IS_ENABLED(LEGACY_IMAGE_FORMAT)
        case IMAGE_FORMAT_LEGACY:
            printf("## Loading init Ramdisk from Legacy "
                    "Image at %08lx ...\n", rd_addr);

            bootstage_mark(BOOTSTAGE_ID_CHECK_RAMDISK);
            rd_hdr = image_get_ramdisk(rd_addr, arch,
                            images->verify);

            if (rd_hdr == NULL)
                return 1;

            rd_data = image_get_data(rd_hdr);
            rd_len = image_get_data_size(rd_hdr);
            rd_load = image_get_load(rd_hdr);
            break;
#endif
#if IMAGE_ENABLE_FIT
        case IMAGE_FORMAT_FIT:
            rd_noffset = fit_image_load(images,
                    rd_addr, &fit_uname_ramdisk,
                    &fit_uname_config, arch,
                    IH_TYPE_RAMDISK,
                    BOOTSTAGE_ID_FIT_RD_START,
                    FIT_LOAD_OPTIONAL_NON_ZERO,
                    &rd_data, &rd_len);
            if (rd_noffset < 0)
                return 1;

            images->fit_hdr_rd = map_sysmem(rd_addr, 0);
            images->fit_uname_rd = fit_uname_ramdisk;
            images->fit_noffset_rd = rd_noffset;
            break;
#endif
#ifdef CONFIG_ANDROID_BOOT_IMAGE
        case IMAGE_FORMAT_ANDROID:
            android_image_get_ramdisk((void *)images->os.start,
                &rd_data, &rd_len);
            break;
#endif
        default:
#ifdef CONFIG_SUPPORT_RAW_INITRD
            end = NULL;
            if (select)
                end = strchr(select, ':');
            if (end) {
                rd_len = simple_strtoul(++end, NULL, 16);
                rd_data = rd_addr;
            } else
#endif
            {
                puts("Wrong Ramdisk Image Format\n");
                rd_data = rd_len = rd_load = 0;
                return 1;
            }
        }
    } else if (images->legacy_hdr_valid &&
            image_check_type(&images->legacy_hdr_os_copy,
                        IH_TYPE_MULTI)) {

        /*
         * Now check if we have a legacy mult-component image,
         * get second entry data start address and len.
         */
        bootstage_mark(BOOTSTAGE_ID_RAMDISK);
        printf("## Loading init Ramdisk from multi component "
                "Legacy Image at %08lx ...\n",
                (ulong)images->legacy_hdr_os);

        image_multi_getimg(images->legacy_hdr_os, 1, &rd_data, &rd_len);
    } else {
        /*
         * no initrd image
         */
        bootstage_mark(BOOTSTAGE_ID_NO_RAMDISK);
        rd_len = rd_data = 0;
    }

    if (!rd_data) {
        debug("## No init Ramdisk\n");
    } else {
        *rd_start = rd_data;
        *rd_end = rd_data + rd_len;
    }
    debug("   ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n",
            *rd_start, *rd_end);

    return 0;
}

android_image_get_ramdisk()函数:

// common/image-android.c
int android_image_get_ramdisk(const struct andr_img_hdr *hdr,
                  ulong *rd_data, ulong *rd_len)
{
    if (!hdr->ramdisk_size) {
        *rd_data = *rd_len = 0;
        return -1;
    }

    printf("RAM disk load addr 0x%08x size %u KiB\n",
           hdr->ramdisk_addr, DIV_ROUND_UP(hdr->ramdisk_size, 1024));

    *rd_data = (unsigned long)hdr;  // 先将hdr的值赋给ra_data,即boot.img在ram中的地址
    *rd_data += hdr->page_size; // boot.img中boot_header占一个页面
    *rd_data += ALIGN(hdr->kernel_size, hdr->page_size);    // kernel的大小,并按page_size对齐

    *rd_len = hdr->ramdisk_size;    // kernel后面,即ramdisk在ram中的地址
    return 0;
}

所以,在我们的开发上,initrd_start应该只和boot.img在ram中的地址,以及kernel size的大小有关。和mkbootimage没有关系,也不是由编译参数来确定的

独特见解