]>
Commit | Line | Data |
---|---|---|
bb2e0039 LV |
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" | |
653c46da | 11 | #include "qemu/log.h" |
bb2e0039 | 12 | #include "qemu/error-report.h" |
bb2e0039 | 13 | #include "exec/exec-all.h" |
bb2e0039 | 14 | #include "exec/translator.h" |
653c46da | 15 | #include "exec/translate-all.h" |
6ba6f818 | 16 | #include "exec/plugin-gen.h" |
653c46da | 17 | #include "tcg/tcg-op-common.h" |
56234233 | 18 | |
dfd1b812 | 19 | static void gen_io_start(void) |
56234233 RH |
20 | { |
21 | tcg_gen_st_i32(tcg_constant_i32(1), cpu_env, | |
22 | offsetof(ArchCPU, parent_obj.can_do_io) - | |
23 | offsetof(ArchCPU, env)); | |
24 | } | |
25 | ||
dfd1b812 RH |
26 | bool translator_io_start(DisasContextBase *db) |
27 | { | |
28 | uint32_t cflags = tb_cflags(db->tb); | |
29 | ||
30 | if (!(cflags & CF_USE_ICOUNT)) { | |
31 | return false; | |
32 | } | |
33 | if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { | |
34 | /* Already started in translator_loop. */ | |
35 | return true; | |
36 | } | |
37 | ||
38 | gen_io_start(); | |
39 | ||
40 | /* | |
41 | * Ensure that this instruction will be the last in the TB. | |
42 | * The target may override this to something more forceful. | |
43 | */ | |
44 | if (db->is_jmp == DISAS_NEXT) { | |
45 | db->is_jmp = DISAS_TOO_MANY; | |
46 | } | |
47 | return true; | |
48 | } | |
49 | ||
56234233 RH |
50 | static TCGOp *gen_tb_start(uint32_t cflags) |
51 | { | |
52 | TCGv_i32 count = tcg_temp_new_i32(); | |
53 | TCGOp *icount_start_insn = NULL; | |
54 | ||
55 | tcg_gen_ld_i32(count, cpu_env, | |
56 | offsetof(ArchCPU, neg.icount_decr.u32) - | |
57 | offsetof(ArchCPU, env)); | |
58 | ||
59 | if (cflags & CF_USE_ICOUNT) { | |
60 | /* | |
61 | * We emit a sub with a dummy immediate argument. Keep the insn index | |
62 | * of the sub so that we later (when we know the actual insn count) | |
63 | * can update the argument with the actual insn count. | |
64 | */ | |
65 | tcg_gen_sub_i32(count, count, tcg_constant_i32(0)); | |
66 | icount_start_insn = tcg_last_op(); | |
67 | } | |
68 | ||
69 | /* | |
70 | * Emit the check against icount_decr.u32 to see if we should exit | |
71 | * unless we suppress the check with CF_NOIRQ. If we are using | |
72 | * icount and have suppressed interruption the higher level code | |
73 | * should have ensured we don't run more instructions than the | |
74 | * budget. | |
75 | */ | |
76 | if (cflags & CF_NOIRQ) { | |
77 | tcg_ctx->exitreq_label = NULL; | |
78 | } else { | |
79 | tcg_ctx->exitreq_label = gen_new_label(); | |
80 | tcg_gen_brcondi_i32(TCG_COND_LT, count, 0, tcg_ctx->exitreq_label); | |
81 | } | |
82 | ||
83 | if (cflags & CF_USE_ICOUNT) { | |
84 | tcg_gen_st16_i32(count, cpu_env, | |
85 | offsetof(ArchCPU, neg.icount_decr.u16.low) - | |
86 | offsetof(ArchCPU, env)); | |
87 | /* | |
88 | * cpu->can_do_io is cleared automatically here at the beginning of | |
89 | * each translation block. The cost is minimal and only paid for | |
90 | * -icount, plus it would be very easy to forget doing it in the | |
91 | * translator. Doing it here means we don't need a gen_io_end() to | |
92 | * go with gen_io_start(). | |
93 | */ | |
94 | tcg_gen_st_i32(tcg_constant_i32(0), cpu_env, | |
95 | offsetof(ArchCPU, parent_obj.can_do_io) - | |
96 | offsetof(ArchCPU, env)); | |
97 | } | |
98 | ||
99 | return icount_start_insn; | |
100 | } | |
101 | ||
102 | static void gen_tb_end(const TranslationBlock *tb, uint32_t cflags, | |
103 | TCGOp *icount_start_insn, int num_insns) | |
104 | { | |
105 | if (cflags & CF_USE_ICOUNT) { | |
106 | /* | |
107 | * Update the num_insn immediate parameter now that we know | |
108 | * the actual insn count. | |
109 | */ | |
110 | tcg_set_insn_param(icount_start_insn, 2, | |
111 | tcgv_i32_arg(tcg_constant_i32(num_insns))); | |
112 | } | |
113 | ||
114 | if (tcg_ctx->exitreq_label) { | |
115 | gen_set_label(tcg_ctx->exitreq_label); | |
116 | tcg_gen_exit_tb(tb, TB_EXIT_REQUESTED); | |
117 | } | |
118 | } | |
119 | ||
b1c09220 | 120 | bool translator_use_goto_tb(DisasContextBase *db, vaddr dest) |
d3a2a1d8 | 121 | { |
84f15616 RH |
122 | /* Suppress goto_tb if requested. */ |
123 | if (tb_cflags(db->tb) & CF_NO_GOTO_TB) { | |
124 | return false; | |
125 | } | |
126 | ||
d3a2a1d8 RH |
127 | /* Check for the dest on the same page as the start of the TB. */ |
128 | return ((db->pc_first ^ dest) & TARGET_PAGE_MASK) == 0; | |
129 | } | |
130 | ||
597f9b2d | 131 | void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, |
b1c09220 AJ |
132 | vaddr pc, void *host_pc, const TranslatorOps *ops, |
133 | DisasContextBase *db) | |
bb2e0039 | 134 | { |
d40c5c79 | 135 | uint32_t cflags = tb_cflags(tb); |
56234233 | 136 | TCGOp *icount_start_insn; |
6ba6f818 | 137 | bool plugin_enabled; |
f9f1f56e | 138 | |
bb2e0039 LV |
139 | /* Initialize DisasContext */ |
140 | db->tb = tb; | |
306c8721 RH |
141 | db->pc_first = pc; |
142 | db->pc_next = pc; | |
bb2e0039 LV |
143 | db->is_jmp = DISAS_NEXT; |
144 | db->num_insns = 0; | |
597f9b2d | 145 | db->max_insns = *max_insns; |
c2ffd754 | 146 | db->singlestep_enabled = cflags & CF_SINGLE_STEP; |
50627f1b RH |
147 | db->host_addr[0] = host_pc; |
148 | db->host_addr[1] = NULL; | |
149 | ||
150 | #ifdef CONFIG_USER_ONLY | |
151 | page_protect(pc); | |
152 | #endif | |
bb2e0039 | 153 | |
b542683d | 154 | ops->init_disas_context(db, cpu); |
bb2e0039 LV |
155 | tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ |
156 | ||
bb2e0039 | 157 | /* Start translating. */ |
56234233 | 158 | icount_start_insn = gen_tb_start(cflags); |
bb2e0039 LV |
159 | ops->tb_start(db, cpu); |
160 | tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ | |
161 | ||
b21af662 | 162 | plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY); |
6ba6f818 | 163 | |
bb2e0039 | 164 | while (true) { |
9b1890ad | 165 | *max_insns = ++db->num_insns; |
bb2e0039 LV |
166 | ops->insn_start(db, cpu); |
167 | tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ | |
168 | ||
6ba6f818 EC |
169 | if (plugin_enabled) { |
170 | plugin_gen_insn_start(cpu, db); | |
171 | } | |
172 | ||
bb2e0039 LV |
173 | /* Disassemble one instruction. The translate_insn hook should |
174 | update db->pc_next and db->is_jmp to indicate what should be | |
175 | done next -- either exiting this loop or locate the start of | |
176 | the next instruction. */ | |
d40c5c79 | 177 | if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) { |
bb2e0039 LV |
178 | /* Accept I/O on the last instruction. */ |
179 | gen_io_start(); | |
180 | ops->translate_insn(db, cpu); | |
bb2e0039 | 181 | } else { |
cfd405ea | 182 | /* we should only see CF_MEMI_ONLY for io_recompile */ |
d40c5c79 | 183 | tcg_debug_assert(!(cflags & CF_MEMI_ONLY)); |
bb2e0039 LV |
184 | ops->translate_insn(db, cpu); |
185 | } | |
186 | ||
6ba6f818 EC |
187 | /* |
188 | * We can't instrument after instructions that change control | |
189 | * flow although this only really affects post-load operations. | |
0f92d94a EC |
190 | * |
191 | * Calling plugin_gen_insn_end() before we possibly stop translation | |
192 | * is important. Even if this ends up as dead code, plugin generation | |
193 | * needs to see a matching plugin_gen_insn_{start,end}() pair in order | |
194 | * to accurately track instrumented helpers that might access memory. | |
6ba6f818 EC |
195 | */ |
196 | if (plugin_enabled) { | |
197 | plugin_gen_insn_end(); | |
198 | } | |
199 | ||
0f92d94a EC |
200 | /* Stop translation if translate_insn so indicated. */ |
201 | if (db->is_jmp != DISAS_NEXT) { | |
202 | break; | |
203 | } | |
204 | ||
bb2e0039 LV |
205 | /* Stop translation if the output buffer is full, |
206 | or we have executed all of the allowed instructions. */ | |
b542683d | 207 | if (tcg_op_buf_full() || db->num_insns >= db->max_insns) { |
bb2e0039 LV |
208 | db->is_jmp = DISAS_TOO_MANY; |
209 | break; | |
210 | } | |
211 | } | |
212 | ||
213 | /* Emit code to exit the TB, as indicated by db->is_jmp. */ | |
214 | ops->tb_stop(db, cpu); | |
56234233 | 215 | gen_tb_end(tb, cflags, icount_start_insn, db->num_insns); |
bb2e0039 | 216 | |
6ba6f818 EC |
217 | if (plugin_enabled) { |
218 | plugin_gen_tb_end(cpu); | |
219 | } | |
220 | ||
bb2e0039 | 221 | /* The disas_log hook may use these values rather than recompute. */ |
d9971435 RH |
222 | tb->size = db->pc_next - db->pc_first; |
223 | tb->icount = db->num_insns; | |
bb2e0039 | 224 | |
bb2e0039 LV |
225 | if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) |
226 | && qemu_log_in_addr_range(db->pc_first)) { | |
c60f599b | 227 | FILE *logfile = qemu_log_trylock(); |
78b54858 RH |
228 | if (logfile) { |
229 | fprintf(logfile, "----------------\n"); | |
8eb806a7 | 230 | ops->disas_log(db, cpu, logfile); |
78b54858 RH |
231 | fprintf(logfile, "\n"); |
232 | qemu_log_unlock(logfile); | |
233 | } | |
bb2e0039 | 234 | } |
bb2e0039 | 235 | } |
f025692c | 236 | |
50627f1b | 237 | static void *translator_access(CPUArchState *env, DisasContextBase *db, |
b1c09220 | 238 | vaddr pc, size_t len) |
f025692c | 239 | { |
50627f1b | 240 | void *host; |
b1c09220 | 241 | vaddr base, end; |
50627f1b RH |
242 | TranslationBlock *tb; |
243 | ||
244 | tb = db->tb; | |
f025692c | 245 | |
50627f1b | 246 | /* Use slow path if first page is MMIO. */ |
28905cfb | 247 | if (unlikely(tb_page_addr0(tb) == -1)) { |
50627f1b | 248 | return NULL; |
f025692c | 249 | } |
50627f1b RH |
250 | |
251 | end = pc + len - 1; | |
252 | if (likely(is_same_page(db, end))) { | |
253 | host = db->host_addr[0]; | |
254 | base = db->pc_first; | |
255 | } else { | |
256 | host = db->host_addr[1]; | |
257 | base = TARGET_PAGE_ALIGN(db->pc_first); | |
258 | if (host == NULL) { | |
28905cfb | 259 | tb_page_addr_t phys_page = |
50627f1b | 260 | get_page_addr_code_hostp(env, base, &db->host_addr[1]); |
2627e452 RH |
261 | |
262 | /* | |
263 | * If the second page is MMIO, treat as if the first page | |
264 | * was MMIO as well, so that we do not cache the TB. | |
265 | */ | |
266 | if (unlikely(phys_page == -1)) { | |
267 | tb_set_page_addr0(tb, -1); | |
268 | return NULL; | |
269 | } | |
270 | ||
28905cfb | 271 | tb_set_page_addr1(tb, phys_page); |
50627f1b RH |
272 | #ifdef CONFIG_USER_ONLY |
273 | page_protect(end); | |
f025692c | 274 | #endif |
50627f1b RH |
275 | host = db->host_addr[1]; |
276 | } | |
277 | ||
278 | /* Use slow path when crossing pages. */ | |
279 | if (is_same_page(db, pc)) { | |
280 | return NULL; | |
281 | } | |
282 | } | |
283 | ||
284 | tcg_debug_assert(pc >= base); | |
285 | return host + (pc - base); | |
f025692c IL |
286 | } |
287 | ||
bc54ef8c RH |
288 | static void plugin_insn_append(abi_ptr pc, const void *from, size_t size) |
289 | { | |
290 | #ifdef CONFIG_PLUGIN | |
291 | struct qemu_plugin_insn *insn = tcg_ctx->plugin_insn; | |
292 | abi_ptr off; | |
293 | ||
294 | if (insn == NULL) { | |
295 | return; | |
296 | } | |
297 | off = pc - insn->vaddr; | |
298 | if (off < insn->data->len) { | |
299 | g_byte_array_set_size(insn->data, off); | |
300 | } else if (off > insn->data->len) { | |
301 | /* we have an unexpected gap */ | |
302 | g_assert_not_reached(); | |
303 | } | |
304 | ||
305 | insn->data = g_byte_array_append(insn->data, from, size); | |
306 | #endif | |
307 | } | |
308 | ||
50627f1b RH |
309 | uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc) |
310 | { | |
311 | uint8_t ret; | |
312 | void *p = translator_access(env, db, pc, sizeof(ret)); | |
313 | ||
314 | if (p) { | |
315 | plugin_insn_append(pc, p, sizeof(ret)); | |
316 | return ldub_p(p); | |
f025692c | 317 | } |
50627f1b RH |
318 | ret = cpu_ldub_code(env, pc); |
319 | plugin_insn_append(pc, &ret, sizeof(ret)); | |
320 | return ret; | |
321 | } | |
f025692c | 322 | |
50627f1b RH |
323 | uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc) |
324 | { | |
325 | uint16_t ret, plug; | |
326 | void *p = translator_access(env, db, pc, sizeof(ret)); | |
f025692c | 327 | |
50627f1b RH |
328 | if (p) { |
329 | plugin_insn_append(pc, p, sizeof(ret)); | |
330 | return lduw_p(p); | |
331 | } | |
332 | ret = cpu_lduw_code(env, pc); | |
333 | plug = tswap16(ret); | |
334 | plugin_insn_append(pc, &plug, sizeof(ret)); | |
335 | return ret; | |
336 | } | |
337 | ||
338 | uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc) | |
339 | { | |
340 | uint32_t ret, plug; | |
341 | void *p = translator_access(env, db, pc, sizeof(ret)); | |
342 | ||
343 | if (p) { | |
344 | plugin_insn_append(pc, p, sizeof(ret)); | |
345 | return ldl_p(p); | |
346 | } | |
347 | ret = cpu_ldl_code(env, pc); | |
348 | plug = tswap32(ret); | |
349 | plugin_insn_append(pc, &plug, sizeof(ret)); | |
350 | return ret; | |
351 | } | |
352 | ||
353 | uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc) | |
354 | { | |
355 | uint64_t ret, plug; | |
356 | void *p = translator_access(env, db, pc, sizeof(ret)); | |
357 | ||
358 | if (p) { | |
359 | plugin_insn_append(pc, p, sizeof(ret)); | |
360 | return ldq_p(p); | |
361 | } | |
362 | ret = cpu_ldq_code(env, pc); | |
363 | plug = tswap64(ret); | |
364 | plugin_insn_append(pc, &plug, sizeof(ret)); | |
365 | return ret; | |
366 | } | |
309e014d RH |
367 | |
368 | void translator_fake_ldb(uint8_t insn8, abi_ptr pc) | |
369 | { | |
370 | plugin_insn_append(pc, &insn8, sizeof(insn8)); | |
371 | } |