summaryrefslogtreecommitdiffstats
path: root/recovery
diff options
context:
space:
mode:
authorMichael Ward <mikeward@google.com>2011-09-22 16:27:59 -0700
committerMichael Ward <mikeward@google.com>2011-09-27 16:45:58 -0700
commit373d6b3ae7661bcd732a5d4dfa7be30a15d0146b (patch)
treec4834d4d5e6ce453dc5b6c159070bd24dc2c6e72 /recovery
parentb4302ff745a11775845cb7a63fbde02f30d13b40 (diff)
downloaddevice_samsung_tuna-373d6b3ae7661bcd732a5d4dfa7be30a15d0146b.zip
device_samsung_tuna-373d6b3ae7661bcd732a5d4dfa7be30a15d0146b.tar.gz
device_samsung_tuna-373d6b3ae7661bcd732a5d4dfa7be30a15d0146b.tar.bz2
Fix how the bootloader gets written.
Zero out the magic number (first 8 bytes of sector 1). Write the partition table to 0x4400 of EMMC, after giving it the correct userdata size. Change the way xloaders are expected to be laid out. Use MSV as part of the determination of which xloader to use. This is backwards-compatible because there are no 4430 EMU devices in circulation, and that's the section that was changed to hold the 4460 HS (prod) xloader. Change-Id: Iba3a646fd08fa71a29a4c52b3ff0da26468b04f2
Diffstat (limited to 'recovery')
-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;
+}