最近刚刚接触Android的系统开发,先从分析bootloader开始,参考的是TCC的Android中的lk包,下面是我的一点成果,晒出来,同时这也是我的第一片博客,请大家多多指教!

  1.        Boot.img结构

android/bootable/bootloader/lk/app/aboot/bootimg.h中可以得知boot.img的结构如下: 

 /*** +-----------------+ ** | boot header     | 1 page** +-----------------+** | kernel          | n pages  ** +-----------------+** | ramdisk         | m pages  ** +-----------------+** | second stage    | o pages** +-----------------+**** n = (kernel_size + page_size - 1) / page_size** m = (ramdisk_size + page_size - 1) / page_size** o = (second_size + page_size - 1) / page_size**/

主要分析boot header

这个头部信息包含了我们的内核启动的参数信息,由结构体boot_img_hdr定义

struct boot_img_hdr{    unsigned char magic[BOOT_MAGIC_SIZE];    unsigned kernel_size;  /* size in bytes */    unsigned kernel_addr;  /* physical load addr */    unsigned ramdisk_size; /* size in bytes */    unsigned ramdisk_addr; /* physical load addr */    unsigned second_size;  /* size in bytes */    unsigned second_addr;  /* physical load addr */    unsigned tags_addr;    /* physical addr for kernel tags */    unsigned page_size;    /* flash page size we assume */    unsigned unused[2];    /* future expansion: should be 0 */    unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */    unsigned char cmdline[BOOT_ARGS_SIZE];    unsigned id[8]; /* timestamp / checksum / sha1 / etc */};


2.        Lk如何解析boot header

          Bootloader的作用是用来引导内核启动的,或者通过按键等控制进入recovery模式。这其中重要的一步就是如何解析boot.img和recovery.img的头部信息,提取这两部分的参数,传递给内核。本部分以TCC8900的Android源码包里面的lk为例,详细说明头部信息的解析过程。

         在这之前先简要描述bootloader运行之后与boot.img和recovery有关的初始化工作。可以参照下面的流程图:

       (由于博客对普通用户暂不支持图片功能,以后会补贴上)

        android/bootable/bootloader/lk/app/aboot/aboot.c中的boot_linux()函数主要实现了内核引导参数参数的处理过程。

void boot_linux(void *kernel, unsigned *tags, const char *cmdline, unsigned machtype,void *ramdisk, unsigned ramdisk_size){unsigned *ptr = tags;void (*entry)(unsigned,unsigned,unsigned*) = kernel;struct ptable *ptable;int cmdline_len = 0;int have_cmdline = 0;/* CORE */*ptr++ = 2;*ptr++ = 0x54410001;if (ramdisk_size) {*ptr++ = 4;*ptr++ = 0x54420005;*ptr++ = (unsigned)ramdisk;*ptr++ = ramdisk_size;}ptr = target_atag_mem(ptr);……if (cmdline && cmdline[0]) {cmdline_len = strlen(cmdline);have_cmdline = 1;}……if (cmdline_len > 0) {const char *src;char *dst;unsigned n;/* include terminating 0 and round up to a word multiple */n = (cmdline_len + 4) & (~3);*ptr++ = (n / 4) + 2;*ptr++ = 0x54410009;dst = (char *)ptr;if (have_cmdline) {src = cmdline;while ((*dst++ = *src++));}……ptr += (n / 4);}/* END */*ptr++ = 0;*ptr++ = 0;……entry(0, machtype, tags);}

1)        void (*entry)(unsigned,unsigned,unsigned*) = kernel;

           此处定义了内核入口函数entry(),将kernel地址传给函数指针。

2)        在boot_linux_from_flash()函数中调用的boot_linux()进行传参:

/* TODO: create/pass atags to kernel *//*开始给内核传递atags参数,start boot_linux*/dprintf(INFO, "\nBooting Linux\n");boot_linux((void *)hdr->kernel_addr, (void *)TAGS_ADDR,   (const char *)cmdline, board_machtype(),   (void *)hdr->ramdisk_addr, hdr->ramdisk_size);

         此处重点分析hdr指针

int boot_linux_from_flash(void){struct boot_img_hdr *hdr = (void*) buf;unsigned n;struct ptentry *ptn;struct ptable *ptable;unsigned offset = 0;struct fbcon_config *fb_display = NULL;char* data;……}

       可以看到hdr是由buf指针传过来的,而buf定义为

static unsigned char buf[16384]; //Equal to max-supported pagesize

       也就是说,这是一段缓冲区,那么这段缓冲区是何时填充的呢,而且初步猜想,这个buf缓冲区存放的就是boot.img的heaer信息。继续看代码:

ptable = flash_get_ptable();

这个函数调用/lk/platform/tcc_shared/nand.c里面的

struct ptable *flash_get_ptable(void){return flash_ptable;}


         返回flash_ptable,这是个全局变量,定义并实现在lk/target/init.c中,通过启动的时候执行/lk/kernel/init/main.c中的target_init(),函数 flash_ptable()将MTD的分区信息拷贝到flash_ptable结构体中。具体实现如下:

static struct ptable flash_ptable;static struct ptentry board_part_list[] = {{.start = 0,.length = 10, /* 10MB */.name = "boot",},{.start = 10,.length = 5, /* 5MB */.name = "kpanic",},{.start = 15,.length = 150, /* 150MB */.name = "system",},{.start = 165,.length = 4, /* 4MB */.name = "splash",},{.start = 169,.length = 40, /* 40MB */.name = "cache",},{.start = 209,.length = VARIABLE_LENGTH,.name = "userdata",},{.start = DIFF_START_ADDR,.length = 10, /* 10MB */.name = "recovery",},{.start = DIFF_START_ADDR,.length = 1, /* 1MB */.name = "misc",},   {       .start = DIFF_START_ADDR,       .length = 1, /* 1MB */       .name = "tcc",}

        在lk/target/tcc8900_evm的init.c里面有一个target_init()函数,这个函数在执行kmain()的时候执行,

// initialize the targetdprintf(SPEW, "initializing target\n");target_init();

       在target_init()函数中会执行:

       if (flash_get_ptable() == NULL) 函数,判断flash_get_ptable()返回的是否是一个空的值,此时去调用/lk/platform/tcc_shared/nand.c里面的struct ptable *flash_get_ptable(void)时,返回的flash_ptable在当前文件下是一个static变量(static struct ptable *flash_ptable = NULL),符合执行初始化的条件,则会通过执行:

ptable_init(&flash_ptable);for( i = 0; i < num_parts; i++ ){ptable_add(&flash_ptable, sPartition_List.parts[i].name, flash_info->offset + sPartition_List.parts[i].start,sPartition_List.parts[i].length, sPartition_List.parts[i].flags);}flash_set_ptable(&flash_ptable);

       先将MTD的分区信息复制给init.c下面的flash_ptable结构体,再调用flash_set_ptable(&flash_ptable)将上面的flash_ptable传递给nand.c中的flash_ptable结构体,那么这以后,aboot.c里面的

ptable = flash_get_ptable();/*就拥有了MTD的分区信息了*/ptn = ptable_find(ptable, "boot");if (flash_read(ptn, offset, buf, page_size)) {dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");return -1;}

      通过flash_read(ptn, offset, buf, page_size)就可以将boot header的相关信息读取到buf缓冲区里面去了。此处的page_size可以自己手动指定,例如可以是2k字节。那么这时,buf里面就是boot.img的头部信息,数据以boot_img_hdr结构封装,那么,就可以使用hdr指针来访问kernel_add、kernel_size等信息了。

3)        处理boot header信息

        获得了boot header信息后,经过boot_linux()函数将boot头部信息提取出来,封装成tag结构体。boot_linux()传入的参数tags的值为TAGS_ADDR,这个值在

android/bootable/bootloader/lk/target/tcc8900_evm/rules.mk中被定义为0x40000100。该地址将会作为参数传给内核入口函数entry(0, machtype, tags)。上面这个函数主要功能就是将boot_img_hdr中的内容一项项填到tag表中。下面列出了tag的结构体,是一TLV(Tag-Length-Value)结构。联合中列举了不同参数的值(Value)的结构。

struct tag {struct tag_header hdr;union {struct tag_corecore;struct tag_mem32mem;struct tag_videotextvideotext;struct tag_ramdiskramdisk;struct tag_initrdinitrd;struct tag_serialnrserialnr;struct tag_revisionrevision;struct tag_videolfbvideolfb;struct tag_cmdlinecmdline;struct tag_acornacorn;struct tag_memclkmemclk;} u;};

       该结构体定义在/arch/arm/include/asm/setup.h中。然后,系统起来之后,会运行/init/main.c中的start_kernel()函数中的setup_arch(char **cmdline_p)函数:

void __init setup_arch(char **cmdline_p){    ......    mdesc = setup_machine(machine_arch_type);...... if (__atags_pointer)tags = phys_to_virt(__atags_pointer);else if (mdesc->boot_params)tags = phys_to_virt(mdesc->boot_params);        ......    if (tags->hdr.tag == ATAG_CORE) {if (meminfo.nr_banks != 0)squash_mem_tags(tags);save_atags(tags);parse_tags(tags);}    ......}


         从上面的代码中可以知道内核参数tags为__atags_pointer或者mdesc->boot_params。而全局变量__atags_pointer在arch/arm/kernel/head-common.S中被赋值为entry(0, machtype, tags)中的tags。

 

更多相关文章

  1. Android深入浅出之Audio 第一部分 AudioTrack分析
  2. android的技术层次
  3. Android(安卓)体系结构介绍
  4. android基础知识15:获得android系统信息04—ActivityManager
  5. android google地图定位开发,且可以自由移动位置重新获取定位,地址
  6. android google地图定位开发,且可以自由移动位置重新获取定位,地址
  7. [转]Android媒体的一些使用总结
  8. Android(安卓)体系结构介绍
  9. android google地图定位开发,且可以自由移动位置重新获取定位,地址

随机推荐

  1. 使三个标签填充容器的宽度
  2. 如何让按钮点击之后才能点击注册,再次点击
  3. 关于HTML(七)--------HTML废弃的标签
  4. 对于表格数据,什么渲染更快,CSS或?
  5. Chrome 50更改隐式表格单元格高度行为
  6. js 隐藏、显示html 标签内容
  7. 经典炫酷的HTML5/jQuery动画应用示例及源
  8. Js 之将html转为图片html2canvas
  9. 用HTML5为你的网页添加音效(兼容Firefox 3
  10. ACCP7.0S1HTML第一章上机练习4