blob: af93afb13d516135e49f547139bbcefa595db452 [file] [log] [blame] [edit]
/* Copyright (c) 2016 Google Inc.
* See LICENSE for details.
*
* Linux emulation for virtual machines. */
#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <vmm/vmm.h>
#include <errno.h>
#include <sys/syscall.h>
#include <vmm/linux_syscalls.h>
#include <sys/time.h>
#include <vmm/linuxemu.h>
#include <dlfcn.h>
static int lemu_debug;
static uth_mutex_t *lemu_logging_lock;
static FILE *lemu_global_logfile;
void init_lemu_logging(int log_level)
{
const char *logfile_name = "lemu.log";
FILE *x = fopen(logfile_name, "w");
lemu_debug = log_level;
lemu_logging_lock = uth_mutex_alloc();
if (x != NULL)
lemu_global_logfile = x;
else
lemu_global_logfile = stderr;
}
void destroy_lemu_logging(void)
{
if (lemu_logging_lock != NULL)
uth_mutex_free(lemu_logging_lock);
if (lemu_global_logfile != stderr)
fclose(lemu_global_logfile);
}
void lemuprint(const uint32_t tid, const char *syscallname,
const bool isError, const char *fmt, ...)
{
va_list valist;
const char *prefix = "[TID %d] %s: ";
bool double_logging = false;
// Do not use global variable as a check to acquire lock.
// make sure it is not changed during our acquire/release.
int debug = lemu_debug;
// If we are not going to log anything anyway, just bail out.
if (!(debug > 0 || isError))
return;
va_start(valist, fmt);
uth_mutex_lock(lemu_logging_lock);
// Print to stderr if debug level is sufficient
if (debug > 1) {
fprintf(stderr, prefix, tid, syscallname);
vfprintf(stderr, fmt, valist);
// Checks if we will double log to stderr
if (lemu_global_logfile == stderr)
double_logging = true;
}
// Log to the global logfile, if we defaulted the global logging to
// stderr then we don't want to log 2 times to stderr.
if (lemu_global_logfile != NULL && !double_logging) {
fprintf(lemu_global_logfile, prefix, tid, syscallname);
vfprintf(lemu_global_logfile, fmt, valist);
}
uth_mutex_unlock(lemu_logging_lock);
va_end(valist);
}
bool dune_sys_read(struct vm_trapframe *tf)
{
ssize_t retval = read(tf->tf_rdi, (void*) tf->tf_rsi, (size_t) tf->tf_rdx);
int err = errno;
if (retval == -1) {
lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], true,
"ERROR %zd\n", err);
tf->tf_rax = -err;
} else {
lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], false,
"SUCCESS %zd\n", retval);
tf->tf_rax = retval;
}
return true;
}
bool dune_sys_write(struct vm_trapframe *tf)
{
ssize_t retval = write((int) tf->tf_rdi, (const void *) tf->tf_rsi,
(size_t) tf->tf_rdx);
int err = errno;
if (retval == -1) {
lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], true,
"ERROR %zd\n", err);
tf->tf_rax = -err;
} else {
lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], false,
"SUCCESS %zd\n", retval);
tf->tf_rax = retval;
}
return true;
}
bool dune_sys_gettid(struct vm_trapframe *tf)
{
tf->tf_rax = tf->tf_guest_pcoreid;
return true;
}
bool dune_sys_gettimeofday(struct vm_trapframe *tf)
{
int retval = gettimeofday((struct timeval*) tf->tf_rdi,
(struct timezone*) tf->tf_rsi);
int err = errno;
if (retval == -1) {
lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], true,
"ERROR %d\n", err);
tf->tf_rax = -err;
} else {
lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], false,
"SUCCESS %d\n", retval);
tf->tf_rax = retval;
}
return true;
}
void init_syscall_table(void)
{
int i;
for (i = 0; i < dune_max_syscall ; ++i) {
dune_syscall_table[i].call = NULL;
dune_syscall_table[i].name = "nosyscall";
}
// For now setup the syscalls here,
// there is probably a better way to do this.
dune_syscall_table[DUNE_SYS_WRITE].call = dune_sys_write;
dune_syscall_table[DUNE_SYS_WRITE].name = syscalls[DUNE_SYS_WRITE];
dune_syscall_table[DUNE_SYS_GETTID].call = dune_sys_gettid;
dune_syscall_table[DUNE_SYS_GETTID].name = syscalls[DUNE_SYS_GETTID];
dune_syscall_table[DUNE_SYS_GETTIMEOFDAY].call = dune_sys_gettimeofday;
dune_syscall_table[DUNE_SYS_GETTIMEOFDAY].name =
syscalls[DUNE_SYS_GETTIMEOFDAY];
dune_syscall_table[DUNE_SYS_READ].call = dune_sys_read;
dune_syscall_table[DUNE_SYS_READ].name = syscalls[DUNE_SYS_READ];
if (dlopen("liblinuxemu_extend.so", RTLD_NOW) == NULL)
fprintf(stderr, "Not using any syscall extensions\n Reason: %s\n",
dlerror());
}
/* TODO: have an array which classifies syscall args
* and "special" system calls (ones with weird return
* values etc.). For some cases, we don't even do a system
* call, and in many cases we have to rearrange arguments
* since Linux and Akaros don't share signatures, so this
* gets tricky. */
bool
linuxemu(struct guest_thread *gth, struct vm_trapframe *tf)
{
bool ret = false;
if (tf->tf_rax >= dune_max_syscall) {
fprintf(stderr, "System call %d is out of range\n", tf->tf_rax);
return false;
}
if (dune_syscall_table[tf->tf_rax].call == NULL) {
fprintf(stderr, "System call #%d (%s) is not implemented\n",
tf->tf_rax, dune_syscall_table[tf->tf_rax].name);
return false;
}
lemuprint(tf->tf_guest_pcoreid, dune_syscall_table[tf->tf_rax].name,
false, "vmcall(%d, %p, %p, %p, %p, %p, %p);\n", tf->tf_rax,
tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_r10, tf->tf_r8,
tf->tf_r9);
tf->tf_rip += 3;
return (dune_syscall_table[tf->tf_rax].call)(tf);
}