| /* |
| * Copyright 2015 The Android Open Source Project |
| * Copyright (C) 2015 Freescale Semiconductor, Inc. |
| * |
| * 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. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| #include <assert.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware/boot_control.h> |
| #include <cutils/log.h> |
| #include <cutils/properties.h> |
| #include <external/zlib/zlib.h> |
| #include "bootctl.h" |
| |
| static uint32_t g_dwCurSlot; |
| static bool g_bInited; |
| static const char *g_aSlotSuffix[SLOT_NUM] = { "_a", "_b" }; |
| |
| #define INIT_CHECK do{ if(!g_bInited) {ALOGE("%s, module not inited", __FUNCTION__); return -EINVAL;} }while(0) |
| #define SLOT_CHECK(slot, retval) do { if(slot >= SLOT_NUM) {ALOGE("%s, slot %d too large", __FUNCTION__, slot); return retval;} }while(0) |
| #define RET_CHECK(ret, func) do { if(ret < 0){ ALOGE("%s, %s failed", __FUNCTION__, #func); return ret;} }while(0) |
| #define TRACE ALOGI("enter %s", __FUNCTION__) |
| |
| void bootctl_init(struct boot_control_module *module); |
| unsigned bootctl_getNumberSlots(struct boot_control_module *module); |
| unsigned bootctl_getCurrentSlot(struct boot_control_module *module); |
| int bootctl_markBootSuccessful(struct boot_control_module *module); |
| int bootctl_setActiveBootSlot(struct boot_control_module *module, unsigned slot); |
| int bootctl_setSlotAsUnbootable(struct boot_control_module *module, unsigned slot); |
| int bootctl_isSlotBootable(struct boot_control_module *module, unsigned slot); |
| const char* bootctl_getSuffix(struct boot_control_module *module, unsigned slot); |
| |
| static struct hw_module_methods_t bootctl_module_methods = { |
| open: NULL |
| }; |
| |
| 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: 0, |
| id: BOOT_CONTROL_HARDWARE_MODULE_ID, |
| name: "Freescale Boot Control", |
| author: "Freescale Semiconductor Inc.", |
| methods: &bootctl_module_methods, |
| dso: NULL, |
| reserved: {0} |
| }, |
| init: bootctl_init, |
| getNumberSlots: bootctl_getNumberSlots, |
| getCurrentSlot: bootctl_getCurrentSlot, |
| markBootSuccessful: bootctl_markBootSuccessful, |
| setActiveBootSlot: bootctl_setActiveBootSlot, |
| setSlotAsUnbootable: bootctl_setSlotAsUnbootable, |
| isSlotBootable: bootctl_isSlotBootable, |
| getSuffix: bootctl_getSuffix, |
| reserved: {0} |
| }; |
| |
| static int crcCheck(TBootCtl *ptBootCtl) { |
| uint32_t crc = 0; |
| uint8_t *pData = NULL; |
| uint32_t len = 0; |
| |
| if(ptBootCtl == NULL) |
| return -EINVAL; |
| |
| pData = (uint8_t *)ptBootCtl + CRC_DATA_OFFSET; |
| len = sizeof(TBootCtl) - CRC_DATA_OFFSET; |
| |
| crc = crc32(0, pData, len); |
| if(crc != ptBootCtl->m_crc) { |
| ALOGE("crcCheck failed, calcuated %d, read %d\n", crc, ptBootCtl->m_crc); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| static int crcSet(TBootCtl *ptBootCtl) { |
| uint32_t crc = 0; |
| uint8_t *pData = NULL; |
| uint32_t len = 0; |
| |
| if(ptBootCtl == NULL) |
| return -EINVAL; |
| |
| pData = (uint8_t *)ptBootCtl + CRC_DATA_OFFSET; |
| len = sizeof(TBootCtl) - CRC_DATA_OFFSET; |
| |
| crc = crc32(0, pData, len); |
| ptBootCtl->m_crc = crc; |
| |
| return 0; |
| } |
| |
| void bootctl_init(struct boot_control_module *module) { |
| int i; |
| |
| TRACE; |
| |
| if(g_bInited) { |
| ALOGI("bootctl_init already called\n"); |
| return; |
| } |
| |
| char propbuf[PROPERTY_VALUE_MAX]; |
| |
| // Get the suffix from the kernel commandline |
| property_get("ro.boot.slot_suffix", propbuf, ""); |
| if (propbuf[0] == '\0') { |
| ALOGE("property_get ro.boot.slot_suffix failed\n"); |
| return; |
| } |
| |
| ALOGI("slot suffix is %s\n", propbuf); |
| |
| for(i = 0; i < SLOT_NUM; i++) { |
| if(strcmp(propbuf, g_aSlotSuffix[i]) == 0) |
| break; |
| } |
| |
| if(i >= SLOT_NUM) { |
| ALOGE("slot suffix %s not valid\n", propbuf); |
| return; |
| } |
| |
| g_dwCurSlot = i; |
| g_bInited = true; |
| |
| return; |
| } |
| |
| unsigned bootctl_getNumberSlots(struct boot_control_module *module) |
| { |
| TRACE; |
| return SLOT_NUM; |
| } |
| |
| unsigned bootctl_getCurrentSlot(struct boot_control_module *module) |
| { |
| TRACE; |
| |
| INIT_CHECK; |
| return g_dwCurSlot; |
| } |
| |
| //return fd |
| static int bootctl_load(TBootCtl *ptBootCtl, bool read_only) { |
| int fd = -1; |
| int ret = 0; |
| char *pMagic = NULL; |
| |
| if(ptBootCtl == NULL) |
| return -EINVAL; |
| |
| fd = open(MISC_PARTITION, read_only ? O_RDONLY : O_RDWR); |
| if(fd < 0) { |
| ALOGE("%s, open %s failed, fd %d, errno %d", __FUNCTION__, MISC_PARTITION, fd, errno); |
| return -errno; |
| } |
| |
| lseek(fd, BOOTCTRL_OFFSET, SEEK_SET); |
| |
| do { |
| ret = read(fd, ptBootCtl, sizeof(TBootCtl)); |
| } while (ret == -1 && errno == EAGAIN); |
| |
| if (ret == -1) { |
| close(fd); |
| ALOGE("%s, read failed, errno %d", __FUNCTION__, errno); |
| return -errno; |
| } |
| |
| // Read on block devices on Linux are never partial so fine to assert everything was read. |
| assert(ret == sizeof(TBootCtl)); |
| |
| pMagic = ptBootCtl->m_magic; |
| if(!((pMagic[0] == '\0') && (pMagic[1] == 'F') && (pMagic[2] == 'S') && (pMagic[3] == 'L'))) |
| { |
| close(fd); |
| ALOGE("%s, magic error, 0x%02x 0x%02x 0x%02x 0x%02x", |
| __FUNCTION__, pMagic[0], pMagic[1], pMagic[2], pMagic[3]); |
| return -EINVAL; |
| } |
| |
| ret = crcCheck(ptBootCtl); |
| if(ret) { |
| close(fd); |
| ALOGE("%s, crcCheck failed", __FUNCTION__); |
| return ret; |
| } |
| |
| return fd; |
| } |
| |
| static int bootctl_store(int fd, TBootCtl *ptBootCtl) { |
| int ret = 0; |
| |
| if((fd < 0) || (ptBootCtl == NULL)) |
| return -EINVAL; |
| |
| crcSet(ptBootCtl); |
| |
| lseek(fd, BOOTCTRL_OFFSET, SEEK_SET); |
| |
| do { |
| ret = write(fd, ptBootCtl, sizeof(TBootCtl)); |
| } while (ret == -1 && errno == EAGAIN); |
| |
| if (ret == -1) { |
| close(fd); |
| ALOGE("%s, write failed, errno %d", __FUNCTION__, errno); |
| return -errno; |
| } |
| |
| // Writes on block devices on Linux are never partial so fine to assert everything was written. |
| assert(ret == sizeof(TBootCtl)); |
| |
| ret = fsync(fd); |
| if (ret == -1) { |
| close(fd); |
| ALOGE("%s, fsync failed, errno %d", __FUNCTION__, errno); |
| return -errno; |
| } |
| |
| close(fd); |
| |
| return 0; |
| } |
| |
| int bootctl_markBootSuccessful(struct boot_control_module *module) |
| { |
| int ret = 0; |
| int fd = 0; |
| TBootCtl tBootCtl; |
| |
| TRACE; |
| INIT_CHECK; |
| |
| ALOGI("bootctl_markBootSuccessful curSlot %d", g_dwCurSlot); |
| |
| fd = bootctl_load(&tBootCtl, false); |
| RET_CHECK(fd, bootctl_load); |
| |
| tBootCtl.m_aSlotMeta[g_dwCurSlot].m_BootSuc = 1; |
| |
| ret = bootctl_store(fd, &tBootCtl); |
| return ret; |
| } |
| |
| int bootctl_setActiveBootSlot(struct boot_control_module *module, unsigned slot) |
| { |
| int ret = 0; |
| int fd = 0; |
| TBootCtl tBootCtl; |
| |
| TRACE; |
| INIT_CHECK; |
| SLOT_CHECK(slot, -EINVAL); |
| |
| if(slot == g_dwCurSlot) { |
| ALOGI("%s, !!! warning, active slot is same as curSlot %d", __FUNCTION__, slot); |
| } |
| |
| fd = bootctl_load(&tBootCtl, false); |
| RET_CHECK(fd, bootctl_load); |
| |
| tBootCtl.m_aSlotMeta[slot].m_BootSuc = 0; |
| tBootCtl.m_aSlotMeta[slot].m_Priority = 15; |
| tBootCtl.m_aSlotMeta[slot].m_TryRemain = 7; |
| |
| // lower other slot priority |
| for(uint32_t i = 0; i < SLOT_NUM; i++) { |
| if(i == slot) |
| continue; |
| |
| if(tBootCtl.m_aSlotMeta[i].m_Priority >= 15) |
| tBootCtl.m_aSlotMeta[i].m_Priority = 14; |
| } |
| |
| ret = bootctl_store(fd, &tBootCtl); |
| return ret; |
| } |
| |
| int bootctl_setSlotAsUnbootable(struct boot_control_module *module, unsigned slot) |
| { |
| int retVal = 0; |
| int ret = 0; |
| int fd = 0; |
| TBootCtl tBootCtl; |
| |
| TRACE; |
| INIT_CHECK; |
| SLOT_CHECK(slot, -EINVAL); |
| |
| if(slot == g_dwCurSlot) { |
| ALOGI("%s, !!! warning, set current slot %d unbootable", __FUNCTION__, slot); |
| } |
| |
| fd = bootctl_load(&tBootCtl, false); |
| RET_CHECK(fd, bootctl_load); |
| |
| tBootCtl.m_aSlotMeta[slot].m_BootSuc = 0; |
| tBootCtl.m_aSlotMeta[slot].m_Priority = 0; |
| tBootCtl.m_aSlotMeta[slot].m_TryRemain = 0; |
| |
| ret = bootctl_store(fd, &tBootCtl); |
| return ret; |
| } |
| |
| int bootctl_isSlotBootable(struct boot_control_module *module, unsigned slot) |
| { |
| int ret = 0; |
| int fd = 0; |
| TBootCtl tBootCtl; |
| |
| TRACE; |
| INIT_CHECK; |
| SLOT_CHECK(slot, -EINVAL); |
| |
| fd = bootctl_load(&tBootCtl, true); |
| RET_CHECK(fd, bootctl_load); |
| |
| if((tBootCtl.m_aSlotMeta[slot].m_BootSuc > 0) || (tBootCtl.m_aSlotMeta[slot].m_TryRemain > 0)) |
| ret = 1; |
| |
| close(fd); |
| |
| return ret; |
| } |
| |
| const char* bootctl_getSuffix(struct boot_control_module *module, unsigned slot) |
| { |
| TRACE; |
| SLOT_CHECK(slot, NULL); |
| |
| return g_aSlotSuffix[slot]; |
| } |