Home | History | Annotate | Download | only in hw
      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 
     24 extern void cpu_loop_exit(void);
     25 
     26 extern int tracing;
     27 extern const char *trace_filename;
     28 
     29 /* for execve */
     30 static char path[CLIENT_PAGE_SIZE];
     31 static char arg[CLIENT_PAGE_SIZE];
     32 static unsigned long vstart;    // VM start
     33 static unsigned long vend;      // VM end
     34 static unsigned long eoff;      // offset in EXE file
     35 static unsigned cmdlen;         // cmdline length
     36 static unsigned pid;            // PID (really thread id)
     37 static unsigned tgid;           // thread group id (really process id)
     38 static unsigned long dsaddr;    // dynamic symbol address
     39 static 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 */
     45 static 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 */
    350 static 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 
    366 static CPUReadMemoryFunc *trace_dev_readfn[] = {
    367    trace_dev_read,
    368    trace_dev_read,
    369    trace_dev_read
    370 };
    371 
    372 static CPUWriteMemoryFunc *trace_dev_writefn[] = {
    373    trace_dev_write,
    374    trace_dev_write,
    375    trace_dev_write
    376 };
    377 
    378 /* initialize the trace device */
    379 void 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