diff options
author | Michael Ward <mikeward@google.com> | 2011-09-27 22:48:44 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-09-27 22:48:44 -0700 |
commit | 01ea0e3d9dd8b30bb5d02978170268f656c37bd9 (patch) | |
tree | 9e45747917d2a3c39eff755a2af6241479e2c8a6 | |
parent | f5ae1169cd52518ca4c03cc806db978ae09fbb8b (diff) | |
parent | 373d6b3ae7661bcd732a5d4dfa7be30a15d0146b (diff) | |
download | device_samsung_tuna-01ea0e3d9dd8b30bb5d02978170268f656c37bd9.zip device_samsung_tuna-01ea0e3d9dd8b30bb5d02978170268f656c37bd9.tar.gz device_samsung_tuna-01ea0e3d9dd8b30bb5d02978170268f656c37bd9.tar.bz2 |
Merge "Fix how the bootloader gets written." into ics-factoryrom
-rw-r--r-- | recovery/bootloader.c | 326 |
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; +} |