diff options
author | Sourabh Banerjee <sbanerje@codeaurora.org> | 2015-10-20 06:42:03 +0530 |
---|---|---|
committer | Mohammed Habibulla <moch@google.com> | 2015-11-05 12:04:29 -0800 |
commit | cdbd9ece90cfa6bb29ec346a575e60807fe4c565 (patch) | |
tree | 45de9c7d2877c79d4d577f63b5979c3c320ba336 | |
parent | 64bec02f0d5c2405b5609e97ad5a4102ac083d90 (diff) | |
download | qcom-cdbd9ece90cfa6bb29ec346a575e60807fe4c565.tar.gz |
boot control HAL for Qualcomm MSM platform
qcom boot control HAL uses GPT attribute flags to manage the slots.
Properties success, priority and try count are used for a given slot.
Current implementation supports maximum 2 slots, 'boot_a' and 'boot_b'.
BUG=24675877
Change-Id: Ic4aa49ee1221ff17a1ddf566ea283d9b37b76530
Signed-off-by: Sourabh Banerjee <sbanerje@codeaurora.org>
-rw-r--r-- | boot_control/Android.mk | 18 | ||||
-rw-r--r-- | boot_control/boot_control_qcom.cpp | 256 | ||||
-rw-r--r-- | boot_control/boot_control_qcom.h | 55 | ||||
-rw-r--r-- | boot_control/gpt.cpp | 357 | ||||
-rw-r--r-- | boot_control/gpt.h | 143 |
5 files changed, 829 insertions, 0 deletions
diff --git a/boot_control/Android.mk b/boot_control/Android.mk new file mode 100644 index 0000000..31344b6 --- /dev/null +++ b/boot_control/Android.mk @@ -0,0 +1,18 @@ +# Copyright 2015 The Android Open Source Project + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + gpt.cpp \ + boot_control_qcom.cpp + +LOCAL_CFLAGS := -Wall -Wno-missing-field-initializers +LOCAL_C_INCLUDES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include + +LOCAL_SHARED_LIBRARIES := libcutils libutils + +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE:= bootctrl.msm8916 +LOCAL_MODULE_TAGS := optional +include $(BUILD_SHARED_LIBRARY) diff --git a/boot_control/boot_control_qcom.cpp b/boot_control/boot_control_qcom.cpp new file mode 100644 index 0000000..c578578 --- /dev/null +++ b/boot_control/boot_control_qcom.cpp @@ -0,0 +1,256 @@ +/* + * 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 <errno.h> +#include <string.h> +#include <sys/types.h> +#include <cutils/log.h> +#include <hardware/hardware.h> +#include <cutils/properties.h> +#include <hardware/boot_control.h> + +#include "boot_control_qcom.h" +#include "gpt.h" + +using namespace std; + +/* Qualcomm boot_control HAL implements reading A/B slot information + * from the contents of GPT. + */ +static struct hw_module_methods_t module_methods = { + .open = nullptr, +}; + +boot_control_module_t HAL_MODULE_INFO_SYM = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = BOOT_CONTROL_HARDWARE_MODULE_ID, + .name = "Qualcomm boot_control HAL", + .author = "CodeAurora Forum", + .methods = &module_methods, + }, + .init = qcom_boot_control::init, + .getNumberSlots = qcom_boot_control::getNumberSlots, + .getCurrentSlot = qcom_boot_control::getCurrentSlot, + .markBootSuccessful = qcom_boot_control::markBootSuccessful, + .setActiveBootSlot = qcom_boot_control::setActiveBootSlot, + .setSlotAsUnbootable = qcom_boot_control::setSlotAsUnbootable, + .isSlotBootable = qcom_boot_control::isSlotBootable, + .getSuffix = qcom_boot_control::getSuffix, +}; + +namespace qcom_boot_control { + +void init(boot_control_module_t *module) +{ + ALOGV("QCOM boot control HAL."); +} + +unsigned getNumberSlots(boot_control_module_t *module) +{ + return MAX_SLOTS; +} + +unsigned getCurrentSlot(boot_control_module_t *module) +{ + char propbuf[PROPERTY_VALUE_MAX]; + + property_get("ro.boot.slot_suffix", propbuf, ""); + ALOGV("getCurrentSlot: slot suffix %s", propbuf); + + if (!strcmp(propbuf, "_a")) { + return 0; + } else if (!strcmp(propbuf, "_b")) { + return 1; + } else { + ALOGE("ERROR: unsupported slot suffix"); + return 0; + } + return 0; +} + +int markBootSuccessful(boot_control_module_t *module) +{ + std::unique_ptr<PartitionTables> gpt = + PartitionTables::read_partitions(BLK_DEV_NODE); + uint32_t partition_index; + unsigned slot; + int ret; + + if (gpt == nullptr) { + ALOGE("markBootSuccessful: read partition returns %d", errno); + return -errno; + } + + slot = getCurrentSlot(module); + + if (slot >= MAX_SLOTS) + return -EINVAL; + + if ((ret = gpt->getIndexForSlottedBootPartition(slot, partition_index))) + return ret; + + /*Clear set success and clear tries count.*/ + gpt->partition_array[partition_index].attribute_flag |= + PART_ATT_SUCCESS_MASK; + gpt->partition_array[partition_index].attribute_flag &= + ~PART_ATT_TRIES_MASK; + + if ((ret = gpt->write_partitions())) { + ALOGE("markBootSuccessful: write partition returns %d", ret); + return ret; + } + + ALOGV("markBootSuccessful: slot:%d partition:%ld", slot, partition_index); + + return 0; +} + +int setActiveBootSlot(boot_control_module_t *module, unsigned slot) +{ + std::unique_ptr<PartitionTables> gpt = + PartitionTables::read_partitions(BLK_DEV_NODE); + uint32_t partition_index; + unsigned other_slot = 1 - slot; + uint64_t attribute_flags; + int ret; + + if (gpt == nullptr) { + ALOGE("setActiveBootSlot: read partition returns %d", errno); + return -errno; + } + + if (slot >= MAX_SLOTS) + return -EINVAL; + + if ((ret = gpt->getIndexForSlottedBootPartition( slot, partition_index))) + return ret; + + /*Set priority = 15 and try count = 7 for the target slot */ + attribute_flags = + ((15ULL << PART_ATT_PRIORITY_OFFSET) & PART_ATT_PRIORITY_MASK) | + ((7ULL << PART_ATT_TRIES_OFFSET) & PART_ATT_TRIES_MASK); + + gpt->partition_array[partition_index].attribute_flag &= ~PART_ATT_ALL_MASK; + gpt->partition_array[partition_index].attribute_flag |= attribute_flags; + + if ((ret = gpt->getIndexForSlottedBootPartition(other_slot, partition_index))) + return ret; + + /*Modify priority for other slot if it has a non-zero priority */ + if (gpt->partition_array[partition_index].attribute_flag & + PART_ATT_PRIORITY_MASK) { + gpt->partition_array[partition_index].attribute_flag &= + ~PART_ATT_PRIORITY_MASK; + gpt->partition_array[partition_index].attribute_flag |= + (14ULL << PART_ATT_PRIORITY_OFFSET) & PART_ATT_PRIORITY_MASK; + } + + if ((ret = gpt->write_partitions())) { + ALOGE("setActiveBootSlot: write partition returns %d", ret); + return ret; + } + + ALOGV("setActiveBootSlot: slot %d", slot); + return 0; +} + +int setSlotAsUnbootable(struct boot_control_module *module, unsigned slot) +{ + std::unique_ptr<PartitionTables> gpt = + PartitionTables::read_partitions(BLK_DEV_NODE); + uint32_t partition_index; + int ret = 0; + + if (gpt == nullptr) { + ALOGV("setSlotAsUnbootable: read partition returns %d", -errno); + return -errno; + } + + if (slot >= MAX_SLOTS) + return -EINVAL; + + if ((ret = gpt->getIndexForSlottedBootPartition(slot, partition_index))) + return ret; + + gpt->partition_array[partition_index].attribute_flag &= ~PART_ATT_ALL_MASK; + + if ((ret = gpt->write_partitions())) { + ALOGE("setSlotAsUnbootable: write partition returns %d", ret); + return ret; + } + + ALOGV("setSlotAsUnbootable: partition index: %d, ret: %d", + partition_index, ret); + + return 0; +} + +int isSlotBootable(struct boot_control_module *module, unsigned slot) +{ + std::unique_ptr<PartitionTables> gpt = + PartitionTables::read_partitions(BLK_DEV_NODE); + uint32_t partition_index = 0; + int ret = 0; + + if (gpt == nullptr) { + ALOGE("isSlotBootable: read partition returns %d", -errno); + return -errno; + } + + if (slot >= MAX_SLOTS) + return -EINVAL; + + if ((ret = gpt->getIndexForSlottedBootPartition(slot, partition_index))) + return ret; + + if (gpt->partition_array[partition_index].attribute_flag & + PART_ATT_SUCCESS_MASK) + ret = 1; + + ALOGV("isSlotBootable: Slot: %d attribute: %llx, ret: %d", + slot, gpt->partition_array[partition_index].attribute_flag, ret); + + return ret; +} + +const char* getSuffix(boot_control_module_t *module, unsigned slot) +{ + static const char* suffix[2] = {"_a", "_b"}; + if (slot >= MAX_SLOTS) + return nullptr; + return suffix[slot]; +} + +}; //namespace qcom_boot_control diff --git a/boot_control/boot_control_qcom.h b/boot_control/boot_control_qcom.h new file mode 100644 index 0000000..812d2ed --- /dev/null +++ b/boot_control/boot_control_qcom.h @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#ifndef HARDWARE_QCOM_BOOTCTL_HAL_H +#define HARDWARE_QCOM_BOOTCTL_HAL_H + +#include <hardware/hardware.h> +#include <hardware/boot_control.h> + +namespace qcom_boot_control { + +#define BLK_DEV_NODE "/dev/block/mmcblk0" + +typedef enum { + SLOT_PROPERTY_ATTRIBUTE_FLAGS, + SLOT_PROPERTY_PARTITION_INDEX, +} slot_prop_t; + +void init(boot_control_module_t *module); +unsigned getNumberSlots(boot_control_module_t *module); +unsigned getCurrentSlot(boot_control_module_t *module); +int markBootSuccessful(boot_control_module_t *module); +int setActiveBootSlot(boot_control_module_t *module, unsigned slot); +int setSlotAsUnbootable(struct boot_control_module *module, unsigned slot); +int isSlotBootable(struct boot_control_module *module, unsigned slot); +const char* getSuffix(boot_control_module_t *module, unsigned slot); + +}; //namespace qcom_boot_control +#endif 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 + diff --git a/boot_control/gpt.h b/boot_control/gpt.h new file mode 100644 index 0000000..30f9d6f --- /dev/null +++ b/boot_control/gpt.h @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#ifndef HARDWARE_QCOM_BOOTCTL_HAL_GPT_H +#define HARDWARE_QCOM_BOOTCTL_HAL_GPT_H + +#include <string> +#include <assert.h> + +#define GPT_HDR_SIGNATURE 0x5452415020494645ULL +#define MAX_GPT_ENTRIES 128 +#define GPT_NAME_NUM_CHARS 36 +#define MAX_SLOTS 2 + +#define PART_ATT_SUCCESS_OFFSET 56 +#define PART_ATT_TRIES_OFFSET 52 +#define PART_ATT_PRIORITY_OFFSET 48 + +#define PART_ATT_SUCCESS_MASK (((uint64_t)0x1) << PART_ATT_SUCCESS_OFFSET) +#define PART_ATT_TRIES_MASK (((uint64_t)0xF) << PART_ATT_TRIES_OFFSET) +#define PART_ATT_PRIORITY_MASK (((uint64_t)0xF) << PART_ATT_PRIORITY_OFFSET) +#define PART_ATT_ALL_MASK (PART_ATT_SUCCESS_MASK | \ + PART_ATT_PRIORITY_MASK | \ + PART_ATT_TRIES_MASK) + +namespace qcom_boot_control { + +typedef struct +{ + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; +} __attribute__((packed)) guid_t; + +typedef struct gpt_header { + uint64_t signature; + uint32_t rev; + uint32_t hdr_size; + uint32_t hdr_crc32; + uint32_t reserved; + uint64_t curr_lba; + uint64_t backup_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + guid_t disk_guid; + uint64_t start_lba_part_array; + uint32_t num_parts; + uint32_t part_size; + uint32_t part_arr_crc32; + uint8_t reserved_zeros[420]; +} __attribute__((packed)) gpt_header_t; + +typedef struct gpt_partition_entry { + guid_t type_guid; + guid_t unique_partition_guid; + uint64_t first_lba; + uint64_t last_lba; + uint64_t attribute_flag; + char16_t name[GPT_NAME_NUM_CHARS]; +} __attribute__((packed)) gpt_partition_entry_t; + +class PartitionTables { +public: + /* + * read_partitions() reads the header and partition array from + * primary or secondary GPT of the disk. Secondary GPT is read in case + * primary GPT fails sanity check, errni is set if both GPT copies + * fail sanity check. + * Returns pointer to allocated object on success, + * and 'nullptr' on error with errno set. + * On failure this method will log to stderr. + */ + static std::unique_ptr<PartitionTables> + read_partitions(const std::string& device); + + /* + * write_partitions() writes the header and partition array to + * primary and secondary GPT on the disk. + * Returns 0 on success, -errno on error. + */ + int write_partitions(); + + /* + * getIndexForSlottedBootPartition() gets the partition index associated + * with the slot parameter passed. + * Returns 0 on success, and partition_index value is valid. + * REtrns -errno on error, the partition_index value is invalid. + */ + int getIndexForSlottedBootPartition(unsigned slot, + uint32_t& partition_index) const; + + /* + * gpt_sanity_check() checks the header for correctness of signature, + * size and CRC. + * Returns 'true' on success, 'false' on error. + */ + bool gpt_sanity_check(); + + gpt_header_t header; + gpt_partition_entry_t partition_array[MAX_GPT_ENTRIES]; + +private: + std::string disk_device = ""; +}; + +#if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6 +static_assert(sizeof(guid_t) == 16, "struct guid_t has wrong size"); +static_assert(sizeof(gpt_header_t) == 512, + "struct gpt_header_t has wrong size"); +static_assert(sizeof(gpt_partition_entry_t) == 128, + "struct gpt_partition_entry_t has wrong size"); +#endif + +}; //qcom_boot_control + +#endif |