今天,我們來看一下boot.img的生成,作用,和修改方法。
1.首先是build/core/main.mk
.PHONY: bootimage
bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
如果我們把out/target/product/crespo/boot.img去掉,再運行make bootimage, 那麼會重新生成boot.img。然後我們看build/core/Makefile裏
INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
tmp_dir_for_image := $(call intermediates-dir-for,EXECUTABLES,boot_img)/bootimg
INTERNAL_BOOTIMAGE_ARGS += --tmpdir $(tmp_dir_for_image)
INTERNAL_BOOTIMAGE_ARGS += --genext2fs $(MKEXT2IMG)
$(INSTALLED_BOOTIMAGE_TARGET): $(MKEXT2IMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: $@")
$(hide) $(MKEXT2BOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
else # TARGET_BOOTIMAGE_USE_EXT2 != true
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: $@")
$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)
endif # TARGET_BOOTIMAGE_USE_EXT2
可知INSTALLED_BOOTIMAGE_TARGET的對象就是out/target/product/crespo/boot.img, 有可能用2種方式生成,一種是ext2格式,另外一種則不是。根據添加調試打印,crespo採用的不是ext2文件格式,因此我們來看一下$(MKBOOTIMG)。
$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
$(call pretty,"Target boot image: $@")
$(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
$(hide) $(call assert-max-image-size,$@,$(BOARD_BOOTIMAGE_PARTITION_SIZE),raw)
展開來就是
out/target/product/crespo/boot.img:out/host/linux-x86/bin/mkbootimg out/target/product/crespo/kernel out/target/product/crespo/ramdisk.img
$(call pretty, "Target boot image: $@")
$(hide) --kernel out/target/product/crespo/kernel --ramdisk out/target/product/crespo/ramdisk.img --cmdline "console=ttyFIQ0 no_console_suspend" --base 0x30000000 --pagesize 4096 --output $@
其中各變量的含義分別爲:
INSTALLED_BOOTIMAGE_TARGET:out/target/product/crespo/boot.img
MKBOOTIMG:out/host/linux-x86/bin/mkbootimg
INTERNAL_BOOTIMAGE_FILES:out/target/product/crespo/kernel out/target/product/crespo/ramdisk.img
INTERNAL_BOOTIMAGE_ARGS: --kernel out/target/product/crespo/kernel --ramdisk out/target/product/crespo/ramdisk.img --cmdline "console=ttyFIQ0 no_console_suspend" --base 0x30000000 --pagesize 4096
BOARD_BOOTIMAGE_PARTITION_SIZE:
因此,其實boot.img是由兩個文件out/target/product/crespo/kernel和out/target/product/crespo/ramdisk.img兩個文件組成的。
2.我們接着看mkbootimg這個工具
這個工具的源碼爲system/core/mkbootimg/mkbootimg.c, 其include了system/core/mkbootimg/bootimg.h, 我們先來看下bootimg.h的其中一段說明
/*
** +-----------------+
** | 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
**
** 0. all entities are page_size aligned in flash
** 1. kernel and ramdisk are required (size != 0)
** 2. second is optional (second_size == 0 -> no second)
** 3. load each element (kernel, ramdisk, second) at
** the specified physical address (kernel_addr, etc)
** 4. prepare tags at tag_addr. kernel_args[] is
** appended to the kernel commandline in the tags.
** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
** 6. if second_size != 0: jump to second_addr
** else: jump to kernel_addr
*/
顯然boot.img由header,kernel,ramdisk,second stage組成,每個段都要補全爲page_size的整數倍大小。其中header指明瞭包含了什麼呢?看一下以下的代碼(system/core/mkbootimg/bootimg.h):
typedef struct boot_img_hdr boot_img_hdr;
#define BOOT_MAGIC "ANDROID!"
#define BOOT_MAGIC_SIZE 8
#define BOOT_NAME_SIZE 16
#define BOOT_ARGS_SIZE 512
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 */
};
可知header裏包括了各個段的解壓出來後存放的地址,和各個段的大小等信息,那麼這些值又是在哪裏賦值的呢?我們來看一下system/core/mkbootimg/mkbootimg.c裏的main函數片段
int main(int argc, char **argv)
{
......
/* default load addresses */
hdr.kernel_addr = 0x10008000;
hdr.ramdisk_addr = 0x11000000;
hdr.second_addr = 0x10F00000;
hdr.tags_addr = 0x10000100;
while(argc > 0){
char *arg = argv[0];
char *val = argv[1];
if(argc < 2) {
return usage();
}
argc -= 2;
argv += 2;
if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {
bootimg = val;
} else if(!strcmp(arg, "--kernel")) {
kernel_fn = val;
} else if(!strcmp(arg, "--ramdisk")) {
ramdisk_fn = val;
} else if(!strcmp(arg, "--second")) {
second_fn = val;
} else if(!strcmp(arg, "--cmdline")) {
cmdline = val;
} else if(!strcmp(arg, "--base")) {
unsigned base = strtoul(val, 0, 16);
hdr.kernel_addr = base + 0x00008000;
hdr.ramdisk_addr = base + 0x01000000;
hdr.second_addr = base + 0x00F00000;
hdr.tags_addr = base + 0x00000100;
} else if(!strcmp(arg, "--board")) {
board = val;
} else if(!strcmp(arg,"--pagesize")) {
pagesize = strtoul(val, 0, 10);
if ((pagesize != 2048) && (pagesize != 4096)) {
fprintf(stderr,"error: unsupported page size %d\n", pagesize);
return -1;
}
} else {
return usage();
}
}
hdr.page_size = pagesize;
........
}
由上可知,如果有傳參數--base, 那麼 tag:base + 0x100 --> kernel:base +0x80000 -> second:base + 0x00f0_0000 -> ramdisk:base + 0x0100_0000, 如果沒有傳參數,則相當於base=0x1000_0000。結合上面的打印參數,crespo把base_addr設爲0x3000_0000, pagesize設爲4096。所以,我們不妨打開boot.img文件,
看到下面的內容:
0000000: 414e 4452 4f49 4421 602d 3300 0080 0030 ANDROID!`-3....0
0000010: 787f 0200 0000 0031 0000 0000 0000 f030 x......1.......0
0000020: 0001 0030 0010 0000 0000 0000 0000 0000 ...0............
0000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000040: 636f 6e73 6f6c 653d 7474 7946 4951 3020 console=ttyFIQ0
0000050: 6e6f 5f63 6f6e 736f 6c65 5f73 7573 7065 no_console_suspe
0000060: 6e64 0000 0000 0000 0000 0000 0000 0000 nd..............
0000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000080: 0000 0000 0000 0000 0000 0000 0000 0000 ................
0000090: 0000 0000 0000 0000 0000 0000 0000 0000 ................
可以知道一下信息:
magic[BOOT_MAGIC_SIZE] = "ANDROID!"
kernel_size = 0x0033_2d06
kernel_addr = 0x3000_8000
ramdisk_size = 0x0002_7f78
ramdisk_addr = 0x3100_0000
second_size = 0x0
second_addr = 0x30f0_0000
tags_addr = 0x3000_0100
page_size = 0x0000_1000
與我們之前的分析是一致的。
3.這些地址的意義
首先,這個BASE_ADDR是在哪裏定義的呢?我們從build/core/Makefile看到下面:
ifdef BOARD_KERNEL_BASE
INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
endif
所以 --base指定的addr是由宏BOARD_KERNEL_BASE定義的,我們查找這個宏,可以得知在device/samsung/crespo/BoardConfigCommon.mk這個文件裏定義了的:
BOARD_NAND_PAGE_SIZE := 4096
BOARD_NAND_SPARE_SIZE := 128
BOARD_KERNEL_BASE := 0x30000000
BOARD_KERNEL_PAGESIZE := 4096
BOARD_KERNEL_CMDLINE := console=ttyFIQ0 no_console_suspend
這是一個關於板子的定義,定義了dram的地址,還定義了cpu abi類型,wifi的驅動,板子名字等等跟板子相關的信息。
其次,這裏簡單地說明一下,這裏的kernel_addr, ramdisk_addr, tags_addr分別對應了我們平時對於Linux系統所涉及的ZTEXTADDR,INITRD_PHYS,PARAMS_PHYS。
4.ramdisk.img如何解壓重做
ramdisk.img其實是 文件系統 -> cpio -> gzip的過程,所以解壓的話可以運行一下命令,則ramdisk.img被解壓到ramdisk目錄
cp ramdisk.img ramdisk.cpio.gz
gzip -d ramdisk.cpio.gz
mkdir ramdisk
cd ramdisk
cpio -i -F ../ramdisk.cpio
修改ramdisk文件後重做ramdisk.img,可以運行一下命令
cd ramdisk
cpio -i -t -F ../ramdisk.cpio > ../list
cpio -o -H newc -O ../ramdisk_new.cpio < list
cd ../
gzip ramdisk_new.cpio
mv ramdisk_new.cpio.gz ramdisk_new.img
上面是鑑於ramdisk裏沒有新建文件的前提。如果有新建或者刪除文件,則需要在重新cpio之前修改list裏的文件就可,或者用find來生成說明ramdisk裏所有打包文件的文件也可以。
5. boot.img的作用:
要將boot.img的作用,首先要看下flash的分區和android系統的啓動流程。首先用adb登上shell, 在cat /proc/mtd,可以看見:
shell@android:/ $ cat /proc/mtd
dev: size erasesize name
mtd0: 00200000 00040000 "bootloader"
mtd1: 00140000 00040000 "misc"
mtd2: 00800000 00040000 "boot"
mtd3: 00800000 00040000 "recovery"
mtd4: 1d580000 00040000 "cache"
mtd5: 00d80000 00040000 "radio"
mtd6: 006c0000 00040000 "efs"
“bootloader”是存放bootloader代碼,這段代碼在啓動中最先運行,負責把boot的東西解包拷貝到ddr裏然後把舞臺讓給boot裏的kernel
“misc”區是有Bootloader Control Block (BCB) ,用於存放recovery引導信息。(猜是在進入recovery模式才掛載)
“boot”區是存放引導手機啓動時的必要的系統,包含一些硬件底層的驅動,主要分爲ramdisk和linux內核兩大塊。(在系統啓動後掛載到根目錄下)。
“recovery”區與boot的相似,但是恢復時啓動的系統,比boot多了些恢復用的程序與資源。(只在進入recovery模式才掛載)
“cache"區是緩存空間,程序或系統用到的緩存數據和指令就存放在這,cpu在調用和執行指令時會優先調用這裏的。(在系統啓動後會掛載到cache/目錄)。
“radio”和“efs”分區還不清楚其作用。
“system”區保存了android系統目錄的所有數據,機器啓動後的全部系統主要都在這裏(在系統啓動後會掛載到system/目錄),這裏沒有顯示,我猜可能掛到mmc裏了
“userdata”區將保存了android數據目錄中的所有數據(在系統啓動後會掛載到data/目錄,裏面是會有很多應用數據以及用戶的preference之類的配置數據,我們在手機設置裏看到的手機內存空間就是指這裏)。我猜也有可能是掛到mmc裏了。
而andoird的啓動流程,是這樣的:bootloader 把boot分區裏的kernel和ramdisk解壓到ddr -> 運行Kernel, 進入built-in驅動程序,然後進入init process,切換到user-space,結束kernel循環,進入process scheduling -> android的init process啓動,讀取init.rc, Native服務啓動,開servicemanager,Zygote和system server
所以到這裏,boot.img就很清楚了,boot.img就是boot分區的鏡像,用於啓動的第二部分,即kernel和一個最小文件系統ramdisk