| /* Copyright Statement: |
| * |
| * This software/firmware and related documentation ("MediaTek Software") are |
| * protected under relevant copyright laws. The information contained herein is |
| * confidential and proprietary to MediaTek Inc. and/or its licensors. Without |
| * the prior written permission of MediaTek inc. and/or its licensors, any |
| * reproduction, modification, use or disclosure of MediaTek Software, and |
| * information contained herein, in whole or in part, shall be strictly |
| * prohibited. |
| * |
| * SPDX-License-Identifier: MediaTekProprietary |
| * MediaTek Inc. (C) 2016. All rights reserved. |
| * |
| * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES |
| * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") |
| * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER |
| * ON AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL |
| * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR |
| * NONINFRINGEMENT. NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH |
| * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, |
| * INCORPORATED IN, OR SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES |
| * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. |
| * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO |
| * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN MEDIATEK |
| * SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK SOFTWARE |
| * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR |
| * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S |
| * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE |
| * RELEASED HEREUNDER WILL BE, AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE |
| * MEDIATEK SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE |
| * CHARGE PAID BY RECEIVER TO MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. |
| * |
| * The following software/firmware and/or related documentation ("MediaTek |
| * Software") have been modified by MediaTek Inc. All revisions are subject to |
| * any receiver's applicable license agreements with MediaTek Inc. |
| */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <hardware/hardware.h> |
| #include <hardware/boot_control.h> |
| //#include <android-base/logging.h> |
| //#include <cutils/properties.h> |
| #define LOG_TAG "bootctrlHAL" |
| //#include <log/log.h> |
| //#include <utils/Log.h> |
| |
| #include "bootctrl.h" |
| #if !defined(ARCH_X86) |
| #include "sd_misc.h" |
| #endif |
| //#include <fstab/fstab.h> |
| #include "avb/libavb/avb_util.h" |
| |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| extern "C"{ |
| #include "mtk_device_wrap.h" |
| } |
| #endif |
| |
| #define FSTAB_SUPPORT 0 |
| // Debug for update_engine_sideload |
| #define ALOGE printf |
| #define ALOGI printf |
| static struct fstab* fstab = NULL; |
| static char *blk_dev_path = "/dev/disk/by-partlabel/misc"; |
| #if FSTAB_SUPPORT |
| static void free_fstab(void) |
| { |
| fs_mgr_free_fstab(fstab); |
| } |
| |
| |
| static char *get_device_path(const char *mount_point) |
| { |
| struct fstab_rec *rec = NULL; |
| char *source = NULL; |
| |
| rec = fs_mgr_get_entry_for_mount_point(fstab, mount_point); |
| if (!rec) { |
| ALOGE("%s failed to get entry for %s \n", __func__ , mount_point); |
| return NULL; |
| } |
| |
| source = strdup(rec->blk_device); |
| return source; |
| } |
| #endif |
| static int bootctrl_read_metadata(AvbABData *bctrl) |
| { |
| int fd, err; |
| ssize_t sz, size; |
| char *buf = (char *)bctrl; |
| |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| fd = mtk_device_wrap_open(blk_dev_path, O_RDONLY); |
| #else |
| fd = open(blk_dev_path, O_RDONLY); |
| #endif |
| |
| if (fd < 0) { |
| err = errno; |
| ALOGE("%s Error opening metadata file: %s\n", __func__ ,strerror(errno)); |
| return -err; |
| } |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| if (mtk_device_wrap_seek(fd, OFFSETOF_SLOT_SUFFIX, SEEK_SET) < 0) |
| #else |
| if (lseek(fd, OFFSETOF_SLOT_SUFFIX, SEEK_SET) < 0) |
| #endif |
| { |
| err = errno; |
| ALOGE("%s Error seeking to metadata offset: %s\n", __func__ ,strerror(errno)); |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| mtk_device_wrap_close(fd); |
| #else |
| close(fd); |
| #endif |
| return -err; |
| } |
| size = sizeof(AvbABData); |
| do { |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| sz = mtk_device_wrap_read(fd, buf, size); |
| #else |
| sz = read(fd, buf, size); |
| #endif |
| if (sz == 0) { |
| break; |
| } else if (sz < 0) { |
| if (errno == EINTR) { |
| continue; |
| } |
| err = -errno; |
| ALOGE("%s Error reading metadata file\n", __func__); |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| mtk_device_wrap_close(fd); |
| #else |
| close(fd); |
| #endif |
| return err; |
| } |
| size -= sz; |
| buf += sz; |
| } while(size > 0); |
| |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| mtk_device_wrap_close(fd); |
| #else |
| close(fd); |
| #endif |
| |
| /* Check bootctrl magic number */ |
| if (memcmp(bctrl->magic , AVB_AB_MAGIC, AVB_AB_MAGIC_LEN) != 0) { |
| ALOGE("metadata is not initialised or corrupted %s.\n", bctrl->magic); |
| return -EIO; |
| } |
| return 0; |
| } |
| |
| static int bootctrl_write_metadata(AvbABData *bctrl) |
| { |
| int fd, err; |
| ssize_t sz, size; |
| char *buf = (char *)bctrl; |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| char *tmpbuf = NULL; |
| #endif |
| |
| ALOGI("Enter in bootctrl: bootctrl_write_metadata \n"); |
| |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| fd = mtk_device_wrap_open(blk_dev_path, O_RDWR); |
| #else |
| fd = open(blk_dev_path, O_RDWR); |
| #endif |
| if (fd < 0) { |
| err = errno; |
| ALOGE("%s Error opening metadata file: %s\n", __func__,strerror(errno)); |
| return -err; |
| } |
| |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| if (mtk_device_wrap_seek(fd, OFFSETOF_SLOT_SUFFIX, SEEK_SET) < 0) |
| #else |
| if (lseek(fd, OFFSETOF_SLOT_SUFFIX, SEEK_SET) < 0) |
| #endif |
| { |
| err = errno; |
| ALOGE("%s Error seeking to metadata offset: %s\n", __func__ ,strerror(errno)); |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| mtk_device_wrap_close(fd); |
| #else |
| close(fd); |
| #endif |
| return -err; |
| } |
| |
| bctrl->crc32 = avb_htobe32( |
| avb_crc32((const uint8_t*)bctrl, sizeof(AvbABData) - sizeof(uint32_t))); |
| |
| size = sizeof(AvbABData); |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| tmpbuf = (char *)malloc(sizeof(char)* OFFSETOF_SLOT_SUFFIX); |
| if (tmpbuf == NULL){ |
| ALOGE("Error Writing metadata: malloc tmpbuf failed\n"); |
| mtk_device_wrap_close(fd); |
| err = -errno; |
| return err; |
| } |
| #endif |
| |
| do { |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| memset(tmpbuf, 0xFF, OFFSETOF_SLOT_SUFFIX); |
| if (mtk_device_wrap_seek(fd, 0, SEEK_SET) < 0){ |
| err = errno; |
| ALOGE("%s Error seeking to metadata offset: %s\n", __func__ ,strerror(errno)); |
| mtk_device_wrap_close(fd); |
| free(tmpbuf); |
| return -err; |
| } |
| if (mtk_device_wrap_write_force(fd, tmpbuf, OFFSETOF_SLOT_SUFFIX) < 0){ |
| err = -errno; |
| ALOGE("%s Error Writing metadata file\n",__func__); |
| mtk_device_wrap_close(fd); |
| free(tmpbuf); |
| return err; |
| } |
| memcpy(tmpbuf, bctrl, size); |
| sz = mtk_device_wrap_write_force(fd, tmpbuf, sizeof(char)* OFFSETOF_SLOT_SUFFIX); |
| #else |
| sz = write(fd, buf, size); |
| #endif |
| if (sz == 0) { |
| break; |
| } else if (sz < 0) { |
| if (errno == EINTR) { |
| continue; |
| } |
| err = -errno; |
| ALOGE("%s Error Writing metadata file\n",__func__); |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| mtk_device_wrap_close(fd); |
| free(tmpbuf); |
| #else |
| close(fd); |
| #endif |
| return err; |
| } |
| size -= sz; |
| buf += sz; |
| } while(size > 0); |
| |
| #if MTK_BOOTDEV_TYPE == BOOTDEV_TYPE_NAND |
| free(tmpbuf); |
| mtk_device_wrap_close(fd); |
| #else |
| close(fd); |
| #endif |
| |
| return 0; |
| } |
| |
| void bootctrl_init(boot_control_module_t *module ) |
| { |
| ALOGI("boot control HAL init"); |
| |
| if(blk_dev_path == NULL) { |
| #if FSTAB_SUPPORT |
| /* Initial read fstab */ |
| fstab = fs_mgr_read_fstab_default(); |
| if (!fstab) { |
| ALOGE("failed to read default fstab"); |
| } |
| blk_dev_path = get_device_path("/misc"); |
| |
| /* Free fstab */ |
| free_fstab(); |
| #endif |
| } |
| |
| ALOGI("%s misc blk device path = %s\n", __func__ ,blk_dev_path); |
| } |
| |
| unsigned bootctrl_get_number_slots(boot_control_module_t *module ) |
| { |
| return 2; |
| } |
| |
| int bootctrl_get_active_slot() |
| { |
| int fd, err, slot; |
| ssize_t size = COMMAND_LINE_SIZE, sz; |
| char *buf, *ptr; |
| char *str; |
| |
| fd = open(COMMAND_LINE_PATH, O_RDONLY); |
| |
| if (fd < 0) { |
| err = -errno; |
| ALOGE("%s error reading commandline\n", __func__); |
| return err; |
| } |
| ptr = buf = (char *)malloc(size); |
| if (!buf) { |
| err = -errno; |
| ALOGE("%s Error allocating memory\n", __func__); |
| close(fd); |
| return err; |
| } |
| do { |
| sz = read(fd, buf, size); |
| if (sz == 0) { |
| break; |
| } else if (sz < 0) { |
| if (errno == EINTR) { |
| continue; |
| } |
| err = -errno; |
| ALOGE("%s Error reading file\n",__func__); |
| free(ptr); |
| close(fd); |
| return err; |
| } |
| size -= sz; |
| buf += sz; |
| } while(size > 0); |
| str = strstr((char *)ptr, SLOT_SUFFIX_STR); |
| if (!str) { |
| err = -EIO; |
| ALOGE("%s cannot find %s in kernel commandline.\n", __func__ , SLOT_SUFFIX_STR); |
| free(ptr); |
| close(fd); |
| return err; |
| } |
| str += strlen(SLOT_SUFFIX_STR) + 1; |
| //str += strlen(SLOT_SUFFIX_STR); |
| slot = (*str == 'a') ? 0 : 1; |
| free(ptr); |
| close(fd); |
| return slot; |
| } |
| |
| |
| uint32_t bootctrl_get_current_slot(boot_control_module_t *module ) |
| { |
| ALOGI("boot control bootctrl_get_current_slot\n"); |
| |
| uint32_t slot = 0; |
| |
| slot = bootctrl_get_active_slot(); |
| |
| ALOGI("bootctrl_get_current_slot %d\n", slot); |
| return slot; |
| } |
| |
| int bootctrl_mark_boot_successful(boot_control_module_t *module ) |
| { |
| ALOGI("boot control bootctrl_mark_boot_successful\n"); |
| int ret; |
| int slot = 0; |
| AvbABData metadata; |
| AvbABSlotData *slotp; |
| |
| ret = bootctrl_read_metadata(&metadata); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| slot = bootctrl_get_active_slot(); |
| if (slot < 0) { |
| ALOGE("bootctrl_mark_boot_successful fail , slot = \n"); |
| return slot; |
| } |
| slotp = &metadata.slots[slot]; |
| slotp->successful_boot = 1; |
| slotp->tries_remaining = 0; |
| |
| return bootctrl_write_metadata(&metadata); |
| } |
| |
| int bootctrl_set_active_boot_slot(boot_control_module_t *module , |
| unsigned slot) |
| { |
| ALOGI("boot control bootctrl_set_active_boot_slot , slot is %d\n", slot); |
| int ret, slot2; |
| AvbABData metadata; |
| AvbABSlotData *slotp; |
| |
| if (slot >= 2) { |
| ALOGE("%s Wrong Slot value %u\n", __func__ , slot); |
| return -EINVAL; |
| } |
| ret = bootctrl_read_metadata(&metadata); |
| if (ret < 0) { |
| return ret; |
| } |
| /* Set highest priority and reset retry count */ |
| slotp = &metadata.slots[slot]; |
| slotp->successful_boot = 0; |
| slotp->priority = AVB_AB_MAX_PRIORITY; |
| slotp->tries_remaining = AVB_AB_MAX_TRIES_REMAINING; |
| |
| /* Ensure other slot doesn't have as high a priority. */ |
| slot2 = (slot == 0) ? 1 : 0; |
| slotp = &metadata.slots[slot2]; |
| if(slotp->priority == AVB_AB_MAX_PRIORITY) |
| slotp->priority = AVB_AB_MAX_PRIORITY - 1; |
| ret = bootctrl_write_metadata(&metadata); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| int bootctrl_set_slot_as_unbootable(boot_control_module_t *module , |
| unsigned slot) |
| { |
| ALOGI("boot control bootctrl_set_slot_as_unbootable\n"); |
| int ret; |
| AvbABData metadata; |
| AvbABSlotData *slotp; |
| |
| if (slot >= 2) { |
| ALOGE("%s Wrong Slot value %u\n", __func__ , slot); |
| return -EINVAL; |
| } |
| ret = bootctrl_read_metadata(&metadata); |
| if (ret < 0) { |
| return ret; |
| } |
| /* Set zero to priority ,successful_boot and tries_remaining */ |
| slotp = &metadata.slots[slot]; |
| slotp->successful_boot = 0; |
| slotp->priority = 0; |
| slotp->tries_remaining = 0; |
| ret = bootctrl_write_metadata(&metadata); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| int bootctrl_is_slot_bootable(boot_control_module_t *module , |
| unsigned slot) |
| { |
| ALOGI("boot control bootctrl_is_slot_bootable\n"); |
| int ret; |
| AvbABData metadata; |
| |
| /* slot 0 is A , slot 1 is B */ |
| if (slot >= 2) { |
| ALOGE("%s Wrong slot value %u\n", __func__,slot); |
| return -EINVAL; |
| } |
| ret = bootctrl_read_metadata(&metadata); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| return (metadata.slots[slot].priority != 0); |
| } |
| |
| int bootctrl_get_bootup_status(boot_control_module_t *module , |
| unsigned slot) |
| { |
| ALOGI("bootctrl bootctrl_get_bootup_status\n"); |
| int ret = -1; |
| AvbABSlotData *slotp; |
| AvbABData metadata; |
| |
| if(slot >= 2) { |
| ALOGE("%s Wrong slot value %u\n", __func__,slot); |
| return -1; |
| } |
| |
| ret = bootctrl_read_metadata(&metadata); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| slotp = &metadata.slots[slot]; |
| |
| ALOGI("bootctrl bootctrl_get_bootup_status = %d\n", slotp->successful_boot); |
| return slotp->successful_boot; |
| } |
| |
| const char *bootctrl_get_suffix(boot_control_module_t *module , |
| unsigned slot) |
| { |
| ALOGI("boot control bootctrl_get_suffix %d\n",slot); |
| static const char* suffix[2] = {BOOTCTRL_SUFFIX_A, BOOTCTRL_SUFFIX_B}; |
| if (slot >= 2) |
| return NULL; |
| return suffix[slot]; |
| } |
| |
| static struct hw_module_methods_t bootctrl_methods = { |
| .open = NULL, |
| }; |
| |
| /* Boot Control Module implementation */ |
| boot_control_module_t HAL_MODULE_INFO_SYM = { |
| .common = { |
| .tag = HARDWARE_MODULE_TAG, |
| .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, |
| .hal_api_version = HARDWARE_HAL_API_VERSION, |
| .id = BOOT_CONTROL_HARDWARE_MODULE_ID, |
| .name = "boot_control HAL", |
| .author = "Mediatek Corporation", |
| .methods = &bootctrl_methods, |
| }, |
| .init = bootctrl_init, |
| .getNumberSlots = bootctrl_get_number_slots, |
| .getCurrentSlot = bootctrl_get_current_slot, |
| .markBootSuccessful = bootctrl_mark_boot_successful, |
| .setActiveBootSlot = bootctrl_set_active_boot_slot, |
| .setSlotAsUnbootable = bootctrl_set_slot_as_unbootable, |
| .isSlotBootable = bootctrl_is_slot_bootable, |
| .getSuffix = bootctrl_get_suffix, |
| .isSlotMarkedSuccessful = bootctrl_get_bootup_status, |
| }; |