1/* Copyright (C) 2007-2008 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10** GNU General Public License for more details.
11*/
12/*
13 * Virtual hardware for bridging the FUSE kernel module
14 * in the emulated OS and outside file system
15 */
16#include "qemu_file.h"
17#include "goldfish_trace.h"
18#ifdef CONFIG_MEMCHECK
19#include "memcheck/memcheck.h"
20#endif  // CONFIG_MEMCHECK
21
22//#define DEBUG   1
23
24extern void cpu_loop_exit(void);
25
26extern int tracing;
27extern const char *trace_filename;
28
29/* for execve */
30static char path[CLIENT_PAGE_SIZE];
31static char arg[CLIENT_PAGE_SIZE];
32static unsigned long vstart;    // VM start
33static unsigned long vend;      // VM end
34static unsigned long eoff;      // offset in EXE file
35static unsigned cmdlen;         // cmdline length
36static unsigned pid;            // PID (really thread id)
37static unsigned tgid;           // thread group id (really process id)
38static unsigned long dsaddr;    // dynamic symbol address
39static unsigned long unmap_start; // start address to unmap
40
41/* for context switch */
42//static unsigned long cs_pid;    // context switch PID
43
44/* I/O write */
45static void trace_dev_write(void *opaque, target_phys_addr_t offset, uint32_t value)
46{
47    trace_dev_state *s = (trace_dev_state *)opaque;
48
49    switch (offset >> 2) {
50    case TRACE_DEV_REG_SWITCH:  // context switch, switch to pid
51        if (trace_filename != NULL) {
52            trace_switch(value);
53#ifdef DEBUG
54            printf("QEMU.trace: kernel, context switch %u\n", value);
55#endif
56        }
57#ifdef CONFIG_MEMCHECK
58        if (memcheck_enabled) {
59            memcheck_switch(value);
60        }
61#endif  // CONFIG_MEMCHECK
62        break;
63    case TRACE_DEV_REG_TGID:    // save the tgid for the following fork/clone
64        tgid = value;
65#ifdef DEBUG
66        if (trace_filename != NULL) {
67            printf("QEMU.trace: kernel, tgid %u\n", value);
68        }
69#endif
70        break;
71    case TRACE_DEV_REG_FORK:    // fork, fork new pid
72        if (trace_filename != NULL) {
73            trace_fork(tgid, value);
74#ifdef DEBUG
75            printf("QEMU.trace: kernel, fork %u\n", value);
76#endif
77        }
78#ifdef CONFIG_MEMCHECK
79        if (memcheck_enabled) {
80            memcheck_fork(tgid, value);
81        }
82#endif  // CONFIG_MEMCHECK
83        break;
84    case TRACE_DEV_REG_CLONE:    // fork, clone new pid (i.e. thread)
85        if (trace_filename != NULL) {
86            trace_clone(tgid, value);
87#ifdef DEBUG
88            printf("QEMU.trace: kernel, clone %u\n", value);
89#endif
90        }
91#ifdef CONFIG_MEMCHECK
92        if (memcheck_enabled) {
93            memcheck_clone(tgid, value);
94        }
95#endif  // CONFIG_MEMCHECK
96        break;
97    case TRACE_DEV_REG_EXECVE_VMSTART:  // execve, vstart
98        vstart = value;
99        break;
100    case TRACE_DEV_REG_EXECVE_VMEND:    // execve, vend
101        vend = value;
102        break;
103    case TRACE_DEV_REG_EXECVE_OFFSET:   // execve, offset in EXE
104        eoff = value;
105        break;
106    case TRACE_DEV_REG_EXECVE_EXEPATH:  // init exec, path of EXE
107        vstrcpy(value, path, CLIENT_PAGE_SIZE);
108        if (trace_filename != NULL) {
109            trace_init_exec(vstart, vend, eoff, path);
110#ifdef DEBUG
111            printf("QEMU.trace: kernel, init exec [%lx,%lx]@%lx [%s]\n",
112                   vstart, vend, eoff, path);
113#endif
114        }
115#ifdef CONFIG_MEMCHECK
116        if (memcheck_enabled) {
117            if (path[0] == '\0') {
118                // vstrcpy may fail to copy path. In this case lets do it
119                // differently.
120                memcheck_get_guest_kernel_string(path, value, CLIENT_PAGE_SIZE);
121            }
122            memcheck_mmap_exepath(vstart, vend, eoff, path);
123        }
124#endif  // CONFIG_MEMCHECK
125        path[0] = 0;
126        break;
127    case TRACE_DEV_REG_CMDLINE_LEN:     // execve, process cmdline length
128        cmdlen = value;
129        break;
130    case TRACE_DEV_REG_CMDLINE:         // execve, process cmdline
131        cpu_memory_rw_debug(cpu_single_env, value, arg, cmdlen, 0);
132        if (trace_filename != NULL) {
133            trace_execve(arg, cmdlen);
134        }
135#ifdef CONFIG_MEMCHECK
136        if (memcheck_enabled) {
137            memcheck_set_cmd_line(arg, cmdlen);
138        }
139#endif  // CONFIG_MEMCHECK
140#ifdef DEBUG
141        if (trace_filename != NULL) {
142            int i;
143            for (i = 0; i < cmdlen; i ++)
144                if (i != cmdlen - 1 && arg[i] == 0)
145                    arg[i] = ' ';
146            printf("QEMU.trace: kernel, execve %s[%d]\n", arg, cmdlen);
147            arg[0] = 0;
148        }
149#endif
150        break;
151    case TRACE_DEV_REG_EXIT:            // exit, exit current process with exit code
152        if (trace_filename != NULL) {
153            trace_exit(value);
154#ifdef DEBUG
155            printf("QEMU.trace: kernel, exit %x\n", value);
156#endif
157        }
158#ifdef CONFIG_MEMCHECK
159        if (memcheck_enabled) {
160            memcheck_exit(value);
161        }
162#endif  // CONFIG_MEMCHECK
163        break;
164    case TRACE_DEV_REG_NAME:            // record thread name
165        vstrcpy(value, path, CLIENT_PAGE_SIZE);
166
167        // Remove the trailing newline if it exists
168        int len = strlen(path);
169        if (path[len - 1] == '\n') {
170            path[len - 1] = 0;
171        }
172        if (trace_filename != NULL) {
173            trace_name(path);
174#ifdef DEBUG
175            printf("QEMU.trace: kernel, name %s\n", path);
176#endif
177        }
178        break;
179    case TRACE_DEV_REG_MMAP_EXEPATH:    // mmap, path of EXE, the others are same as execve
180        vstrcpy(value, path, CLIENT_PAGE_SIZE);
181        if (trace_filename != NULL) {
182            trace_mmap(vstart, vend, eoff, path);
183#ifdef DEBUG
184            printf("QEMU.trace: kernel, mmap [%lx,%lx]@%lx [%s]\n", vstart, vend, eoff, path);
185#endif
186        }
187#ifdef CONFIG_MEMCHECK
188        if (memcheck_enabled) {
189            if (path[0] == '\0') {
190                // vstrcpy may fail to copy path. In this case lets do it
191                // differently.
192                memcheck_get_guest_kernel_string(path, value, CLIENT_PAGE_SIZE);
193            }
194            memcheck_mmap_exepath(vstart, vend, eoff, path);
195        }
196#endif  // CONFIG_MEMCHECK
197        path[0] = 0;
198        break;
199    case TRACE_DEV_REG_INIT_PID:        // init, name the pid that starts before device registered
200        pid = value;
201#ifdef CONFIG_MEMCHECK
202        if (memcheck_enabled) {
203            memcheck_init_pid(value);
204        }
205#endif  // CONFIG_MEMCHECK
206        break;
207    case TRACE_DEV_REG_INIT_NAME:       // init, the comm of the init pid
208        vstrcpy(value, path, CLIENT_PAGE_SIZE);
209        if (trace_filename != NULL) {
210            trace_init_name(tgid, pid, path);
211#ifdef DEBUG
212            printf("QEMU.trace: kernel, init name %u [%s]\n", pid, path);
213#endif
214        }
215        path[0] = 0;
216        break;
217
218    case TRACE_DEV_REG_DYN_SYM_ADDR:    // dynamic symbol address
219        dsaddr = value;
220        break;
221    case TRACE_DEV_REG_DYN_SYM:         // add dynamic symbol
222        vstrcpy(value, arg, CLIENT_PAGE_SIZE);
223        if (trace_filename != NULL) {
224            trace_dynamic_symbol_add(dsaddr, arg);
225#ifdef DEBUG
226            printf("QEMU.trace: dynamic symbol %lx:%s\n", dsaddr, arg);
227#endif
228        }
229        arg[0] = 0;
230        break;
231    case TRACE_DEV_REG_REMOVE_ADDR:         // remove dynamic symbol addr
232        if (trace_filename != NULL) {
233            trace_dynamic_symbol_remove(value);
234#ifdef DEBUG
235            printf("QEMU.trace: dynamic symbol remove %lx\n", dsaddr);
236#endif
237        }
238        break;
239
240    case TRACE_DEV_REG_PRINT_STR:       // print string
241        vstrcpy(value, arg, CLIENT_PAGE_SIZE);
242        printf("%s", arg);
243        arg[0] = 0;
244        break;
245    case TRACE_DEV_REG_PRINT_NUM_DEC:   // print number in decimal
246        printf("%d", value);
247        break;
248    case TRACE_DEV_REG_PRINT_NUM_HEX:   // print number in hexical
249        printf("%x", value);
250        break;
251
252    case TRACE_DEV_REG_STOP_EMU:        // stop the VM execution
253        if (trace_filename != NULL) {
254            // To ensure that the number of instructions executed in this
255            // block is correct, we pretend that there was an exception.
256            trace_exception(0);
257        }
258        cpu_single_env->exception_index = EXCP_HLT;
259        cpu_single_env->halted = 1;
260        qemu_system_shutdown_request();
261        cpu_loop_exit();
262        break;
263
264    case TRACE_DEV_REG_ENABLE:          // tracing enable: 0 = stop, 1 = start
265        if (value == 1) {
266            if (trace_filename != NULL) {
267                start_tracing();
268            }
269        }
270        else if (value == 0) {
271            if (trace_filename != NULL) {
272                stop_tracing();
273
274                // To ensure that the number of instructions executed in this
275                // block is correct, we pretend that there was an exception.
276                trace_exception(0);
277            }
278        }
279        break;
280
281    case TRACE_DEV_REG_UNMAP_START:
282        unmap_start = value;
283        break;
284    case TRACE_DEV_REG_UNMAP_END:
285        if (trace_filename != NULL) {
286            trace_munmap(unmap_start, value);
287        }
288#ifdef CONFIG_MEMCHECK
289        if (memcheck_enabled) {
290            memcheck_unmap(unmap_start, value);
291        }
292#endif  // CONFIG_MEMCHECK
293        break;
294
295    case TRACE_DEV_REG_METHOD_ENTRY:
296    case TRACE_DEV_REG_METHOD_EXIT:
297    case TRACE_DEV_REG_METHOD_EXCEPTION:
298    case TRACE_DEV_REG_NATIVE_ENTRY:
299    case TRACE_DEV_REG_NATIVE_EXIT:
300    case TRACE_DEV_REG_NATIVE_EXCEPTION:
301        if (trace_filename != NULL) {
302            if (tracing) {
303                int call_type = (offset - 4096) >> 2;
304                trace_interpreted_method(value, call_type);
305            }
306        }
307        break;
308
309#ifdef CONFIG_MEMCHECK
310    case TRACE_DEV_REG_MALLOC:
311        if (memcheck_enabled) {
312            memcheck_guest_alloc(value);
313        }
314        break;
315
316    case TRACE_DEV_REG_FREE_PTR:
317        if (memcheck_enabled) {
318            memcheck_guest_free(value);
319        }
320        break;
321
322    case TRACE_DEV_REG_QUERY_MALLOC:
323        if (memcheck_enabled) {
324            memcheck_guest_query_malloc(value);
325        }
326        break;
327
328    case TRACE_DEV_REG_LIBC_INIT:
329        if (memcheck_enabled) {
330            memcheck_guest_libc_initialized(value);
331        }
332        break;
333
334    case TRACE_DEV_REG_PRINT_USER_STR:
335        if (memcheck_enabled) {
336            memcheck_guest_print_str(value);
337        }
338        break;
339#endif // CONFIG_MEMCHECK
340
341    default:
342        if (offset < 4096) {
343            cpu_abort(cpu_single_env, "trace_dev_write: Bad offset %x\n", offset);
344        }
345        break;
346    }
347}
348
349/* I/O read */
350static uint32_t trace_dev_read(void *opaque, target_phys_addr_t offset)
351{
352    trace_dev_state *s = (trace_dev_state *)opaque;
353
354    switch (offset >> 2) {
355    case TRACE_DEV_REG_ENABLE:          // tracing enable
356        return tracing;
357    default:
358        if (offset < 4096) {
359            cpu_abort(cpu_single_env, "trace_dev_read: Bad offset %x\n", offset);
360        }
361        return 0;
362    }
363    return 0;
364}
365
366static CPUReadMemoryFunc *trace_dev_readfn[] = {
367   trace_dev_read,
368   trace_dev_read,
369   trace_dev_read
370};
371
372static CPUWriteMemoryFunc *trace_dev_writefn[] = {
373   trace_dev_write,
374   trace_dev_write,
375   trace_dev_write
376};
377
378/* initialize the trace device */
379void trace_dev_init()
380{
381    int iomemtype;
382    trace_dev_state *s;
383
384    s = (trace_dev_state *)qemu_mallocz(sizeof(trace_dev_state));
385    s->dev.name = "qemu_trace";
386    s->dev.id = -1;
387    s->dev.base = 0;       // will be allocated dynamically
388    s->dev.size = 0x2000;
389    s->dev.irq = 0;
390    s->dev.irq_count = 0;
391
392    goldfish_device_add(&s->dev, trace_dev_readfn, trace_dev_writefn, s);
393
394    path[0] = arg[0] = '\0';
395}
396