xref: /openbmc/qemu/accel/tcg/translator.c (revision 042d6b02)
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/log.h"
12 #include "qemu/error-report.h"
13 #include "exec/exec-all.h"
14 #include "exec/translator.h"
15 #include "exec/translate-all.h"
16 #include "exec/plugin-gen.h"
17 #include "tcg/tcg-op-common.h"
18 
19 static void gen_io_start(void)
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 
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 
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 
120 bool translator_use_goto_tb(DisasContextBase *db, target_ulong dest)
121 {
122     /* Suppress goto_tb if requested. */
123     if (tb_cflags(db->tb) & CF_NO_GOTO_TB) {
124         return false;
125     }
126 
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 
131 void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns,
132                      target_ulong pc, void *host_pc,
133                      const TranslatorOps *ops, DisasContextBase *db)
134 {
135     uint32_t cflags = tb_cflags(tb);
136     TCGOp *icount_start_insn;
137     bool plugin_enabled;
138 
139     /* Initialize DisasContext */
140     db->tb = tb;
141     db->pc_first = pc;
142     db->pc_next = pc;
143     db->is_jmp = DISAS_NEXT;
144     db->num_insns = 0;
145     db->max_insns = *max_insns;
146     db->singlestep_enabled = cflags & CF_SINGLE_STEP;
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
153 
154     ops->init_disas_context(db, cpu);
155     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
156 
157     /* Start translating.  */
158     icount_start_insn = gen_tb_start(cflags);
159     ops->tb_start(db, cpu);
160     tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
161 
162     plugin_enabled = plugin_gen_tb_start(cpu, db, cflags & CF_MEMI_ONLY);
163 
164     while (true) {
165         *max_insns = ++db->num_insns;
166         ops->insn_start(db, cpu);
167         tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */
168 
169         if (plugin_enabled) {
170             plugin_gen_insn_start(cpu, db);
171         }
172 
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.  */
177         if (db->num_insns == db->max_insns && (cflags & CF_LAST_IO)) {
178             /* Accept I/O on the last instruction.  */
179             gen_io_start();
180             ops->translate_insn(db, cpu);
181         } else {
182             /* we should only see CF_MEMI_ONLY for io_recompile */
183             tcg_debug_assert(!(cflags & CF_MEMI_ONLY));
184             ops->translate_insn(db, cpu);
185         }
186 
187         /*
188          * We can't instrument after instructions that change control
189          * flow although this only really affects post-load operations.
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.
195          */
196         if (plugin_enabled) {
197             plugin_gen_insn_end();
198         }
199 
200         /* Stop translation if translate_insn so indicated.  */
201         if (db->is_jmp != DISAS_NEXT) {
202             break;
203         }
204 
205         /* Stop translation if the output buffer is full,
206            or we have executed all of the allowed instructions.  */
207         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
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);
215     gen_tb_end(tb, cflags, icount_start_insn, db->num_insns);
216 
217     if (plugin_enabled) {
218         plugin_gen_tb_end(cpu);
219     }
220 
221     /* The disas_log hook may use these values rather than recompute.  */
222     tb->size = db->pc_next - db->pc_first;
223     tb->icount = db->num_insns;
224 
225     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
226         && qemu_log_in_addr_range(db->pc_first)) {
227         FILE *logfile = qemu_log_trylock();
228         if (logfile) {
229             fprintf(logfile, "----------------\n");
230             ops->disas_log(db, cpu, logfile);
231             fprintf(logfile, "\n");
232             qemu_log_unlock(logfile);
233         }
234     }
235 }
236 
237 static void *translator_access(CPUArchState *env, DisasContextBase *db,
238                                target_ulong pc, size_t len)
239 {
240     void *host;
241     target_ulong base, end;
242     TranslationBlock *tb;
243 
244     tb = db->tb;
245 
246     /* Use slow path if first page is MMIO. */
247     if (unlikely(tb_page_addr0(tb) == -1)) {
248         return NULL;
249     }
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) {
259             tb_page_addr_t phys_page =
260                 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
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 
271             tb_set_page_addr1(tb, phys_page);
272 #ifdef CONFIG_USER_ONLY
273             page_protect(end);
274 #endif
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);
286 }
287 
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 
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);
317     }
318     ret = cpu_ldub_code(env, pc);
319     plugin_insn_append(pc, &ret, sizeof(ret));
320     return ret;
321 }
322 
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));
327 
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 }
367 
368 void translator_fake_ldb(uint8_t insn8, abi_ptr pc)
369 {
370     plugin_insn_append(pc, &insn8, sizeof(insn8));
371 }
372