453 lines
14 KiB
C++
453 lines
14 KiB
C++
/* 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.
|
|
*/
|
|
/* 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 <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <linux/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include "sysenv_utils.h"
|
|
#include <fstab/fstab.h>
|
|
|
|
#define ENV_PARTITION_COUNT (2)
|
|
|
|
using android::fs_mgr::Fstab;
|
|
using android::fs_mgr::GetEntryForMountPoint;
|
|
using android::fs_mgr::ReadDefaultFstab;
|
|
|
|
struct env_struct g_env[SYSENV_AREA_MAX];
|
|
static int env_valid[SYSENV_AREA_MAX] = {0};
|
|
static char *env_buffer[SYSENV_AREA_MAX];
|
|
static int env_init_done[SYSENV_AREA_MAX] = {0};
|
|
static char env_path[64];
|
|
static const char *env_partitions[ENV_PARTITION_COUNT] = {"/para", "/misc"};
|
|
|
|
static int get_partition_path(char* path, int size)
|
|
{
|
|
Fstab fstab;
|
|
int ret = 0, i;
|
|
|
|
if (!ReadDefaultFstab(&fstab)) {
|
|
ERR_LOG("failed to get fstab to get partition path");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < ENV_PARTITION_COUNT; i++) {
|
|
auto rec = GetEntryForMountPoint(&fstab, env_partitions[i]);
|
|
if (rec == nullptr) {
|
|
ERR_LOG("failed to get device path by mount point %s", env_partitions[i]);
|
|
ret = -1;
|
|
} else {
|
|
ret = snprintf(path, size, "%s", rec->blk_device.c_str());
|
|
if(ret < 0){
|
|
ERR_LOG("%s failed to snprinf\n", __func__);
|
|
return -1;
|
|
}
|
|
ret = 0;
|
|
DEBUG_LOG("partition path = %s\n", path);
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int write_env_area(char *env_buf, int area)
|
|
{
|
|
unsigned int i, checksum = 0;
|
|
int result = 0;
|
|
int ret = 0;
|
|
int fd = 0;
|
|
off_t offset = 0;
|
|
char env_sig[8];
|
|
if (area >= SYSENV_AREA_MAX) {
|
|
ERR_LOG("Invalid sysenv area %d\n", area);
|
|
return -1;
|
|
}
|
|
memset(env_sig, 0, sizeof(env_sig));
|
|
memcpy(env_sig, ENV_SIG, strlen(ENV_SIG));
|
|
memcpy(env_buf, env_sig, sizeof(ENV_SIG));
|
|
memcpy(env_buf + CFG_ENV_SIG_1_OFFSET, env_sig, sizeof(ENV_SIG));
|
|
|
|
for (i = 0; i < CFG_ENV_DATA_SIZE; i++)
|
|
checksum += *(env_buf + CFG_ENV_DATA_OFFSET + i);
|
|
*((int *)env_buf + CFG_ENV_CHECKSUM_OFFSET / 4) = checksum;
|
|
fd = open(env_path, O_RDWR);
|
|
if (fd < 0) {
|
|
ERR_LOG("open %s fail: %s\n", env_path, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (area == SYSENV_RW_AREA)
|
|
offset = CFG_ENV_RW_OFFSET;
|
|
else
|
|
offset = CFG_ENV_RO_OFFSET;
|
|
DEBUG_LOG("seek to %llx", (unsigned long long)offset);
|
|
if (lseek(fd, offset, SEEK_SET) != offset)
|
|
ERR_LOG("seek to %lld fail: %s\n", (long long)offset, strerror(errno));
|
|
ret = write(fd, (char *)env_buf, CFG_ENV_SIZE);
|
|
if (ret < 0) {
|
|
ERR_LOG("write env fail: %s\n", strerror(errno));
|
|
result = -1;
|
|
}
|
|
DEBUG_LOG("write 0x%zx data size %d\n", (uintptr_t) env_buf, ret);
|
|
if (fsync(fd) < 0)
|
|
WARN_LOG("write env sync fail: %s\n", strerror(errno));
|
|
close(fd);
|
|
return result;
|
|
}
|
|
|
|
static int read_env_area(char *env_buf, int area)
|
|
{
|
|
int result = 0;
|
|
int ret = 0;
|
|
int fd;
|
|
int offset = 0;
|
|
|
|
fd = open(env_path, O_RDONLY);
|
|
if (fd < 0) {
|
|
ERR_LOG("open %s fail: %s\n", env_path, strerror(errno));
|
|
return -1;
|
|
}
|
|
if (area == SYSENV_RW_AREA)
|
|
offset = CFG_ENV_RW_OFFSET;
|
|
else
|
|
offset = CFG_ENV_RO_OFFSET;
|
|
if (lseek(fd, offset, SEEK_SET) != offset)
|
|
ERR_LOG("seek to %d fail!\n", offset);
|
|
ret = read(fd, (char *)env_buf, CFG_ENV_SIZE);
|
|
if (ret < 0) {
|
|
ERR_LOG("read env fail: %s\n", strerror(errno));
|
|
result = -1;
|
|
}
|
|
DEBUG_LOG("read %d data from 0x%x to 0x%zx\n", ret, offset, (uintptr_t) env_buf);
|
|
close(fd);
|
|
return result;
|
|
}
|
|
|
|
static char env_get_char(int index, int area)
|
|
{
|
|
if (area < 0 || area >= SYSENV_AREA_MAX)
|
|
area = 0;
|
|
return *(g_env[area].env_data + index);
|
|
}
|
|
|
|
static char *env_get_addr(int index, int area)
|
|
{
|
|
if (area < 0 || area >= SYSENV_AREA_MAX)
|
|
area = 0;
|
|
return g_env[area].env_data + index;
|
|
|
|
}
|
|
|
|
static int envmatch(const char *s1, int i2, int area)
|
|
{
|
|
while (*s1 == env_get_char(i2++, area)) {
|
|
if (*s1++ == '=')
|
|
return i2;
|
|
}
|
|
if (*s1 == '\0' && env_get_char(i2 - 1, area) == '=')
|
|
return i2;
|
|
return -1;
|
|
}
|
|
|
|
static char *findenv(const char *name, int area)
|
|
{
|
|
int i, nxt, val;
|
|
|
|
for (i = 0; env_get_char(i, area) != '\0'; i = nxt + 1) {
|
|
for (nxt = i; env_get_char(nxt, area) != '\0'; ++nxt) {
|
|
if (nxt >= CFG_ENV_SIZE)
|
|
return NULL;
|
|
}
|
|
val = envmatch((char *)name, i, area);
|
|
if (val < 0)
|
|
continue;
|
|
return (char *)env_get_addr(val, area);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int get_env_valid_length(int area)
|
|
{
|
|
unsigned int len = 0;
|
|
if (area < 0 || area >= SYSENV_AREA_MAX)
|
|
area = 0;
|
|
if (!env_valid[area])
|
|
return 0;
|
|
for (len = 0; len < CFG_ENV_DATA_SIZE; len++) {
|
|
if (g_env[area].env_data[len] == '\0' && g_env[area].env_data[len + 1] == '\0')
|
|
break;
|
|
}
|
|
return len;
|
|
}
|
|
|
|
static void get_env_info(unsigned int area)
|
|
{
|
|
int ret, i;
|
|
int checksum = 0;
|
|
|
|
DEBUG_LOG("initialize\n");
|
|
|
|
if (area >= SYSENV_AREA_MAX) {
|
|
ERR_LOG("Invalid sysenv area %d\n", area);
|
|
return;
|
|
}
|
|
|
|
if (!env_init_done[area]) {
|
|
ret = get_partition_path(env_path, sizeof(env_path));
|
|
if (ret < 0) {
|
|
ERR_LOG("get_partition_path fail\n");
|
|
return;
|
|
}
|
|
if (env_buffer[area] == NULL) {
|
|
env_buffer[area] = (char *) malloc(CFG_ENV_SIZE);
|
|
ERR_LOG("env_buffer[%d] : 0x%zx\n", area, (uintptr_t) env_buffer[area]);
|
|
if (!env_buffer[area]) {
|
|
ERR_LOG("allocate %d of env buffer fail!\n", CFG_ENV_SIZE);
|
|
return;
|
|
}
|
|
g_env[area].env_data = env_buffer[area] + CFG_ENV_DATA_OFFSET;
|
|
}
|
|
}
|
|
|
|
ret = read_env_area(env_buffer[area], area);
|
|
if (ret < 0) {
|
|
ERR_LOG("read_env_area fail\n");
|
|
free(env_buffer[area]);
|
|
env_buffer[area] = NULL;
|
|
return;
|
|
}
|
|
|
|
memcpy(g_env[area].sig_head, env_buffer[area], sizeof(g_env[area].sig_head));
|
|
memcpy(g_env[area].sig_tail, env_buffer[area] + CFG_ENV_SIG_1_OFFSET, sizeof(g_env[area].sig_tail));
|
|
|
|
if (!strncmp(g_env[area].sig_head, ENV_SIG, sizeof(g_env[area].sig_head))
|
|
&& !strncmp(g_env[area].sig_tail, ENV_SIG, sizeof(g_env[area].sig_tail))) {
|
|
g_env[area].checksum = *((int *)env_buffer[area] + CFG_ENV_CHECKSUM_OFFSET / 4);
|
|
for (i = 0; i < (int) CFG_ENV_DATA_SIZE; i++)
|
|
checksum += g_env[area].env_data[i];
|
|
if (checksum != g_env[area].checksum) {
|
|
ERR_LOG("checksum mismatch\n");
|
|
env_valid[area] = 0;
|
|
} else {
|
|
DEBUG_LOG("ENV initialize success\n");
|
|
env_valid[area] = 1;
|
|
}
|
|
} else {
|
|
WARN_LOG("Incorrect sig, probably sysenv is still empty\n");
|
|
env_valid[area] = 0;
|
|
}
|
|
if (!env_valid[area])
|
|
memset(env_buffer[area], 0x00, CFG_ENV_SIZE);
|
|
|
|
env_init_done[area] = 1;
|
|
return;
|
|
}
|
|
|
|
char* sysenv_get_all(unsigned int area)
|
|
{
|
|
char *env_ret = NULL, *env_ptr = NULL;
|
|
int env_valid_length = 0;
|
|
char buf[128];
|
|
int i, nxt, offset = 0;
|
|
|
|
if (area >= SYSENV_AREA_MAX) {
|
|
ERR_LOG("Invalid sysenv area %d\n", area);
|
|
return NULL;
|
|
}
|
|
|
|
get_env_info(area);
|
|
|
|
if (!env_valid[area]) {
|
|
DEBUG_LOG("sysenv is empty now\n");
|
|
return NULL;
|
|
}
|
|
env_valid_length = get_env_valid_length(area);
|
|
DEBUG_LOG("valid length of area %d is %d\n", area, env_valid_length);
|
|
|
|
env_ret = (char *) malloc(env_valid_length + 2); // last \n + \0
|
|
if (env_ret == NULL) {
|
|
ERR_LOG("allocate %d buffer fail!\n", env_valid_length + 2);
|
|
return NULL;
|
|
}
|
|
memset(env_ret, 0, env_valid_length + 2);
|
|
env_ptr = env_ret;
|
|
|
|
for (i = 0; env_get_char(i, area) != '\0'; i = nxt + 1) {
|
|
for (nxt = i; env_get_char(nxt, area) != '\0'; ++nxt) {
|
|
if (nxt >= (int)CFG_ENV_DATA_SIZE) {
|
|
break;
|
|
}
|
|
}
|
|
memset(buf, 0, sizeof(buf));
|
|
if (nxt - i > (int)sizeof(buf) - 1) {
|
|
WARN_LOG("item larger than buffer (size: %d, buf: %d)\n", nxt-i, (int)sizeof(buf));
|
|
} else {
|
|
if (offset + nxt - i > env_valid_length) {
|
|
ERR_LOG("out of buffer, offset:%d, len:%d, buffer_len:%d\n", offset, nxt-i, env_valid_length);
|
|
break;
|
|
}
|
|
DEBUG_LOG("offset:%d, len:%d, buffer_len:%d\n", offset, nxt-i, env_valid_length);
|
|
memcpy(env_ret + offset, g_env[area].env_data + i, nxt - i);
|
|
env_ret[offset + nxt - i] = '\n';
|
|
offset = offset + nxt - i + 1;
|
|
}
|
|
}
|
|
return env_ret;
|
|
}
|
|
|
|
static const char *sysenv_get_with_area(const char *name, int area)
|
|
{
|
|
DEBUG_LOG("get env name=%s\n", name);
|
|
|
|
get_env_info(area);
|
|
|
|
if (!env_valid[area])
|
|
return NULL;
|
|
return findenv(name, area);
|
|
}
|
|
|
|
const char *sysenv_get(const char *name)
|
|
{
|
|
return sysenv_get_with_area(name, SYSENV_RW_AREA);
|
|
}
|
|
|
|
const char *sysenv_get_static(const char *name)
|
|
{
|
|
return sysenv_get_with_area(name, SYSENV_RO_AREA);
|
|
}
|
|
|
|
static int sysenv_set_internal(const char *name, const char *value, unsigned int area)
|
|
{
|
|
int len;
|
|
int oldval = -1;
|
|
char *env = NULL, *nxt = NULL;
|
|
int ret = 0;
|
|
char *env_data = NULL;
|
|
|
|
DEBUG_LOG("area: %d, name: %s, value: %s\n", area, name, value);
|
|
|
|
if (area >= SYSENV_AREA_MAX) {
|
|
ERR_LOG("Invalid sysenv area %d\n", area);
|
|
return -1;
|
|
}
|
|
|
|
get_env_info(area);
|
|
|
|
env_data = g_env[area].env_data;
|
|
if (!env_buffer[area])
|
|
return -1;
|
|
if (!env_valid[area]) {
|
|
env = env_data;
|
|
goto add;
|
|
}
|
|
/* find match name and return the val header pointer*/
|
|
for (env = env_data; *env; env = nxt + 1) {
|
|
for (nxt = env; *nxt; ++nxt)
|
|
;
|
|
oldval = envmatch((char *)name, env - env_data, area);
|
|
if (oldval >= 0)
|
|
break;
|
|
} /* end find */
|
|
if (oldval > 0) {
|
|
if (*++nxt == '\0') {
|
|
if (env > env_data)
|
|
env--;
|
|
else
|
|
*env = '\0';
|
|
} else {
|
|
for (;;) {
|
|
*env = *nxt++;
|
|
if (((*env == '\0') && (*nxt == '\0')) || (nxt - env_data >= (int)CFG_ENV_DATA_SIZE))
|
|
break;
|
|
++env;
|
|
}
|
|
}
|
|
*++env = '\0';
|
|
}
|
|
|
|
for (env = env_data; *env || *(env + 1); ++env)
|
|
;
|
|
if (env > env_data)
|
|
++env;
|
|
add:
|
|
if (*value == '\0') {
|
|
DEBUG_LOG("clear env name=%s\n", name);
|
|
goto write_env;
|
|
}
|
|
|
|
len = strlen(name) + 1; // =
|
|
len += strlen(value) + 1; // \0
|
|
if (len > (&env_data[CFG_ENV_DATA_SIZE] - env)) {
|
|
ERR_LOG("env data overflow, %s deleted\n", name);
|
|
return -1;
|
|
}
|
|
while ((*env = *name++) != '\0')
|
|
env++;
|
|
*env = '=';
|
|
while ((*++env = *value++) != '\0')
|
|
;
|
|
write_env:
|
|
/* end is marked with double '\0' */
|
|
*++env = '\0';
|
|
memset(env, 0x00, CFG_ENV_DATA_SIZE - (env - env_data));
|
|
|
|
ret = write_env_area(env_buffer[area], area);
|
|
if (ret < 0) {
|
|
ERR_LOG("error: write env area fail\n");
|
|
memset(env_buffer[area], 0x00, CFG_ENV_SIZE);
|
|
return -1;
|
|
}
|
|
env_valid[area] = 1;
|
|
return 0;
|
|
}
|
|
|
|
int sysenv_set(const char *name, const char *value)
|
|
{
|
|
return sysenv_set_internal(name, value, SYSENV_RW_AREA);
|
|
}
|
|
|
|
/* This API is "only" used for meta tool now to write static data which will not change during runtime.
|
|
* The real "read-only" might be taken by power on write protect in the future.
|
|
*/
|
|
int sysenv_set_static(const char *name, const char *value)
|
|
{
|
|
return sysenv_set_internal(name, value, SYSENV_RO_AREA);
|
|
}
|