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 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 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) 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 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 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 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 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