blob: 4ca916f91df744f1f15745f8b484f6da6242b198 [file] [log] [blame]
/*
* 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];
}