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
このヘッダ情報には、構造体boot_によってカーネルが起動するパラメータ情報が含まれています.img_HDr定義
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()関数は主にカーネルブートパラメータの処理過程を実現した.
1) void (*entry)(unsigned,unsigned,unsigned*) = kernel;
ここではカーネルエントリ関数entry()を定義し、kernelアドレスを関数ポインタに渡します.
2)boot_linux_from_Flash()関数で呼び出されたboot_linux()を参照するには、次の手順に従います.
ここではhdrポインタを重点的に分析します.
hdrはbufポインタから渡され、bufは
つまり、これはバッファです.では、このバッファはいつ充填されたのでしょうか.そして、このbufバッファがbootに格納されていると初歩的に推測しています.imgのheaer情報.コードを見続けます:
この関数呼び出し/lk/platform/tcc_shared/nand.cの中の
flash_に戻るptable、これはグローバル変数であり、lk/target/initを定義し、実装する.cでは、起動時に/lk/kernel/init/mainを実行する.cのtarget_Init()関数flash_ptable()MTDのパーティション情報をflash_にコピーptable構造体.具体的には、次のようになります.
lk/target/tcc 8900_evmのinit.cにはtargetが入っていますInit()関数です.この関数はkmain()を実行するときに実行されます.
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)であり、初期化の実行条件に合致する場合は、次のように実行されます.
まずMTDのパーティション情報をinitにコピーする.c下のflash_ptable構造体、flashを呼び出すset_ptable(&flash_ptable)上のflash_ptableはnandに伝達する.cのflash_ptable構造体は、その後、aboot.cの中の
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)の構造を列挙した.
この構造体は/arch/arm/include/asm/setupで定義.h中.その後、システムが起動すると、/init/mainが実行されます.cのstart_kernel()関数のsetup_Arch(char**cmdline_p)関数:
カーネルパラメータtagsが__であることは、上のコードからわかります.atags_pointerまたはmdesc->boot_params.グローバル変数_atags_pointerはarch/arm/kernel/head-common.Sにはentry(0,machtype,tags)のtagsが割り当てられている.
/*
** +-----------------+
** | 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が割り当てられている.