Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 1 | /* |
| 2 | * AArch64 translation |
| 3 | * |
| 4 | * Copyright (c) 2013 Alexander Graf <agraf@suse.de> |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 18 | */ |
| 19 | #include <stdarg.h> |
| 20 | #include <stdlib.h> |
| 21 | #include <stdio.h> |
| 22 | #include <string.h> |
| 23 | #include <inttypes.h> |
| 24 | |
| 25 | #include "cpu.h" |
| 26 | #include "tcg-op.h" |
| 27 | #include "qemu/log.h" |
| 28 | #include "translate.h" |
| 29 | #include "qemu/host-utils.h" |
| 30 | |
Peter Maydell | 089a8d9 | 2013-12-03 15:26:18 +0000 | [diff] [blame^] | 31 | #include "exec/gen-icount.h" |
| 32 | |
Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 33 | #include "helper.h" |
| 34 | #define GEN_HELPER 1 |
| 35 | #include "helper.h" |
| 36 | |
| 37 | static TCGv_i64 cpu_X[32]; |
| 38 | static TCGv_i64 cpu_pc; |
| 39 | static TCGv_i32 pstate; |
| 40 | |
| 41 | static const char *regnames[] = { |
| 42 | "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", |
| 43 | "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", |
| 44 | "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", |
| 45 | "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp" |
| 46 | }; |
| 47 | |
| 48 | /* initialize TCG globals. */ |
| 49 | void a64_translate_init(void) |
| 50 | { |
| 51 | int i; |
| 52 | |
| 53 | cpu_pc = tcg_global_mem_new_i64(TCG_AREG0, |
| 54 | offsetof(CPUARMState, pc), |
| 55 | "pc"); |
| 56 | for (i = 0; i < 32; i++) { |
| 57 | cpu_X[i] = tcg_global_mem_new_i64(TCG_AREG0, |
| 58 | offsetof(CPUARMState, xregs[i]), |
| 59 | regnames[i]); |
| 60 | } |
| 61 | |
| 62 | pstate = tcg_global_mem_new_i32(TCG_AREG0, |
| 63 | offsetof(CPUARMState, pstate), |
| 64 | "pstate"); |
| 65 | } |
| 66 | |
| 67 | void aarch64_cpu_dump_state(CPUState *cs, FILE *f, |
| 68 | fprintf_function cpu_fprintf, int flags) |
| 69 | { |
| 70 | ARMCPU *cpu = ARM_CPU(cs); |
| 71 | CPUARMState *env = &cpu->env; |
Peter Maydell | 6cd096b | 2013-11-26 17:21:48 +0000 | [diff] [blame] | 72 | uint32_t psr = pstate_read(env); |
Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 73 | int i; |
| 74 | |
| 75 | cpu_fprintf(f, "PC=%016"PRIx64" SP=%016"PRIx64"\n", |
| 76 | env->pc, env->xregs[31]); |
| 77 | for (i = 0; i < 31; i++) { |
| 78 | cpu_fprintf(f, "X%02d=%016"PRIx64, i, env->xregs[i]); |
| 79 | if ((i % 4) == 3) { |
| 80 | cpu_fprintf(f, "\n"); |
| 81 | } else { |
| 82 | cpu_fprintf(f, " "); |
| 83 | } |
| 84 | } |
Peter Maydell | 6cd096b | 2013-11-26 17:21:48 +0000 | [diff] [blame] | 85 | cpu_fprintf(f, "PSTATE=%08x (flags %c%c%c%c)\n", |
| 86 | psr, |
| 87 | psr & PSTATE_N ? 'N' : '-', |
| 88 | psr & PSTATE_Z ? 'Z' : '-', |
| 89 | psr & PSTATE_C ? 'C' : '-', |
| 90 | psr & PSTATE_V ? 'V' : '-'); |
Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 91 | cpu_fprintf(f, "\n"); |
| 92 | } |
| 93 | |
| 94 | void gen_a64_set_pc_im(uint64_t val) |
| 95 | { |
| 96 | tcg_gen_movi_i64(cpu_pc, val); |
| 97 | } |
| 98 | |
| 99 | static void gen_exception(int excp) |
| 100 | { |
| 101 | TCGv_i32 tmp = tcg_temp_new_i32(); |
| 102 | tcg_gen_movi_i32(tmp, excp); |
| 103 | gen_helper_exception(cpu_env, tmp); |
| 104 | tcg_temp_free_i32(tmp); |
| 105 | } |
| 106 | |
| 107 | static void gen_exception_insn(DisasContext *s, int offset, int excp) |
| 108 | { |
| 109 | gen_a64_set_pc_im(s->pc - offset); |
| 110 | gen_exception(excp); |
Peter Maydell | 089a8d9 | 2013-12-03 15:26:18 +0000 | [diff] [blame^] | 111 | s->is_jmp = DISAS_EXC; |
| 112 | } |
| 113 | |
| 114 | static inline bool use_goto_tb(DisasContext *s, int n, uint64_t dest) |
| 115 | { |
| 116 | /* No direct tb linking with singlestep or deterministic io */ |
| 117 | if (s->singlestep_enabled || (s->tb->cflags & CF_LAST_IO)) { |
| 118 | return false; |
| 119 | } |
| 120 | |
| 121 | /* Only link tbs from inside the same guest page */ |
| 122 | if ((s->tb->pc & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) { |
| 123 | return false; |
| 124 | } |
| 125 | |
| 126 | return true; |
| 127 | } |
| 128 | |
| 129 | static inline void gen_goto_tb(DisasContext *s, int n, uint64_t dest) |
| 130 | { |
| 131 | TranslationBlock *tb; |
| 132 | |
| 133 | tb = s->tb; |
| 134 | if (use_goto_tb(s, n, dest)) { |
| 135 | tcg_gen_goto_tb(n); |
| 136 | gen_a64_set_pc_im(dest); |
| 137 | tcg_gen_exit_tb((tcg_target_long)tb + n); |
| 138 | s->is_jmp = DISAS_TB_JUMP; |
| 139 | } else { |
| 140 | gen_a64_set_pc_im(dest); |
| 141 | if (s->singlestep_enabled) { |
| 142 | gen_exception(EXCP_DEBUG); |
| 143 | } |
| 144 | tcg_gen_exit_tb(0); |
| 145 | s->is_jmp = DISAS_JUMP; |
| 146 | } |
Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | static void real_unallocated_encoding(DisasContext *s) |
| 150 | { |
| 151 | fprintf(stderr, "Unknown instruction: %#x\n", s->insn); |
| 152 | gen_exception_insn(s, 4, EXCP_UDEF); |
| 153 | } |
| 154 | |
| 155 | #define unallocated_encoding(s) do { \ |
| 156 | fprintf(stderr, "unallocated encoding at line: %d\n", __LINE__); \ |
| 157 | real_unallocated_encoding(s); \ |
| 158 | } while (0) |
| 159 | |
Peter Maydell | 089a8d9 | 2013-12-03 15:26:18 +0000 | [diff] [blame^] | 160 | static void disas_a64_insn(CPUARMState *env, DisasContext *s) |
Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 161 | { |
| 162 | uint32_t insn; |
| 163 | |
| 164 | insn = arm_ldl_code(env, s->pc, s->bswap_code); |
| 165 | s->insn = insn; |
| 166 | s->pc += 4; |
| 167 | |
| 168 | switch ((insn >> 24) & 0x1f) { |
| 169 | default: |
| 170 | unallocated_encoding(s); |
| 171 | break; |
| 172 | } |
Peter Maydell | 089a8d9 | 2013-12-03 15:26:18 +0000 | [diff] [blame^] | 173 | } |
Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 174 | |
Peter Maydell | 089a8d9 | 2013-12-03 15:26:18 +0000 | [diff] [blame^] | 175 | void gen_intermediate_code_internal_a64(ARMCPU *cpu, |
| 176 | TranslationBlock *tb, |
| 177 | bool search_pc) |
| 178 | { |
| 179 | CPUState *cs = CPU(cpu); |
| 180 | CPUARMState *env = &cpu->env; |
| 181 | DisasContext dc1, *dc = &dc1; |
| 182 | CPUBreakpoint *bp; |
| 183 | uint16_t *gen_opc_end; |
| 184 | int j, lj; |
| 185 | target_ulong pc_start; |
| 186 | target_ulong next_page_start; |
| 187 | int num_insns; |
| 188 | int max_insns; |
| 189 | |
| 190 | pc_start = tb->pc; |
| 191 | |
| 192 | dc->tb = tb; |
| 193 | |
| 194 | gen_opc_end = tcg_ctx.gen_opc_buf + OPC_MAX_SIZE; |
| 195 | |
| 196 | dc->is_jmp = DISAS_NEXT; |
| 197 | dc->pc = pc_start; |
| 198 | dc->singlestep_enabled = cs->singlestep_enabled; |
| 199 | dc->condjmp = 0; |
| 200 | |
| 201 | dc->aarch64 = 1; |
| 202 | dc->thumb = 0; |
| 203 | dc->bswap_code = 0; |
| 204 | dc->condexec_mask = 0; |
| 205 | dc->condexec_cond = 0; |
| 206 | #if !defined(CONFIG_USER_ONLY) |
| 207 | dc->user = 0; |
| 208 | #endif |
| 209 | dc->vfp_enabled = 0; |
| 210 | dc->vec_len = 0; |
| 211 | dc->vec_stride = 0; |
| 212 | |
| 213 | next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; |
| 214 | lj = -1; |
| 215 | num_insns = 0; |
| 216 | max_insns = tb->cflags & CF_COUNT_MASK; |
| 217 | if (max_insns == 0) { |
| 218 | max_insns = CF_COUNT_MASK; |
| 219 | } |
| 220 | |
| 221 | gen_tb_start(); |
| 222 | |
| 223 | tcg_clear_temp_count(); |
| 224 | |
| 225 | do { |
| 226 | if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { |
| 227 | QTAILQ_FOREACH(bp, &env->breakpoints, entry) { |
| 228 | if (bp->pc == dc->pc) { |
| 229 | gen_exception_insn(dc, 0, EXCP_DEBUG); |
| 230 | /* Advance PC so that clearing the breakpoint will |
| 231 | invalidate this TB. */ |
| 232 | dc->pc += 2; |
| 233 | goto done_generating; |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | if (search_pc) { |
| 239 | j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; |
| 240 | if (lj < j) { |
| 241 | lj++; |
| 242 | while (lj < j) { |
| 243 | tcg_ctx.gen_opc_instr_start[lj++] = 0; |
| 244 | } |
| 245 | } |
| 246 | tcg_ctx.gen_opc_pc[lj] = dc->pc; |
| 247 | tcg_ctx.gen_opc_instr_start[lj] = 1; |
| 248 | tcg_ctx.gen_opc_icount[lj] = num_insns; |
| 249 | } |
| 250 | |
| 251 | if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) { |
| 252 | gen_io_start(); |
| 253 | } |
| 254 | |
| 255 | if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { |
| 256 | tcg_gen_debug_insn_start(dc->pc); |
| 257 | } |
| 258 | |
| 259 | disas_a64_insn(env, dc); |
| 260 | |
| 261 | if (tcg_check_temp_count()) { |
| 262 | fprintf(stderr, "TCG temporary leak before "TARGET_FMT_lx"\n", |
| 263 | dc->pc); |
| 264 | } |
| 265 | |
| 266 | /* Translation stops when a conditional branch is encountered. |
| 267 | * Otherwise the subsequent code could get translated several times. |
| 268 | * Also stop translation when a page boundary is reached. This |
| 269 | * ensures prefetch aborts occur at the right place. |
| 270 | */ |
| 271 | num_insns++; |
| 272 | } while (!dc->is_jmp && tcg_ctx.gen_opc_ptr < gen_opc_end && |
| 273 | !cs->singlestep_enabled && |
| 274 | !singlestep && |
| 275 | dc->pc < next_page_start && |
| 276 | num_insns < max_insns); |
| 277 | |
| 278 | if (tb->cflags & CF_LAST_IO) { |
| 279 | gen_io_end(); |
| 280 | } |
| 281 | |
| 282 | if (unlikely(cs->singlestep_enabled) && dc->is_jmp != DISAS_EXC) { |
| 283 | /* Note that this means single stepping WFI doesn't halt the CPU. |
| 284 | * For conditional branch insns this is harmless unreachable code as |
| 285 | * gen_goto_tb() has already handled emitting the debug exception |
| 286 | * (and thus a tb-jump is not possible when singlestepping). |
| 287 | */ |
| 288 | assert(dc->is_jmp != DISAS_TB_JUMP); |
| 289 | if (dc->is_jmp != DISAS_JUMP) { |
| 290 | gen_a64_set_pc_im(dc->pc); |
| 291 | } |
| 292 | gen_exception(EXCP_DEBUG); |
| 293 | } else { |
| 294 | switch (dc->is_jmp) { |
| 295 | case DISAS_NEXT: |
| 296 | gen_goto_tb(dc, 1, dc->pc); |
| 297 | break; |
| 298 | default: |
| 299 | case DISAS_JUMP: |
| 300 | case DISAS_UPDATE: |
| 301 | /* indicate that the hash table must be used to find the next TB */ |
| 302 | tcg_gen_exit_tb(0); |
| 303 | break; |
| 304 | case DISAS_TB_JUMP: |
| 305 | case DISAS_EXC: |
| 306 | case DISAS_SWI: |
| 307 | break; |
| 308 | case DISAS_WFI: |
| 309 | /* This is a special case because we don't want to just halt the CPU |
| 310 | * if trying to debug across a WFI. |
| 311 | */ |
| 312 | gen_helper_wfi(cpu_env); |
| 313 | break; |
| 314 | } |
| 315 | } |
| 316 | |
| 317 | done_generating: |
| 318 | gen_tb_end(tb, num_insns); |
| 319 | *tcg_ctx.gen_opc_ptr = INDEX_op_end; |
| 320 | |
| 321 | #ifdef DEBUG_DISAS |
| 322 | if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { |
| 323 | qemu_log("----------------\n"); |
| 324 | qemu_log("IN: %s\n", lookup_symbol(pc_start)); |
| 325 | log_target_disas(env, pc_start, dc->pc - pc_start, |
| 326 | dc->thumb | (dc->bswap_code << 1)); |
| 327 | qemu_log("\n"); |
| 328 | } |
| 329 | #endif |
| 330 | if (search_pc) { |
| 331 | j = tcg_ctx.gen_opc_ptr - tcg_ctx.gen_opc_buf; |
| 332 | lj++; |
| 333 | while (lj <= j) { |
| 334 | tcg_ctx.gen_opc_instr_start[lj++] = 0; |
| 335 | } |
| 336 | } else { |
| 337 | tb->size = dc->pc - pc_start; |
| 338 | tb->icount = num_insns; |
Alexander Graf | 14ade10 | 2013-09-03 20:12:10 +0100 | [diff] [blame] | 339 | } |
| 340 | } |