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 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include <fcntl.h>
66 #include <errno.h>
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 <filename>\n"
111 " --ramdisk <filename>\n"
112 " [ --second <2ndbootloader-filename> ]\n"
113 " [ --cmdline <kernel-commandline> ]\n"
114 " [ --board <boardname> ]\n"
115 " [ --base <address> ]\n"
116 " [ --pagesize <pagesize> ]\n"
117 " -o|--output <filename>\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并且大于0
144 * 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; //默认一页占用2K
176 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或者4k
227 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 be
302 * 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 }