1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <linux/fs.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <sys/ioctl.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include "bootloader.h"
28
29 #define SECTOR_SIZE 512
30 #define NUM_SECONDARY_GPT_SECTORS 34
31 #define PIT_PARTITION_TABLE_SIZE 0x1000 // 4KB
32 #define BOOT_PART_LEN 0x20000 // 128KB
33 #define SBL_OFFSET (PIT_PARTITION_TABLE_SIZE + (BOOT_PART_LEN * 4))
34
35 #define SMALL_BUFFER_SIZE 0x20
36
37 // A combination of these defines the specification of the device.
38 #define OMAP4460 0x1
39 #define OMAP4430 0x2
40 #define CHIP_HS 0x4
41 #define CHIP_EMU 0x8
42 #define MSV_PROD 0x10
43
44 // Location of the PIT partition table in EMMC
45 #define PIT_PARTITION_TABLE_LOCATION 0x4400
46
47 static const char* FAMILY_LOCATION = "/sys/board_properties/soc/family";
48 static const char* TYPE_LOCATION = "/sys/board_properties/soc/type";
49 static const char* MSV_LOCATION = "/sys/board_properties/soc/msv";
50
51 static const char* MMC_LOCATION = "/dev/block/mmcblk0";
52
53 /* pit structure = header + (pit partition info * n) */
54 struct pit_header {
55 unsigned int magic;
56 int count; /* onenand + mmc partitions */
57 int dummy[5];
58 } __attribute__((packed));
59
60 struct pit_partinfo {
61 int binary; /* BINARY_TYPE_ */
62 int device; /* PARTITION_DEV_TYPE_ */
63 int id; /* partition id */
64 int attribute; /* PARTITION_ATTR_ */
65 int update; /* PARTITION_UPDATE_ATTR_ - dedicated. */
66 unsigned int blksize; /* mmc start sector */
67 unsigned int blklen; /* sector count */
68 unsigned int offset; /* file offset (in TAR) */
69 unsigned int filesize; /* file size */
70 char name[32]; /* partition name */
71 char filename[32]; /* file name */
72 char deltaname[32]; /* delta file name - dedicated. */
73 } __attribute__((packed));
74
read_whole_file(const char * fname,char * buffer,int buffer_size)75 unsigned int read_whole_file(const char* fname, char* buffer,
76 int buffer_size) {
77 memset(buffer, 0, buffer_size);
78
79 FILE* f = fopen(fname, "rb");
80 if (f == NULL) {
81 fprintf(stderr, "Cannot open %s!\n", fname);
82 return -1;
83 }
84
85 int read_byte_count = fread(buffer, 1, buffer_size - 1, f);
86 fclose(f);
87 if (read_byte_count < 0) {
88 fprintf(stderr, "Couldn't read %s\n", fname);
89 return -1;
90 }
91
92 // Remove any newlines at the end.
93 while (buffer[read_byte_count - 1] == '\n') {
94 buffer[--read_byte_count] = 0;
95 }
96
97 return 0;
98 }
99
100 // Get the specifications for this device
get_specification()101 int get_specification() {
102 int spec = 0;
103
104 char file_data[SMALL_BUFFER_SIZE];
105
106 if (read_whole_file(FAMILY_LOCATION, file_data, SMALL_BUFFER_SIZE) == 0) {
107 if (strcmp(file_data, "OMAP4430") == 0) {
108 spec |= OMAP4430;
109 } else if (strcmp(file_data, "OMAP4460") == 0) {
110 spec |= OMAP4460;
111 } else {
112 fprintf(stderr, "Unknown family: %s\n", file_data);
113 return -1;
114 }
115 } else {
116 fprintf(stderr, "No family\n");
117 return -1;
118 }
119
120 if (read_whole_file(TYPE_LOCATION, file_data, SMALL_BUFFER_SIZE) == 0) {
121 if (strcmp(file_data, "HS") == 0) {
122 spec |= CHIP_HS;
123 } else if (strcmp(file_data, "EMU") == 0) {
124 spec |= CHIP_EMU;
125 } else {
126 fprintf(stderr, "Unknown chip type: %s\n", file_data);
127 return -1;
128 }
129 } else {
130 fprintf(stderr, "No chip type\n");
131 return -1;
132 }
133
134 // MSV is either prod (non-zero) or eng (zero). Default to eng.
135 if (read_whole_file(MSV_LOCATION, file_data, SMALL_BUFFER_SIZE) == 0) {
136 if (strtoul(file_data, NULL, 16) != 0) {
137 spec |= MSV_PROD;
138 }
139 } else {
140 fprintf(stderr, "No msv\n");
141 }
142
143 return spec;
144 }
145
146
147 // Four different xloaders are supported by bootloader.img:
148 // 4460 EMU, 4460 HS (eng), 4460 HS (prod), 4430 HS.
149 // The layout of the bootloader.img is:
150 //
151 // PIT Partition table (4KB)
152 // 4460 EMU xloader (128KB)
153 // 4460 HS (eng) xloader (128KB)
154 // 4460 HS (prod) xloader (128KB)
155 // 4430 HS xloader(128KB)
156 // sbl (the rest)
get_xloader_offset()157 int get_xloader_offset() {
158 int spec = get_specification();
159
160 if (spec < 0) {
161 return -1;
162 }
163
164 if (spec & OMAP4460 &&
165 spec & CHIP_EMU) {
166 return 0;
167 } else if (spec & OMAP4460 &&
168 spec & CHIP_HS &&
169 !(spec & MSV_PROD)) {
170 return BOOT_PART_LEN;
171 } else if (spec & OMAP4460 &&
172 spec & CHIP_HS &&
173 spec & MSV_PROD) {
174 return BOOT_PART_LEN * 2;
175 } else if (spec & OMAP4430 &&
176 spec & CHIP_HS) {
177 return BOOT_PART_LEN * 3;
178 }
179
180 fprintf(stderr, "Unsupported spec for bootloader.img: %d", spec);
181 return -1;
182 }
183
write_pit_partition_table(const char * image_data,size_t image_size)184 int write_pit_partition_table(const char* image_data,
185 size_t image_size) {
186 int written = 0;
187 int close_status = 0;
188 int to_write;
189 const char* curr;
190
191
192 int mmcfd = open(MMC_LOCATION, O_RDWR);
193 if (mmcfd < 0) {
194 fprintf(stderr, "Could not open %s\n", MMC_LOCATION);
195 return -1;
196 }
197
198 // zero out gpt magic field
199 if (lseek(mmcfd, SECTOR_SIZE, SEEK_SET) < 0) {
200 fprintf(stderr, "Couldn't seek to the start of sector 1\n");
201 close(mmcfd);
202 return -1;
203 }
204
205 char buf[SECTOR_SIZE];
206 if (read(mmcfd, buf, SECTOR_SIZE) != SECTOR_SIZE) {
207 fprintf(stderr, "Failed to read sector 1\n");
208 close(mmcfd);
209 return -1;
210 }
211
212 memset(buf, 0, 8);
213
214 if (lseek(mmcfd, SECTOR_SIZE, SEEK_SET) < 0) {
215 fprintf(stderr, "Couldn't seek to the start of sector 1, part 2\n");
216 close(mmcfd);
217 return -1;
218 }
219
220 to_write = SECTOR_SIZE;
221 curr = buf;
222 while (to_write > 0) {
223 written = write(mmcfd, curr, to_write);
224 if (written < 0 && errno != EINTR) {
225 fprintf(stderr, "Couldn't overwrite sector 1\n");
226 close(mmcfd);
227 return -1;
228 }
229 if (written > 0) {
230 to_write -= written;
231 curr += written;
232 }
233 }
234
235 // modify the pit partition info to reflect userdata size
236 // before writing the pit partition table
237 char pit_partition_copy[PIT_PARTITION_TABLE_SIZE];
238 memcpy(pit_partition_copy, image_data, PIT_PARTITION_TABLE_SIZE);
239
240 struct pit_header* hd = (struct pit_header*) pit_partition_copy;
241 int i;
242 for (i = 0; i < hd->count; i++) {
243 struct pit_partinfo* pi = (struct pit_partinfo*)
244 (pit_partition_copy + sizeof(*hd) + sizeof(*pi) * i);
245 if (strcmp(pi->name, "userdata") == 0) {
246 unsigned int num_sectors;
247 if (ioctl(mmcfd, BLKGETSIZE, &num_sectors) < 0) {
248 fprintf(stderr, "Couldn't get sector count\n");
249 close(mmcfd);
250 return -1;
251 }
252
253 // There are NUM_SECONDARY_GPT_SECTORS sectors reserved at the end of the
254 // device to hold a backup copy of the GPT, so we subtract that number.
255 pi->blklen = num_sectors - pi->blksize - NUM_SECONDARY_GPT_SECTORS;
256 break;
257 }
258 }
259
260 if (i == hd->count) {
261 fprintf(stderr, "No userdata partition found\n");
262 close(mmcfd);
263 return -1;
264 }
265
266 // copy the modified pit partition table data to the correct location
267 if (lseek(mmcfd, PIT_PARTITION_TABLE_LOCATION, SEEK_SET) < 0) {
268 fprintf(stderr, "Couldn't seek to the pit partition table location\n");
269 close(mmcfd);
270 return -1;
271 }
272
273 to_write = PIT_PARTITION_TABLE_SIZE;
274 curr = pit_partition_copy;
275 while (to_write > 0) {
276 written = write(mmcfd, curr, to_write);
277 if (written < 0 && errno != EINTR) {
278 fprintf(stderr, "Failed writing pit partition table\n");
279 close(mmcfd);
280 return -1;
281 }
282 if (written > 0) {
283 to_write -= written;
284 curr += written;
285 }
286 }
287
288 if (close(mmcfd) != 0) {
289 fprintf(stderr, "Failed to close file\n");
290 return -1;
291 }
292
293 return 0;
294 }
295
write_xloader(const char * image_data,size_t image_size,const char * xloader_loc)296 int write_xloader(const char* image_data,
297 size_t image_size,
298 const char* xloader_loc) {
299 int xloader_offset = get_xloader_offset();
300
301 if (xloader_offset < 0) {
302 return -1;
303 }
304
305 // The offsets into xloader part of the bootloader image
306 xloader_offset += PIT_PARTITION_TABLE_SIZE;
307
308 FILE* xloader = fopen(xloader_loc, "r+b");
309 if (xloader == NULL) {
310 fprintf(stderr, "Could not open %s\n", xloader_loc);
311 return -1;
312 }
313
314 // index into the correct xloader offset
315 int written = fwrite(image_data+xloader_offset, 1, BOOT_PART_LEN, xloader);
316 int close_status = fclose(xloader);
317 if (written != BOOT_PART_LEN || close_status != 0) {
318 fprintf(stderr, "Failed writing to /xloader\n");
319 return -1;
320 }
321
322 return 0;
323 }
324
write_sbl(const char * image_data,size_t image_size,const char * sbl_loc)325 int write_sbl(const char* image_data,
326 size_t image_size,
327 const char* sbl_loc) {
328 unsigned int sbl_size = image_size - SBL_OFFSET;
329 FILE* sbl = fopen(sbl_loc, "r+b");
330 if (sbl == NULL) {
331 fprintf(stderr, "Could not open %s\n", sbl_loc);
332 return -1;
333 }
334
335 int written = fwrite(image_data+SBL_OFFSET, 1, sbl_size, sbl);
336 int close_status = fclose(sbl);
337 if (written != sbl_size || close_status != 0) {
338 fprintf(stderr, "Failed writing to /sbl\n");
339 return -1;
340 }
341
342 return 0;
343 }
344
update_bootloader(const char * image_data,size_t image_size,const char * xloader_loc,const char * sbl_loc)345 int update_bootloader(const char* image_data,
346 size_t image_size,
347 const char* xloader_loc,
348 const char* sbl_loc) {
349 if (image_size < SBL_OFFSET) {
350 fprintf(stderr, "image size %d is too small\n", image_size);
351 return -1;
352 }
353
354 if (write_pit_partition_table(image_data, image_size) < 0) {
355 return -1;
356 }
357
358 if (write_xloader(image_data, image_size, xloader_loc) < 0) {
359 return -1;
360 }
361
362 if (write_sbl(image_data, image_size, sbl_loc) < 0) {
363 return -1;
364 }
365
366 return 0;
367 }
368