1 /* 2 * Generic intermediate code generation. 3 * 4 * Copyright (C) 2016-2017 Lluís Vilanova <vilanova@ac.upc.edu> 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 * See the COPYING file in the top-level directory. 8 */ 9 10 #include "qemu/osdep.h" 11 #include "qemu/error-report.h" 12 #include "tcg/tcg.h" 13 #include "tcg/tcg-op.h" 14 #include "exec/exec-all.h" 15 #include "exec/gen-icount.h" 16 #include "exec/log.h" 17 #include "exec/translator.h" 18 #include "exec/plugin-gen.h" 19 #include "exec/replay-core.h" 20 21 void translator_loop_temp_check(DisasContextBase *db) 22 { 23 } 24 25 bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest) 26 { 27 /* Suppress goto_tb if requested. */ 28 if (tb_cflags(db->tb) & CF_NO_GOTO_TB) { 29 return false; 30 } 31 32 /* Check for the dest on the same page as the start of the TB. */ 33 return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; 34 } 35 36 void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, 37 target_ulong pc, void *host_pc, 38 const TranslatorOps *ops, DisasContextBase *db) 39 { 40 uint32_t cflags = tb_cflags(tb); 41 bool plugin_enabled; 42 43 /* Initialize DisasContext */ 44 db->tb = tb; 45 db->pc_first = pc; 46 db->pc_next = pc; 47 db->is_jmp = DISAS_NEXT; 48 db->num_insns = 0; 49 db->max_insns = *max_insns; 50 db->singlestep_enabled = cflags & CF_SINGLE_STEP; 51 db->host_addr[0] = host_pc; 52 db->host_addr[1] = NULL; 53 54 #ifdef CONFIG_USER_ONLY 55 page_protect(pc); 56 #endif 57 58 ops->init_disas_context(db, cpu); 59 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 60 61 /* Start translating. */ 62 gen_tb_start(db->tb); 63 ops->tb_start(db, cpu); 64 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 65 66 plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY); 67 68 while (true) { 69 *max_insns = ++db->num_insns; 70 ops->insn_start(db, cpu); 71 tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ 72 73 if (plugin_enabled) { 74 plugin_gen_insn_start(cpu, db); 75 } 76 77 /* Disassemble one instruction. The translate_insn hook should 78 update db->pc_next and db->is_jmp to indicate what should be 79 done next -- either exiting this loop or locate the start of 80 the next instruction. */ 81 if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { 82 /* Accept I/O on the last instruction. */ 83 gen_io_start(); 84 ops->translate_insn(db, cpu); 85 } else { 86 /* we should only see CF_MEMI_ONLY for io_recompile */ 87 tcg_debug_assert(!(cflags & CF_MEMI_ONLY)); 88 ops->translate_insn(db, cpu); 89 } 90 91 /* 92 * We can't instrument after instructions that change control 93 * flow although this only really affects post-load operations. 94 * 95 * Calling plugin_gen_insn_end() before we possibly stop translation 96 * is important. Even if this ends up as dead code, plugin generation 97 * needs to see a matching plugin_gen_insn_{start,end}() pair in order 98 * to accurately track instrumented helpers that might access memory. 99 */ 100 if (plugin_enabled) { 101 plugin_gen_insn_end(); 102 } 103 104 /* Stop translation if translate_insn so indicated. */ 105 if (db->is_jmp != DISAS_NEXT) { 106 break; 107 } 108 109 /* Stop translation if the output buffer is full, 110 or we have executed all of the allowed instructions. */ 111 if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { 112 db->is_jmp = DISAS_TOO_MANY; 113 break; 114 } 115 } 116 117 /* Emit code to exit the TB, as indicated by db->is_jmp. */ 118 ops->tb_stop(db, cpu); 119 gen_tb_end(db->tb, db->num_insns); 120 121 if (plugin_enabled) { 122 plugin_gen_tb_end(cpu); 123 } 124 125 /* The disas_log hook may use these values rather than recompute. */ 126 tb->size = db->pc_next - db->pc_first; 127 tb->icount = db->num_insns; 128 129 #ifdef DEBUG_DISAS 130 if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) 131 && qemu_log_in_addr_range(db->pc_first)) { 132 FILE *logfile = qemu_log_trylock(); 133 if (logfile) { 134 fprintf(logfile, "----------------\n"); 135 ops->disas_log(db, cpu, logfile); 136 fprintf(logfile, "\n"); 137 qemu_log_unlock(logfile); 138 } 139 } 140 #endif 141 } 142 143 static void *translator_access(CPUArchState *env, DisasContextBase *db, 144 target_ulong pc, size_t len) 145 { 146 void *host; 147 target_ulong base, end; 148 TranslationBlock *tb; 149 150 tb = db->tb; 151 152 /* Use slow path if first page is MMIO. */ 153 if (unlikely(tb_page_addr0(tb) == -1)) { 154 return NULL; 155 } 156 157 end = pc + len - 1; 158 if (likely(is_same_page(db, end))) { 159 host = db->host_addr[0]; 160 base = db->pc_first; 161 } else { 162 host = db->host_addr[1]; 163 base = TARGET_PAGE_ALIGN(db->pc_first); 164 if (host == NULL) { 165 tb_page_addr_t phys_page = 166 get_page_addr_code_hostp(env, base, &db->host_addr[1]); 167 168 /* 169 * If the second page is MMIO, treat as if the first page 170 * was MMIO as well, so that we do not cache the TB. 171 */ 172 if (unlikely(phys_page == -1)) { 173 tb_set_page_addr0(tb, -1); 174 return NULL; 175 } 176 177 tb_set_page_addr1(tb, phys_page); 178 #ifdef CONFIG_USER_ONLY 179 page_protect(end); 180 #endif 181 host = db->host_addr[1]; 182 } 183 184 /* Use slow path when crossing pages. */ 185 if (is_same_page(db, pc)) { 186 return NULL; 187 } 188 } 189 190 tcg_debug_assert(pc >= base); 191 return host + (pc - base); 192 } 193 194 uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 195 { 196 uint8_t ret; 197 void *p = translator_access(env, db, pc, sizeof(ret)); 198 199 if (p) { 200 plugin_insn_append(pc, p, sizeof(ret)); 201 return ldub_p(p); 202 } 203 ret = cpu_ldub_code(env, pc); 204 plugin_insn_append(pc, &ret, sizeof(ret)); 205 return ret; 206 } 207 208 uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 209 { 210 uint16_t ret, plug; 211 void *p = translator_access(env, db, pc, sizeof(ret)); 212 213 if (p) { 214 plugin_insn_append(pc, p, sizeof(ret)); 215 return lduw_p(p); 216 } 217 ret = cpu_lduw_code(env, pc); 218 plug = tswap16(ret); 219 plugin_insn_append(pc, &plug, sizeof(ret)); 220 return ret; 221 } 222 223 uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 224 { 225 uint32_t ret, plug; 226 void *p = translator_access(env, db, pc, sizeof(ret)); 227 228 if (p) { 229 plugin_insn_append(pc, p, sizeof(ret)); 230 return ldl_p(p); 231 } 232 ret = cpu_ldl_code(env, pc); 233 plug = tswap32(ret); 234 plugin_insn_append(pc, &plug, sizeof(ret)); 235 return ret; 236 } 237 238 uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) 239 { 240 uint64_t ret, plug; 241 void *p = translator_access(env, db, pc, sizeof(ret)); 242 243 if (p) { 244 plugin_insn_append(pc, p, sizeof(ret)); 245 return ldq_p(p); 246 } 247 ret = cpu_ldq_code(env, pc); 248 plug = tswap64(ret); 249 plugin_insn_append(pc, &plug, sizeof(ret)); 250 return ret; 251 } 252