1 /********************************************************************** 2 * mkbootimg hacking 3 * 声明: 4 * 1. 本文源代码来自myzr_android4_2_2_1_1_0.tar.bz2中的mkbootimg.c; 5 * 2. 通过阅读该源码,可知Android的boot.img合成原理; 6 * 7 * 深圳 南山平山村 曾剑鋒 Mon May 4 13:09:49 CST 2015 8 **********************************************************************/ 9 10 /* tools/mkbootimg/mkbootimg.c 11 ** 12 ** Copyright 2007, The Android Open Source Project 13 ** 14 ** Licensed under the Apache License, Version 2.0 (the "License"); 15 ** you may not use this file except in compliance with the License. 16 ** You may obtain a copy of the License at 17 ** 18 ** http://www.apache.org/licenses/LICENSE-2.0 19 ** 20 ** Unless required by applicable law or agreed to in writing, software 21 ** distributed under the License is distributed on an "AS IS" BASIS, 22 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 23 ** See the License for the specific language governing permissions and 24 ** limitations under the License. 25 */ 26 27 typedef struct boot_img_hdr boot_img_hdr; 28 29 #define BOOT_MAGIC "ANDROID!" 30 #define BOOT_MAGIC_SIZE 8 31 #define BOOT_NAME_SIZE 16 32 #define BOOT_ARGS_SIZE 512 33 34 /** 35 * 用于暂存需要的数据的结构体 36 */ 37 struct boot_img_hdr 38 { 39 unsigned char magic[BOOT_MAGIC_SIZE]; //文件类型标识 40 41 unsigned kernel_size; /* size in bytes 内核文件大小 */ 42 unsigned kernel_addr; /* physical load addr 内核文件的起始地址 */ 43 44 unsigned ramdisk_size; /* size in bytes randisk文件的大小*/ 45 unsigned ramdisk_addr; /* physical load addr randisk文件的起始地址 */ 46 47 unsigned second_size; /* size in bytes */ 48 unsigned second_addr; /* physical load addr */ 49 50 unsigned tags_addr; /* physical addr for kernel tags */ 51 unsigned page_size; /* flash page size we assume */ 52 unsigned unused[2]; /* future expansion: should be 0 */ 53 54 unsigned char name[BOOT_NAME_SIZE]; /* asciiz product name */ 55 56 unsigned char cmdline[BOOT_ARGS_SIZE]; /* 如果U-Boot没有指定bootargs,就会使用这里的参数 */ 57 58 unsigned id[8]; /* timestamp / checksum / sha1 / etc 个人感觉主要用于保存校验数据 */ 59 }; 60 61 #include62 #include 63 #include 64 #include 65 #include 66 #include 67 68 #include "mincrypt/sha.h" 69 #include "bootimg.h" 70 71 /** 72 * 主要用于加载各种需要的文件的函数 73 */ 74 static void *load_file(const char *fn, unsigned *_sz) 75 { 76 char *data; 77 int sz; 78 int fd; 79 80 data = 0; 81 fd = open(fn, O_RDONLY); //带开文件 82 if(fd < 0) return 0; 83 84 sz = lseek(fd, 0, SEEK_END); //跳到文件末尾,这样就知道文件的大小了 85 if(sz < 0) goto oops; 86 87 if(lseek(fd, 0, SEEK_SET) != 0) goto oops; //返回到文件头 88 89 data = (char*) malloc(sz); //分配和文件一样大小的内存空间 90 if(data == 0) goto oops; 91 92 if(read(fd, data, sz) != sz) goto oops; //将文件内容读取到内存中 93 close(fd); 94 95 if(_sz) *_sz = sz; //相当于返回文件的大小 96 return data; //返回文件数据的首地址 97 98 oops: 99 close(fd);100 if(data != 0) free(data);101 return 0;102 }103 104 /**105 * mkbootimg使用说明106 */107 int usage(void)108 {109 fprintf(stderr,"usage: mkbootimg\n"110 " --kernel \n"111 " --ramdisk \n"112 " [ --second <2ndbootloader-filename> ]\n"113 " [ --cmdline ]\n"114 " [ --board ]\n"115 " [ --base ]\n"116 " [ --pagesize ]\n"117 " -o|--output \n"118 );119 return 1;120 }121 122 123 124 /**125 * boot.img中每部分数据都是以页为单位存储的,如果数据不足一页的倍数,126 * 那么就将该页剩下的空间以0填充127 */128 static unsigned char padding[4096] = { 0, };129 130 int write_padding(int fd, unsigned pagesize, unsigned itemsize)131 {132 /**133 * pagesize一般都是比较大的整数,例如:1k,2k,4k等等,那么134 * pagesize-1,就变成了由0开始,后面跟了一堆1,例如:135 * 1k = 10000000000(二进制);136 * 1k-1 = 01111111111(二进制);137 */138 unsigned pagemask = pagesize - 1; 139 unsigned count;140 141 /**142 * 由上面的解析可知,这里是确保itemsize需要填充的0的个数143 * 小于pagesize并且大于0144 * itemsize&pagemask相当于itemsize对pagemask取余145 */146 if((itemsize & pagemask) == 0) {147 return 0;148 }149 150 /**151 * 计算出需要填充多少个0, itemsize&pagemask相当于itemsize对pagemask取余152 */153 count = pagesize - (itemsize & pagemask);154 155 if(write(fd, padding, count) != count) {156 return -1;157 } else {158 return 0;159 }160 }161 162 int main(int argc, char **argv)163 {164 boot_img_hdr hdr;165 166 char *kernel_fn = 0;167 void *kernel_data = 0;168 char *ramdisk_fn = 0;169 void *ramdisk_data = 0;170 char *second_fn = 0;171 void *second_data = 0;172 char *cmdline = "";173 char *bootimg = 0;174 char *board = "";175 unsigned pagesize = 2048; //默认一页占用2K176 int fd;177 SHA_CTX ctx;178 uint8_t* sha;179 unsigned base = 0x10000000; //基址180 unsigned kernel_offset = 0x00008000;181 unsigned ramdisk_offset = 0x01000000;182 unsigned second_offset = 0x00f00000;183 unsigned tags_offset = 0x00000100;184 185 //可执行文件必须有参数,需知道内核文件在那里,ramdisk在那里等等信息186 argc--; 187 argv++;188 189 memset(&hdr, 0, sizeof(hdr)); //清空结构体数据190 191 /**192 * 获取命令行参数193 */194 while(argc > 0){195 char *arg = argv[0];196 char *val = argv[1];197 if(argc < 2) {198 return usage();199 }200 argc -= 2;201 argv += 2;202 if(!strcmp(arg, "--output") || !strcmp(arg, "-o")) {203 bootimg = val;204 } else if(!strcmp(arg, "--kernel")) {205 kernel_fn = val;206 } else if(!strcmp(arg, "--ramdisk")) {207 ramdisk_fn = val;208 } else if(!strcmp(arg, "--second")) {209 second_fn = val;210 } else if(!strcmp(arg, "--cmdline")) {211 cmdline = val;212 } else if(!strcmp(arg, "--base")) {213 base = strtoul(val, 0, 16);214 } else if(!strcmp(arg, "--kernel_offset")) {215 kernel_offset = strtoul(val, 0, 16);216 } else if(!strcmp(arg, "--ramdisk_offset")) {217 ramdisk_offset = strtoul(val, 0, 16);218 } else if(!strcmp(arg, "--second_offset")) {219 second_offset = strtoul(val, 0, 16);220 } else if(!strcmp(arg, "--tags_offset")) {221 tags_offset = strtoul(val, 0, 16);222 } else if(!strcmp(arg, "--board")) {223 board = val;224 } else if(!strcmp(arg,"--pagesize")) {225 pagesize = strtoul(val, 0, 10);226 if ((pagesize != 2048) && (pagesize != 4096)) { //页大小只能是两种情况,2k或者4k227 fprintf(stderr,"error: unsupported page size %d\n", pagesize);228 return -1;229 }230 } else {231 return usage();232 }233 }234 hdr.page_size = pagesize; //设置页大小235 236 /**237 * 计算各种偏移地址238 */239 hdr.kernel_addr = base + kernel_offset;240 hdr.ramdisk_addr = base + ramdisk_offset;241 hdr.second_addr = base + second_offset;242 hdr.tags_addr = base + tags_offset;243 244 if(bootimg == 0) {245 fprintf(stderr,"error: no output filename specified\n");246 return usage();247 }248 249 if(kernel_fn == 0) {250 fprintf(stderr,"error: no kernel image specified\n");251 return usage();252 }253 254 if(ramdisk_fn == 0) {255 fprintf(stderr,"error: no ramdisk image specified\n");256 return usage();257 }258 259 if(strlen(board) >= BOOT_NAME_SIZE) {260 fprintf(stderr,"error: board name too large\n");261 return usage();262 }263 264 strcpy(hdr.name, board); //板子类型265 266 memcpy(hdr.magic, BOOT_MAGIC, BOOT_MAGIC_SIZE); //文件类型267 268 //kernel命令行参数,如果U-Boot没有给出,将是用这里的参数269 if(strlen(cmdline) > (BOOT_ARGS_SIZE - 1)) { 270 fprintf(stderr,"error: kernel commandline too large\n");271 return 1;272 }273 strcpy((char*)hdr.cmdline, cmdline);274 275 //加载内核文件,同时获取内核文件的大小276 kernel_data = load_file(kernel_fn, &hdr.kernel_size);277 if(kernel_data == 0) {278 fprintf(stderr,"error: could not load kernel '%s'\n", kernel_fn);279 return 1;280 }281 282 if(!strcmp(ramdisk_fn,"NONE")) {283 ramdisk_data = 0;284 hdr.ramdisk_size = 0;285 } else {286 ramdisk_data = load_file(ramdisk_fn, &hdr.ramdisk_size);287 if(ramdisk_data == 0) {288 fprintf(stderr,"error: could not load ramdisk '%s'\n", ramdisk_fn);289 return 1;290 }291 }292 293 if(second_fn) {294 second_data = load_file(second_fn, &hdr.second_size);295 if(second_data == 0) {296 fprintf(stderr,"error: could not load secondstage '%s'\n", second_fn);297 return 1;298 }299 }300 301 /* put a hash of the contents in the header so boot images can be302 * differentiated based on their first 2k.303 */304 /**305 * 个人理解这里是产生一些校验数据,可以不用关心,不影响阅读306 */307 SHA_init(&ctx);308 SHA_update(&ctx, kernel_data, hdr.kernel_size);309 SHA_update(&ctx, &hdr.kernel_size, sizeof(hdr.kernel_size));310 SHA_update(&ctx, ramdisk_data, hdr.ramdisk_size);311 SHA_update(&ctx, &hdr.ramdisk_size, sizeof(hdr.ramdisk_size));312 SHA_update(&ctx, second_data, hdr.second_size);313 SHA_update(&ctx, &hdr.second_size, sizeof(hdr.second_size));314 sha = SHA_final(&ctx);315 memcpy(hdr.id, sha,316 SHA_DIGEST_SIZE > sizeof(hdr.id) ? sizeof(hdr.id) : SHA_DIGEST_SIZE);317 318 /**319 * 打开输出目标文件,如果文件不存在,那么就创建该文件,如果存在,那么就清空320 */321 fd = open(bootimg, O_CREAT | O_TRUNC | O_WRONLY, 0644);322 if(fd < 0) {323 fprintf(stderr,"error: could not create '%s'\n", bootimg);324 return 1;325 }326 327 /**328 * 这里相当于写入文件头,和写bmp文件差不多的意思329 */330 if(write(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) goto fail;331 if(write_padding(fd, pagesize, sizeof(hdr))) goto fail;332 333 /**334 * 接下来按一定顺序写内容335 */336 if(write(fd, kernel_data, hdr.kernel_size) != hdr.kernel_size) goto fail;337 if(write_padding(fd, pagesize, hdr.kernel_size)) goto fail;338 339 if(write(fd, ramdisk_data, hdr.ramdisk_size) != hdr.ramdisk_size) goto fail;340 if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;341 342 if(second_data) {343 if(write(fd, second_data, hdr.second_size) != hdr.second_size) goto fail;344 if(write_padding(fd, pagesize, hdr.ramdisk_size)) goto fail;345 }346 347 return 0;348 349 fail:350 unlink(bootimg);351 close(fd);352 fprintf(stderr,"error: failed writing '%s': %s\n", bootimg,353 strerror(errno));354 return 1;355 }