summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--recovery/bootloader.c326
1 files changed, 268 insertions, 58 deletions
diff --git a/recovery/bootloader.c b/recovery/bootloader.c
index e178b7b..afe13a7 100644
--- a/recovery/bootloader.c
+++ b/recovery/bootloader.c
@@ -14,24 +14,63 @@
* limitations under the License.
*/
-#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
#include "bootloader.h"
-#define PARTITION_TABLE_SIZE 4096 // 4KB
-
+#define SECTOR_SIZE 512
+#define NUM_SECONDARY_GPT_SECTORS 34
+#define PIT_PARTITION_TABLE_SIZE 0x1000 // 4KB
#define BOOT_PART_LEN 0x20000 // 128KB
+#define SBL_OFFSET (PIT_PARTITION_TABLE_SIZE + (BOOT_PART_LEN * 4))
#define SMALL_BUFFER_SIZE 0x20
+// A combination of these defines the specification of the device.
+#define OMAP4460 0x1
+#define OMAP4430 0x2
+#define CHIP_HS 0x4
+#define CHIP_EMU 0x8
+#define MSV_PROD 0x10
+
+// Location of the PIT partition table in EMMC
+#define PIT_PARTITION_TABLE_LOCATION 0x4400
+
static const char* FAMILY_LOCATION = "/sys/board_properties/soc/family";
static const char* TYPE_LOCATION = "/sys/board_properties/soc/type";
+static const char* MSV_LOCATION = "/sys/board_properties/soc/msv";
+
+static const char* MMC_LOCATION = "/dev/block/mmcblk0";
+
+/* pit structure = header + (pit partition info * n) */
+struct pit_header {
+ unsigned int magic;
+ int count; /* onenand + mmc partitions */
+ int dummy[5];
+} __attribute__((packed));
+
+struct pit_partinfo {
+ int binary; /* BINARY_TYPE_ */
+ int device; /* PARTITION_DEV_TYPE_ */
+ int id; /* partition id */
+ int attribute; /* PARTITION_ATTR_ */
+ int update; /* PARTITION_UPDATE_ATTR_ - dedicated. */
+ unsigned int blksize; /* mmc start sector */
+ unsigned int blklen; /* sector count */
+ unsigned int offset; /* file offset (in TAR) */
+ unsigned int filesize; /* file size */
+ char name[32]; /* partition name */
+ char filename[32]; /* file name */
+ char deltaname[32]; /* delta file name - dedicated. */
+} __attribute__((packed));
unsigned int read_whole_file(const char* fname, char* buffer,
int buffer_size) {
@@ -40,90 +79,231 @@ unsigned int read_whole_file(const char* fname, char* buffer,
FILE* f = fopen(fname, "rb");
if (f == NULL) {
fprintf(stderr, "Cannot open %s!\n", fname);
- return 0;
+ return -1;
}
- int read_byte_count = fread(buffer, 1, buffer_size, f);
+ int read_byte_count = fread(buffer, 1, buffer_size - 1, f);
fclose(f);
- if (read_byte_count >= buffer_size) {
- fprintf(stderr, "The data in %s is too large for the buffer", fname);
- return 0;
+ if (read_byte_count < 0) {
+ fprintf(stderr, "Couldn't read %s\n", fname);
+ return -1;
+ }
+
+ // Remove any newlines at the end.
+ while (buffer[read_byte_count - 1] == '\n') {
+ buffer[--read_byte_count] = 0;
}
- return 1;
+ return 0;
}
-// Four different device/chip type xloaders are supported by a bootloader.img:
-// 4460 EMU, 4460 HS, 4430 EMU, 4430 HS.
+// Get the specifications for this device
+int get_specification() {
+ int spec = 0;
+
+ char file_data[SMALL_BUFFER_SIZE];
+
+ if (read_whole_file(FAMILY_LOCATION, file_data, SMALL_BUFFER_SIZE) == 0) {
+ if (strcmp(file_data, "OMAP4430") == 0) {
+ spec |= OMAP4430;
+ } else if (strcmp(file_data, "OMAP4460") == 0) {
+ spec |= OMAP4460;
+ } else {
+ fprintf(stderr, "Unknown family: %s\n", file_data);
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "No family\n");
+ return -1;
+ }
+
+ if (read_whole_file(TYPE_LOCATION, file_data, SMALL_BUFFER_SIZE) == 0) {
+ if (strcmp(file_data, "HS") == 0) {
+ spec |= CHIP_HS;
+ } else if (strcmp(file_data, "EMU") == 0) {
+ spec |= CHIP_EMU;
+ } else {
+ fprintf(stderr, "Unknown chip type: %s\n", file_data);
+ return -1;
+ }
+ } else {
+ fprintf(stderr, "No chip type\n");
+ return -1;
+ }
+
+ // MSV is either prod (non-zero) or eng (zero). Default to eng.
+ if (read_whole_file(MSV_LOCATION, file_data, SMALL_BUFFER_SIZE) == 0) {
+ if (strtoul(file_data, NULL, 16) != 0) {
+ spec |= MSV_PROD;
+ }
+ } else {
+ fprintf(stderr, "No msv\n");
+ }
+
+ return spec;
+}
+
+
+// Four different xloaders are supported by bootloader.img:
+// 4460 EMU, 4460 HS (eng), 4460 HS (prod), 4430 HS.
// The layout of the bootloader.img is:
//
-// Partition table (4KB)
+// PIT Partition table (4KB)
// 4460 EMU xloader (128KB)
-// 4460 HS xloader (128KB)
-// 4430 EMU xloader (128KB)
+// 4460 HS (eng) xloader (128KB)
+// 4460 HS (prod) xloader (128KB)
// 4430 HS xloader(128KB)
// sbl (the rest)
-unsigned int get_xloader_offset() {
- unsigned int offset = 0;
- char* file_data = malloc(SMALL_BUFFER_SIZE);
- if (file_data == NULL) {
+int get_xloader_offset() {
+ int spec = get_specification();
+
+ if (spec < 0) {
return -1;
}
- if (!read_whole_file(FAMILY_LOCATION, file_data, SMALL_BUFFER_SIZE)) {
- fprintf(stderr, "Cannot read the family\n");
- free(file_data);
+ if (spec & OMAP4460 &&
+ spec & CHIP_EMU) {
+ return 0;
+ } else if (spec & OMAP4460 &&
+ spec & CHIP_HS &&
+ !(spec & MSV_PROD)) {
+ return BOOT_PART_LEN;
+ } else if (spec & OMAP4460 &&
+ spec & CHIP_HS &&
+ spec & MSV_PROD) {
+ return BOOT_PART_LEN * 2;
+ } else if (spec & OMAP4430 &&
+ spec & CHIP_HS) {
+ return BOOT_PART_LEN * 3;
+ }
+
+ fprintf(stderr, "Unsupported spec for bootloader.img: %d", spec);
+ return -1;
+}
+
+int write_pit_partition_table(const char* image_data,
+ size_t image_size) {
+ int written = 0;
+ int close_status = 0;
+ int to_write;
+ const char* curr;
+
+
+ int mmcfd = open(MMC_LOCATION, O_RDWR);
+ if (mmcfd < 0) {
+ fprintf(stderr, "Could not open %s\n", MMC_LOCATION);
return -1;
}
- if (strncmp(file_data, "OMAP4430", 8) == 0) {
- offset += (BOOT_PART_LEN * 2);
- } else if (strncmp(file_data, "OMAP4460", 8) != 0) {
- fprintf(stderr, "Unknown family: %s\n", file_data);
- free(file_data);
+ // zero out gpt magic field
+ if (lseek(mmcfd, SECTOR_SIZE, SEEK_SET) < 0) {
+ fprintf(stderr, "Couldn't seek to the start of sector 1\n");
+ close(mmcfd);
return -1;
}
- if (!read_whole_file(TYPE_LOCATION, file_data, SMALL_BUFFER_SIZE)) {
- fprintf(stderr, "Cannot read the type\n");
- free(file_data);
+ char buf[SECTOR_SIZE];
+ if (read(mmcfd, buf, SECTOR_SIZE) != SECTOR_SIZE) {
+ fprintf(stderr, "Failed to read sector 1\n");
+ close(mmcfd);
return -1;
}
- if (strncmp(file_data, "HS", 2) == 0) {
- offset += BOOT_PART_LEN;
- } else if (strncmp(file_data, "EMU", 3) != 0) {
- fprintf(stderr, "Unknown type: %s\n", file_data);
- free(file_data);
+ memset(buf, 0, 8);
+
+ if (lseek(mmcfd, SECTOR_SIZE, SEEK_SET) < 0) {
+ fprintf(stderr, "Couldn't seek to the start of sector 1, part 2\n");
+ close(mmcfd);
return -1;
}
- return offset;
-}
+ to_write = SECTOR_SIZE;
+ curr = buf;
+ while (to_write > 0) {
+ written = write(mmcfd, curr, to_write);
+ if (written < 0 && errno != EINTR) {
+ fprintf(stderr, "Couldn't overwrite sector 1\n");
+ close(mmcfd);
+ return -1;
+ }
+ if (written > 0) {
+ to_write -= written;
+ curr += written;
+ }
+ }
-int update_bootloader(const char* image_data,
- size_t image_size,
- const char* xloader_loc,
- const char* sbl_loc) {
- unsigned int xloader_offset=0;
- unsigned int sbl_offset=0;
+ // modify the pit partition info to reflect userdata size
+ // before writing the pit partition table
+ char pit_partition_copy[PIT_PARTITION_TABLE_SIZE];
+ memcpy(pit_partition_copy, image_data, PIT_PARTITION_TABLE_SIZE);
+
+ struct pit_header* hd = (struct pit_header*) pit_partition_copy;
+ int i;
+ for (i = 0; i < hd->count; i++) {
+ struct pit_partinfo* pi = (struct pit_partinfo*)
+ (pit_partition_copy + sizeof(*hd) + sizeof(*pi) * i);
+ if (strcmp(pi->name, "userdata") == 0) {
+ unsigned int num_sectors;
+ if (ioctl(mmcfd, BLKGETSIZE, &num_sectors) < 0) {
+ fprintf(stderr, "Couldn't get sector count\n");
+ close(mmcfd);
+ return -1;
+ }
- int type_family_offset = get_xloader_offset();
- if (type_family_offset < 0) {
+ // There are NUM_SECONDARY_GPT_SECTORS sectors reserved at the end of the
+ // device to hold a backup copy of the GPT, so we subtract that number.
+ pi->blklen = num_sectors - pi->blksize - NUM_SECONDARY_GPT_SECTORS;
+ break;
+ }
+ }
+
+ if (i == hd->count) {
+ fprintf(stderr, "No userdata partition found\n");
+ close(mmcfd);
return -1;
}
- // The offsets into the relevant parts of the bootloader image
- xloader_offset = PARTITION_TABLE_SIZE + type_family_offset;
- sbl_offset = PARTITION_TABLE_SIZE + (BOOT_PART_LEN * 4);
+ // copy the modified pit partition table data to the correct location
+ if (lseek(mmcfd, PIT_PARTITION_TABLE_LOCATION, SEEK_SET) < 0) {
+ fprintf(stderr, "Couldn't seek to the pit partition table location\n");
+ close(mmcfd);
+ return -1;
+ }
- if (image_size < sbl_offset) {
- fprintf(stderr, "image size %d is too small\n", image_size);
+ to_write = PIT_PARTITION_TABLE_SIZE;
+ curr = pit_partition_copy;
+ while (to_write > 0) {
+ written = write(mmcfd, curr, to_write);
+ if (written < 0 && errno != EINTR) {
+ fprintf(stderr, "Failed writing pit partition table\n");
+ close(mmcfd);
+ return -1;
+ }
+ if (written > 0) {
+ to_write -= written;
+ curr += written;
+ }
+ }
+
+ if (close(mmcfd) != 0) {
+ fprintf(stderr, "Failed to close file\n");
return -1;
}
- int written = 0;
- int close_status = 0;
+ return 0;
+}
+
+int write_xloader(const char* image_data,
+ size_t image_size,
+ const char* xloader_loc) {
+ int xloader_offset = get_xloader_offset();
+
+ if (xloader_offset < 0) {
+ return -1;
+ }
+
+ // The offsets into xloader part of the bootloader image
+ xloader_offset += PIT_PARTITION_TABLE_SIZE;
FILE* xloader = fopen(xloader_loc, "r+b");
if (xloader == NULL) {
@@ -132,22 +312,28 @@ int update_bootloader(const char* image_data,
}
// index into the correct xloader offset
- written = fwrite(image_data+xloader_offset, 1, BOOT_PART_LEN, xloader);
- close_status = fclose(xloader);
+ int written = fwrite(image_data+xloader_offset, 1, BOOT_PART_LEN, xloader);
+ int close_status = fclose(xloader);
if (written != BOOT_PART_LEN || close_status != 0) {
fprintf(stderr, "Failed writing to /xloader\n");
return -1;
}
- unsigned int sbl_size = image_size - sbl_offset;
+ return 0;
+}
+
+int write_sbl(const char* image_data,
+ size_t image_size,
+ const char* sbl_loc) {
+ unsigned int sbl_size = image_size - SBL_OFFSET;
FILE* sbl = fopen(sbl_loc, "r+b");
if (sbl == NULL) {
fprintf(stderr, "Could not open %s\n", sbl_loc);
return -1;
}
- written = fwrite(image_data+sbl_offset, 1, sbl_size, sbl);
- close_status = fclose(sbl);
+ int written = fwrite(image_data+SBL_OFFSET, 1, sbl_size, sbl);
+ int close_status = fclose(sbl);
if (written != sbl_size || close_status != 0) {
fprintf(stderr, "Failed writing to /sbl\n");
return -1;
@@ -155,3 +341,27 @@ int update_bootloader(const char* image_data,
return 0;
}
+
+int update_bootloader(const char* image_data,
+ size_t image_size,
+ const char* xloader_loc,
+ const char* sbl_loc) {
+ if (image_size < SBL_OFFSET) {
+ fprintf(stderr, "image size %d is too small\n", image_size);
+ return -1;
+ }
+
+ if (write_pit_partition_table(image_data, image_size) < 0) {
+ return -1;
+ }
+
+ if (write_xloader(image_data, image_size, xloader_loc) < 0) {
+ return -1;
+ }
+
+ if (write_sbl(image_data, image_size, sbl_loc) < 0) {
+ return -1;
+ }
+
+ return 0;
+}