diff options
author | Steve Kondik <steve@cyngn.com> | 2016-07-14 14:53:51 -0700 |
---|---|---|
committer | Steve Kondik <steve@cyngn.com> | 2016-07-14 14:53:51 -0700 |
commit | 8726f0c2ae3079ff934219aaa7debd0fdc32efaa (patch) | |
tree | 1577ad71fe1024dccb14606cbb1dfc93b7a9da91 | |
parent | b80081c99e1648054230774c7cc5431c36551b37 (diff) |
qcom-common: Add QC releasetools and recovery extensions
* Bring in Qualcomm's upstream tools for image creation.
* These are needed to properly write out firmware images on
many new devices, particularly devices which support
backup/failsafe partitions.
* This also supports encrypted files, and TZ-side key
descruction when wiping.
Change-Id: I4e4eaccf1f647bbd316f6bb1fa34425b4c3e411f
-rw-r--r-- | recovery/Android.mk | 1 | ||||
-rw-r--r-- | recovery/miniui/Android.mk | 19 | ||||
-rw-r--r-- | recovery/miniui/msm_recovery_ui.cpp | 56 | ||||
-rw-r--r-- | recovery/oem-recovery/Android.mk | 12 | ||||
-rw-r--r-- | recovery/oem-recovery/dec.c | 401 | ||||
-rw-r--r-- | recovery/oem-recovery/dec.h | 33 | ||||
-rw-r--r-- | recovery/oem-recovery/gpt-utils.c | 777 | ||||
-rw-r--r-- | recovery/oem-recovery/gpt-utils.h | 50 | ||||
-rw-r--r-- | recovery/oem-recovery/oem-updater.c | 89 | ||||
-rwxr-xr-x | releasetools.py | 362 |
10 files changed, 1800 insertions, 0 deletions
diff --git a/recovery/Android.mk b/recovery/Android.mk new file mode 100644 index 0000000..6571161 --- /dev/null +++ b/recovery/Android.mk @@ -0,0 +1 @@ +include $(all-subdir-makefiles) diff --git a/recovery/miniui/Android.mk b/recovery/miniui/Android.mk new file mode 100644 index 0000000..ff727d3 --- /dev/null +++ b/recovery/miniui/Android.mk @@ -0,0 +1,19 @@ +ifeq ($(TARGET_RECOVERY_UI_LIB),librecovery_ui_msm) +ifneq ($(TARGET_SIMULATOR),true) +ifneq ($(filter arm arm64, $(TARGET_ARCH)),) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE := librecovery_ui_msm + +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES += bootable/recovery + +LOCAL_SRC_FILES += msm_recovery_ui.cpp + +include $(BUILD_STATIC_LIBRARY) +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR +endif diff --git a/recovery/miniui/msm_recovery_ui.cpp b/recovery/miniui/msm_recovery_ui.cpp new file mode 100644 index 0000000..8f29050 --- /dev/null +++ b/recovery/miniui/msm_recovery_ui.cpp @@ -0,0 +1,56 @@ +/* + 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. + */ + +#include "device.h" +#include "screen_ui.h" + + +class MSM_Device : public Device +{ +public: + MSM_Device(ScreenRecoveryUI* ui) : Device(ui) {} + + bool PostWipeDevice() { + clear_keys_required = true; + return true; + } + + char const* GetRebootReason() { + if (clear_keys_required) + return "keys clear"; + return ""; + } + +private: + bool clear_keys_required = false; +}; + +Device* make_device() { + return new MSM_Device(new ScreenRecoveryUI); +} diff --git a/recovery/oem-recovery/Android.mk b/recovery/oem-recovery/Android.mk new file mode 100644 index 0000000..5b39fec --- /dev/null +++ b/recovery/oem-recovery/Android.mk @@ -0,0 +1,12 @@ +ifneq ($(filter librecovery_updater_msm,$(TARGET_RECOVERY_UPDATER_LIBS)),) +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_C_INCLUDES := bootable/recovery \ + system/core/libsparse \ + $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr/include +LOCAL_SRC_FILES := gpt-utils.c dec.c oem-updater.c +LOCAL_ADDITIONAL_DEPENDENCIES := $(TARGET_OUT_INTERMEDIATES)/KERNEL_OBJ/usr +LOCAL_MODULE := librecovery_updater_msm +include $(BUILD_STATIC_LIBRARY) +endif diff --git a/recovery/oem-recovery/dec.c b/recovery/oem-recovery/dec.c new file mode 100644 index 0000000..5f48f72 --- /dev/null +++ b/recovery/oem-recovery/dec.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2013, 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. + */ + +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/mman.h> +#include <linux/qseecom.h> +#include <linux/msm_ion.h> + +/* Service IDs */ +#define SCM_SVC_SSD 0x07 + +/* Service specific command IDs */ +#define SSD_PARSE_MD_ID 0x06 +#define SSD_DECRYPT_IMG_FRAG_ID 0x07 + +/* SSD parsing status messages from TZ */ +#define SSD_PMD_ENCRYPTED 0 +#define SSD_PMD_NOT_ENCRYPTED 1 +#define SSD_PMD_PARSING_INCOMPLETE 6 + +#define SSD_HEADER_MIN_SIZE 128 +#define MULTIPLICATION_FACTOR 2 + +#define SMCMOD_DECRYPT_REQ_OP_METADATA 1 +#define SMCMOD_DECRYPT_REQ_OP_IMG_FRAG 2 + +struct smcmod_decrypt_req { + uint32_t service_id; /* in */ + uint32_t command_id; /* in */ + uint32_t operation; /* in */ + + union { + struct { + uint32_t len; + uint32_t ion_fd; + } metadata; + struct { + uint32_t ctx_id; + uint32_t last_frag; + uint32_t frag_len; + uint32_t ion_fd; + uint32_t offset; + } img_frag; + } request; + + union { + struct { + uint32_t status; + uint32_t ctx_id; + uint32_t end_offset; + } metadata; + struct { + uint32_t status; + } img_frag; + } response; +}; + +#define SMCMOD_IOC_MAGIC 0x97 +#define SMCMOD_IOCTL_DECRYPT_CMD \ + _IOWR(SMCMOD_IOC_MAGIC, 37, struct smcmod_decrypt_req) + +struct ion_buf_handle { + unsigned char *buffer; + uint32_t buffer_len; + int ion_fd; + int ifd_data_fd; + struct ion_handle_data ion_alloc_handle; +}; + +static int +ion_memalloc(struct ion_buf_handle *buf, uint32_t size, uint32_t heap) +{ + struct ion_allocation_data alloc_data; + struct ion_fd_data fd_data; + unsigned char *va; + struct ion_handle_data handle_data; + int ion_fd; + int rc; + + ion_fd = open("/dev/ion", O_RDONLY); + if (ion_fd < 0) { + fprintf(stderr, "Cannot open ION device (%s)\n", strerror(errno)); + return -1; + } + + alloc_data.len = (size + 4095) & ~4095; + alloc_data.align = 4096; + + alloc_data.flags = 0; + alloc_data.heap_id_mask = ION_HEAP(heap); + + /* Set the buffers to be uncached */ + alloc_data.flags = 0; + + rc = ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data); + if (rc) { + fprintf(stderr, "ION buffer allocation failed (%s)\n", + strerror(errno)); + goto alloc_fail; + } + + if (alloc_data.handle) { + fd_data.handle = alloc_data.handle; + } else { + fprintf(stderr, "ION alloc data returned NULL\n"); + rc = -1; + goto alloc_fail; + } + + rc = ioctl(ion_fd, ION_IOC_MAP, &fd_data); + if (rc) { + fprintf(stderr, "ION map call failed(%s)\n", strerror(errno)); + goto ioctl_fail; + } + + va = mmap(NULL, alloc_data.len, PROT_READ | PROT_WRITE, + MAP_SHARED, fd_data.fd, 0); + if (va == MAP_FAILED) { + fprintf(stderr, "ION memory map failed (%s)\n", strerror(errno)); + rc = -1; + goto map_fail; + } + + buf->ion_fd = ion_fd; + buf->ifd_data_fd = fd_data.fd; + buf->buffer = va; + buf->ion_alloc_handle.handle = alloc_data.handle; + buf->buffer_len = alloc_data.len; + + memset(buf->buffer, 0, buf->buffer_len); + return 0; + +map_fail: +ioctl_fail: + handle_data.handle = alloc_data.handle; + if (buf->ifd_data_fd) + close(buf->ifd_data_fd); + rc = ioctl(ion_fd, ION_IOC_FREE, &handle_data); + if (rc) + fprintf(stderr, "ION free failed (%s)\n", strerror(errno)); +alloc_fail: + if (ion_fd >= 0) + close(ion_fd); + buf->ion_fd = -1; + return rc; +} + +static int ion_memfree(struct ion_buf_handle *handle) +{ + struct ion_handle_data handle_data; + int ret; + + ret = munmap(handle->buffer, (handle->buffer_len + 4095) & ~4095); + if (ret) + fprintf(stderr, "munmap failed (%s)\n", strerror(errno)); + + handle_data.handle = handle->ion_alloc_handle.handle; + close(handle->ifd_data_fd); + ret = ioctl(handle->ion_fd, ION_IOC_FREE, &handle_data); + if (ret) + fprintf(stderr, "ION free failed (%s)\n", strerror(errno)); + close(handle->ion_fd); + + return ret; +} + +static int ion_buffer_clean_inval(struct ion_buf_handle *buf, uint32_t cmd) +{ + struct ion_flush_data data; + int rc; + + data.fd = buf->ifd_data_fd; + data.vaddr = buf->buffer; + data.length = buf->buffer_len; + data.offset = 0; + data.handle = buf->ion_alloc_handle.handle; + + rc = ioctl(buf->ion_fd, cmd, &data); + if (rc < 0) + fprintf(stderr, "clean_inval cache failed (%s)\n", strerror(errno)); + + return rc; +} + +static int is_encrypted(int smcmod_fd, struct ion_buf_handle *buf, + uint32_t len, uint32_t *ctx_id, uint32_t *end_offset) +{ + struct smcmod_decrypt_req req; + uint32_t status; + int ret; + + req.service_id = SCM_SVC_SSD; + req.command_id = SSD_PARSE_MD_ID; + req.operation = SMCMOD_DECRYPT_REQ_OP_METADATA; + req.request.metadata.len = + (len >= SSD_HEADER_MIN_SIZE) ? SSD_HEADER_MIN_SIZE : len; + req.request.metadata.ion_fd = buf->ifd_data_fd; + + do { + ret = ioctl(smcmod_fd, SMCMOD_IOCTL_DECRYPT_CMD, &req); + if (ret < 0) + fprintf(stderr, "%s: ioctl ret=%d, %s\n", __func__, ret, + strerror(errno)); + + status = req.response.metadata.status; + + if (!ret && (status == SSD_PMD_PARSING_INCOMPLETE)) { + req.request.metadata.len *= MULTIPLICATION_FACTOR; + continue; + } else { + break; + } + } while (1); + + if (!ret) { + if (status == SSD_PMD_ENCRYPTED) { + *ctx_id = req.response.metadata.ctx_id; + *end_offset = req.response.metadata.end_offset; + ret = 1; + } else { + fprintf(stderr, "Image is not encrypted (response status %d)\n", + status); + } + } else { + fprintf(stderr, "%s: call failed\n", __func__); + } + + return ret; +} + +static int decrypt(int smcmod_fd, struct ion_buf_handle *buf, uint32_t len, + uint32_t md_ctx, uint32_t offset) +{ + struct smcmod_decrypt_req req; + int ret; + + req.service_id = SCM_SVC_SSD; + req.command_id = SSD_DECRYPT_IMG_FRAG_ID; + req.operation = SMCMOD_DECRYPT_REQ_OP_IMG_FRAG; + req.request.img_frag.ctx_id = md_ctx; + req.request.img_frag.last_frag = 1; + req.request.img_frag.ion_fd = buf->ifd_data_fd; + req.request.img_frag.frag_len = len - offset; + req.request.img_frag.offset = offset; + + ret = ioctl(smcmod_fd, SMCMOD_IOCTL_DECRYPT_CMD, &req); + if (ret < 0) { + fprintf(stderr, "decrypt ioctl failed (%s)\n", strerror(errno)); + return ret; + } + + return 0; +} + +static int save_file(const char *fname, unsigned char *buf, size_t len) +{ + FILE *file; + size_t written; + + file = fopen(fname, "wb"); + if (!file) { + fprintf(stderr, "Failed to open %s (%s)\n", fname, strerror(errno)); + return -errno; + } + + written = fwrite(buf, len, 1, file); + if (written != 1) { + fclose(file); + fprintf(stderr, "Failed to write %s (%s)\n", fname, strerror(errno)); + return -errno; + } + fflush(file); + fclose(file); + fprintf(stdout, "%s written %d bytes\n", fname, len); + return 0; +} + +int decrypt_image(const char *src_file, const char *dst_file) +{ + int ret = -1; + uint32_t md_ctx = 0, offset = 0; + uint32_t fsize = 0; + FILE *file = NULL; + struct ion_buf_handle ionbuf; + int smcmod_fd = -1; + int qseecom_fd = -1; + size_t read; + + memset(&ionbuf, 0, sizeof(ionbuf)); + ionbuf.ion_fd = -1; + + qseecom_fd = open("/dev/qseecom", O_RDWR); + if (qseecom_fd < 0) { + fprintf(stderr, "Failed to open /dev/qseecom device (%s)\n", + strerror(errno)); + goto exit; + } + + smcmod_fd = open("/dev/smcmod", O_RDWR); + if (smcmod_fd < 0) { + fprintf(stderr, "Failed to open /dev/smcmod device (%s)\n", + strerror(errno)); + goto exit; + } + + file = fopen(src_file, "rb"); + if (!file) { + fprintf(stderr, "Failed to open %s (%s)\n", src_file, strerror(errno)); + goto exit; + } + + fseek(file, 0, SEEK_END); + fsize = ftell(file); + fseek(file, 0, SEEK_SET); + + ret = ion_memalloc(&ionbuf, fsize, ION_PIL1_HEAP_ID); + if (ret) + goto exit; + + read = fread(ionbuf.buffer, fsize, 1, file); + if (read != 1) { + fprintf(stderr, "Failed to read %s (%s)\n", src_file, strerror(errno)); + ret = -errno; + goto exit; + } + + ret = ion_buffer_clean_inval(&ionbuf, ION_IOC_CLEAN_INV_CACHES); + if (ret < 0) + goto exit; + + ret = ioctl(qseecom_fd, QSEECOM_IOCTL_PERF_ENABLE_REQ); + if (ret < 0) + goto exit; + + ret = is_encrypted(smcmod_fd, &ionbuf, fsize, &md_ctx, &offset); + if (ret < 0) + goto exit; + + if (ret == 1) { + fprintf(stdout, "decrypting %s ...\n", src_file); + ret = decrypt(smcmod_fd, &ionbuf, fsize, md_ctx, offset); + if (ret < 0) + goto exit; + + ion_buffer_clean_inval(&ionbuf, ION_IOC_INV_CACHES); + + ret = save_file(dst_file, ionbuf.buffer + offset, fsize - offset); + if (ret < 0) + goto exit; + + fprintf(stdout, "decrypting done!\n"); + } + +exit: + if (ionbuf.ion_fd >= 0) + ion_memfree(&ionbuf); + + if (qseecom_fd >= 0) { + ioctl(qseecom_fd, QSEECOM_IOCTL_PERF_DISABLE_REQ); + close(qseecom_fd); + } + + if (smcmod_fd >= 0) + close(smcmod_fd); + + if (file) + fclose(file); + + return ret; +} diff --git a/recovery/oem-recovery/dec.h b/recovery/oem-recovery/dec.h new file mode 100644 index 0000000..b08f5b2 --- /dev/null +++ b/recovery/oem-recovery/dec.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013, 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 __DEC_H__ +#define __DEC_H__ +int decrypt_image(const char *src_file, const char *dst_file); +#endif diff --git a/recovery/oem-recovery/gpt-utils.c b/recovery/oem-recovery/gpt-utils.c new file mode 100644 index 0000000..03967b4 --- /dev/null +++ b/recovery/oem-recovery/gpt-utils.c @@ -0,0 +1,777 @@ +/* + * Copyright (c) 2013, 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 _LARGEFILE64_SOURCE /* enable lseek64() */ + +/****************************************************************************** + * INCLUDE SECTION + ******************************************************************************/ +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <linux/fs.h> +#include "gpt-utils.h" +#include "sparse_crc32.h" + + + +/****************************************************************************** + * DEFINE SECTION + ******************************************************************************/ +#define BLK_DEV_FILE "/dev/block/mmcblk0" +#define UFS_DEV_DIR "/dev/block/sda" +#define BOOT_DEV_DIR "/dev/block/bootdevice/by-name" +/* list the names of the backed-up partitions to be swapped */ +#define PTN_SWAP_LIST "sbl1", "rpm", "tz", "aboot", "hyp", "lksecapp", "keymaster", "cmnlib", "cmnlib64", "pmic" +/* extension used for the backup partitions - tzbak, abootbak, etc. */ +#define BAK_PTN_NAME_EXT "bak" + +/* GPT defines */ +#define GPT_SIGNATURE "EFI PART" +#define HEADER_SIZE_OFFSET 12 +#define HEADER_CRC_OFFSET 16 +#define PRIMARY_HEADER_OFFSET 24 +#define BACKUP_HEADER_OFFSET 32 +#define FIRST_USABLE_LBA_OFFSET 40 +#define LAST_USABLE_LBA_OFFSET 48 +#define PENTRIES_OFFSET 72 +#define PARTITION_COUNT_OFFSET 80 +#define PENTRY_SIZE_OFFSET 84 +#define PARTITION_CRC_OFFSET 88 + +#define TYPE_GUID_OFFSET 0 +#define TYPE_GUID_SIZE 16 +#define PTN_ENTRY_SIZE 128 +#define UNIQUE_GUID_OFFSET 16 +#define FIRST_LBA_OFFSET 32 +#define LAST_LBA_OFFSET 40 +#define ATTRIBUTE_FLAG_OFFSET 48 +#define PARTITION_NAME_OFFSET 56 + +#define MAX_GPT_NAME_SIZE 72 +#define MAX_PATH_LEN 255 +#define MAX_LUNS 26 +//This will allow us to get the root lun path from the path to the partition. +//i.e: from /dev/block/sdaXXX get /dev/block/sda. The assumption here is that +//the boot critical luns lie between sda to sdz which is acceptable because +//only user added external disks,etc would lie beyond that limit which do not +//contain partitions that interest us here. +#define PATH_TRUNCATE_LOC 14 + +/****************************************************************************** + * MACROS + ******************************************************************************/ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +#define GET_4_BYTES(ptr) ((uint32_t) *((uint8_t *)(ptr)) | \ + ((uint32_t) *((uint8_t *)(ptr) + 1) << 8) | \ + ((uint32_t) *((uint8_t *)(ptr) + 2) << 16) | \ + ((uint32_t) *((uint8_t *)(ptr) + 3) << 24)) + +#define GET_8_BYTES(ptr) ((uint64_t) *((uint8_t *)(ptr)) | \ + ((uint64_t) *((uint8_t *)(ptr) + 1) << 8) | \ + ((uint64_t) *((uint8_t *)(ptr) + 2) << 16) | \ + ((uint64_t) *((uint8_t *)(ptr) + 3) << 24) | \ + ((uint64_t) *((uint8_t *)(ptr) + 4) << 32) | \ + ((uint64_t) *((uint8_t *)(ptr) + 5) << 40) | \ + ((uint64_t) *((uint8_t *)(ptr) + 6) << 48) | \ + ((uint64_t) *((uint8_t *)(ptr) + 7) << 56)) + +#define PUT_4_BYTES(ptr, y) *((uint8_t *)(ptr)) = (y) & 0xff; \ + *((uint8_t *)(ptr) + 1) = ((y) >> 8) & 0xff; \ + *((uint8_t *)(ptr) + 2) = ((y) >> 16) & 0xff; \ + *((uint8_t *)(ptr) + 3) = ((y) >> 24) & 0xff; + + + +/****************************************************************************** + * TYPES + ******************************************************************************/ +enum gpt_instance { + PRIMARY_GPT = 0, + SECONDARY_GPT +}; + +enum boot_chain { + NORMAL_BOOT = 0, + BACKUP_BOOT +}; + +enum gpt_state { + GPT_OK = 0, + GPT_BAD_SIGNATURE, + GPT_BAD_CRC +}; +//List of LUN's containing boot critical images. +//Required in the case of UFS devices +struct update_data { + char lun_list[MAX_LUNS][MAX_PATH_LEN]; + int num_valid_entries; +}; + +/****************************************************************************** + * FUNCTIONS + ******************************************************************************/ +/** + * ========================================================================== + * + * \brief Read/Write len bytes from/to block dev + * + * \param [in] fd block dev file descriptor (returned from open) + * \param [in] rw RW flag: 0 - read, != 0 - write + * \param [in] offset block dev offset [bytes] - RW start position + * \param [in] buf Pointer to the buffer containing the data + * \param [in] len RW size in bytes. Buf must be at least that big + * + * \return 0 on success + * + * ========================================================================== + */ +static int blk_rw(int fd, int rw, int64_t offset, uint8_t *buf, unsigned len) +{ + int r; + + if (lseek64(fd, offset, SEEK_SET) < 0) { + fprintf(stderr, "block dev lseek64 %lld failed: %s\n", offset, + strerror(errno)); + return -1; + } + + if (rw) + r = write(fd, buf, len); + else + r = read(fd, buf, len); + + if (r < 0) + fprintf(stderr, "block dev %s failed: %s\n", rw ? "write" : "read", + strerror(errno)); + else + r = 0; + + return r; +} + + + +/** + * ========================================================================== + * + * \brief Search within GPT for partition entry with the given name + * or it's backup twin (name-bak). + * + * \param [in] ptn_name Partition name to seek + * \param [in] pentries_start Partition entries array start pointer + * \param [in] pentries_end Partition entries array end pointer + * \param [in] pentry_size Single partition entry size [bytes] + * + * \return First partition entry pointer that matches the name or NULL + * + * ========================================================================== + */ +static uint8_t *gpt_pentry_seek(const char *ptn_name, + const uint8_t *pentries_start, + const uint8_t *pentries_end, + uint32_t pentry_size) +{ + char *pentry_name; + unsigned len = strlen(ptn_name); + + for (pentry_name = (char *) (pentries_start + PARTITION_NAME_OFFSET); + pentry_name < (char *) pentries_end; pentry_name += pentry_size) { + char name8[MAX_GPT_NAME_SIZE / 2]; + unsigned i; + + /* Partition names in GPT are UTF-16 - ignoring UTF-16 2nd byte */ + for (i = 0; i < sizeof(name8); i++) + name8[i] = pentry_name[i * 2]; + if (!strncmp(ptn_name, name8, len)) + if (name8[len] == 0 || !strcmp(&name8[len], BAK_PTN_NAME_EXT)) + return (uint8_t *) (pentry_name - PARTITION_NAME_OFFSET); + } + + return NULL; +} + + + +/** + * ========================================================================== + * + * \brief Swaps boot chain in GPT partition entries array + * + * \param [in] pentries_start Partition entries array start + * \param [in] pentries_end Partition entries array end + * \param [in] pentry_size Single partition entry size + * + * \return 0 on success, 1 if no backup partitions found + * + * ========================================================================== + */ +static int gpt_boot_chain_swap(const uint8_t *pentries_start, + const uint8_t *pentries_end, + uint32_t pentry_size) +{ + const char ptn_swap_list[][MAX_GPT_NAME_SIZE] = { PTN_SWAP_LIST }; + + int backup_not_found = 1; + unsigned i; + + for (i = 0; i < ARRAY_SIZE(ptn_swap_list); i++) { + uint8_t *ptn_entry; + uint8_t *ptn_bak_entry; + uint8_t ptn_swap[PTN_ENTRY_SIZE]; + + ptn_entry = gpt_pentry_seek(ptn_swap_list[i], pentries_start, + pentries_end, pentry_size); + if (ptn_entry == NULL) + continue; + + ptn_bak_entry = gpt_pentry_seek(ptn_swap_list[i], + ptn_entry + pentry_size, pentries_end, pentry_size); + if (ptn_bak_entry == NULL) { + fprintf(stderr, "'%s' partition not backup - skip safe update\n", + ptn_swap_list[i]); + continue; + } + + /* swap primary <-> backup partition entries */ + memcpy(ptn_swap, ptn_entry, PTN_ENTRY_SIZE); + memcpy(ptn_entry, ptn_bak_entry, PTN_ENTRY_SIZE); + memcpy(ptn_bak_entry, ptn_swap, PTN_ENTRY_SIZE); + backup_not_found = 0; + } + + return backup_not_found; +} + + + +/** + * ========================================================================== + * + * \brief Sets secondary GPT boot chain + * + * \param [in] fd block dev file descriptor + * \param [in] boot Boot chain to switch to + * + * \return 0 on success + * + * ========================================================================== + */ +static int gpt2_set_boot_chain(int fd, enum boot_chain boot) +{ + int64_t gpt2_header_offset; + uint64_t pentries_start_offset; + uint32_t gpt_header_size; + uint32_t pentry_size; + uint32_t pentries_array_size; + + uint8_t *gpt_header = NULL; + uint8_t *pentries = NULL; + uint32_t crc; + uint32_t blk_size = 0; + int r; + + if (ioctl(fd, BLKSSZGET, &blk_size) != 0) { + fprintf(stderr, "Failed to get GPT device block size: %s\n", + strerror(errno)); + r = -1; + goto EXIT; + } + gpt_header = (uint8_t*)malloc(blk_size); + if (!gpt_header) { + fprintf(stderr, "Failed to allocate memory to hold GPT block\n"); + r = -1; + goto EXIT; + } + gpt2_header_offset = lseek64(fd, 0, SEEK_END) - blk_size; + if (gpt2_header_offset < 0) { + fprintf(stderr, "Getting secondary GPT header offset failed: %s\n", + strerror(errno)); + r = -1; + goto EXIT; + } + + /* Read primary GPT header from block dev */ + r = blk_rw(fd, 0, blk_size, gpt_header, blk_size); + + if (r) { + fprintf(stderr, "Failed to read primary GPT header from blk dev\n"); + goto EXIT; + } + pentries_start_offset = + GET_8_BYTES(gpt_header + PENTRIES_OFFSET) * blk_size; + pentry_size = GET_4_BYTES(gpt_header + PENTRY_SIZE_OFFSET); + pentries_array_size = + GET_4_BYTES(gpt_header + PARTITION_COUNT_OFFSET) * pentry_size; + + pentries = (uint8_t *) calloc(1, pentries_array_size); + if (pentries == NULL) { + fprintf(stderr, + "Failed to alloc memory for GPT partition entries array\n"); + r = -1; + goto EXIT; + } + /* Read primary GPT partititon entries array from block dev */ + r = blk_rw(fd, 0, pentries_start_offset, pentries, pentries_array_size); + if (r) + goto EXIT; + + crc = sparse_crc32(0, pentries, pentries_array_size); + if (GET_4_BYTES(gpt_header + PARTITION_CRC_OFFSET) != crc) { + fprintf(stderr, "Primary GPT partition entries array CRC invalid\n"); + r = -1; + goto EXIT; + } + + /* Read secondary GPT header from block dev */ + r = blk_rw(fd, 0, gpt2_header_offset, gpt_header, blk_size); + if (r) + goto EXIT; + + gpt_header_size = GET_4_BYTES(gpt_header + HEADER_SIZE_OFFSET); + pentries_start_offset = + GET_8_BYTES(gpt_header + PENTRIES_OFFSET) * blk_size; + + if (boot == BACKUP_BOOT) { + r = gpt_boot_chain_swap(pentries, pentries + pentries_array_size, + pentry_size); + if (r) + goto EXIT; + } + + crc = sparse_crc32(0, pentries, pentries_array_size); + PUT_4_BYTES(gpt_header + PARTITION_CRC_OFFSET, crc); + + /* header CRC is calculated with this field cleared */ + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, 0); + crc = sparse_crc32(0, gpt_header, gpt_header_size); + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, crc); + + /* Write the modified GPT header back to block dev */ + r = blk_rw(fd, 1, gpt2_header_offset, gpt_header, blk_size); + if (!r) + /* Write the modified GPT partititon entries array back to block dev */ + r = blk_rw(fd, 1, pentries_start_offset, pentries, + pentries_array_size); + +EXIT: + if(gpt_header) + free(gpt_header); + if (pentries) + free(pentries); + return r; +} + + + +/** + * ========================================================================== + * + * \brief Checks GPT state (header signature and CRC) + * + * \param [in] fd block dev file descriptor + * \param [in] gpt GPT header to be checked + * \param [out] state GPT header state + * + * \return 0 on success + * + * ========================================================================== + */ +static int gpt_get_state(int fd, enum gpt_instance gpt, enum gpt_state *state) +{ + int64_t gpt_header_offset; + uint32_t gpt_header_size; + uint8_t *gpt_header = NULL; + uint32_t crc; + uint32_t blk_size = 0; + + *state = GPT_OK; + + if (ioctl(fd, BLKSSZGET, &blk_size) != 0) { + fprintf(stderr, "Failed to get GPT device block size: %s\n", + strerror(errno)); + goto error; + } + fprintf(stderr, "gpt_get_state: Block size is %d\n", + blk_size); + gpt_header = (uint8_t*)malloc(blk_size); + if (!gpt_header) { + fprintf(stderr, "gpt_get_state:Failed to alloc memory for header\n"); + goto error; + } + if (gpt == PRIMARY_GPT) + gpt_header_offset = blk_size; + else { + gpt_header_offset = lseek64(fd, 0, SEEK_END) - blk_size; + if (gpt_header_offset < 0) { + fprintf(stderr, "gpt_get_state:Seek to end of GPT part fail\n"); + goto error; + } + } + + if (blk_rw(fd, 0, gpt_header_offset, gpt_header, blk_size)) { + fprintf(stderr, "gpt_get_state: blk_rw failed\n"); + goto error; + } + if (memcmp(gpt_header, GPT_SIGNATURE, sizeof(GPT_SIGNATURE))) + *state = GPT_BAD_SIGNATURE; + gpt_header_size = GET_4_BYTES(gpt_header + HEADER_SIZE_OFFSET); + + crc = GET_4_BYTES(gpt_header + HEADER_CRC_OFFSET); + /* header CRC is calculated with this field cleared */ + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, 0); + if (sparse_crc32(0, gpt_header, gpt_header_size) != crc) + *state = GPT_BAD_CRC; + free(gpt_header); + return 0; +error: + if (gpt_header) + free(gpt_header); + return -1; +} + + + +/** + * ========================================================================== + * + * \brief Sets GPT header state (used to corrupt and fix GPT signature) + * + * \param [in] fd block dev file descriptor + * \param [in] gpt GPT header to be checked + * \param [in] state GPT header state to set (GPT_OK or GPT_BAD_SIGNATURE) + * + * \return 0 on success + * + * ========================================================================== + */ +static int gpt_set_state(int fd, enum gpt_instance gpt, enum gpt_state state) +{ + int64_t gpt_header_offset; + uint32_t gpt_header_size; + uint8_t *gpt_header = NULL; + uint32_t crc; + uint32_t blk_size = 0; + + if (ioctl(fd, BLKSSZGET, &blk_size) != 0) { + fprintf(stderr, "Failed to get GPT device block size: %s\n", + strerror(errno)); + goto error; + } + gpt_header = (uint8_t*)malloc(blk_size); + if (!gpt_header) { + fprintf(stderr, "Failed to alloc memory for gpt header\n"); + goto error; + } + if (gpt == PRIMARY_GPT) + gpt_header_offset = blk_size; + else { + gpt_header_offset = lseek64(fd, 0, SEEK_END) - blk_size; + if (gpt_header_offset < 0) { + fprintf(stderr, "Failed to seek to end of GPT device\n"); + goto error; + } + } + if (blk_rw(fd, 0, gpt_header_offset, gpt_header, blk_size)) { + fprintf(stderr, "Failed to r/w gpt header\n"); + goto error; + } + if (state == GPT_OK) + memcpy(gpt_header, GPT_SIGNATURE, sizeof(GPT_SIGNATURE)); + else if (state == GPT_BAD_SIGNATURE) + *gpt_header = 0; + else { + fprintf(stderr, "gpt_set_state: Invalid state\n"); + goto error; + } + + gpt_header_size = GET_4_BYTES(gpt_header + HEADER_SIZE_OFFSET); + + /* header CRC is calculated with this field cleared */ + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, 0); + crc = sparse_crc32(0, gpt_header, gpt_header_size); + PUT_4_BYTES(gpt_header + HEADER_CRC_OFFSET, crc); + + if (blk_rw(fd, 1, gpt_header_offset, gpt_header, blk_size)) { + fprintf(stderr, "gpt_set_state: blk write failed\n"); + goto error; + } + return 0; +error: + if(gpt_header) + free(gpt_header); + return -1; +} + +//dev_path is the path to the block device that contains the GPT image that +//needs to be updated. This would be the device which holds one or more critical +//boot partitions and their backups. In the case of EMMC this function would +//be invoked only once on /dev/block/mmcblk1 since it holds the GPT image +//containing all the partitions For UFS devices it could potentially be +//invoked multiple times, once for each LUN containing critical image(s) and +//their backups +int prepare_partitions(enum boot_update_stage stage, const char *dev_path) +{ + int r; + int fd = -1; + enum gpt_state gpt_prim, gpt_second; + enum boot_update_stage internal_stage; + + if (!dev_path) { + fprintf(stderr, "Invalid dev_path passed to prepare_partitions\n"); + r = -1; + goto EXIT; + } + fd = open(dev_path, O_RDWR); + if (fd < 0) { + fprintf(stderr, "Opening '%s' failed: %s\n", BLK_DEV_FILE, + strerror(errno)); + r = -1; + goto EXIT; + } + r = gpt_get_state(fd, PRIMARY_GPT, &gpt_prim) || + gpt_get_state(fd, SECONDARY_GPT, &gpt_second); + if (r) { + fprintf(stderr, "Getting GPT headers state failed\n"); + goto EXIT; + } + + /* These 2 combinations are unexpected and unacceptable */ + if (gpt_prim == GPT_BAD_CRC || gpt_second == GPT_BAD_CRC) { + fprintf(stderr, "GPT headers CRC corruption detected, aborting\n"); + r = -1; + goto EXIT; + } + if (gpt_prim == GPT_BAD_SIGNATURE && gpt_second == GPT_BAD_SIGNATURE) { + fprintf(stderr, "Both GPT headers corrupted, aborting\n"); + r = -1; + goto EXIT; + } + + /* Check internal update stage according GPT headers' state */ + if (gpt_prim == GPT_OK && gpt_second == GPT_OK) + internal_stage = UPDATE_MAIN; + else if (gpt_prim == GPT_BAD_SIGNATURE) + internal_stage = UPDATE_BACKUP; + else if (gpt_second == GPT_BAD_SIGNATURE) + internal_stage = UPDATE_FINALIZE; + else { + fprintf(stderr, "Abnormal GPTs state: primary (%d), secondary (%d), " + "aborting\n", gpt_prim, gpt_second); + r = -1; + goto EXIT; + } + + /* Stage already set - ready for update, exitting */ + if ((int) stage == (int) internal_stage - 1) + goto EXIT; + /* Unexpected stage given */ + if (stage != internal_stage) { + r = -1; + goto EXIT; + } + + switch (stage) { + case UPDATE_MAIN: + r = gpt2_set_boot_chain(fd, BACKUP_BOOT); + if (r) { + if (r < 0) + fprintf(stderr, + "Setting secondary GPT to backup boot failed\n"); + /* No backup partitions - do not corrupt GPT, do not flag error */ + else + r = 0; + goto EXIT; + } + + r = gpt_set_state(fd, PRIMARY_GPT, GPT_BAD_SIGNATURE); + if (r) { + fprintf(stderr, "Corrupting primary GPT header failed\n"); + goto EXIT; + } + + break; + case UPDATE_BACKUP: + r = gpt_set_state(fd, PRIMARY_GPT, GPT_OK); + if (r) { + fprintf(stderr, "Fixing primary GPT header failed\n"); + goto EXIT; + } + + r = gpt_set_state(fd, SECONDARY_GPT, GPT_BAD_SIGNATURE); + if (r) { + fprintf(stderr, "Corrupting secondary GPT header failed\n"); + goto EXIT; + } + + break; + case UPDATE_FINALIZE: + r = gpt2_set_boot_chain(fd, NORMAL_BOOT); + if (r < 0) { + fprintf(stderr, "Setting secondary GPT to normal boot failed\n"); + goto EXIT; + } + + r = gpt_set_state(fd, SECONDARY_GPT, GPT_OK); + if (r) { + fprintf(stderr, "Fixing secondary GPT header failed\n"); + goto EXIT; + } + + break; + default:; + } + +EXIT: + if (fd >= 0) { + fsync(fd); + close(fd); + } + return r; +} + +int add_lun_to_update_list(char *lun_path, struct update_data *dat) +{ + int i = 0; + struct stat st; + if (!lun_path || !dat){ + fprintf(stderr, "Invalid data passed to add_lun_to_update_list"); + return -1; + } + if (stat(lun_path, &st)) { + fprintf(stderr, "Unable to access %s. Skipping adding to list", + lun_path); + return -1; + } + if (dat->num_valid_entries == 0) { + fprintf(stderr, "Copying %s into lun_list[%d]\n", + lun_path, + i); + strlcpy(dat->lun_list[0], lun_path, + MAX_PATH_LEN * sizeof(char)); + dat->num_valid_entries = 1; + } else { + for (i = 0; (i < dat->num_valid_entries) && + (dat->num_valid_entries < MAX_LUNS - 1); i++) { + //Check if the current LUN is not already part + //of the lun list + if (!strncmp(lun_path,dat->lun_list[i], + strlen(dat->lun_list[i]))) { + //LUN already in list..Return + return 0; + } + } + fprintf(stderr, "Copying %s into lun_list[%d]\n", + lun_path, + dat->num_valid_entries); + //Add LUN path lun list + strlcpy(dat->lun_list[dat->num_valid_entries], lun_path, + MAX_PATH_LEN * sizeof(char)); + dat->num_valid_entries++; + } + return 0; +} + +int prepare_boot_update(enum boot_update_stage stage) +{ + int r, fd; + int is_ufs = 0; + struct stat ufs_dir_stat; + struct update_data data; + int rcode = 0; + int i = 0; + int is_error = 0; + const char ptn_swap_list[][MAX_GPT_NAME_SIZE] = { PTN_SWAP_LIST }; + //Holds /dev/block/bootdevice/by-name/*bak entry + char buf[MAX_PATH_LEN] = {0}; + //Holds the resolved path of the symlink stored in buf + char real_path[MAX_PATH_LEN] = {0}; + + if(stat(UFS_DEV_DIR, &ufs_dir_stat)) { + is_ufs = 0; + } else { + fprintf(stderr, "UFS device detected\n"); + is_ufs = 1; + } + if (!is_ufs) { + //emmc device. Just pass in path to mmcblk0 + return prepare_partitions(stage, BLK_DEV_FILE); + } else { + //Now we need to find the list of LUNs over + //which the boot critical images are spread + //and set them up for failsafe updates.To do + //this we find out where the symlinks for the + //each of the paths under + ///dev/block/bootdevice/by-name/PTN_SWAP_LIST + //actually point to. + memset(&data, '\0', sizeof(struct update_data)); + for (i=0; i < ARRAY_SIZE(ptn_swap_list); i++) { + snprintf(buf, sizeof(buf), + "%s/%sbak", + BOOT_DEV_DIR, + ptn_swap_list[i]); + fprintf(stderr, "Attempting to process %s\n", buf); + if (stat(buf, &ufs_dir_stat)) { + fprintf(stderr, "%s not present. Skipping\n", + buf); + continue; + } + if (readlink(buf, real_path, sizeof(real_path)) < 0) + { + fprintf(stderr, "readlink error. Skipping %s", + strerror(errno)); + } else { + real_path[PATH_TRUNCATE_LOC] = '\0'; + add_lun_to_update_list(real_path, &data); + } + memset(buf, '\0', sizeof(buf)); + memset(real_path, '\0', sizeof(real_path)); + } + for (i=0; i < data.num_valid_entries; i++) { + fprintf(stderr, "Preparing %s for update stage %d\n", + data.lun_list[i], + stage); + rcode = prepare_partitions(stage, data.lun_list[i]); + if (rcode != 0) + { + fprintf(stderr, "Failed to prepare %s.Continuing..\n", + data.lun_list[i]); + is_error = 1; + } + } + } + if (is_error) + return -1; + return 0; +} diff --git a/recovery/oem-recovery/gpt-utils.h b/recovery/oem-recovery/gpt-utils.h new file mode 100644 index 0000000..637fb33 --- /dev/null +++ b/recovery/oem-recovery/gpt-utils.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013, 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 __GPT_UTILS_H__ +#define __GPT_UTILS_H__ +#include <unistd.h> +#include <stdlib.h> +/****************************************************************************** + * TYPES + ******************************************************************************/ +enum boot_update_stage { + UPDATE_MAIN = 1, + UPDATE_BACKUP, + UPDATE_FINALIZE +}; + + + +/****************************************************************************** + * FUNCTION PROTOTYPES + ******************************************************************************/ +int prepare_boot_update(enum boot_update_stage stage); + +#endif /* __GPT_UTILS_H__ */ diff --git a/recovery/oem-recovery/oem-updater.c b/recovery/oem-recovery/oem-updater.c new file mode 100644 index 0000000..f74da83 --- /dev/null +++ b/recovery/oem-recovery/oem-updater.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013, 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "edify/expr.h" +#include "dec.h" +#include "gpt-utils.h" + +Value* DecryptFn(const char* name, State* state, int argc, Expr* argv[]) { + int rc = -1; + const char *src_file, *dst_file; + + if (argc != 2) + return ErrorAbort(state, "%s expects 2 args, got %d", name, argc); + + if (ReadArgs(state, argv, 2, &src_file, &dst_file)) + return NULL; + + rc = decrypt_image(src_file, dst_file); + + free(src_file); + free(dst_file); + + return StringValue(strdup(rc >= 0 ? "t" : "")); +} + +Value* BootUpdateFn(const char* name, State* state, int argc, Expr* argv[]) +{ + int rc = 0; + char *stageStr; + enum boot_update_stage stage; + + if (argc != 1) + return ErrorAbort(state, "%s() expects 1 args, got %d", name, argc); + + if (ReadArgs(state, argv, 1, &stageStr)) + return NULL; + + if (!strcmp(stageStr, "main")) + stage = UPDATE_MAIN; + else if (!strcmp(stageStr, "backup")) + stage = UPDATE_BACKUP; + else if (!strcmp(stageStr, "finalize")) + stage = UPDATE_FINALIZE; + else { + fprintf(stderr, "Unrecognized boot update stage, exitting\n"); + rc = -1; + } + + free(stageStr); + + if (!rc) + rc = prepare_boot_update(stage); + + return StringValue(strdup(rc ? "" : "t")); +} + +void Register_librecovery_updater_msm() { + RegisterFunction("msm.decrypt", DecryptFn); + RegisterFunction("msm.boot_update", BootUpdateFn); +} diff --git a/releasetools.py b/releasetools.py new file mode 100755 index 0000000..f50b751 --- /dev/null +++ b/releasetools.py @@ -0,0 +1,362 @@ +# Copyright (C) 2009 The Android Open Source Project +# Copyright (c) 2011-2013, The Linux Foundation. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Emit commands needed for QCOM devices during OTA installation +(installing the radio image).""" + +import common +import re + + +bootImages = {} +binImages = {} +fwImages = {} + + +# Parse filesmap file containing firmware residing places +def LoadFilesMap(zip, name="RADIO/filesmap"): + try: + data = zip.read(name) + except KeyError: + print "Warning: could not find %s in %s." % (name, zip) + data = "" + d = {} + for line in data.split("\n"): + line = line.strip() + if not line or line.startswith("#"): + continue + pieces = line.split() + if not (len(pieces) == 2): + raise ValueError("malformed filesmap line: \"%s\"" % (line,)) + d[pieces[0]] = pieces[1] + return d + + +# Read firmware images from target files zip +def GetRadioFiles(z): + out = {} + for info in z.infolist(): + f = info.filename + if f.startswith("RADIO/") and (f.__len__() > len("RADIO/")): + fn = f[6:] + if fn.startswith("filesmap"): + continue + data = z.read(f) + out[fn] = common.File(f, data) + return out + + +# Get firmware residing place from filesmap +def GetFileDestination(fn, filesmap): + # if file is encoded disregard the .enc extention + if fn.endswith('.enc'): + fn = fn[:-4] + + # get backup destination as well if present + backup = None + if fn + ".bak" in filesmap: + backup = filesmap[fn + ".bak"] + + # If full filename is not specified in filesmap get only the name part + # and look for this token + if fn not in filesmap: + fn = fn.split(".")[0] + ".*" + if fn not in filesmap: + print "warning radio-update: '%s' not found in filesmap" % (fn) + return None, backup + return filesmap[fn], backup + + +# Separate image types as each type needs different handling +def SplitFwTypes(files): + boot = {} + bin = {} + fw = {} + + for f in files: + extIdx = -1 + dotSeparated = f.split(".") + while True: + if dotSeparated[extIdx] != 'p' and dotSeparated[extIdx] != 'enc': + break + extIdx -= 1 + + if dotSeparated[extIdx] == 'mbn' or dotSeparated[extIdx] == 'elf': + boot[f] = files[f] + elif dotSeparated[extIdx] == 'bin': + bin[f] = files[f] + else: + fw[f] = files[f] + return boot, bin, fw + + +# Prepare radio-update files and verify them +def OTA_VerifyEnd(info, api_version, target_zip, source_zip=None): + if api_version < 3: + print "warning radio-update: no support for api_version less than 3" + return False + + print "Loading radio filesmap..." + filesmap = LoadFilesMap(target_zip) + if filesmap == {}: + print "warning radio-update: no or invalid filesmap file found" + return False + + print "Loading radio target..." + tgt_files = GetRadioFiles(target_zip) + if tgt_files == {}: + print "warning radio-update: no radio images in input target_files" + return False + + src_files = None + if source_zip is not None: + print "Loading radio source..." + src_files = GetRadioFiles(source_zip) + + update_list = {} + largest_source_size = 0 + + print "Preparing radio-update files..." + for fn in tgt_files: + dest, destBak = GetFileDestination(fn, filesmap) + if dest is None: + continue + + tf = tgt_files[fn] + sf = None + if src_files is not None: + sf = src_files.get(fn, None) + + full = sf is None or fn.endswith('.enc') + if not full: + # no difference - skip this file + if tf.sha1 == sf.sha1: + continue + d = common.Difference(tf, sf) + _, _, d = d.ComputePatch() + # no difference - skip this file + if d is None: + continue + # if patch is almost as big as the file - don't bother patching + full = len(d) > tf.size * common.OPTIONS.patch_threshold + if not full: + f = "patch/firmware-update/" + fn + ".p" + common.ZipWriteStr(info.output_zip, f, d) + update_list[f] = (dest, destBak, tf, sf) + largest_source_size = max(largest_source_size, sf.size) + if full: + f = "firmware-update/" + fn + common.ZipWriteStr(info.output_zip, f, tf.data) + update_list[f] = (dest, destBak, None, None) + + global bootImages + global binImages + global fwImages + bootImages, binImages, fwImages = SplitFwTypes(update_list) + + # If there are incremental patches verify them + if largest_source_size != 0: + info.script.Comment("---- radio update verification ----") + info.script.Print("Verifying radio-update...") + + for f in bootImages: + dest, destBak, tf, sf = bootImages[f] + # Not incremental + if sf is None: + continue + info.script.PatchCheck("EMMC:%s:%d:%s:%d:%s" % + (dest, sf.size, sf.sha1, tf.size, tf.sha1)) + if destBak is not None: + info.script.PatchCheck("EMMC:%s:%d:%s:%d:%s" % + (destBak, sf.size, sf.sha1, tf.size, tf.sha1)) + for f in binImages: + dest, destBak, tf, sf = binImages[f] + # Not incremental + if sf is None: + continue + info.script.PatchCheck("EMMC:%s:%d:%s:%d:%s" % + (dest, sf.size, sf.sha1, tf.size, tf.sha1)) + + last_mounted = "" + for f in fwImages: + dest, destBak, tf, sf = fwImages[f] + # Not incremental + if sf is None: + continue + # Get the filename without the path and the patch (.p) extention + f = f.split("/")[-1][:-2] + # Parse filesmap destination paths for "/dev/" pattern in the beginng. + # This would mean that the file must be written to block device - + # fs mount needed + if dest.startswith("/dev/"): + if last_mounted != dest: + info.script.AppendExtra('unmount("/firmware");') + info.script.AppendExtra('mount("vfat", "EMMC", "%s", "/firmware");' % + (dest)) + last_mounted = dest + dest = "/firmware/image/" + f + else: + dest = dest + "/" + f + info.script.PatchCheck(dest, tf.sha1, sf.sha1) + + info.script.CacheFreeSpaceCheck(largest_source_size) + return True + + +def FullOTA_Assertions(info): + #TODO: Implement device specific asserstions. + return + + +def IncrementalOTA_Assertions(info): + #TODO: Implement device specific asserstions. + return + + +def IncrementalOTA_VerifyEnd(info): + OTA_VerifyEnd(info, info.target_version, info.target_zip, info.source_zip) + return + + +# This function handles only non-HLOS whole partition images +def InstallRawImage(script, f, dest, tf, sf): + if f.endswith('.p'): + script.ApplyPatch("EMMC:%s:%d:%s:%d:%s" % + (dest, sf.size, sf.sha1, tf.size, tf.sha1), + "-", tf.size, tf.sha1, sf.sha1, f) + elif f.endswith('.enc'): + # Get the filename without the path + fn = f.split("/")[-1] + script.AppendExtra('package_extract_file("%s", "/tmp/%s");' % (f, fn)) + script.AppendExtra('msm.decrypt("/tmp/%s", "%s");' % (fn, dest)) + else: + script.AppendExtra('package_extract_file("%s", "%s");' % (f, dest)) + return + + +# This function handles only non-HLOS boot images - files list must contain +# only such images (aboot, tz, etc) +def InstallBootImages(script, files): + bakExists = False + # update main partitions + script.AppendExtra('ifelse(msm.boot_update("main"), (') + for f in files: + dest, destBak, tf, sf = files[f] + if destBak is not None: + bakExists = True + InstallRawImage(script, f, dest, tf, sf) + script.AppendExtra('), "");') + + # update backup partitions + if bakExists: + script.AppendExtra('ifelse(msm.boot_update("backup"), (') + for f in files: + dest, destBak, tf, sf = files[f] + if destBak is not None: + InstallRawImage(script, f, destBak, tf, sf) + script.AppendExtra('), "");') + # just finalize primary update stage + else: + script.AppendExtra('msm.boot_update("backup");') + + # finalize partitions update + script.AppendExtra('msm.boot_update("finalize");') + return + + +# This function handles only non-HLOS bin images +def InstallBinImages(script, files): + for f in files: + dest, _, tf, sf = files[f] + InstallRawImage(script, f, dest, tf, sf) + return + + +# This function handles only non-HLOS firmware files that are not whole +# partition images (modem, dsp, etc) +def InstallFwImages(script, files): + last_mounted = "" + + for f in files: + dest, _, tf, sf = files[f] + # Get the filename without the path + fn = f.split("/")[-1] + # Parse filesmap destination paths for "/dev/" pattern in the beginng. + # This would mean that the file must be written to block device - + # fs mount needed + if dest.startswith("/dev/"): + if last_mounted != dest: + script.AppendExtra('unmount("/firmware");') + script.AppendExtra('mount("vfat", "EMMC", "%s", "/firmware");' % + (dest)) + last_mounted = dest + dest = "/firmware/image/" + fn + else: + dest = dest + "/" + fn + + if f.endswith('.p'): + script.ApplyPatch(dest[:-2], "-", tf.size, tf.sha1, sf.sha1, f) + elif f.endswith('.enc'): + script.AppendExtra('package_extract_file("%s", "/tmp/%s");' % (f, fn)) + script.AppendExtra('msm.decrypt("/tmp/%s", "%s");' % (fn, dest[:-4])) + else: + script.AppendExtra('package_extract_file("%s", "%s");' % (f, dest)) + + if last_mounted != "": + script.AppendExtra('unmount("/firmware");') + return + + +def OTA_InstallEnd(info): + print "Applying radio-update script modifications..." + info.script.Comment("---- radio update tasks ----") + info.script.Print("Patching firmware images...") + + if bootImages != {}: + InstallBootImages(info.script, bootImages) + if binImages != {}: + InstallBinImages(info.script, binImages) + if fwImages != {}: + InstallFwImages(info.script, fwImages) + return + + +def FullOTA_InstallEnd_MMC(info): + if OTA_VerifyEnd(info, info.input_version, info.input_zip): + OTA_InstallEnd(info) + return + + +def FullOTA_InstallEnd_MTD(info): + print "warning radio-update: radio update for NAND devices not supported" + return + + +def FullOTA_InstallEnd(info): + FullOTA_InstallEnd_MMC(info) + return + +def IncrementalOTA_InstallEnd_MMC(info): + OTA_InstallEnd(info) + return + + +def IncrementalOTA_InstallEnd_MTD(info): + print "warning radio-update: radio update for NAND devices not supported" + return + +def IncrementalOTA_InstallEnd(info): + IncrementalOTA_InstallEnd_MMC(info) + return |