1. basic CPU structure 2. registers 3. no instructions 4. saving sreg, rampD, rampX, rampY, rampD, eind in HW representation saving cpu features
Signed-off-by: Michael Rolnik <mrol...@gmail.com> --- arch_init.c | 2 + configure | 5 + default-configs/avr-softmmu.mak | 21 +++ include/disas/bfd.h | 6 + include/sysemu/arch_init.h | 1 + target-avr/Makefile.objs | 23 +++ target-avr/cpu-qom.h | 84 +++++++++++ target-avr/cpu.c | 304 ++++++++++++++++++++++++++++++++++++++++ target-avr/cpu.h | 180 ++++++++++++++++++++++++ target-avr/gdbstub.c | 84 +++++++++++ target-avr/helper.c | 87 ++++++++++++ target-avr/helper.h | 21 +++ target-avr/machine.c | 116 +++++++++++++++ target-avr/machine.h | 21 +++ target-avr/translate.c | 291 ++++++++++++++++++++++++++++++++++++++ 15 files changed, 1246 insertions(+) create mode 100644 default-configs/avr-softmmu.mak create mode 100644 target-avr/Makefile.objs create mode 100644 target-avr/cpu-qom.h create mode 100644 target-avr/cpu.c create mode 100644 target-avr/cpu.h create mode 100644 target-avr/gdbstub.c create mode 100644 target-avr/helper.c create mode 100644 target-avr/helper.h create mode 100644 target-avr/machine.c create mode 100644 target-avr/machine.h create mode 100644 target-avr/translate.c diff --git a/arch_init.c b/arch_init.c index fa05973..be6e6de 100644 --- a/arch_init.c +++ b/arch_init.c @@ -80,6 +80,8 @@ int graphic_depth = 32; #define QEMU_ARCH QEMU_ARCH_UNICORE32 #elif defined(TARGET_TRICORE) #define QEMU_ARCH QEMU_ARCH_TRICORE +#elif defined(TARGET_AVR) +#define QEMU_ARCH QEMU_ARCH_AVR #endif const uint32_t arch_type = QEMU_ARCH; diff --git a/configure b/configure index e9090a0..29eafbb 100755 --- a/configure +++ b/configure @@ -5641,6 +5641,8 @@ case "$target_name" in x86_64) TARGET_BASE_ARCH=i386 ;; + avr) + ;; alpha) ;; arm|armeb) @@ -5837,6 +5839,9 @@ disas_config() { for i in $ARCH $TARGET_BASE_ARCH ; do case "$i" in + avr) + disas_config "AVR" + ;; alpha) disas_config "ALPHA" ;; diff --git a/default-configs/avr-softmmu.mak b/default-configs/avr-softmmu.mak new file mode 100644 index 0000000..003465d --- /dev/null +++ b/default-configs/avr-softmmu.mak @@ -0,0 +1,21 @@ +# +# QEMU AVR CPU +# +# Copyright (c) 2016 Michael Rolnik +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# <http://www.gnu.org/licenses/lgpl-2.1.html> +# + +# Default configuration for avr-softmmu diff --git a/include/disas/bfd.h b/include/disas/bfd.h index a112e9c..b13fc53 100644 --- a/include/disas/bfd.h +++ b/include/disas/bfd.h @@ -213,6 +213,12 @@ enum bfd_architecture #define bfd_mach_m32r 0 /* backwards compatibility */ bfd_arch_mn10200, /* Matsushita MN10200 */ bfd_arch_mn10300, /* Matsushita MN10300 */ + bfd_arch_avr, /* Atmel AVR microcontrollers. */ +#define bfd_mach_avr1 1 +#define bfd_mach_avr2 2 +#define bfd_mach_avr3 3 +#define bfd_mach_avr4 4 +#define bfd_mach_avr5 5 bfd_arch_cris, /* Axis CRIS */ #define bfd_mach_cris_v0_v10 255 #define bfd_mach_cris_v32 32 diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index d690dfa..8c75777 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -23,6 +23,7 @@ enum { QEMU_ARCH_UNICORE32 = (1 << 14), QEMU_ARCH_MOXIE = (1 << 15), QEMU_ARCH_TRICORE = (1 << 16), + QEMU_ARCH_AVR = (1 << 17), }; extern const uint32_t arch_type; diff --git a/target-avr/Makefile.objs b/target-avr/Makefile.objs new file mode 100644 index 0000000..2a10104 --- /dev/null +++ b/target-avr/Makefile.objs @@ -0,0 +1,23 @@ +# +# QEMU AVR CPU +# +# Copyright (c) 2016 Michael Rolnik +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see +# <http://www.gnu.org/licenses/lgpl-2.1.html> +# + +obj-y += translate.o cpu.o helper.o +obj-y += gdbstub.o +obj-$(CONFIG_SOFTMMU) += machine.o diff --git a/target-avr/cpu-qom.h b/target-avr/cpu-qom.h new file mode 100644 index 0000000..c5d4f92 --- /dev/null +++ b/target-avr/cpu-qom.h @@ -0,0 +1,84 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#ifndef QEMU_AVR_CPU_QOM_H +#define QEMU_AVR_CPU_QOM_H + +#include "qom/cpu.h" + +#define TYPE_AVR_CPU "avr" + +#define AVR_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(AVRCPUClass, (klass), TYPE_AVR_CPU) +#define AVR_CPU(obj) \ + OBJECT_CHECK(AVRCPU, (obj), TYPE_AVR_CPU) +#define AVR_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AVRCPUClass, (obj), TYPE_AVR_CPU) + +/** +* AVRCPUClass: +* @parent_realize: The parent class' realize handler. +* @parent_reset: The parent class' reset handler. +* @vr: Version Register value. +* +* A AVR CPU model. +*/ +typedef struct AVRCPUClass { + CPUClass parent_class; + + DeviceRealize parent_realize; + void (*parent_reset)(CPUState *cpu); +} AVRCPUClass; + +/** +* AVRCPU: +* @env: #CPUAVRState +* +* A AVR CPU. +*/ +typedef struct AVRCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUAVRState env; +} AVRCPU; + +static inline AVRCPU *avr_env_get_cpu(CPUAVRState *env) +{ + return container_of(env, AVRCPU, env); +} + +#define ENV_GET_CPU(e) CPU(avr_env_get_cpu(e)) +#define ENV_OFFSET offsetof(AVRCPU, env) + +#ifndef CONFIG_USER_ONLY +extern const struct VMStateDescription vms_avr_cpu; +#endif + +void avr_cpu_do_interrupt(CPUState *cpu); +bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req); +void avr_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags); +hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int avr_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int avr_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +#endif diff --git a/target-avr/cpu.c b/target-avr/cpu.c new file mode 100644 index 0000000..1fdc1e6 --- /dev/null +++ b/target-avr/cpu.c @@ -0,0 +1,304 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" +#include "migration/vmstate.h" +#include "machine.h" + +static void avr_cpu_set_pc(CPUState *cs, vaddr value) +{ + AVRCPU *cpu = AVR_CPU(cs); + + cpu->env.pc_w = value / 2; /* internally PC points to words */ +} + +static bool avr_cpu_has_work(CPUState *cs) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + return (cs->interrupt_request + & (CPU_INTERRUPT_HARD + | CPU_INTERRUPT_RESET)) + && cpu_interrupts_enabled(env); +} +static void avr_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + env->pc_w = tb->pc / 2; /* internally PC points to words */ +} + +static void avr_cpu_reset(CPUState *s) +{ + AVRCPU *cpu = AVR_CPU(s); + AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu); + CPUAVRState *env = &cpu->env; + + mcc->parent_reset(s); + + env->pc_w = 0; + env->sregI = 1; + env->sregC = 0; + env->sregZ = 0; + env->sregN = 0; + env->sregV = 0; + env->sregS = 0; + env->sregH = 0; + env->sregT = 0; + + env->rampD = 0; + env->rampX = 0; + env->rampY = 0; + env->rampZ = 0; + env->eind = 0; + env->sp = 0; + + memset(env->io, 0, sizeof(env->io)); + memset(env->r, 0, sizeof(env->r)); + + tlb_flush(s, 1); +} + +static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_arch_avr; + info->print_insn = NULL; +} + +static void avr_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev); + + qemu_init_vcpu(cs); + cpu_reset(cs); + + mcc->parent_realize(dev, errp); +} + +static void avr_cpu_set_int(void *opaque, int irq, int level) +{ + AVRCPU *cpu = opaque; + CPUAVRState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + uint64_t mask = (1ull << irq); + if (level) { + env->intsrc |= mask; + cpu_interrupt(cs, CPU_INTERRUPT_HARD); + } else { + env->intsrc &= ~mask; + if (env->intsrc == 0) { + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); + } + } +} + +static void avr_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + AVRCPU *cpu = AVR_CPU(obj); + static int inited; + + cs->env_ptr = &cpu->env; + cpu_exec_init(cs, &error_abort); + +#ifndef CONFIG_USER_ONLY + qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int, 37); +#endif + + if (tcg_enabled() && !inited) { + inited = 1; + avr_translate_init(); + } +} + +static ObjectClass *avr_cpu_class_by_name(const char *cpu_model) +{ + ObjectClass *oc; + char *typename; + char **cpuname; + + if (!cpu_model) { + return NULL; + } + + cpuname = g_strsplit(cpu_model, ",", 1); + typename = g_strdup_printf("%s-" TYPE_AVR_CPU, cpuname[0]); + oc = object_class_by_name(typename); + + g_strfreev(cpuname); + g_free(typename); + + if (!oc + || !object_class_dynamic_cast(oc, TYPE_AVR_CPU) + || object_class_is_abstract(oc)) { + return NULL; + } + + return oc; +} + +static void avr_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + AVRCPUClass *mcc = AVR_CPU_CLASS(oc); + + mcc->parent_realize = dc->realize; + dc->realize = avr_cpu_realizefn; + + mcc->parent_reset = cc->reset; + cc->reset = avr_cpu_reset; + + cc->class_by_name = avr_cpu_class_by_name; + + cc->has_work = avr_cpu_has_work; + cc->do_interrupt = avr_cpu_do_interrupt; + cc->cpu_exec_interrupt = avr_cpu_exec_interrupt; + cc->dump_state = avr_cpu_dump_state; + cc->set_pc = avr_cpu_set_pc; +#if !defined(CONFIG_USER_ONLY) + cc->memory_rw_debug = avr_cpu_memory_rw_debug; +#endif +#ifdef CONFIG_USER_ONLY + cc->handle_mmu_fault = avr_cpu_handle_mmu_fault; +#else + cc->get_phys_page_debug = avr_cpu_get_phys_page_debug; + cc->vmsd = &vms_avr_cpu; +#endif + cc->disas_set_info = avr_cpu_disas_set_info; + cc->synchronize_from_tb = avr_cpu_synchronize_from_tb; + cc->gdb_read_register = avr_cpu_gdb_read_register; + cc->gdb_write_register = avr_cpu_gdb_write_register; + cc->gdb_num_core_regs = 35; + + /* + * Reason: avr_cpu_initfn() calls cpu_exec_init(), which saves + * the object in cpus -> dangling pointer after final + * object_unref(). + */ + dc->cannot_destroy_with_object_finalize_yet = true; +} + +static void avr_any_initfn(Object *obj) +{ + /* Set cpu feature flags */ +} + +typedef struct AVRCPUInfo { + const char *name; + void (*initfn)(Object *obj); +} AVRCPUInfo; + +static const AVRCPUInfo avr_cpus[] = { + { .name = "any", .initfn = avr_any_initfn }, +}; + +static gint avr_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *class_a = (ObjectClass *)a; + ObjectClass *class_b = (ObjectClass *)b; + const char *name_a; + const char *name_b; + + name_a = object_class_get_name(class_a); + name_b = object_class_get_name(class_b); + if (strcmp(name_a, "any-" TYPE_AVR_CPU) == 0) { + return 1; + } else if (strcmp(name_b, "any-" TYPE_AVR_CPU) == 0) { + return -1; + } else { + return strcmp(name_a, name_b); + } +} + +static void avr_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + const char *typename; + char *name; + + typename = object_class_get_name(oc); + name = g_strndup(typename, strlen(typename) - strlen("-" TYPE_AVR_CPU)); + (*s->cpu_fprintf)(s->file, " %s\n", name); + g_free(name); +} + +void avr_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_AVR_CPU, false); + list = g_slist_sort(list, avr_cpu_list_compare); + (*cpu_fprintf)(f, "Available CPUs:\n"); + g_slist_foreach(list, avr_cpu_list_entry, &s); + g_slist_free(list); +} +AVRCPU *cpu_avr_init(const char *cpu_model) +{ + return AVR_CPU(cpu_generic_init(TYPE_AVR_CPU, cpu_model)); +} + +static void cpu_register(const AVRCPUInfo *info) +{ + TypeInfo type_info = { + .parent = TYPE_AVR_CPU, + .instance_size = sizeof(AVRCPU), + .instance_init = info->initfn, + .class_size = sizeof(AVRCPUClass), + }; + + type_info.name = g_strdup_printf("%s-" TYPE_AVR_CPU, info->name); + type_register(&type_info); + g_free((void *)type_info.name); +} + +static const TypeInfo avr_cpu_type_info = { + .name = TYPE_AVR_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(AVRCPU), + .instance_init = avr_cpu_initfn, + .class_size = sizeof(AVRCPUClass), + .class_init = avr_cpu_class_init, + .abstract = true, +}; + +static void avr_cpu_register_types(void) +{ + int i; + type_register_static(&avr_cpu_type_info); + + for (i = 0; i < ARRAY_SIZE(avr_cpus); i++) { + cpu_register(&avr_cpus[i]); + } +} + +type_init(avr_cpu_register_types) diff --git a/target-avr/cpu.h b/target-avr/cpu.h new file mode 100644 index 0000000..74bbd8c --- /dev/null +++ b/target-avr/cpu.h @@ -0,0 +1,180 @@ + /* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#if !defined(CPU_AVR_H) +#define CPU_AVR_H + +#include "qemu-common.h" + +#define TARGET_LONG_BITS 32 + +#define CPUArchState struct CPUAVRState + +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +/* + TARGET_PAGE_BITS cannot be more than 8 bits because + 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they + should be implemented as a device and not memory + 2. SRAM starts at the address 0x0100 +*/ +#define TARGET_PAGE_BITS 8 +#define TARGET_PHYS_ADDR_SPACE_BITS 24 +#define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define NB_MMU_MODES 2 + +#define MMU_CODE_IDX 0 +#define MMU_DATA_IDX 1 + +#define EXCP_RESET 1 +#define EXCP_INT(n) (EXCP_RESET + (n) + 1) + +#define PHYS_ADDR_MASK 0xfff00000 + +#define PHYS_BASE_CODE 0x00000000 +#define PHYS_BASE_DATA 0x00800000 +#define PHYS_BASE_REGS 0x10000000 + +#define VIRT_BASE_CODE 0x00000000 +#define VIRT_BASE_DATA 0x00000000 +#define VIRT_BASE_REGS 0x00000000 + +/* + there are three groups of registers + 1. CPU regs - accessable by LD/ST and CPU itself + 2. CPU IO regs - accessable by LD/ST and IN/OUT + 3. EXT IO regs - accessable by LD/ST +*/ +#define AVR_CPU_REGS 0x0020 +#define AVR_CPU_IO_REGS 0x0040 +#define AVR_EXT_IO_REGS 0x00a0 +#define AVR_IO_REGS (AVR_CPU_IO_REGS + AVR_EXT_IO_REGS) +#define AVR_REGS (AVR_IO_REGS + AVR_CPU_REGS) + +#define AVR_CPU_REGS_BASE 0x0000 +#define AVR_CPU_IO_REGS_BASE (AVR_CPU_REGS_BASE + AVR_CPU_REGS) +#define AVR_EXT_IO_REGS_BASE (AVR_CPU_IO_REGS_BASE + AVR_CPU_IO_REGS) + +#define AVR_CPU_REGS_LAST (AVR_CPU_REGS_BASE + AVR_CPU_REGS - 1) +#define AVR_CPU_IO_REGS_LAST (AVR_CPU_IO_REGS_BASE + AVR_CPU_IO_REGS - 1) +#define AVR_EXT_IO_REGS_LAST (AVR_EXT_IO_REGS_BASE + AVR_EXT_IO_REGS - 1) + +typedef struct CPUAVRState CPUAVRState; + +struct CPUAVRState { + uint32_t pc_w; /* 0x003fffff up to 22 bits */ + + uint32_t sregC; /* 0x00000001 1 bits */ + uint32_t sregZ; /* 0x0000ffff 16 bits, negative logic */ + uint32_t sregN; /* 0x00000001 1 bits */ + uint32_t sregV; /* 0x00000001 1 bits */ + uint32_t sregS; /* 0x00000001 1 bits */ + uint32_t sregH; /* 0x00000001 1 bits */ + uint32_t sregT; /* 0x00000001 1 bits */ + uint32_t sregI; /* 0x00000001 1 bits */ + + uint32_t rampD; /* 0x00ff0000 8 bits */ + uint32_t rampX; /* 0x00ff0000 8 bits */ + uint32_t rampY; /* 0x00ff0000 8 bits */ + uint32_t rampZ; /* 0x00ff0000 8 bits */ + uint32_t eind; /* 0x00ff0000 8 bits */ + + uint32_t io[AVR_CPU_IO_REGS]; + /* 8 bits each */ + uint32_t r[AVR_CPU_REGS]; + /* 8 bits each */ + uint32_t sp; /* 16 bits */ + + + uint64_t intsrc; /* interrupt sources */ + + bool fullacc;/* CPU/MEM if true MEM only otherwise */ + /* Those resources are used only in QEMU core */ + CPU_COMMON +}; + +#define cpu_list avr_cpu_list +#define cpu_signal_handler cpu_avr_signal_handler + +#include "exec/cpu-all.h" +#include "cpu-qom.h" + +static inline int cpu_mmu_index(CPUAVRState *env, bool ifetch) +{ + return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; +} + +void avr_translate_init(void); + +AVRCPU *cpu_avr_init(const char *cpu_model); + +#define cpu_init(cpu_model) CPU(cpu_avr_init(cpu_model)) + +void avr_cpu_list(FILE *f, fprintf_function cpu_fprintf); +int cpu_avr_exec(CPUState *cpu); +int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc); +int avr_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, + int mmu_idx); +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf, + int len, bool is_write); + +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc, + target_ulong *cs_base, uint32_t *pflags) +{ + *pc = env->pc_w * 2; + *cs_base = 0; + *pflags = 0; +} + +static inline int cpu_interrupts_enabled(CPUAVRState *env1) +{ + return env1->sregI != 0; +} + +static inline uint8_t cpu_get_sreg(CPUAVRState *env) +{ + uint8_t sreg; + sreg = (env->sregC & 0x01) << 0 + | (env->sregZ == 0 ? 1 : 0) << 1 + | (env->sregN) << 2 + | (env->sregV) << 3 + | (env->sregS) << 4 + | (env->sregH) << 5 + | (env->sregT) << 6 + | (env->sregI) << 7; + return sreg; +} + +static inline void cpu_set_sreg(CPUAVRState *env, uint8_t sreg) +{ + env->sregC = (sreg >> 0) & 0x01; + env->sregZ = (sreg >> 1) & 0x01 ? 0 : 1; + env->sregN = (sreg >> 2) & 0x01; + env->sregV = (sreg >> 3) & 0x01; + env->sregS = (sreg >> 4) & 0x01; + env->sregH = (sreg >> 5) & 0x01; + env->sregT = (sreg >> 6) & 0x01; + env->sregI = (sreg >> 7) & 0x01; +} + +#include "exec/exec-all.h" + +#endif /* !defined (CPU_AVR_H) */ diff --git a/target-avr/gdbstub.c b/target-avr/gdbstub.c new file mode 100644 index 0000000..a53ba09 --- /dev/null +++ b/target-avr/gdbstub.c @@ -0,0 +1,84 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int avr_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + /* R */ + if (n < 32) { + return gdb_get_reg8(mem_buf, env->r[n]); + } + + /* SREG */ + if (n == 32) { + uint8_t sreg = cpu_get_sreg(env); + + return gdb_get_reg8(mem_buf, sreg); + } + + /* SP */ + if (n == 33) { + return gdb_get_reg16(mem_buf, env->sp & 0x0000ffff); + } + + /* PC */ + if (n == 34) { + return gdb_get_reg32(mem_buf, env->pc_w * 2); + } + + return 0; +} + +int avr_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + uint16_t tmp = ldl_p(mem_buf); + + /* R */ + if (n < 32) { + env->r[n] = tmp; + } + + /* SREG */ + if (n == 32) { + cpu_set_sreg(env, tmp); + } + + /* SP */ + if (n == 33) { + env->sp = tmp; + return 2; + } + + /* PC */ + if (n == 34) { + env->pc_w = tmp / 2; + return 4; + } + + return 1; +} diff --git a/target-avr/helper.c b/target-avr/helper.c new file mode 100644 index 0000000..3e23646 --- /dev/null +++ b/target-avr/helper.c @@ -0,0 +1,87 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "hw/irq.h" +#include "include/hw/sysbus.h" +#include "include/sysemu/sysemu.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" + +bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + return false; +} + +void avr_cpu_do_interrupt(CPUState *cs) +{ +} + +int avr_cpu_memory_rw_debug(CPUState *cs, vaddr addr, uint8_t *buf, + int len, bool is_write) +{ + return cpu_memory_rw_debug(cs, addr, buf, len, is_write); +} + +hwaddr avr_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + return addr; /* I assume 1:1 address correspondance */ +} + +int avr_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int mmu_idx) +{ + cs->exception_index = EXCP_DEBUG; + cpu_dump_state(cs, stderr, fprintf, 0); + return 1; +} + +void tlb_fill(CPUState *cs, target_ulong vaddr, int is_write, + int mmu_idx, uintptr_t retaddr) +{ + target_ulong page_size = TARGET_PAGE_SIZE; + int prot = 0; + MemTxAttrs attrs = {}; + uint32_t paddr; + + vaddr &= TARGET_PAGE_MASK; + + if (mmu_idx == MMU_CODE_IDX) { + paddr = PHYS_BASE_CODE + vaddr; + prot = PAGE_READ | PAGE_EXEC; + } else { + paddr = PHYS_BASE_DATA + vaddr; + prot = PAGE_READ | PAGE_WRITE; + } + + tlb_set_page_with_attrs(cs, vaddr, paddr, attrs, prot, mmu_idx, page_size); +} + +void helper_debug(CPUAVRState *env) +{ + CPUState *cs = CPU(avr_env_get_cpu(env)); + + cs->exception_index = EXCP_DEBUG; + cpu_loop_exit(cs); +} + diff --git a/target-avr/helper.h b/target-avr/helper.h new file mode 100644 index 0000000..b5ef3bf --- /dev/null +++ b/target-avr/helper.h @@ -0,0 +1,21 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +DEF_HELPER_1(debug, void, env) diff --git a/target-avr/machine.c b/target-avr/machine.c new file mode 100644 index 0000000..3b7fd3d --- /dev/null +++ b/target-avr/machine.c @@ -0,0 +1,116 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "cpu.h" +#include "hw/boards.h" +#include "machine.h" +#include "migration/qemu-file.h" + +static int get_sreg(QEMUFile *f, void *opaque, size_t size) +{ + CPUAVRState *env = opaque; + uint8_t sreg; + + qemu_get_8s(f, &sreg); + cpu_set_sreg(env, sreg); + return 0; +} + +static void put_sreg(QEMUFile *f, void *opaque, size_t size) +{ + CPUAVRState *env = opaque; + uint8_t sreg = cpu_get_sreg(env); + + qemu_put_8s(f, &sreg); +} + +static const VMStateInfo vms_sreg = { + .name = "sreg", + .get = get_sreg, + .put = put_sreg, +}; + +static int get_segment(QEMUFile *f, void *opaque, size_t size) +{ + uint32_t *ramp = opaque; + uint8_t temp = *ramp >> 16; + + qemu_get_8s(f, &temp); + return 0; +} + +static void put_segment(QEMUFile *f, void *opaque, size_t size) +{ + uint32_t *ramp = opaque; + uint8_t temp = 0; + + qemu_put_8s(f, &temp); + *ramp = ((uint32_t)temp) << 16; +} + +static const VMStateInfo vms_rampD = { + .name = "rampD", + .get = get_segment, + .put = put_segment, +}; +static const VMStateInfo vms_rampX = { + .name = "rampX", + .get = get_segment, + .put = put_segment, +}; +static const VMStateInfo vms_rampY = { + .name = "rampY", + .get = get_segment, + .put = put_segment, +}; +static const VMStateInfo vms_rampZ = { + .name = "rampZ", + .get = get_segment, + .put = put_segment, +}; +static const VMStateInfo vms_eind = { + .name = "eind", + .get = get_segment, + .put = put_segment, +}; + +const VMStateDescription vms_avr_cpu = { + .name = "cpu", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(env.pc_w, AVRCPU), + VMSTATE_UINT32(env.sp, AVRCPU), + + VMSTATE_UINT32_ARRAY(env.r, AVRCPU, AVR_CPU_REGS), + VMSTATE_UINT32_ARRAY(env.io, AVRCPU, AVR_CPU_IO_REGS), + + VMSTATE_SINGLE_TEST(env, AVRCPU, NULL, 0, vms_sreg, CPUAVRState), + VMSTATE_SINGLE_TEST(env.rampD, AVRCPU, NULL, 0, vms_rampD, uint32_t), + VMSTATE_SINGLE_TEST(env.rampX, AVRCPU, NULL, 0, vms_rampX, uint32_t), + VMSTATE_SINGLE_TEST(env.rampY, AVRCPU, NULL, 0, vms_rampY, uint32_t), + VMSTATE_SINGLE_TEST(env.rampZ, AVRCPU, NULL, 0, vms_rampZ, uint32_t), + VMSTATE_SINGLE_TEST(env.eind, AVRCPU, NULL, 0, vms_eind, uint32_t), + + VMSTATE_END_OF_LIST() + } +}; diff --git a/target-avr/machine.h b/target-avr/machine.h new file mode 100644 index 0000000..4cc8d6b --- /dev/null +++ b/target-avr/machine.h @@ -0,0 +1,21 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +extern const VMStateDescription vmstate_avr_cpu; diff --git a/target-avr/translate.c b/target-avr/translate.c new file mode 100644 index 0000000..66ec98b --- /dev/null +++ b/target-avr/translate.c @@ -0,0 +1,291 @@ +/* + * QEMU AVR CPU + * + * Copyright (c) 2016 Michael Rolnik + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "exec/exec-all.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" + +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" + +typedef struct DisasContext DisasContext; +typedef struct InstInfo InstInfo; + +/*This is the state at translation time. */ +struct DisasContext { + struct TranslationBlock *tb; + + /*Routine used to access memory */ + int memidx; + int bstate; + int singlestep; +}; + +enum { + BS_NONE = 0, /* Nothing special (none of the below */ + BS_STOP = 1, /* We want to stop translation for any reason */ + BS_BRANCH = 2, /* A branch condition is reached */ + BS_EXCP = 3, /* An exception condition is reached */ +}; + +static TCGv_env cpu_env; + +static TCGv cpu_pc; + +static TCGv cpu_Cf; +static TCGv cpu_Zf; +static TCGv cpu_Nf; +static TCGv cpu_Vf; +static TCGv cpu_Sf; +static TCGv cpu_Hf; +static TCGv cpu_Tf; +static TCGv cpu_If; + +static TCGv cpu_rampD; +static TCGv cpu_rampX; +static TCGv cpu_rampY; +static TCGv cpu_rampZ; + +static TCGv cpu_io[64]; +static TCGv cpu_r[32]; +static TCGv cpu_eind; +static TCGv cpu_sp; + +#include "exec/gen-icount.h" +#define REG(x) (cpu_r[x]) + +void avr_translate_init(void) +{ + int i; + static int done_init; + + if (done_init) { + return; + } +#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x) + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + cpu_pc = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(pc_w), "pc"); + cpu_Cf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregC), "Cf"); + cpu_Zf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregZ), "Zf"); + cpu_Nf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregN), "Nf"); + cpu_Vf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregV), "Vf"); + cpu_Sf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregS), "Sf"); + cpu_Hf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregH), "Hf"); + cpu_Tf = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregT), "Tf"); + cpu_If = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sregI), "If"); + cpu_rampD = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampD), "rampD"); + cpu_rampX = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampX), "rampX"); + cpu_rampY = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampY), "rampY"); + cpu_rampZ = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(rampZ), "rampZ"); + cpu_eind = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(eind), "eind"); + cpu_sp = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(sp), "sp"); + + for (i = 0; i < 64; i++) { + char name[16]; + + sprintf(name, "io[%d]", i); + + cpu_io[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(io[i]), name); + } + for (i = 0; i < 32; i++) { + char name[16]; + + sprintf(name, "r[%d]", i); + + cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]), name); + } + + done_init = 1; +} + +static inline void gen_goto_tb(CPUAVRState *env, DisasContext *ctx, int n, + target_ulong dest) +{ + TranslationBlock *tb; + + tb = ctx->tb; + + if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK) + && (ctx->singlestep == 0)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb((uintptr_t)tb + n); + } else { + tcg_gen_movi_i32(cpu_pc, dest); + + if (ctx->singlestep) { + gen_helper_debug(cpu_env); + } + tcg_gen_exit_tb(0); + } +} + +/*generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUAVRState *env, struct TranslationBlock *tb) +{ + AVRCPU *cpu = avr_env_get_cpu(env); + CPUState *cs = CPU(cpu); + DisasContext ctx; + target_ulong pc_start; + int num_insns, + max_insns; + target_ulong cpc; + target_ulong npc; + + pc_start = tb->pc / 2; + ctx.tb = tb; + ctx.memidx = 0; + ctx.bstate = BS_NONE; + ctx.singlestep = cs->singlestep_enabled; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + + gen_tb_start(tb); + + /* decode first instruction */ + cpc = pc_start; + npc = cpc + 1; + do { + /* translate current instruction */ + tcg_gen_insn_start(cpc); + num_insns++; + + /* just skip to next instruction */ + cpc++; + npc++; + ctx.bstate = BS_NONE; + + if (unlikely(cpu_breakpoint_test(cs, cpc * 2, BP_ANY))) { + tcg_gen_movi_i32(cpu_pc, cpc); + gen_helper_debug(cpu_env); + ctx.bstate = BS_EXCP; + /*The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + goto done_generating; + } + + if (num_insns >= max_insns) { + break; /* max translated instructions limit reached */ + } + if (ctx.singlestep) { + break; /* single step */ + } + if ((cpc & (TARGET_PAGE_SIZE - 1)) == 0) { + break; /* page boundary */ + } + } while (ctx.bstate == BS_NONE && !tcg_op_buf_full()); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + if (ctx.singlestep) { + if (ctx.bstate == BS_STOP || ctx.bstate == BS_NONE) { + tcg_gen_movi_tl(cpu_pc, npc); + } + gen_helper_debug(cpu_env); + tcg_gen_exit_tb(0); + } else { + switch (ctx.bstate) { + case BS_STOP: + case BS_NONE: + gen_goto_tb(env, &ctx, 0, npc); + break; + case BS_EXCP: + tcg_gen_exit_tb(0); + break; + default: + break; + } + } + +done_generating: + env->fullwr = false; + gen_tb_end(tb, num_insns); + + tb->size = (npc - pc_start) * 2; + tb->icount = num_insns; +} + +void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc_w = data[0]; +} + +void avr_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + AVRCPU *cpu = AVR_CPU(cs); + CPUAVRState *env = &cpu->env; + + cpu_fprintf(f, "\n"); + cpu_fprintf(f, "PC: %06x\n", env->pc_w); + cpu_fprintf(f, "SP: %04x\n", env->sp); + cpu_fprintf(f, "rampD: %02x\n", env->rampD >> 16); + cpu_fprintf(f, "rampX: %02x\n", env->rampX >> 16); + cpu_fprintf(f, "rampY: %02x\n", env->rampY >> 16); + cpu_fprintf(f, "rampZ: %02x\n", env->rampZ >> 16); + cpu_fprintf(f, "EIND: %02x\n", env->eind); + cpu_fprintf(f, "X: %02x%02x\n", env->r[27], env->r[26]); + cpu_fprintf(f, "Y: %02x%02x\n", env->r[29], env->r[28]); + cpu_fprintf(f, "Z: %02x%02x\n", env->r[31], env->r[30]); + cpu_fprintf(f, "SREG: [ %c %c %c %c %c %c %c %c ]\n", + env->sregI ? 'I' : '-', + env->sregT ? 'T' : '-', + env->sregH ? 'H' : '-', + env->sregS ? 'S' : '-', + env->sregV ? 'V' : '-', + env->sregN ? '-' : 'N', /* Zf has negative logic */ + env->sregZ ? 'Z' : '-', + env->sregC ? 'I' : '-'); + + cpu_fprintf(f, "\n"); + for (int i = 0; i < ARRAY_SIZE(env->r); i++) { + cpu_fprintf(f, "R[%02d]: %02x ", i, env->r[i]); + + if ((i % 8) == 7) { + cpu_fprintf(f, "\n"); + } + } + + cpu_fprintf(f, "\n"); + for (int i = 0; i < ARRAY_SIZE(env->io); i++) { + cpu_fprintf(f, "IO[%02d]: %02x ", i, env->io[i]); + + if ((i % 8) == 7) { + cpu_fprintf(f, "\n"); + } + } +} -- 2.4.9 (Apple Git-60)