您的位置:首页 > 其它

uboot 命令分析(一) — bootm

2016-10-11 09:09 956 查看

bootm 用于将内核镜像加载到内存的指定地址处,如果有需要还要解压镜像,然后根据操作系统和体系结构的不同给内核传递不同的启动参数,最后启动内核。

一、arm
架构处理器对
Linux 内核启动之前环境的五点需求


1、cpu 寄存器设置

    * R0 = 0

    * R1 = 板级 id

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

2、cpu 模式

    * 禁止所有中断

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

3、缓存、MMU

    * 关闭 MMU

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

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

4、设备

    * DMA 设备应当停止工作

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

这些需求都由 boot loader 实现,在常用的 uboot 中完成一系列的初始化后最后通过 bootm 命令加载 linux 内核。该命令用法介绍如下:

[cpp]
view plain
copy
print?

# help bootm  
bootm - boot application image from memory  
  
Usage:  
bootm [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  
  
Sub-commands to do part of the bootm sequence.  The sub-commands must be  
issued in the order below (it's ok to not issue all sub-commands):  
        start [addr [arg ...]]  
        loados  - load OS image  
        cmdline - OS specific command line processing/setup  
        bdt     - OS specific bd_t processing  
        prep    - OS specific prep before relocation or go  
        go      - start OS  



# help bootm
bootm - boot application image from memory

Usage:
bootm [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

Sub-commands to do part of the bootm sequence.  The sub-commands must be
issued in the order below (it's ok to not issue all sub-commands):
start [addr [arg ...]]
loados  - load OS image
cmdline - OS specific command line processing/setup
bdt     - OS specific bd_t processing
prep    - OS specific prep before relocation or go
go      - start OS
二、基础数据结构

在 bootm 中常用的数据结构
image_info_t 和 bootm_headers_t,定义如下:

[cpp]
view plain
copy
print?

/* 镜像信息 */  
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;  
  
/* bootm 头 */  
typedef struct bootm_headers {  
    image_header_t  *legacy_hdr_os;     /* 指向镜像头的指针 */  
    image_header_t  legacy_hdr_os_copy; /* 镜像头的备份 */  
    ulong           legacy_hdr_valid;   /* 镜像头存在标记 */  
  
    image_info_t    os;                 /* 系统镜像信息 */  
    ulong           ep;                 /* 系统入口地址 */  
    ulong           rd_start, rd_end;   /* 虚拟磁盘起始地址 */  
    ulong           ft_len;             /* 平坦设备树的长度 */  
    ulong           initrd_start;  
    ulong           initrd_end;  
    ulong           cmdline_start;  
    ulong           cmdline_end;  
    bd_t            *kbd;  
    int             verify;             /* getenv("verify")[0] != 'n' */  
  
#define BOOTM_STATE_START       (0x00000001)  
#define BOOTM_STATE_LOADOS      (0x00000002)  
#define BOOTM_STATE_RAMDISK     (0x00000004)  
#define BOOTM_STATE_FDT         (0x00000008)  
#define BOOTM_STATE_OS_CMDLINE  (0x00000010)  
#define BOOTM_STATE_OS_BD_T     (0x00000020)  
#define BOOTM_STATE_OS_PREP     (0x00000040)  
#define BOOTM_STATE_OS_GO       (0x00000080)  
    int             state;              /* 状态标记 */  
  
    struct lmb  lmb;                    /* 逻辑内存块 */  
} bootm_headers_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;

/* bootm 头 */
typedef struct bootm_headers {
image_header_t  *legacy_hdr_os;		/* 指向镜像头的指针 */
image_header_t  legacy_hdr_os_copy;	/* 镜像头的备份 */
ulong           legacy_hdr_valid;   /* 镜像头存在标记 */

image_info_t    os;                 /* 系统镜像信息 */
ulong           ep;		            /* 系统入口地址 */
ulong           rd_start, rd_end;   /* 虚拟磁盘起始地址 */
ulong           ft_len;             /* 平坦设备树的长度 */
ulong           initrd_start;
ulong           initrd_end;
ulong           cmdline_start;
ulong           cmdline_end;
bd_t            *kbd;
int             verify;             /* getenv("verify")[0] != 'n' */

#define BOOTM_STATE_START       (0x00000001)
#define BOOTM_STATE_LOADOS      (0x00000002)
#define BOOTM_STATE_RAMDISK     (0x00000004)
#define BOOTM_STATE_FDT         (0x00000008)
#define BOOTM_STATE_OS_CMDLINE  (0x00000010)
#define BOOTM_STATE_OS_BD_T     (0x00000020)
#define BOOTM_STATE_OS_PREP     (0x00000040)
#define BOOTM_STATE_OS_GO       (0x00000080)
int             state;              /* 状态标记 */

struct lmb	lmb;		            /* 逻辑内存块 */
} bootm_headers_t;
三、代码解析
bootm 的主函数为 do_bootm,代码如下:

[cpp]
view plain
copy
print?

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])  
{  
    ulong  iflag;  
    ulong  load_end = 0;  
    int    ret;  
    boot_os_fn  *boot_fn;  
  
    if (bootm_start(cmdtp, flag, argc, argv))     /* 获取镜像信息 */  
        return 1;  
  
    iflag = disable_interrupts();     /* 关闭中断 */  
    usb_stop();                       /* 关闭usb设备 */  
  
    ret = bootm_load_os(images.os, &load_end, 1);  /* 加载内核 */  
  
    lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));  
  
    if (images.os.os == IH_OS_LINUX)  /* 如果有需要关闭内核的串口 */  
        fixup_silent_linux();  
  
    boot_fn = boot_os[images.os.os];  /* 获取启动函数 */  
  
    arch_preboot_os();                /* 启动前准备 */  
  
    boot_fn(0, argc, argv, &images);  /* 启动,不再返回 */  
  
    puts ("\n## Control returned to monitor - resetting...\n");  
    do_reset (cmdtp, flag, argc, argv);  
  
    return 1;  
}  



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

if (bootm_start(cmdtp, flag, argc, argv))     /* 获取镜像信息 */
return 1;

iflag = disable_interrupts();     /* 关闭中断 */
usb_stop();                       /* 关闭usb设备 */

ret = bootm_load_os(images.os, &load_end, 1);  /* 加载内核 */

lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));

if (images.os.os == IH_OS_LINUX)  /* 如果有需要关闭内核的串口 */
fixup_silent_linux();

boot_fn = boot_os[images.os.os];  /* 获取启动函数 */

arch_preboot_os();                /* 启动前准备 */

boot_fn(0, argc, argv, &images);  /* 启动,不再返回 */

puts ("\n## Control returned to monitor - resetting...\n");
do_reset (cmdtp, flag, argc, argv);

return 1;
}
该函数的实现分为 3 个部分:首先通过 bootm_start 函数分析镜像的信息,如果满足判定条件则进入 bootm_load_os 函数进行加载,加载完成后就可以调用 boot_fn 开始启动。
1、bootm_start

[cpp]
view plain
copy
print?

static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])  
{  
    ulong       mem_start;  
    phys_size_t mem_size;  
    void        *os_hdr;  
    int     ret;  
  
    memset ((void *)&images, 0, sizeof (images));  
    images.verify = getenv_yesno ("verify");  /* 获取环境变量 */  
  
    lmb_init(&images.lmb);  
  
    mem_start = getenv_bootm_low();  
    mem_size = getenv_bootm_size();  
  
    lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size);  
  
    arch_lmb_reserve(&images.lmb);  
    board_lmb_reserve(&images.lmb);  
  
    /* 获取镜像头,加载地址,长度 */  
    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!\n");  
        return 1;  
    }  
  
    /* 获取镜像参数 */  
    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;  
    default:  
        puts ("ERROR: unknown image format type!\n");  
        return 1;  
    }  
  
    /* 查询内核入口地址 */  
    if (images.legacy_hdr_valid) {  
        images.ep = image_get_ep (&images.legacy_hdr_os_copy);  
    } else {  
        puts ("Could not find kernel entry point!\n");  
        return 1;  
    }  
  
    if (images.os.os == IH_OS_LINUX) {  
        /* 查询是否存在虚拟磁盘 */  
        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\n");  
            return 1;  
        }  
    }  
    images.os.start = (ulong)os_hdr;  /* 赋值加载地址 */  
    images.state = BOOTM_STATE_START; /* 更新状态 */  
  
    return 0;  
}  



static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong		mem_start;
phys_size_t	mem_size;
void		*os_hdr;
int		ret;

memset ((void *)&images, 0, sizeof (images));
images.verify = getenv_yesno ("verify");  /* 获取环境变量 */

lmb_init(&images.lmb);

mem_start = getenv_bootm_low();
mem_size = getenv_bootm_size();

lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size);

arch_lmb_reserve(&images.lmb);
board_lmb_reserve(&images.lmb);

/* 获取镜像头,加载地址,长度 */
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!\n");
return 1;
}

/* 获取镜像参数 */
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;
default:
puts ("ERROR: unknown image format type!\n");
return 1;
}

/* 查询内核入口地址 */
if (images.legacy_hdr_valid) {
images.ep = image_get_ep (&images.legacy_hdr_os_copy);
} else {
puts ("Could not find kernel entry point!\n");
return 1;
}

if (images.os.os == IH_OS_LINUX) {
/* 查询是否存在虚拟磁盘 */
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\n");
return 1;
}
}
images.os.start = (ulong)os_hdr;  /* 赋值加载地址 */
images.state = BOOTM_STATE_START; /* 更新状态 */

return 0;
}
该函数主要进行镜像的有效性判定、校验、计算入口地址等操作,大部分工作通过 boot_get_kernel -> image_get_kernel 完成,代码如下:

[cpp]
view plain
copy
print?

static image_header_t *image_get_kernel (ulong img_addr, int verify)  
{  
    image_header_t *hdr = (image_header_t *)img_addr;  
  
    if (!image_check_magic(hdr)) {   /* 魔数比较 */  
        puts ("Bad Magic Number\n");  
        show_boot_progress (-1);  
        return NULL;  
    }  
    show_boot_progress (2);  
  
    if (!image_check_hcrc (hdr)) {   /* 镜像头校验和 */  
        puts ("Bad Header Checksum\n");  
        show_boot_progress (-2);  
        return NULL;  
    }  
  
    show_boot_progress (3);  
    image_print_contents (hdr);      /* 打印镜像头信息 */  
  
    if (verify) {                    /* 校验镜像 */  
        puts ("   Verifying Checksum ... ");  
        if (!image_check_dcrc (hdr)) {  
            printf ("Bad Data CRC\n");  
            show_boot_progress (-3);  
            return NULL;  
        }  
        puts ("OK\n");  
    }  
    show_boot_progress (4);  
  
    if (!image_check_target_arch (hdr)) {   /* 处理器类型比较 */  
        printf ("Unsupported Architecture 0x%x\n", image_get_arch (hdr));  
        show_boot_progress (-4);  
        return NULL;  
    }  
    return hdr;  
}  
  
static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],  
        bootm_headers_t *images, ulong *os_data, ulong *os_len)  
{  
    ulong  img_addr;  
    image_header_t  *hdr;  
  
    if (argc < 2) {  /* 如果没有参数则用默认加载地址 */  
        img_addr = load_addr;  
        debug ("*  kernel: default image load address = 0x%08lx\n", load_addr);  
    } else {  
        img_addr = simple_strtoul(argv[1], NULL, 16);  
        debug ("*  kernel: cmdline image address = 0x%08lx\n", img_addr);  
    }  
  
    *os_data = *os_len = 0;  
    switch (genimg_get_format ((void *)img_addr)) {  
    case IMAGE_FORMAT_LEGACY:  
        printf ("## Booting kernel from Legacy Image at %08lx ...\n", img_addr);  
        hdr = image_get_kernel (img_addr, images->verify);  /* 获取内核 */  
        if (!hdr)  
            return NULL;  
        show_boot_progress (5);  
  
        /* get os_data and os_len */  
        switch (image_get_type (hdr)) {  
        case IH_TYPE_KERNEL:  
            *os_data = image_get_data (hdr);       /* 内核入口地址 */  
            *os_len = image_get_data_size (hdr);   /* 内核长度(不包括头部) */  
            break;  
        default:  
            printf ("Wrong Image Type for %s command\n", cmdtp->name);  
            show_boot_progress (-5);  
            return NULL;  
        }  
  
        /* 备份镜像头以防止在内核解压时被覆盖 */  
        memmove (&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t));  
  
        images->legacy_hdr_os = hdr;  
        images->legacy_hdr_valid = 1;  /* 标记存在入口点 */  
        break;  
    default:  
        printf ("Wrong Image Format for %s command\n", cmdtp->name);  
        show_boot_progress (-108);  
        return NULL;  
    }  
  
    debug ("   kernel data at 0x%08lx, len = 0x%08lx (%ld)\n", *os_data, *os_len, *os_len);  
  
    return (void *)img_addr;  /* 返回加载地址 */  
}  



static image_header_t *image_get_kernel (ulong img_addr, int verify)
{
image_header_t *hdr = (image_header_t *)img_addr;

if (!image_check_magic(hdr)) {   /* 魔数比较 */
puts ("Bad Magic Number\n");
show_boot_progress (-1);
return NULL;
}
show_boot_progress (2);

if (!image_check_hcrc (hdr)) {   /* 镜像头校验和 */
puts ("Bad Header Checksum\n");
show_boot_progress (-2);
return NULL;
}

show_boot_progress (3);
image_print_contents (hdr);      /* 打印镜像头信息 */

if (verify) {                    /* 校验镜像 */
puts ("   Verifying Checksum ... ");
if (!image_check_dcrc (hdr)) {
printf ("Bad Data CRC\n");
show_boot_progress (-3);
return NULL;
}
puts ("OK\n");
}
show_boot_progress (4);

if (!image_check_target_arch (hdr)) {   /* 处理器类型比较 */
printf ("Unsupported Architecture 0x%x\n", image_get_arch (hdr));
show_boot_progress (-4);
return NULL;
}
return hdr;
}

static void *boot_get_kernel (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
bootm_headers_t *images, ulong *os_data, ulong *os_len)
{
ulong  img_addr;
image_header_t  *hdr;

if (argc < 2) {  /* 如果没有参数则用默认加载地址 */
img_addr = load_addr;
debug ("*  kernel: default image load address = 0x%08lx\n",	load_addr);
} else {
img_addr = simple_strtoul(argv[1], NULL, 16);
debug ("*  kernel: cmdline image address = 0x%08lx\n", img_addr);
}

*os_data = *os_len = 0;
switch (genimg_get_format ((void *)img_addr)) {
case IMAGE_FORMAT_LEGACY:
printf ("## Booting kernel from Legacy Image at %08lx ...\n", img_addr);
hdr = image_get_kernel (img_addr, images->verify);  /* 获取内核 */
if (!hdr)
return NULL;
show_boot_progress (5);

/* get os_data and os_len */
switch (image_get_type (hdr)) {
case IH_TYPE_KERNEL:
*os_data = image_get_data (hdr);       /* 内核入口地址 */
*os_len = image_get_data_size (hdr);   /* 内核长度(不包括头部) */
break;
default:
printf ("Wrong Image Type for %s command\n", cmdtp->name);
show_boot_progress (-5);
return NULL;
}

/* 备份镜像头以防止在内核解压时被覆盖 */
memmove (&images->legacy_hdr_os_copy, hdr, sizeof(image_header_t));

images->legacy_hdr_os = hdr;
images->legacy_hdr_valid = 1;  /* 标记存在入口点 */
break;
default:
printf ("Wrong Image Format for %s command\n", cmdtp->name);
show_boot_progress (-108);
return NULL;
}

debug ("   kernel data at 0x%08lx, len = 0x%08lx (%ld)\n", *os_data, *os_len, *os_len);

return (void *)img_addr;  /* 返回加载地址 */
}

在有效性判定完成时会打印出镜像的一些信息,代码如下:

[cpp]
view plain
copy
print?

void image_print_contents (const void *ptr)  
{  
    const image_header_t *hdr = (const image_header_t *)ptr;  
    const char *p;  
  
    p = "   ";  
    printf ("%sImage Name:   %.*s\n", p, IH_NMLEN, image_get_name (hdr));  /* 镜像名称 */  
#if defined(CONFIG_TIMESTAMP) || defined(CONFIG_CMD_DATE) || defined(USE_HOSTCC)  
    printf ("%sCreated:      ", p);  /* 创建时间 */  
    genimg_print_time ((time_t)image_get_time (hdr));  
#endif  
    printf ("%sImage Type:   ", p);  /* 镜像类型 */  
    image_print_type (hdr);  
    printf ("%sData Size:    ", p);  /* 镜像大小 */  
    genimg_print_size (image_get_data_size (hdr));  
    printf ("%sLoad Address: %08x\n", p, image_get_load (hdr));  /* 加载地址 */  
    printf ("%sEntry Point:  %08x\n", p, image_get_ep (hdr));    /* 入口地址 */  
}  



void image_print_contents (const void *ptr)
{
const image_header_t *hdr = (const image_header_t *)ptr;
const char *p;

p = "   ";
printf ("%sImage Name:   %.*s\n", p, IH_NMLEN, image_get_name (hdr));  /* 镜像名称 */
#if defined(CONFIG_TIMESTAMP) || defined(CONFIG_CMD_DATE) || defined(USE_HOSTCC)
printf ("%sCreated:      ", p);  /* 创建时间 */
genimg_print_time ((time_t)image_get_time (hdr));
#endif
printf ("%sImage Type:   ", p);  /* 镜像类型 */
image_print_type (hdr);
printf ("%sData Size:    ", p);  /* 镜像大小 */
genimg_print_size (image_get_data_size (hdr));
printf ("%sLoad Address: %08x\n", p, image_get_load (hdr));  /* 加载地址 */
printf ("%sEntry Point:  %08x\n", p, image_get_ep (hdr));    /* 入口地址 */
}

2、bootm_load_os

这个函数主要判段镜像是否需要解压,并且将镜像移动到加载地址:

[cpp]
view plain
copy
print?

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;   /* 镜像最大长度 */  
  
    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);  
  
            if (load != image_start) {  
                memmove_wd ((void *)load, (void *)image_start, image_len, CHUNKSZ);  
            }  
        }  
        *load_end = load + image_len;  
        puts("OK\n");  
        break;  
    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\n");  
            return BOOTM_ERR_RESET;  
        }  
  
        *load_end = load + image_len;  
        break;  
    ...  
    default:  
        printf ("Unimplemented compression type %d\n", comp);  
        return BOOTM_ERR_UNIMPLEMENTED;  
    }  
    puts ("OK\n");  
    debug ("   kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);  
  
    if ((load < blob_end) && (*load_end > blob_start)) {  
        debug ("images.os.start = 0x%lX, images.os.end = 0x%lx\n", blob_start, blob_end);  
        debug ("images.os.load = 0x%lx, load_end = 0x%lx\n", load, *load_end);  
        return BOOTM_ERR_OVERLAP;  
    }  
  
    return 0;  
}  



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;   /* 镜像最大长度 */

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);

if (load != image_start) {
memmove_wd ((void *)load, (void *)image_start, image_len, CHUNKSZ);
}
}
*load_end = load + image_len;
puts("OK\n");
break;
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\n");
return BOOTM_ERR_RESET;
}

*load_end = load + image_len;
break;
...
default:
printf ("Unimplemented compression type %d\n", comp);
return BOOTM_ERR_UNIMPLEMENTED;
}
puts ("OK\n");
debug ("   kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);

if ((load < blob_end) && (*load_end > blob_start)) {
debug ("images.os.start = 0x%lX, images.os.end = 0x%lx\n", blob_start, blob_end);
debug ("images.os.load = 0x%lx, load_end = 0x%lx\n", load, *load_end);
return BOOTM_ERR_OVERLAP;
}

return 0;
}
3、do_bootm_linux

在 arm linux 平台中 boot_fn 函数指针指向的函数是位于 lib_arm/bootm.c 的 do_bootm_linux,这是内核启动前最后的一个函数,该函数主要完成启动参数的初始化,并将板子设定为满足内核启动的环境,代码如下:

[cpp]
view plain
copy
print?

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  (*theKernel)(int zero, int arch, uint params);  
  
    char  *commandline = getenv ("bootargs");          /* 从环境变量中获取命令参数 */  
  
    if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))    /* 状态判定 */  
        return 1;  
  
    theKernel = (void (*)(int, int, uint))images->ep;  /* 内核入口函数 */  
  
    s = getenv ("machid");  /* 从环境变量中获取机器id */  
    if (s) {  
        machid = simple_strtoul (s, NULL, 16);  
        printf ("Using machid 0x%x from environment\n", machid);  
    }  
  
    debug ("## Transferring control to Linux (at address %08lx) ...\n", (ulong) theKernel);  
  
    /* 初始化启动参数 */  
    setup_start_tag (bd);                     /* 初始化参数列表起始符 */  
    setup_serial_tag (¶ms);               /* 初始化串口参数 */  
    setup_revision_tag (¶ms);             /* 初始化版本参数 */  
    setup_memory_tags (bd);                   /* 初始化内存参数 */  
    setup_commandline_tag (bd, commandline);  /* 初始化命令参数 */  
    if (images->rd_start && images->rd_end)  
        setup_initrd_tag (bd, images->rd_start, images->rd_end);  /* 初始化虚拟磁盘参数 */  
    setup_videolfb_tag ((gd_t *) gd);         /* 初始化fb参数 */  
    setup_end_tag (bd);                       /* 初始化参数列表结束符 */  
  
    printf ("\nStarting kernel ...\n\n");  
  
    cleanup_before_linux ();  /* 启动前清空缓存 */  
  
    /* 启动内核,满足arm架构linux内核启动时的寄存器设置条件:第一个参数为0 
       第二个参数为板子id需与内核中的id匹配,第三个参数为启动参数地址 */  
    theKernel (0, machid, bd->bi_boot_params);     /* does not return */  
  
    return 1;  
}  



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  (*theKernel)(int zero, int arch, uint params);

char  *commandline = getenv ("bootargs");          /* 从环境变量中获取命令参数 */

if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))    /* 状态判定 */
return 1;

theKernel = (void (*)(int, int, uint))images->ep;  /* 内核入口函数 */

s = getenv ("machid");  /* 从环境变量中获取机器id */
if (s) {
machid = simple_strtoul (s, NULL, 16);
printf ("Using machid 0x%x from environment\n", machid);
}

debug ("## Transferring control to Linux (at address %08lx) ...\n", (ulong) theKernel);

/* 初始化启动参数 */
setup_start_tag (bd);                     /* 初始化参数列表起始符 */
setup_serial_tag (¶ms);               /* 初始化串口参数 */
setup_revision_tag (¶ms);             /* 初始化版本参数 */
setup_memory_tags (bd);                   /* 初始化内存参数 */
setup_commandline_tag (bd, commandline);  /* 初始化命令参数 */
if (images->rd_start && images->rd_end)
setup_initrd_tag (bd, images->rd_start, images->rd_end);  /* 初始化虚拟磁盘参数 */
setup_videolfb_tag ((gd_t *) gd);         /* 初始化fb参数 */
setup_end_tag (bd);                       /* 初始化参数列表结束符 */

printf ("\nStarting kernel ...\n\n");

cleanup_before_linux ();  /* 启动前清空缓存 */

/* 启动内核,满足arm架构linux内核启动时的寄存器设置条件:第一个参数为0
第二个参数为板子id需与内核中的id匹配,第三个参数为启动参数地址 */
theKernel (0, machid, bd->bi_boot_params);     /* does not return */

return 1;
}

这里还列举部分启动参数的初始化函数:

[cpp]
view plain
copy
print?

static void setup_start_tag (bd_t *bd)  
{  
    params = (struct tag *) bd->bi_boot_params; /* 启动参数保存在板子指定的内存空间 CONFIG_ATAG_ADDR */  
  
    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指向下一个结构 */  
}  
  
static void setup_memory_tags (bd_t *bd)  
{  
    int i;  
  
    for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) {  
        params->hdr.tag = ATAG_MEM;  /* 内存参数的标签 */  
        params->hdr.size = tag_size (tag_mem32);  
  
        params->u.mem.start = bd->bi_dram[i].start;  /* 物理内存起始地址 */  
        params->u.mem.size = bd->bi_dram[i].size;    /* 物理内存结束地址 */  
  
        params = tag_next (params);  
    }  
}  
  
static void setup_commandline_tag (bd_t *bd, char *commandline)  
{  
    char *p;  
  
    if (!commandline)  
        return;  
  
    for (p = commandline; *p == ' '; p++);  /* 定位到第一个有效字符 */  
  
    if (*p == '\0')                         /* 没有有效字符则返回 */  
        return;  
  
    params->hdr.tag = ATAG_CMDLINE;         /* 命令参数的标签 */  
    params->hdr.size = (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2;  /* 整个标签的长度 */  
  
    strcpy (params->u.cmdline.cmdline, p);  /* 将命令参数拷贝到标签,结束符为'\0' */  
  
    params = tag_next (params);  
}  
  
static void setup_end_tag (bd_t *bd)  
{  
    params->hdr.tag = ATAG_NONE;  /* 结束标签 */  
    params->hdr.size = 0;  
}  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: