#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#define CONFIG_HEARTBEAT_DEVICE "/dev/uio0"
#define CONFIG_INTR_MAP_SIZE (256)
#define CONFIG_SLEEP_MS (1)
#define CONFIG_TIMEOUT_MS (1000)
enum ivshmem_registers {
IntrMask = 0,
IntrStatus = 4,
IVPosition = 8,
Doorbell = 12,
IVLiveList = 16
};
#if defined(USE_REMOTE_MESSAGE)
#define CONFIG_REMOTE_SERVICE_ENDPOINT (27)
#define CONFIG_LOCAL_SERVICE_ENDPOINT (4096)
#include <rpmsg_char_helper.h>
#endif
struct {
int fd;
uint8_t *intr_map_addr;
} heartbeat;
#if defined(USE_REMOTE_MESSAGE)
struct {
int fd;
} crash_report;
#endif
static int setup_heartbeat() {
int ret;
heartbeat.fd = open(CONFIG_HEARTBEAT_DEVICE, O_RDWR);
if(heartbeat.fd < 0) {
printf("fd open: %s\n", strerror(errno));
return -1;
}
heartbeat.intr_map_addr = mmap(0, CONFIG_INTR_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, heartbeat.fd, 0);
if(heartbeat.intr_map_addr == MAP_FAILED) {
printf("intr mmap: %s\n", strerror(errno));
close(heartbeat.fd);
return -1;
}
return 0;
}
static void fini_heartbeat()
{
munmap(heartbeat.intr_map_addr, CONFIG_INTR_MAP_SIZE);
close(heartbeat.fd);
}
static void intr_heartbeat()
{
*((uint32_t *)(&heartbeat.intr_map_addr[Doorbell])) = 0;
}
static int read_heartbeat_timeout()
{
int ret;
fd_set set;
struct timeval t;
uint64_t usecs = CONFIG_TIMEOUT_MS * (uint64_t)1000;
uint32_t readbuf;
FD_ZERO(&set);
FD_SET(heartbeat.fd, &set);
t.tv_sec = (uint32_t)(usecs / (uint64_t)1000000);
t.tv_usec = (uint32_t)(usecs % (uint64_t)1000000);
ret = select(heartbeat.fd + 1, &set, NULL, NULL, &t);
if(ret <= 0) {
if(!ret)
printf("Timed out!\n");
else if(errno == -EINTR)
printf("Interrupted!\n");
else
printf("select: %s\n", strerror(errno));
return -1;
}
ret = read(heartbeat.fd, &readbuf, sizeof(readbuf));
if(ret <= 0) {
if(!ret)
printf("read 0 bytes!\n");
else
printf("read: %s\n", strerror(errno));
return -1;
}
return 0;
}
#if defined(USE_REMOTE_MESSAGE)
static int setup_crash_report()
{
char *remote_core_name = "r5f-main-0-core-1";
int remote_service_endpt = CONFIG_REMOTE_SERVICE_ENDPOINT;
int local_endpt = CONFIG_LOCAL_SERVICE_ENDPOINT;
char *local_name = "crash-report";
rproc_device_t *dev;
rproc_char_device_t *cdev;
rproc_char_endpt_t *ept = NULL;
char *path;
dev = rproc_device_find_for_name(remote_core_name);
if(!dev) {
fprintf(stderr, "rproc_device_find_for_name failed\n");
return -1;
}
cdev = rproc_device_find_chrdev_by_remote_port(dev, remote_service_endpt);
if(!cdev) {
fprintf(stderr, "rproc_device_find_chrdev_by_remote_port failed\n");
return -1;
}
if(!ept)
ept = rproc_char_device_find_endpt_by_name(cdev, local_name);
if(!ept)
ept = rproc_char_device_find_endpt_by_local_port(cdev, local_endpt);
if(!ept)
ept = rproc_char_device_create_endpt(cdev, local_name, local_endpt);
if(!ept) {
fprintf(stderr, "rproc_char_device_create_endpt failed\n");
return -1;
}
path = rproc_char_endpt_get_dev_name(ept);
if(!path) {
fprintf(stderr, "rproc_char_endpt_get_dev_name failed\n");
return -1;
}
crash_report.fd = open(path, O_RDWR);
if(crash_report.fd < 0) {
fprintf(stderr, "could not open crash report fd\n");
return -1;
}
return 0;
}
#define REQUEST_IVI_VM_CRASH (0xdeadbead)
static void send_crash_report()
{
int ret;
uint32_t req = REQUEST_IVI_VM_CRASH;
ret = write(crash_report.fd, &req, sizeof(req));
if(ret < 0)
printf("write: %s\n", strerror(errno));
else if(!ret)
printf("write: returns 0\n");
}
#else
static inline int setup_crash_report()
{
return 0;
}
static void send_crash_report()
{
}
#endif
int main() {
uint64_t last_value = 0;
uint32_t count_no_upd = 0;
int ret;
if(setup_heartbeat()) {
return -1;
}
if(setup_crash_report()) {
fini_heartbeat();
return -1;
}
while(true) {
usleep(CONFIG_SLEEP_MS * 1000);
intr_heartbeat();
ret = read_heartbeat_timeout();
if(ret) {
printf("IVI VM Crash detected. Sending crash notification\n");
send_crash_report();
break;
}
}
fini_heartbeat();
return 0;
}