Added logging functionality to linuxemu
[akaros.git] / user / vmm / linuxemu.c
1 /* Copyright (c) 2016 Google Inc.
2  * See LICENSE for details.
3  *
4  * Linux emulation for virtual machines. */
5
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <vmm/vmm.h>
13 #include <errno.h>
14 #include <sys/syscall.h>
15 #include <vmm/linux_syscalls.h>
16 #include <sys/time.h>
17 #include <vmm/linuxemu.h>
18 #include <dlfcn.h>
19
20
21 static int lemu_debug;
22 static uth_mutex_t *lemu_logging_lock;
23 static FILE *lemu_global_logfile;
24
25 void init_lemu_logging(int log_level)
26 {
27         const char *logfile_name = "lemu.log";
28         FILE *x = fopen(logfile_name, "w");
29
30         lemu_debug = log_level;
31         lemu_logging_lock = uth_mutex_alloc();
32
33         if (x != NULL)
34                 lemu_global_logfile = x;
35         else
36                 lemu_global_logfile = stderr;
37 }
38
39 void destroy_lemu_logging(void)
40 {
41         if (lemu_logging_lock != NULL)
42                 uth_mutex_free(lemu_logging_lock);
43
44         if (lemu_global_logfile != stderr)
45                 fclose(lemu_global_logfile);
46 }
47
48
49 void lemuprint(const uint32_t tid, const char *syscallname,
50                const bool isError, const char *fmt, ...)
51 {
52         va_list valist;
53         const char *prefix = "[TID %d] %s: ";
54         bool double_logging = false;
55
56
57         // Do not use global variable as a check to acquire lock.
58         // make sure it is not changed during our acquire/release.
59         int debug = lemu_debug;
60
61         // If we are not going to log anything anyway, just bail out.
62         if (!(debug > 0 || isError))
63                 return;
64
65         va_start(valist, fmt);
66
67         uth_mutex_lock(lemu_logging_lock);
68
69         // Print to stderr if debug level is sufficient
70         if (debug > 1) {
71                 fprintf(stderr, prefix, tid, syscallname);
72                 vfprintf(stderr, fmt, valist);
73                 // Checks if we will double log to stderr
74                 if (lemu_global_logfile == stderr)
75                         double_logging = true;
76         }
77
78         // Log to the global logfile, if we defaulted the global logging to
79         // stderr then we don't want to log 2 times to stderr.
80         if (lemu_global_logfile != NULL && !double_logging) {
81                 fprintf(lemu_global_logfile, prefix, tid, syscallname);
82                 vfprintf(lemu_global_logfile, fmt, valist);
83         }
84
85         uth_mutex_unlock(lemu_logging_lock);
86
87         va_end(valist);
88 }
89
90
91 bool dune_sys_read(struct vm_trapframe *tf)
92 {
93         ssize_t retval = read(tf->tf_rdi, (void*) tf->tf_rsi, (size_t) tf->tf_rdx);
94         int err = errno;
95
96         if (retval == -1) {
97                 lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], true,
98                           "ERROR %zd\n", err);
99                 tf->tf_rax = -err;
100         } else {
101                 lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], false,
102                           "SUCCESS %zd\n", retval);
103                 tf->tf_rax = retval;
104         }
105         return true;
106 }
107
108
109 bool dune_sys_write(struct vm_trapframe *tf)
110 {
111         ssize_t retval = write((int) tf->tf_rdi, (const void *) tf->tf_rsi,
112                                (size_t) tf->tf_rdx);
113         int err = errno;
114
115         if (retval == -1) {
116                 lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], true,
117                           "ERROR %zd\n", err);
118                 tf->tf_rax = -err;
119         } else {
120                 lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], false,
121                           "SUCCESS %zd\n", retval);
122                 tf->tf_rax = retval;
123         }
124         return true;
125 }
126
127 bool dune_sys_gettid(struct vm_trapframe *tf)
128 {
129         tf->tf_rax = tf->tf_guest_pcoreid;
130         return true;
131 }
132
133 bool dune_sys_gettimeofday(struct vm_trapframe *tf)
134 {
135         int retval = gettimeofday((struct timeval*) tf->tf_rdi,
136                                   (struct timezone*) tf->tf_rsi);
137         int err = errno;
138
139         if (retval == -1) {
140                 lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], true,
141                           "ERROR %d\n", err);
142                 tf->tf_rax = -err;
143         } else {
144                 lemuprint(tf->tf_guest_pcoreid, syscalls[tf->tf_rax], false,
145                           "SUCCESS %d\n", retval);
146                 tf->tf_rax = retval;
147         }
148         return true;
149 }
150
151 void init_syscall_table(void)
152 {
153         int i;
154
155         for (i = 0; i < dune_max_syscall ; ++i) {
156                 dune_syscall_table[i].call = NULL;
157                 dune_syscall_table[i].name = "nosyscall";
158         }
159         // For now setup the syscalls here,
160         // there is probably a better way to do this.
161         dune_syscall_table[DUNE_SYS_WRITE].call = dune_sys_write;
162         dune_syscall_table[DUNE_SYS_WRITE].name = syscalls[DUNE_SYS_WRITE];
163         dune_syscall_table[DUNE_SYS_GETTID].call = dune_sys_gettid;
164         dune_syscall_table[DUNE_SYS_GETTID].name = syscalls[DUNE_SYS_GETTID];
165         dune_syscall_table[DUNE_SYS_GETTIMEOFDAY].call = dune_sys_gettimeofday;
166         dune_syscall_table[DUNE_SYS_GETTIMEOFDAY].name =
167                 syscalls[DUNE_SYS_GETTIMEOFDAY];
168         dune_syscall_table[DUNE_SYS_READ].call = dune_sys_read;
169         dune_syscall_table[DUNE_SYS_READ].name = syscalls[DUNE_SYS_READ];
170         if (dlopen("liblinuxemu_extend.so", RTLD_NOW) == NULL)
171                 fprintf(stderr, "Not using any syscall extensions\n Reason: %s\n",
172                         dlerror());
173
174 }
175
176
177 /* TODO: have an array which classifies syscall args
178  * and "special" system calls (ones with weird return
179  * values etc.). For some cases, we don't even do a system
180  * call, and in many cases we have to rearrange arguments
181  * since Linux and Akaros don't share signatures, so this
182  * gets tricky. */
183 bool
184 linuxemu(struct guest_thread *gth, struct vm_trapframe *tf)
185 {
186         bool ret = false;
187
188         if (tf->tf_rax >= dune_max_syscall) {
189                 fprintf(stderr, "System call %d is out of range\n", tf->tf_rax);
190                 return false;
191         }
192
193
194         if (dune_syscall_table[tf->tf_rax].call == NULL) {
195                 fprintf(stderr, "System call #%d (%s) is not implemented\n",
196                         tf->tf_rax, dune_syscall_table[tf->tf_rax].name);
197                 return false;
198         }
199
200         lemuprint(tf->tf_guest_pcoreid, dune_syscall_table[tf->tf_rax].name,
201                   false, "vmcall(%d, %p, %p, %p, %p, %p, %p);\n", tf->tf_rax,
202                   tf->tf_rdi, tf->tf_rsi, tf->tf_rdx, tf->tf_r10, tf->tf_r8,
203                   tf->tf_r9);
204
205         tf->tf_rip += 3;
206
207         return (dune_syscall_table[tf->tf_rax].call)(tf);
208 }