blob: 8d291bdaf270565df53fd0ccc0875cc1764d729e [file] [log] [blame]
/*
* Copyright (C) 2017 Samsung Electronics Co. Ltd.
*
* 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.
*/
#define LOG_TAG "main_abox"
//#define LOG_NDEBUG 0
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <fnmatch.h>
#include <sys/epoll.h>
#include <sys/stat.h>
#include <log/log.h>
#include <cutils/uevent.h>
#define MAX_EPOLL_EVENTS (8)
#define BUFFER_SIZE (4096)
#define MAX_DUMP_COUNT (3)
#define DEVPATH "DEVPATH="
#define SYS_PATH "/sys"
#define SERVICE_FILE "/service"
#define RESET_FILE "/reset"
#define FAILSAFE_RESET_FILE "/failsafe/reset"
#define DEBUG_FILE "/0.abox-debug"
#define DEBUG_FILE_LEG "/0.abox_debug"
#define SRAM_FILE "/calliope_sram"
#define DRAM_FILE "/calliope_dram"
#define IVA_FILE "/calliope_iva"
#define PRIV_FILE "/calliope_priv"
#define SLOG_FILE "/calliope_slog"
#define GPR_FILE "/gpr"
#define DEBUG_PATH "/d/abox"
#define PROC_PATH "/proc/abox"
#define REGMAP_PATH "/d/regmap"
#define REGISTERS_FILE "/registers"
#define LOG_FILE "/log-00"
#define COUNT "COUNT="
struct abox_t {
int fd;
int epoll_fd;
};
static struct abox_t abox;
static char dev_path[128];
static int dev_path_len;
static char sys_path[128];
static int sys_path_len;
static char debug_path[128];
static int debug_path_len;
static char debug_path_leg[128];
static int debug_path_leg_len;
static char regmap_path[128];
static int regmap_path_len;
static char out_path[128];
static int out_path_len;
static void rm_old_dump(const char *path, const char *prefix)
{
struct dirent **list;
char pattern[128];
int n, m;
ALOGD("%s(%s, %s)", __func__, path, prefix);
if (snprintf(pattern, sizeof(pattern), "%s*", prefix + 1) < 0) {
ALOGE("%s: pattern error: %s", __func__, strerror(errno));
return;
}
n = scandir(path, &list, NULL, alphasort);
if (n < 0) {
ALOGE("%s: scandir failed: %s", __func__, strerror(errno));
return;
}
m = 0;
while (n--) {
if (!fnmatch(pattern, list[n]->d_name, FNM_FILE_NAME)) {
if (++m > MAX_DUMP_COUNT) {
char *tgt;
if (asprintf(&tgt, "%s/%s", path, list[n]->d_name) != -1) {
remove(tgt);
free(tgt);
}
}
}
free(list[n]);
}
free(list);
}
static int __dump(const char *in_prefix, const char *in_file, const char *out_prefix, const char *out_suffix)
{
char buf[4096], in_path[128], out_path[128];
int fd_in, fd_out, n;
int total = 0;
mode_t mask;
ALOGD("%s(%s, %s, %s, %s)", __func__, in_prefix, in_file, out_prefix, out_suffix);
if (snprintf(in_path, sizeof(in_path), "%s%s", in_prefix, in_file) < 0) {
ALOGE("%s: in path error: %s", __func__, strerror(errno));
return -1;
}
if (snprintf(out_path, sizeof(out_path), "%s%s_%s", out_prefix, in_file, out_suffix) < 0) {
ALOGE("%s: out path error: %s", __func__, strerror(errno));
return -1;
}
mask = umask(002);
fd_in = open(in_path, O_RDONLY | O_NONBLOCK);
if (fd_in > -1) {
fd_out = open(out_path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
if (fd_out > -1) {
while((n = read(fd_in, buf, sizeof(buf))) > 0) {
if (write(fd_out, buf, n) < 0) {
ALOGE("%s: write error: %s", __func__, strerror(errno));
}
total += n;
}
close(fd_out);
} else {
ALOGE("%s: open error: %s, fd_out=%s", __func__, strerror(errno), out_path);
total = -1;
}
close(fd_in);
} else {
ALOGE("%s: open error: %s, fd_in=%s", __func__, strerror(errno), in_path);
total = -1;
}
mask = umask(mask);
rm_old_dump(out_prefix, in_file);
return total;
}
static void dump(void)
{
char str_time[32];
time_t t;
struct tm *lt;
ALOGD("%s", __func__);
t = time(NULL);
lt = localtime(&t);
if (lt == NULL) {
ALOGE("%s: time conversion error: %s", __func__, strerror(errno));
return;
}
if (strftime(str_time, sizeof(str_time), "%Y%m%d_%H%M%S", lt) == 0) {
ALOGE("%s: time error: %s", __func__, strerror(errno));
}
if (mkdir(out_path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
ALOGW("mkdir(%s) failed: %s", out_path, strerror(errno));
}
if (__dump(debug_path, SRAM_FILE, out_path, str_time) <= 0)
__dump(debug_path_leg, SRAM_FILE, out_path, str_time);
if (__dump(debug_path, DRAM_FILE, out_path, str_time) <= 0)
__dump(debug_path_leg, DRAM_FILE, out_path, str_time);
if (__dump(debug_path, PRIV_FILE, out_path, str_time) <= 0)
__dump(debug_path_leg, PRIV_FILE, out_path, str_time);
if (__dump(debug_path, SLOG_FILE, out_path, str_time) <= 0)
__dump(debug_path_leg, SLOG_FILE, out_path, str_time);
if (__dump(debug_path, GPR_FILE, out_path, str_time) <= 0)
__dump(debug_path_leg, GPR_FILE, out_path, str_time);
if (__dump(DEBUG_PATH, LOG_FILE, out_path, str_time) <= 0)
__dump(PROC_PATH, LOG_FILE, out_path, str_time);
__dump(regmap_path, REGISTERS_FILE, out_path, str_time);
}
static void reset(void)
{
int fd, n;
const char str[] = "CALLIOPE";
char *path;
ALOGD("%s", __func__);
n = asprintf(&path, "%s%s", sys_path, RESET_FILE);
if (n >= 0) {
ALOGD("%s : %s", __func__, path);
if (access(path, F_OK) != 0) {
free(path);
n = asprintf(&path, "%s%s", PROC_PATH, FAILSAFE_RESET_FILE);
ALOGD("%s = %s", __func__, path);
}
}
if (n >= 0) {
fd = open(path, O_WRONLY);
if (fd < 0) {
ALOGE("%s: open error: %s", __func__, strerror(errno));
} else {
n = write(fd, str, strlen(str));
if (n < (int)strlen(str)) {
ALOGE("%s: write error: %s", __func__, strerror(errno));
}
close(fd);
}
free(path);
} else {
ALOGE("%s: path error: %s", __func__, strerror(errno));
}
}
static int recv_event(void)
{
char msg[BUFFER_SIZE + 2];
char *cp;
int count;
int n;
n = uevent_kernel_multicast_recv(abox.fd, msg, BUFFER_SIZE);
if (n <= 0)
return n;
msg[n] = 0;
msg[n+1] = 0;
cp = msg;
while (*cp) {
// ALOGV("UEVENT: %s", cp);
if (!strncmp(cp, DEVPATH, sizeof(DEVPATH) - 1) &&
!strncmp(cp + sizeof(DEVPATH) - 1, dev_path, dev_path_len)) {
do {
while (*cp++) {}
// ALOGD("UEVENT: %s", cp);
if (sscanf(cp, COUNT"%d", &count) > 0) {
ALOGD("%s, count=%d", cp, count);
if (count > 0) {
ALOGW("fault report from Calliope: %d", count);
dump();
reset();
}
break;
}
} while (*cp);
}
/* advance to after the next \0 */
while (*cp++) {}
}
return 0;
}
static void main_loop(void)
{
ALOGI("%s", __func__);
while (true) {
epoll_event events[MAX_EPOLL_EVENTS];
int nevents;
nevents = epoll_wait(abox.epoll_fd, events, MAX_EPOLL_EVENTS, -1);
if (nevents < 0 && errno != EINTR) {
ALOGE("epoll_wait failed");
break;
}
recv_event();
}
}
static void report_service(void)
{
int fd, n;
const char str[] = "1";
char *path;
ALOGI("%s", __func__);
n = asprintf(&path, "%s%s", sys_path, SERVICE_FILE);
if (n >= 0) {
fd = open(path, O_WRONLY);
if (fd < 0) {
ALOGE("%s: open error: %s", __func__, strerror(errno));
} else {
n = write(fd, str, strlen(str));
if (n < (int)strlen(str)) {
ALOGE("%s: write error: %s", __func__, strerror(errno));
}
close(fd);
}
free(path);
} else {
ALOGE("%s: path error: %s", __func__, strerror(errno));
}
}
int main(int argc, char **argv)
{
epoll_event ev;
int ret = 0;
ALOGD("%s", __func__);
if (argc > 2) {
dev_path_len = snprintf(dev_path, sizeof(dev_path), "/devices/platform/%s", argv[1]);
if (dev_path_len < 0 || (size_t)dev_path_len >= sizeof(dev_path)) {
ALOGE("invalid argument: %s", argv[1]);
return -1;
}
ALOGD("dev_path=%s", dev_path);
sys_path_len = snprintf(sys_path, sizeof(sys_path), "%s%s", SYS_PATH, dev_path);
if (sys_path_len < 0 || (size_t)sys_path_len >= sizeof(sys_path)) {
ALOGE("invalid argument: %s", argv[1]);
return -1;
}
ALOGD("sys_path=%s", sys_path);
debug_path_len = snprintf(debug_path, sizeof(debug_path), "%s%s", sys_path, DEBUG_FILE);
if (debug_path_len < 0 || (size_t)debug_path_len >= sizeof(debug_path)) {
ALOGE("invalid argument: %s", argv[1]);
return -1;
}
ALOGD("debug_path=%s", debug_path);
/* compatibility with legacy kernel */
debug_path_leg_len = snprintf(debug_path_leg, sizeof(debug_path_leg), "%s%s", sys_path, DEBUG_FILE_LEG);
if (debug_path_leg_len < 0 || (size_t)debug_path_leg_len >= sizeof(debug_path_leg)) {
ALOGE("invalid argument: %s", argv[1]);
return -1;
}
ALOGD("debug_path_leg=%s", debug_path_leg);
regmap_path_len = snprintf(regmap_path, sizeof(regmap_path), "%s/%s", REGMAP_PATH, argv[1]);
if (regmap_path_len < 0 || (size_t)regmap_path_len >= sizeof(regmap_path)) {
ALOGE("invalid argument: %s", argv[1]);
return -1;
}
ALOGD("regmap_path=%s", regmap_path);
out_path_len = snprintf(out_path, sizeof(out_path), "%s", argv[2]);
if (out_path_len < 0 || (size_t)out_path_len >= sizeof(out_path)) {
ALOGE("invalid argument: %s", argv[2]);
return -1;
}
ALOGD("out_path=%s", out_path);
} else {
ALOGE("insufficient argument");
return -1;
}
ret = abox.epoll_fd = epoll_create(MAX_EPOLL_EVENTS);
if (ret >= 0) {
ret = abox.fd = uevent_open_socket(BUFFER_SIZE, true);
if (ret >= 0) {
ret = fcntl(abox.fd, F_SETFL, O_NONBLOCK);
if (ret >= 0) {
ev.events = EPOLLIN | EPOLLWAKEUP;
ret = epoll_ctl(abox.epoll_fd, EPOLL_CTL_ADD, abox.fd, &ev);
if (ret >= 0) {
report_service();
main_loop();
} else {
ALOGE("epoll_ctl failed: %s", strerror(errno));
}
} else {
ALOGE("fcntl failed: %s", strerror(errno));
}
close(abox.fd);
} else {
ALOGE("uevent_open_socket failed: %s", strerror(errno));
}
close(abox.epoll_fd);
} else {
ALOGE("epoll_create failed: %s", strerror(errno));
}
return ret;
}