Signed-off-by: Michael Rolnik <mrol...@gmail.com> --- arch_init.c | 2 + configure | 5 + default-configs/avr-softmmu.mak | 1 + disas/Makefile.objs | 1 + disas/avr.c | 10 ++ include/disas/bfd.h | 7 + include/sysemu/arch_init.h | 1 + target-avr/Makefile.objs | 3 + target-avr/cpu-qom.h | 80 +++++++++++ target-avr/cpu.c | 288 ++++++++++++++++++++++++++++++++++++++++ target-avr/cpu.h | 134 +++++++++++++++++++ target-avr/gdbstub.c | 99 ++++++++++++++ target-avr/helper.c | 85 ++++++++++++ target-avr/helper.h | 21 +++ target-avr/machine.c | 54 ++++++++ target-avr/machine.h | 21 +++ target-avr/translate.c | 288 ++++++++++++++++++++++++++++++++++++++++ 17 files changed, 1100 insertions(+) create mode 100644 default-configs/avr-softmmu.mak create mode 100644 disas/avr.c 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 b5aab72..90af399 100755 --- a/configure +++ b/configure @@ -5630,6 +5630,8 @@ case "$target_name" in x86_64) TARGET_BASE_ARCH=i386 ;; + avr) + ;; alpha) ;; arm|armeb) @@ -5826,6 +5828,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..ca94aad --- /dev/null +++ b/default-configs/avr-softmmu.mak @@ -0,0 +1 @@ +# Default configuration for avr-softmmu diff --git a/disas/Makefile.objs b/disas/Makefile.objs index abeba84..218e434 100644 --- a/disas/Makefile.objs +++ b/disas/Makefile.objs @@ -21,6 +21,7 @@ common-obj-$(CONFIG_S390_DIS) += s390.o common-obj-$(CONFIG_SH4_DIS) += sh4.o common-obj-$(CONFIG_SPARC_DIS) += sparc.o common-obj-$(CONFIG_LM32_DIS) += lm32.o +common-obj-$(CONFIG_AVR_DIS) += avr.o # TODO: As long as the TCG interpreter and its generated code depend # on the QEMU target, we cannot compile the disassembler here. diff --git a/disas/avr.c b/disas/avr.c new file mode 100644 index 0000000..f916e72 --- /dev/null +++ b/disas/avr.c @@ -0,0 +1,10 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "disas/bfd.h" + +int print_insn_avr(bfd_vma addr, disassemble_info *info) +{ + int length = 0;; + /* TODO */ + return length; +} diff --git a/include/disas/bfd.h b/include/disas/bfd.h index a112e9c..04e2201 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 @@ -415,6 +421,7 @@ int print_insn_crisv10 (bfd_vma, disassemble_info*); int print_insn_microblaze (bfd_vma, disassemble_info*); int print_insn_ia64 (bfd_vma, disassemble_info*); int print_insn_lm32 (bfd_vma, disassemble_info*); +int print_insn_avr (bfd_vma, disassemble_info*); #if 0 /* Fetch the disassembler for a given BFD, if that support is available. */ 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..c503546 --- /dev/null +++ b/target-avr/Makefile.objs @@ -0,0 +1,3 @@ +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..914fb81 --- /dev/null +++ b/target-avr/cpu-qom.h @@ -0,0 +1,80 @@ +/* + * 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 vmstate_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..cfc1aee --- /dev/null +++ b/target-avr/cpu.c @@ -0,0 +1,288 @@ +/* + * 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 = value / 2; /* internaly PC points to words, not bytes */ +} + +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 = tb->pc / 2; +} + +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); + + memset(env, 0, sizeof(CPUAVRState)); + env->pc = 0; + env->sregI = 1; + + tlb_flush(s, 1); +} + +static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) +{ + info->mach = bfd_arch_avr; + info->print_insn = print_insn_avr; +} + +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 = &vmstate_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..f0d79a2 --- /dev/null +++ b/target-avr/cpu.h @@ -0,0 +1,134 @@ +/* + * 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> + */ + +/* + NOTE: all registers are assumed to hold 8 bit values. + so all operations done on registers should preseve this property +*/ + +/* + NOTE: the flags C,H,V,N,V have either 0 or 1 values + NOTE: the flag Z has inverse logic, when the value of Zf is 0 the flag is assumed to be set, non zero - not set +*/ + + +#if !defined(__CPU_AVR_H__) +#define __CPU_AVR_H__ + +#include "qemu-common.h" + +#define TARGET_LONG_BITS 32 + +#define CPUArchState struct CPUAVRState + +#define TARGET_IS_LIENDIAN 1 + +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +#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 CODE_INDEX 0 +#define DATA_INDEX 1 + +#define EXCP_RESET 1 +#define EXCP_INT(n) (EXCP_RESET + (n) + 1) + +#define PHYS_ADDR_MASK 0xfff00000 +#define PHYS_CODE_BASE 0x00000000 +#define PHYS_DATA_BASE 0x00800000 + +typedef struct CPUAVRState CPUAVRState; + +struct CPUAVRState { + uint32_t pc; /* 0x003fffff 22 bits */ + + uint32_t sregC; /* 0x00000001 1 bits */ + uint32_t sregZ; /* 0x000000ff 8 bits */ + 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[64]; /* 8 bits each */ + uint32_t r[32]; /* 8 bits each */ + uint32_t sp; /* 8 bits */ + + + uint64_t intsrc; /* interrupt sources */ + + /* Those resources are used only in QEMU core */ + CPU_COMMON +}; + +#define cpu_list avr_cpu_list +#define cpu_exec cpu_avr_exec +#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 0; +} + +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); + +#ifndef CONFIG_USER_ONLY +QEMU_NORETURN void avr_cpu_unassigned_access(CPUState *cpu, hwaddr addr, bool is_write, bool is_exec, int unused, unsigned size); +#endif + +static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *pflags) +{ + *pc = env->pc * 2; + *cs_base = 0; + *pflags = 0; +} + +static inline int cpu_interrupts_enabled(CPUAVRState *env1) +{ + return env1->sregI != 0; +} + +#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..84557b8 --- /dev/null +++ b/target-avr/gdbstub.c @@ -0,0 +1,99 @@ +/* + * 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 = 0; + + sreg = (env->sregC & 0x01) << 0 + | (env->sregZ & 0x01) << 1 + | (env->sregN & 0x01) << 2 + | (env->sregV & 0x01) << 3 + | (env->sregS & 0x01) << 4 + | (env->sregH & 0x01) << 5 + | (env->sregT & 0x01) << 6 + | (env->sregI & 0x01) << 7; + 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 * 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) { + env->sregC = (tmp >> 0) & 0x01; + env->sregZ = (tmp >> 1) & 0x01; + env->sregN = (tmp >> 2) & 0x01; + env->sregV = (tmp >> 3) & 0x01; + env->sregS = (tmp >> 4) & 0x01; + env->sregH = (tmp >> 5) & 0x01; + env->sregT = (tmp >> 6) & 0x01; + env->sregI = (tmp >> 7) & 0x01; + } + + /* SP */ + if (n == 33) { + env->sp = tmp; + return 2; + } + + /* PC */ + if (n == 34) { + env->pc = tmp / 2; + return 4; + } + + return 1; +} diff --git a/target-avr/helper.c b/target-avr/helper.c new file mode 100644 index 0000000..fbab91d --- /dev/null +++ b/target-avr/helper.c @@ -0,0 +1,85 @@ +/* + * 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 == CODE_INDEX) { + paddr = PHYS_CODE_BASE + vaddr; + prot = PAGE_READ | PAGE_EXEC; + } else { + paddr = PHYS_DATA_BASE + 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..98d44e9 --- /dev/null +++ b/target-avr/machine.c @@ -0,0 +1,54 @@ +/* + * 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" + +const VMStateDescription vmstate_avr_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(r, CPUAVRState, 32), + + VMSTATE_UINT32(sregC, CPUAVRState), + VMSTATE_UINT32(sregZ, CPUAVRState), + VMSTATE_UINT32(sregN, CPUAVRState), + VMSTATE_UINT32(sregV, CPUAVRState), + VMSTATE_UINT32(sregS, CPUAVRState), + VMSTATE_UINT32(sregH, CPUAVRState), + VMSTATE_UINT32(sregT, CPUAVRState), + VMSTATE_UINT32(sregI, CPUAVRState), + + VMSTATE_UINT32(rampD, CPUAVRState), + VMSTATE_UINT32(rampX, CPUAVRState), + VMSTATE_UINT32(rampY, CPUAVRState), + VMSTATE_UINT32(rampZ, CPUAVRState), + + VMSTATE_UINT32(eind, CPUAVRState), + VMSTATE_UINT32(sp, CPUAVRState), + VMSTATE_UINT32(pc, CPUAVRState), + + 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..029cffa --- /dev/null +++ b/target-avr/translate.c @@ -0,0 +1,288 @@ +/* + * 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), "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, offsetof(CPUAVRState, 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, offsetof(CPUAVRState, 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: + 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 = 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); + cpu_fprintf(f, "SP: %04x\n", env->sp); + cpu_fprintf(f, "rampX: %02x\n", env->rampX); + cpu_fprintf(f, "rampY: %02x\n", env->rampY); + cpu_fprintf(f, "rampZ: %02x\n", env->rampZ); + cpu_fprintf(f, "rampD: %02x\n", env->rampD); + 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, " [ I T H S V N Z C ]\n"); + cpu_fprintf(f, "SREG: [ %d %d %d %d %d %d %d %d ]\n", + env->sregI, + env->sregT, + env->sregH, + env->sregS, + env->sregV, + env->sregN, + env->sregZ, + env->sregC); + + 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)