xref: /openbmc/qemu/accel/tcg/translator.c (revision df22fbb7)
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 "sysemu/replay.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, tb, 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         /* Stop translation if translate_insn so indicated.  */
104         if (db->is_jmp != DISAS_NEXT) {
105             break;
106         }
107 
108         /*
109          * We can't instrument after instructions that change control
110          * flow although this only really affects post-load operations.
111          */
112         if (plugin_enabled) {
113             plugin_gen_insn_end();
114         }
115 
116         /* Stop translation if the output buffer is full,
117            or we have executed all of the allowed instructions.  */
118         if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
119             db->is_jmp = DISAS_TOO_MANY;
120             break;
121         }
122     }
123 
124     /* Emit code to exit the TB, as indicated by db->is_jmp.  */
125     ops->tb_stop(db, cpu);
126     gen_tb_end(db->tb, db->num_insns);
127 
128     if (plugin_enabled) {
129         plugin_gen_tb_end(cpu);
130     }
131 
132     /* The disas_log hook may use these values rather than recompute.  */
133     tb->size = db->pc_next - db->pc_first;
134     tb->icount = db->num_insns;
135 
136 #ifdef DEBUG_DISAS
137     if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
138         && qemu_log_in_addr_range(db->pc_first)) {
139         FILE *logfile = qemu_log_trylock();
140         if (logfile) {
141             fprintf(logfile, "----------------\n");
142             ops->disas_log(db, cpu, logfile);
143             fprintf(logfile, "\n");
144             qemu_log_unlock(logfile);
145         }
146     }
147 #endif
148 }
149 
150 static void *translator_access(CPUArchState *env, DisasContextBase *db,
151                                target_ulong pc, size_t len)
152 {
153     void *host;
154     target_ulong base, end;
155     TranslationBlock *tb;
156 
157     tb = db->tb;
158 
159     /* Use slow path if first page is MMIO. */
160     if (unlikely(tb->page_addr[0] == -1)) {
161         return NULL;
162     }
163 
164     end = pc + len - 1;
165     if (likely(is_same_page(db, end))) {
166         host = db->host_addr[0];
167         base = db->pc_first;
168     } else {
169         host = db->host_addr[1];
170         base = TARGET_PAGE_ALIGN(db->pc_first);
171         if (host == NULL) {
172             tb->page_addr[1] =
173                 get_page_addr_code_hostp(env, base, &db->host_addr[1]);
174 #ifdef CONFIG_USER_ONLY
175             page_protect(end);
176 #endif
177             /* We cannot handle MMIO as second page. */
178             assert(tb->page_addr[1] != -1);
179             host = db->host_addr[1];
180         }
181 
182         /* Use slow path when crossing pages. */
183         if (is_same_page(db, pc)) {
184             return NULL;
185         }
186     }
187 
188     tcg_debug_assert(pc >= base);
189     return host + (pc - base);
190 }
191 
192 uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
193 {
194     uint8_t ret;
195     void *p = translator_access(env, db, pc, sizeof(ret));
196 
197     if (p) {
198         plugin_insn_append(pc, p, sizeof(ret));
199         return ldub_p(p);
200     }
201     ret = cpu_ldub_code(env, pc);
202     plugin_insn_append(pc, &ret, sizeof(ret));
203     return ret;
204 }
205 
206 uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
207 {
208     uint16_t ret, plug;
209     void *p = translator_access(env, db, pc, sizeof(ret));
210 
211     if (p) {
212         plugin_insn_append(pc, p, sizeof(ret));
213         return lduw_p(p);
214     }
215     ret = cpu_lduw_code(env, pc);
216     plug = tswap16(ret);
217     plugin_insn_append(pc, &plug, sizeof(ret));
218     return ret;
219 }
220 
221 uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
222 {
223     uint32_t ret, plug;
224     void *p = translator_access(env, db, pc, sizeof(ret));
225 
226     if (p) {
227         plugin_insn_append(pc, p, sizeof(ret));
228         return ldl_p(p);
229     }
230     ret = cpu_ldl_code(env, pc);
231     plug = tswap32(ret);
232     plugin_insn_append(pc, &plug, sizeof(ret));
233     return ret;
234 }
235 
236 uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, abi_ptr pc)
237 {
238     uint64_t ret, plug;
239     void *p = translator_access(env, db, pc, sizeof(ret));
240 
241     if (p) {
242         plugin_insn_append(pc, p, sizeof(ret));
243         return ldq_p(p);
244     }
245     ret = cpu_ldq_code(env, pc);
246     plug = tswap64(ret);
247     plugin_insn_append(pc, &plug, sizeof(ret));
248     return ret;
249 }
250