From patchwork Thu Jun 2 20:06:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Rolnik X-Patchwork-Id: 629429 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3rLJNx11dpz9t5l for ; Fri, 3 Jun 2016 06:14:57 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=a3mnAL6c; dkim-atps=neutral Received: from localhost ([::1]:50131 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b8Z15-0004wi-3c for incoming@patchwork.ozlabs.org; Thu, 02 Jun 2016 16:14:55 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55240) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b8Yto-0006FI-31 for qemu-devel@nongnu.org; Thu, 02 Jun 2016 16:07:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1b8Yth-0003lm-Pw for qemu-devel@nongnu.org; Thu, 02 Jun 2016 16:07:22 -0400 Received: from mail-wm0-x242.google.com ([2a00:1450:400c:c09::242]:34652) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1b8Ytf-0003lL-Eq for qemu-devel@nongnu.org; Thu, 02 Jun 2016 16:07:17 -0400 Received: by mail-wm0-x242.google.com with SMTP id n184so18061365wmn.1 for ; Thu, 02 Jun 2016 13:07:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=oGrWHaFzElRExZ6jJYHz8vQLetF+q/IRVnLEOporvhM=; b=a3mnAL6cpO5ZetAiitHYOSbfb3exSINVa1rArMD3UYgDhJeO7I7yu9vVJaOIjCYopW DGM0IsehoIKiT6kYW6ZPGQlUXSxktDDmylSaHgzvzfNcpYWvMCb+IyCqBLCIWISASr9O WmltL7hYiFpHZvMgy0ztBKDsh6b5yC7wfH3wcyJTyHgEAYOgceKlJ2qZRw2Z01Z5XD02 Sy1YTckzoY+ixxQ1Xy/D7HeJf0ow+N+x29BVN/NE3CHhojfpsfGtitLtbIe/IGCZRlJA TuT8KUUwLjHciiz7dxmunTVo5EJZyDY9oqPRnC8fsnvK6xiqz2mmg9VGpHBfE7kQh9W/ VMDA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=oGrWHaFzElRExZ6jJYHz8vQLetF+q/IRVnLEOporvhM=; b=THEyI7P7/xPuuoQz1LnUzy9nBZDj9c+XGSFGFXMKqcH2Q+o4IczjtT/dLTuNBv3JLn Q+Jn2U6qKFUOoNSwQtPLD9q/whih5y40j5Iz0AXIq0ranIfw0tHJysXsis5msPejGqUA RqdRR6LCIPIuEzEsdaxXsjjoXx09jGKh57khkkrkLAYb+tIB7beDcEtPpoIS+Vrej1Yp pcsr5fpV7r+7MwfmqhhlVoIEXg5G0tXMq7+80FQG7M9rRnobeGytGARy86YNoejWpOzK Js/huWdusIB444pY5ISD1jgPZjM/hQMXZ4O0ReTNkLB37sxL7WJavb3QWo48ly8GlQb0 iHog== X-Gm-Message-State: ALyK8tIH5YKlLsns84dQxBy71kAZeg2rBxiVdg2ayWQ/6p+LjmRIakYEmKxXS096erywXw== X-Received: by 10.194.111.229 with SMTP id il5mr11659293wjb.82.1464898034306; Thu, 02 Jun 2016 13:07:14 -0700 (PDT) Received: from a0999b0126e1.ant.amazon.com ([94.230.86.227]) by smtp.gmail.com with ESMTPSA id i4sm2127090wjj.49.2016.06.02.13.07.12 (version=TLS1 cipher=AES128-SHA bits=128/128); Thu, 02 Jun 2016 13:07:13 -0700 (PDT) From: Michael Rolnik X-Google-Original-From: Michael Rolnik To: qemu-devel@nongnu.org Date: Thu, 2 Jun 2016 23:06:53 +0300 Message-Id: <1464898022-97990-1-git-send-email-rolnik@amazon.com> X-Mailer: git-send-email 2.4.9 (Apple Git-60) In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2a00:1450:400c:c09::242 Subject: [Qemu-devel] [PATCH 01/10] target-avr: AVR cores support is added. 1. basic CPU structure 2. registers 3. no instructions X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Michael Rolnik , Michael Rolnik Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Michael Rolnik --- 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 | 97 +++++++++++++ target-avr/cpu.c | 315 ++++++++++++++++++++++++++++++++++++++++ target-avr/cpu.h | 152 +++++++++++++++++++ target-avr/gdbstub.c | 105 ++++++++++++++ target-avr/helper.c | 105 ++++++++++++++ target-avr/helper.h | 21 +++ target-avr/machine.c | 54 +++++++ target-avr/machine.h | 21 +++ target-avr/translate.c | 300 ++++++++++++++++++++++++++++++++++++++ 17 files changed, 1200 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..76ca908 --- /dev/null +++ b/target-avr/cpu-qom.h @@ -0,0 +1,97 @@ +/* + * 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 + * + */ + +#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..ff26018 --- /dev/null +++ b/target-avr/cpu.c @@ -0,0 +1,315 @@ +/* + * 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 + * + */ + +#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..3692329 --- /dev/null +++ b/target-avr/cpu.h @@ -0,0 +1,152 @@ +/* + * 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 + * + */ + +#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; /* 22 bits */ + + uint32_t sregC; /* 1 bits */ + uint32_t sregZ; /* 1 bits */ + uint32_t sregN; /* 1 bits */ + uint32_t sregV; /* 1 bits */ + uint32_t sregS; /* 1 bits */ + uint32_t sregH; /* 1 bits */ + uint32_t sregT; /* 1 bits */ + uint32_t sregI; /* 1 bits */ + + uint32_t rampD; /* 8 bits */ + uint32_t rampX; /* 8 bits */ + uint32_t rampY; /* 8 bits */ + uint32_t rampZ; /* 8 bits */ + + uint32_t io[64]; /* 8 bits each */ + uint32_t r[32]; /* 8 bits each */ + uint32_t eind; /* 8 bits */ + 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..5923f49 --- /dev/null +++ b/target-avr/gdbstub.c @@ -0,0 +1,105 @@ +/* + * 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 + * + */ + +#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..aec37af --- /dev/null +++ b/target-avr/helper.c @@ -0,0 +1,105 @@ +/* + * 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 + * + */ + +#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..017e076 --- /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 + * + */ + +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 + * + */ + +#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 + * + */ + +extern const VMStateDescription vmstate_avr_cpu; diff --git a/target-avr/translate.c b/target-avr/translate.c new file mode 100644 index 0000000..e98aaef --- /dev/null +++ b/target-avr/translate.c @@ -0,0 +1,300 @@ +/* + * 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 + * + */ + +#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"); + } + } +}