summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Kondik <steve@cyngn.com>2016-07-14 14:53:51 -0700
committerSteve Kondik <steve@cyngn.com>2016-07-14 14:53:51 -0700
commit8726f0c2ae3079ff934219aaa7debd0fdc32efaa (patch)
tree1577ad71fe1024dccb14606cbb1dfc93b7a9da91
parentb80081c99e1648054230774c7cc5431c36551b37 (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.mk1
-rw-r--r--recovery/miniui/Android.mk19
-rw-r--r--recovery/miniui/msm_recovery_ui.cpp56
-rw-r--r--recovery/oem-recovery/Android.mk12
-rw-r--r--recovery/oem-recovery/dec.c401
-rw-r--r--recovery/oem-recovery/dec.h33
-rw-r--r--recovery/oem-recovery/gpt-utils.c777
-rw-r--r--recovery/oem-recovery/gpt-utils.h50
-rw-r--r--recovery/oem-recovery/oem-updater.c89
-rwxr-xr-xreleasetools.py362
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