Android bootloader—LK的分析之如何解析boot.img【转载】

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

1.        Boot.img结构

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

[cpp] view plaincopyprint?

1.  /* 

2. ** +-----------------+  

3. ** | boot header     | 1 page 

4. ** +-----------------+ 

5. ** | kernel          | n pages   

6. ** +-----------------+ 

7. ** | ramdisk         | m pages   

8. ** +-----------------+ 

9. ** | second stage    | o pages 

10. ** +-----------------+ 

11. ** 

12. ** n = (kernel_size + page_size - 1) / page_size 

13. ** m = (ramdisk_size + page_size - 1) / page_size 

14. ** o = (second_size + page_size - 1) / page_size 

15. **/  

主要分析boot header

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

[cpp] view plaincopyprint?

1. struct boot_img_hdr  

2. {  

3.     unsigned char magic[BOOT_MAGIC_SIZE];  

4.   

5.     unsigned kernel_size;  /* size in bytes */  

6.     unsigned kernel_addr;  /* physical load addr */  

7.   

8.     unsigned ramdisk_size; /* size in bytes */  

9.     unsigned ramdisk_addr; /* physical load addr */  

10.   

11.     unsigned second_size;  /* size in bytes */  

12.     unsigned second_addr;  /* physical load addr */  

13.   

14.     unsigned tags_addr;    /* physical addr for kernel tags */  

15.     unsigned page_size;    /* flash page size we assume */  

16.     unsigned unused[2];    /* future expansion: should be 0 */  

17.   

18.     unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */  

19.   

20.     unsigned char cmdline[BOOT_ARGS_SIZE];  

21.   

22.     unsigned id[8]; /* timestamp / checksum / sha1 / etc */  

23. };  

 

2.        Lk如何解析boot header

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

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

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

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

[cpp] view plaincopyprint?

1. void boot_linux(void *kernel, unsigned *tags,   

2.         const char *cmdline, unsigned machtype,  

3.         void *ramdisk, unsigned ramdisk_size)  

4. {  

5.     unsigned *ptr = tags;  

6.     void (*entry)(unsigned,unsigned,unsigned*) = kernel;  

7.     struct ptable *ptable;  

8.     int cmdline_len = 0;  

9.     int have_cmdline = 0;  

10.     /* CORE */  

11.     *ptr++ = 2;  

12.     *ptr++ = 0x54410001;  

13.     if (ramdisk_size) {  

14.         *ptr++ = 4;  

15.         *ptr++ = 0x54420005;  

16.         *ptr++ = (unsigned)ramdisk;  

17.         *ptr++ = ramdisk_size;  

18.     }  

19.     ptr = target_atag_mem(ptr);  

20.     ……  

21.     if (cmdline && cmdline[0]) {  

22.         cmdline_len = strlen(cmdline);  

23.         have_cmdline = 1;  

24.     }  

25.     ……  

26.     if (cmdline_len > 0) {  

27.         const char *src;  

28.         char *dst;  

29.         unsigned n;  

30.         /* include terminating 0 and round up to a word multiple */  

31.         n = (cmdline_len + 4) & (~3);  

32.         *ptr++ = (n / 4) + 2;  

33.         *ptr++ = 0x54410009;  

34.         dst = (char *)ptr;  

35.         if (have_cmdline) {  

36.             src = cmdline;  

37.             while ((*dst++ = *src++));  

38.         }  

39.         ……  

40.         ptr += (n / 4);  

41.     }  

42.     /* END */  

43.     *ptr++ = 0;  

44.     *ptr++ = 0;  

45.     ……  

46.     entry(0, machtype, tags);  

47. }  

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

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

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

[cpp] view plaincopyprint?

1. /* TODO: create/pass atags to kernel */  

2.     /*开始给内核传递atags参数,start boot_linux*/  

3. dprintf(INFO, "\nBooting Linux\n");  

4. boot_linux((void *)hdr->kernel_addr, (void *)TAGS_ADDR,             (const char *)cmdline, board_machtype(),  

5.            (void *)hdr->ramdisk_addr, hdr->ramdisk_size);   

         此处重点分析hdr指针

[cpp] view plaincopyprint?

1. int boot_linux_from_flash(void)  

2. {  

3.         struct boot_img_hdr *hdr = (void*) buf;  

4.         unsigned n;  

5.         struct ptentry *ptn;  

6.         struct ptable *ptable;  

7.         unsigned offset = 0;  

8.         struct fbcon_config *fb_display = NULL;   

9.         char* data;  

10. ……  

11. }  

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

[cpp] view plaincopyprint?

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

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

[cpp] view plaincopyprint?

1. ptable = flash_get_ptable();  

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

[cpp] view plaincopyprint?

1. struct ptable *flash_get_ptable(void)  

2. {  

3.     return flash_ptable;  

4. }  


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

[cpp] view plaincopyprint?

1. static struct ptable flash_ptable;  

2.   

3. static struct ptentry board_part_list[] = {  

4.     {  

5.         .start = 0,  

6.         .length = 10, /* 10MB */  

7.         .name = "boot",  

8.     },  

9.     {  

10.         .start = 10,  

11.         .length = 5, /* 5MB */  

12.         .name = "kpanic",  

13.     },  

14.     {  

15.         .start = 15,  

16.         .length = 150, /* 150MB */  

17.         .name = "system",  

18.     },  

19.     {  

20.         .start = 165,  

21.         .length = 4, /* 4MB */  

22.         .name = "splash",  

23.     },  

24.     {  

25.         .start = 169,  

26.         .length = 40, /* 40MB */  

27.         .name = "cache",  

28.     },  

29.     {  

30.         .start = 209,  

31.         .length = VARIABLE_LENGTH,  

32.         .name = "userdata",  

33.     },  

34.     {  

35.         .start = DIFF_START_ADDR,  

36.         .length = 10, /* 10MB */  

37.         .name = "recovery",  

38.     },  

39.     {  

40.         .start = DIFF_START_ADDR,  

41.         .length = 1, /* 1MB */  

42.         .name = "misc",  

43.     },  

44.    {  

45.        .start = DIFF_START_ADDR,  

46.        .length = 1, /* 1MB */  

47.        .name = "tcc",  

48.     }  

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

[cpp] view plaincopyprint?

1. // initialize the target  

2.   

3. dprintf(SPEW, "initializing target\n");  

4.   

5. 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),符合执行初始化的条件,则会通过执行:

[cpp] view plaincopyprint?

1. ptable_init(&flash_ptable);  

2. for( i = 0; i < num_parts; i++ )  

3. {  

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

5. }  

6. flash_set_ptable(&flash_ptable);  

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

[cpp] view plaincopyprint?

1. ptable = flash_get_ptable();/*就拥有了MTD的分区信息了*/  

2. ptn = ptable_find(ptable, "boot");  

3.     if (flash_read(ptn, offset, buf, page_size)) {  

4.         dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");  

5.         return -1;  

6.     }  

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

[cpp] view plaincopyprint?

1. struct tag {  

2.     struct tag_header hdr;  

3.     union {  

4.         struct tag_core     core;  

5.         struct tag_mem32    mem;  

6.         struct tag_videotext    videotext;  

7.         struct tag_ramdisk  ramdisk;  

8.         struct tag_initrd   initrd;  

9.         struct tag_serialnr serialnr;  

10.         struct tag_revision revision;  

11.         struct tag_videolfb videolfb;  

12.         struct tag_cmdline  cmdline;  

13.         struct tag_acorn    acorn;  

14.         struct tag_memclk   memclk;  

15.     } u;  

16. };  

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

[cpp] view plaincopyprint?

1. void __init setup_arch(char **cmdline_p)  

2. {  

3.     ......  

4.     mdesc = setup_machine(machine_arch_type);  

5.     ......  

6.    

7.     if (__atags_pointer)  

8.         tags = phys_to_virt(__atags_pointer);  

9.     else if (mdesc->boot_params)  

10.         tags = phys_to_virt(mdesc->boot_params);  

11.       

12.     ......  

13.       

14.     if (tags->hdr.tag == ATAG_CORE) {  

15.         if (meminfo.nr_banks != 0)  

16.             squash_mem_tags(tags);  

17.         save_atags(tags);  

18.         parse_tags(tags);  

19.     }  

20.       

21.     ......  

22. }  


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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值