• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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