Android bootloader-LKの分析はbootをどのように解析するか.img

8121 ワード

最近Androidのシステム开発に触れたばかりで、まずbootloaderの分析から始めます.参考はTCCのAndroidのlkバッグです.以下は私の成果です.
  •        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のヘッダ情報は,この2つの部分のパラメータを抽出し,カーネルに伝達する.このセクションでは、TCC 8900の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, "
    Booting Linux
    "); 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/tcc 8900_evmのinit.cにはtargetが入っていますInit()関数です.この関数はkmain()を実行するときに実行されます.
    // initialize the target
    
    dprintf(SPEW, "initializing target
    "); 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
    "); return -1; }

    Flash_経由read(ptn,offset,buf,page_size)はbootヘッダに関する情報をbufバッファに読み込むことができます.ここのpage_sizeは、手動で指定できます.たとえば、2 kバイトです.では、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では0 x 400000100と定義されている.このアドレスは、カーネルエントリ関数entry(0,machtype,tags)にパラメータとして渡されます.上の関数の主な機能はboot_img_hdrの内容の1つをtagテーブルに記入します.以下にtagの構造体を示し、TLV(Tag-Longth-Value)構造である.連合では,異なるパラメータの値(Value)の構造を列挙した.
    struct tag {
    	struct tag_header hdr;
    	union {
    		struct tag_core		core;
    		struct tag_mem32	mem;
    		struct tag_videotext	videotext;
    		struct tag_ramdisk	ramdisk;
    		struct tag_initrd	initrd;
    		struct tag_serialnr	serialnr;
    		struct tag_revision	revision;
    		struct tag_videolfb	videolfb;
    		struct tag_cmdline	cmdline;
    		struct tag_acorn	acorn;
    		struct tag_memclk	memclk;
    	} 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が割り当てられている.