今天,我们来看一下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