summaryrefslogtreecommitdiff
path: root/boot_control/gpt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'boot_control/gpt.cpp')
-rw-r--r--boot_control/gpt.cpp357
1 files changed, 357 insertions, 0 deletions
diff --git a/boot_control/gpt.cpp b/boot_control/gpt.cpp
new file mode 100644
index 0000000..962a61c
--- /dev/null
+++ b/boot_control/gpt.cpp
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "boot_control_hw"
+#define LOG_NDEBUG 0
+
+#include <string>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+#include <sys/types.h>
+#include <cutils/log.h>
+#include <utils/Unicode.h>
+
+#include "gpt.h"
+
+using namespace std;
+
+namespace {
+
+static uint64_t lba_to_offset(uint64_t lba)
+{
+ return lba * 512;
+}
+
+/*
+ * A8h reflected is 15h, i.e. 10101000 <--> 00010101
+ */
+int reflect(int data, int len)
+{
+ int ref = 0;
+ int i;
+ for (i = 0; i < len; i++) {
+ if (data & 0x1) {
+ ref |= (1 << ((len - 1) - i));
+ }
+ data = (data >> 1);
+ }
+ return ref;
+}
+
+uint32_t calculate_crc32(void *buf, uint32_t len)
+{
+ uint32_t i, j;
+ uint32_t byte_length = 8; /*length of unit (i.e. byte) */
+ int msb = 0;
+ int polynomial = 0x04C11DB7; /* IEEE 32bit polynomial */
+ unsigned int regs = 0xFFFFFFFF; /* init to all ones */
+ int regs_mask = 0xFFFFFFFF; /* ensure only 32 bit answer */
+ int regs_msb = 0;
+ unsigned int reflected_regs;
+
+ for (i = 0; i < len; i++) {
+ int data_byte = *((uint8_t *)buf + i);
+ data_byte = reflect(data_byte, 8);
+ for (j = 0; j < byte_length; j++) {
+ msb = data_byte >> (byte_length - 1); /* get MSB */
+ msb &= 1; /* ensure just 1 bit */
+ regs_msb = (regs >> 31) & 1; /* MSB of regs */
+ regs = regs << 1; /* shift regs for CRC-CCITT */
+ if (regs_msb ^ msb) { /* MSB is a 1 */
+ regs = regs ^ polynomial; /* XOR with generator poly */
+ }
+ regs = regs & regs_mask; /* Mask off excess upper bits */
+ data_byte <<= 1; /* get to next bit */
+ }
+ }
+ regs = regs & regs_mask;
+ reflected_regs = reflect(regs, 32) ^ 0xFFFFFFFF;
+
+ return reflected_regs;
+}
+
+int open_disk(const std::string& device, bool open_for_write)
+{
+ int fd;
+ int oflag = open_for_write ? O_RDWR : O_RDONLY;
+
+ fd = open (device.c_str(), oflag);
+ if (-1 == fd)
+ return -errno;
+
+ ALOGV("open_disk: %s", device.c_str());
+ return fd;
+}
+
+ssize_t read_from_disk(const std::string& device, uint64_t offset,
+ size_t size, void *buf)
+{
+ ssize_t read_bytes;
+ ssize_t ret = 0;
+ int fd = open_disk(device, false);
+
+ if (-1 == fd)
+ return fd; // fd is set to -errno from open_disk(bool)
+
+ if (-1 == lseek64(fd, offset, SEEK_SET)) {
+ close(fd);
+ return -errno;
+ }
+
+ do {
+ read_bytes = read(fd, buf, size);
+ } while (-1 == read_bytes && EINTR == errno);
+
+ if (-1 == read_bytes)
+ ret = -errno;
+ else
+ ret = read_bytes;
+
+ close(fd);
+ return ret;
+}
+
+ssize_t write_to_disk(const std::string& device, uint64_t offset,
+ size_t size, void *buf)
+{
+ ssize_t written_bytes;
+ ssize_t ret = 0;
+ int fd = open_disk (device, true);
+
+ if (-1 == fd)
+ return fd; // fd is set to -errno from open_disk(true)
+
+ if (-1 == lseek64(fd, offset, SEEK_SET)) {
+ close(fd);
+ return -errno;
+ }
+
+ do {
+ written_bytes = write(fd, buf, size);
+ } while(-1 == written_bytes && EINTR == errno);
+
+
+ if (-1 == written_bytes)
+ ret = -errno;
+ else
+ ret = written_bytes;
+
+ close(fd);
+ return ret;
+}
+
+};
+
+namespace qcom_boot_control {
+
+int PartitionTables::write_partitions()
+{
+ ssize_t ret = 0;
+ uint64_t size_num_bytes = 0;
+ uint64_t num_lbas = 0;
+ int fd = open(disk_device.c_str(), O_RDONLY);
+
+ if (-1 == fd)
+ return -errno;
+
+ ret = ioctl(fd, BLKGETSIZE64, &size_num_bytes);
+
+ close(fd);
+
+ if (-1 == ret)
+ return -errno;
+
+ num_lbas = size_num_bytes / 512;
+
+ ALOGV("Total disk LBAs %lld", num_lbas);
+
+ /* Set header's fields for secondary partition */
+ header.curr_lba = num_lbas - 1;
+ header.backup_lba = 1;
+ header.start_lba_part_array = num_lbas - 33;
+
+ /* Calculate CRCs for secondary header */
+ header.hdr_crc32 = 0;
+ header.part_arr_crc32 = calculate_crc32(partition_array,
+ header.num_parts *
+ sizeof(gpt_partition_entry_t));
+ header.hdr_crc32 = calculate_crc32(&header, header.hdr_size);
+
+ /* Write secondary partition array to disk*/
+ if ((ret = write_to_disk(disk_device, lba_to_offset(num_lbas - 33),
+ sizeof(partition_array), partition_array)) < 0)
+ return ret;
+
+ /* Write secondary header to disk*/
+ if ((ret = write_to_disk(disk_device, lba_to_offset(num_lbas - 1),
+ sizeof(gpt_header_t), &header)) < 0)
+ return ret;
+
+ /* Set header's fields for primary partition */
+ header.curr_lba = 1;
+ header.backup_lba = num_lbas - 1;
+ header.start_lba_part_array = 2;
+
+ /* Calculate CRCs for primary header */
+ header.hdr_crc32 = 0;
+ header.part_arr_crc32 = calculate_crc32(partition_array,
+ header.num_parts *
+ sizeof(gpt_partition_entry_t));
+ header.hdr_crc32 = calculate_crc32(&header, header.hdr_size);
+
+ /* Write primary partition array to disk*/
+ if ((ret = write_to_disk(disk_device, lba_to_offset(2),
+ sizeof(partition_array), partition_array)) < 0)
+ return ret;
+
+ /* Write primary header to disk*/
+ if ((ret = write_to_disk(disk_device, lba_to_offset(1),
+ sizeof(gpt_header_t), &header)) < 0)
+ return ret;
+
+ return 0;
+}
+
+std::unique_ptr<PartitionTables>
+ PartitionTables::read_partitions(const std::string& device)
+{
+ std::unique_ptr<PartitionTables> gpt(new PartitionTables);
+ ssize_t ret;
+ uint64_t size_num_bytes = 0;
+ uint64_t num_lbas = 0;
+ int fd = open(device.c_str(), O_RDONLY);
+
+ if (-1 == fd)
+ return nullptr;
+
+ gpt->disk_device = device;
+
+ ret = ioctl(fd, BLKGETSIZE64, &size_num_bytes);
+
+ close(fd);
+
+ if (-1 == ret)
+ return nullptr;
+
+ num_lbas = size_num_bytes / 512;
+
+ ALOGV("Total disk LBAs %lld", num_lbas);
+
+ /* Read primary header*/
+ if ((ret = read_from_disk(device, lba_to_offset(1),
+ sizeof(gpt_header_t), &gpt->header)) < 0)
+ return nullptr;
+
+ if (gpt->gpt_sanity_check()) {
+ /* Read primary paritition array.*/
+ if ((ret = read_from_disk(device, lba_to_offset(2),
+ sizeof(partition_array),
+ gpt->partition_array)) < 0)
+ return nullptr;
+
+ /* Primary header and partiiton array read*/
+ return gpt;
+ }
+
+ /*
+ * The seconadary header and partition array is used only in case
+ * the primary header fails sanity check.
+ */
+ ALOGE("Attempting to read secondary header and partition array.");
+
+ /* Read secondary header. */
+ if ((ret = read_from_disk(device, lba_to_offset(num_lbas - 1),
+ sizeof(gpt_header_t), &gpt->header)) < 0)
+ return nullptr;
+
+ if (gpt->gpt_sanity_check()) {
+ /* Read secondary partition array.*/
+ if ((ret = read_from_disk(device, lba_to_offset(num_lbas - 33),
+ sizeof(partition_array),
+ gpt->partition_array)) < 0)
+ return nullptr;
+
+ /* Secondary header and partition array read*/
+ return gpt;
+ }
+
+ ALOGE("Sanity check failed for both headers.");
+ errno = EIO;
+
+ return nullptr;
+}
+
+bool PartitionTables::gpt_sanity_check()
+{
+ gpt_header_t tmp_hdr;
+
+ if (header.signature != GPT_HDR_SIGNATURE)
+ return false;
+
+ if (header.hdr_size != 92)
+ return false;
+
+ /*
+ * Calculate header's CRC32 and compare against the CRC32 read from disk
+ */
+ memcpy(&tmp_hdr, &header, header.hdr_size);
+ tmp_hdr.hdr_crc32 = 0;
+ if (header.hdr_crc32 != calculate_crc32(&tmp_hdr, tmp_hdr.hdr_size))
+ return false;
+
+ return true;
+}
+
+int PartitionTables::getIndexForSlottedBootPartition(unsigned slot,
+ uint32_t& partition_index) const
+{
+ unsigned i;
+ const char16_t* boot_partition_for_slot = slot == 0 ? u"boot_a" : u"boot_b";
+
+ assert(slot <= MAX_SLOTS);
+
+ for (i = 0; i < header.num_parts; i++) {
+ if (0 == strcmp16(partition_array[i].name, boot_partition_for_slot)) {
+ partition_index = i;
+ return 0;
+ }
+ }
+
+ ALOGV("getIndexForSlottedBootPartition: partition %s does not exist",
+ boot_partition_for_slot);
+
+ return -EINVAL;
+}
+
+}; //qcom_boot_control
+