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