1 /* 2 * QEMU AVR CPU helpers 3 * 4 * Copyright (c) 2016-2020 Michael Rolnik 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.1 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 18 * <http://www.gnu.org/licenses/lgpl-2.1.html> 19 */ 20 21 #include "qemu/osdep.h" 22 #include "qemu/log.h" 23 #include "cpu.h" 24 #include "hw/core/tcg-cpu-ops.h" 25 #include "exec/exec-all.h" 26 #include "exec/address-spaces.h" 27 #include "exec/helper-proto.h" 28 29 bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 30 { 31 AVRCPU *cpu = AVR_CPU(cs); 32 CPUAVRState *env = &cpu->env; 33 34 if (interrupt_request & CPU_INTERRUPT_RESET) { 35 if (cpu_interrupts_enabled(env)) { 36 cs->exception_index = EXCP_RESET; 37 avr_cpu_do_interrupt(cs); 38 39 cs->interrupt_request &= ~CPU_INTERRUPT_RESET; 40 return true; 41 } 42 } 43 if (interrupt_request & CPU_INTERRUPT_HARD) { 44 if (cpu_interrupts_enabled(env) && env->intsrc != 0) { 45 int index = ctz32(env->intsrc); 46 cs->exception_index = EXCP_INT(index); 47 avr_cpu_do_interrupt(cs); 48 49 env->intsrc &= env->intsrc - 1; /* clear the interrupt */ 50 if (!env->intsrc) { 51 cs->interrupt_request &= ~CPU_INTERRUPT_HARD; 52 } 53 return true; 54 } 55 } 56 return false; 57 } 58 59 void avr_cpu_do_interrupt(CPUState *cs) 60 { 61 AVRCPU *cpu = AVR_CPU(cs); 62 CPUAVRState *env = &cpu->env; 63 64 uint32_t ret = env->pc_w; 65 int vector = 0; 66 int size = avr_feature(env, AVR_FEATURE_JMP_CALL) ? 2 : 1; 67 int base = 0; 68 69 if (cs->exception_index == EXCP_RESET) { 70 vector = 0; 71 } else if (env->intsrc != 0) { 72 vector = ctz32(env->intsrc) + 1; 73 } 74 75 if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) { 76 cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); 77 cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); 78 cpu_stb_data(env, env->sp--, (ret & 0xff0000) >> 16); 79 } else if (avr_feature(env, AVR_FEATURE_2_BYTE_PC)) { 80 cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); 81 cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); 82 } else { 83 cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); 84 } 85 86 env->pc_w = base + vector * size; 87 env->sregI = 0; /* clear Global Interrupt Flag */ 88 89 cs->exception_index = -1; 90 } 91 92 hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) 93 { 94 return addr; /* I assume 1:1 address correspondence */ 95 } 96 97 bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, 98 MMUAccessType access_type, int mmu_idx, 99 bool probe, uintptr_t retaddr) 100 { 101 int prot, page_size = TARGET_PAGE_SIZE; 102 uint32_t paddr; 103 104 address &= TARGET_PAGE_MASK; 105 106 if (mmu_idx == MMU_CODE_IDX) { 107 /* Access to code in flash. */ 108 paddr = OFFSET_CODE + address; 109 prot = PAGE_READ | PAGE_EXEC; 110 if (paddr >= OFFSET_DATA) { 111 /* 112 * This should not be possible via any architectural operations. 113 * There is certainly not an exception that we can deliver. 114 * Accept probing that might come from generic code. 115 */ 116 if (probe) { 117 return false; 118 } 119 error_report("execution left flash memory"); 120 abort(); 121 } 122 } else { 123 /* Access to memory. */ 124 paddr = OFFSET_DATA + address; 125 prot = PAGE_READ | PAGE_WRITE; 126 if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { 127 /* 128 * Access to CPU registers, exit and rebuilt this TB to use 129 * full access in case it touches specially handled registers 130 * like SREG or SP. For probing, set page_size = 1, in order 131 * to force tlb_fill to be called for the next access. 132 */ 133 if (probe) { 134 page_size = 1; 135 } else { 136 AVRCPU *cpu = AVR_CPU(cs); 137 CPUAVRState *env = &cpu->env; 138 env->fullacc = 1; 139 cpu_loop_exit_restore(cs, retaddr); 140 } 141 } 142 } 143 144 tlb_set_page(cs, address, paddr, prot, mmu_idx, page_size); 145 return true; 146 } 147 148 /* 149 * helpers 150 */ 151 152 void helper_sleep(CPUAVRState *env) 153 { 154 CPUState *cs = env_cpu(env); 155 156 cs->exception_index = EXCP_HLT; 157 cpu_loop_exit(cs); 158 } 159 160 void helper_unsupported(CPUAVRState *env) 161 { 162 CPUState *cs = env_cpu(env); 163 164 /* 165 * I count not find what happens on the real platform, so 166 * it's EXCP_DEBUG for meanwhile 167 */ 168 cs->exception_index = EXCP_DEBUG; 169 if (qemu_loglevel_mask(LOG_UNIMP)) { 170 qemu_log("UNSUPPORTED\n"); 171 cpu_dump_state(cs, stderr, 0); 172 } 173 cpu_loop_exit(cs); 174 } 175 176 void helper_debug(CPUAVRState *env) 177 { 178 CPUState *cs = env_cpu(env); 179 180 cs->exception_index = EXCP_DEBUG; 181 cpu_loop_exit(cs); 182 } 183 184 void helper_break(CPUAVRState *env) 185 { 186 CPUState *cs = env_cpu(env); 187 188 cs->exception_index = EXCP_DEBUG; 189 cpu_loop_exit(cs); 190 } 191 192 void helper_wdr(CPUAVRState *env) 193 { 194 qemu_log_mask(LOG_UNIMP, "WDG reset (not implemented)\n"); 195 } 196 197 /* 198 * This function implements IN instruction 199 * 200 * It does the following 201 * a. if an IO register belongs to CPU, its value is read and returned 202 * b. otherwise io address is translated to mem address and physical memory 203 * is read. 204 * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation 205 * 206 */ 207 target_ulong helper_inb(CPUAVRState *env, uint32_t port) 208 { 209 target_ulong data = 0; 210 211 switch (port) { 212 case 0x38: /* RAMPD */ 213 data = 0xff & (env->rampD >> 16); 214 break; 215 case 0x39: /* RAMPX */ 216 data = 0xff & (env->rampX >> 16); 217 break; 218 case 0x3a: /* RAMPY */ 219 data = 0xff & (env->rampY >> 16); 220 break; 221 case 0x3b: /* RAMPZ */ 222 data = 0xff & (env->rampZ >> 16); 223 break; 224 case 0x3c: /* EIND */ 225 data = 0xff & (env->eind >> 16); 226 break; 227 case 0x3d: /* SPL */ 228 data = env->sp & 0x00ff; 229 break; 230 case 0x3e: /* SPH */ 231 data = env->sp >> 8; 232 break; 233 case 0x3f: /* SREG */ 234 data = cpu_get_sreg(env); 235 break; 236 default: 237 /* not a special register, pass to normal memory access */ 238 data = address_space_ldub(&address_space_memory, 239 OFFSET_IO_REGISTERS + port, 240 MEMTXATTRS_UNSPECIFIED, NULL); 241 } 242 243 return data; 244 } 245 246 /* 247 * This function implements OUT instruction 248 * 249 * It does the following 250 * a. if an IO register belongs to CPU, its value is written into the register 251 * b. otherwise io address is translated to mem address and physical memory 252 * is written. 253 * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation 254 * 255 */ 256 void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data) 257 { 258 data &= 0x000000ff; 259 260 switch (port) { 261 case 0x38: /* RAMPD */ 262 if (avr_feature(env, AVR_FEATURE_RAMPD)) { 263 env->rampD = (data & 0xff) << 16; 264 } 265 break; 266 case 0x39: /* RAMPX */ 267 if (avr_feature(env, AVR_FEATURE_RAMPX)) { 268 env->rampX = (data & 0xff) << 16; 269 } 270 break; 271 case 0x3a: /* RAMPY */ 272 if (avr_feature(env, AVR_FEATURE_RAMPY)) { 273 env->rampY = (data & 0xff) << 16; 274 } 275 break; 276 case 0x3b: /* RAMPZ */ 277 if (avr_feature(env, AVR_FEATURE_RAMPZ)) { 278 env->rampZ = (data & 0xff) << 16; 279 } 280 break; 281 case 0x3c: /* EIDN */ 282 env->eind = (data & 0xff) << 16; 283 break; 284 case 0x3d: /* SPL */ 285 env->sp = (env->sp & 0xff00) | (data); 286 break; 287 case 0x3e: /* SPH */ 288 if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) { 289 env->sp = (env->sp & 0x00ff) | (data << 8); 290 } 291 break; 292 case 0x3f: /* SREG */ 293 cpu_set_sreg(env, data); 294 break; 295 default: 296 /* not a special register, pass to normal memory access */ 297 address_space_stb(&address_space_memory, OFFSET_IO_REGISTERS + port, 298 data, MEMTXATTRS_UNSPECIFIED, NULL); 299 } 300 } 301 302 /* 303 * this function implements LD instruction when there is a possibility to read 304 * from a CPU register 305 */ 306 target_ulong helper_fullrd(CPUAVRState *env, uint32_t addr) 307 { 308 uint8_t data; 309 310 env->fullacc = false; 311 312 if (addr < NUMBER_OF_CPU_REGISTERS) { 313 /* CPU registers */ 314 data = env->r[addr]; 315 } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { 316 /* IO registers */ 317 data = helper_inb(env, addr - NUMBER_OF_CPU_REGISTERS); 318 } else { 319 /* memory */ 320 data = address_space_ldub(&address_space_memory, OFFSET_DATA + addr, 321 MEMTXATTRS_UNSPECIFIED, NULL); 322 } 323 return data; 324 } 325 326 /* 327 * this function implements ST instruction when there is a possibility to write 328 * into a CPU register 329 */ 330 void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) 331 { 332 env->fullacc = false; 333 334 /* Following logic assumes this: */ 335 assert(OFFSET_CPU_REGISTERS == OFFSET_DATA); 336 assert(OFFSET_IO_REGISTERS == OFFSET_CPU_REGISTERS + 337 NUMBER_OF_CPU_REGISTERS); 338 339 if (addr < NUMBER_OF_CPU_REGISTERS) { 340 /* CPU registers */ 341 env->r[addr] = data; 342 } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { 343 /* IO registers */ 344 helper_outb(env, addr - NUMBER_OF_CPU_REGISTERS, data); 345 } else { 346 /* memory */ 347 address_space_stb(&address_space_memory, OFFSET_DATA + addr, data, 348 MEMTXATTRS_UNSPECIFIED, NULL); 349 } 350 } 351