aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kconfig4
-rw-r--r--Kconfig.host7
-rw-r--r--MAINTAINERS59
-rw-r--r--Makefile12
-rw-r--r--Makefile.target7
-rw-r--r--accel/Kconfig9
-rw-r--r--accel/stubs/tcg-stub.c7
-rw-r--r--block/iscsi.c22
-rwxr-xr-xconfigure7
-rw-r--r--cpus-common.c18
-rw-r--r--default-configs/avr-softmmu.mak5
-rw-r--r--docs/system/target-avr.rst37
-rw-r--r--docs/system/targets.rst1
-rw-r--r--exec.c22
-rw-r--r--fsdev/qemu-fsdev.c3
-rw-r--r--gdb-xml/avr-cpu.xml49
-rw-r--r--hw/Kconfig1
-rw-r--r--hw/avr/Kconfig9
-rw-r--r--hw/avr/Makefile.objs3
-rw-r--r--hw/avr/arduino.c149
-rw-r--r--hw/avr/atmega.c458
-rw-r--r--hw/avr/atmega.h48
-rw-r--r--hw/avr/boot.c115
-rw-r--r--hw/avr/boot.h33
-rw-r--r--hw/char/Kconfig3
-rw-r--r--hw/char/Makefile.objs1
-rw-r--r--hw/char/avr_usart.c320
-rw-r--r--hw/core/null-machine.c5
-rw-r--r--hw/i386/pc_sysfw.c5
-rw-r--r--hw/intc/apic.c18
-rw-r--r--hw/intc/apic_common.c19
-rw-r--r--hw/misc/Kconfig3
-rw-r--r--hw/misc/Makefile.objs2
-rw-r--r--hw/misc/avr_power.c113
-rw-r--r--hw/misc/trace-events4
-rw-r--r--hw/timer/Kconfig3
-rw-r--r--hw/timer/Makefile.objs2
-rw-r--r--hw/timer/avr_timer16.c621
-rw-r--r--hw/timer/trace-events12
-rw-r--r--include/disas/dis-asm.h19
-rw-r--r--include/elf.h4
-rw-r--r--include/hw/char/avr_usart.h93
-rw-r--r--include/hw/core/cpu.h37
-rw-r--r--include/hw/i386/apic_internal.h1
-rw-r--r--include/hw/misc/avr_power.h46
-rw-r--r--include/hw/timer/avr_timer16.h94
-rw-r--r--include/qemu/error-report.h2
-rw-r--r--include/qemu/main-loop.h5
-rw-r--r--include/qemu/osdep.h21
-rw-r--r--include/qom/object.h26
-rw-r--r--include/sysemu/arch_init.h1
-rw-r--r--include/sysemu/cpu-throttle.h68
-rw-r--r--include/sysemu/hvf.h2
-rw-r--r--include/sysemu/hw_accel.h13
-rw-r--r--linux-headers/asm-arm/unistd-common.h1
-rw-r--r--linux-headers/asm-x86/kvm.h5
-rw-r--r--migration/migration.c1
-rw-r--r--migration/ram.c1
-rw-r--r--qapi/machine.json3
-rw-r--r--qemu-options.hx12
-rw-r--r--qom/object.c21
-rw-r--r--qom/object_interfaces.c7
-rwxr-xr-xscripts/checkpatch.pl6
-rwxr-xr-xscripts/tap-driver.pl2
-rw-r--r--softmmu/Makefile.objs11
-rw-r--r--softmmu/arch_init.c (renamed from arch_init.c)2
-rw-r--r--softmmu/balloon.c (renamed from balloon.c)0
-rw-r--r--softmmu/cpu-throttle.c122
-rw-r--r--softmmu/cpus.c (renamed from cpus.c)107
-rw-r--r--softmmu/ioport.c (renamed from ioport.c)0
-rw-r--r--softmmu/memory.c (renamed from memory.c)0
-rw-r--r--softmmu/memory_mapping.c (renamed from memory_mapping.c)0
-rw-r--r--softmmu/qtest.c (renamed from qtest.c)0
-rw-r--r--softmmu/vl.c14
-rw-r--r--target/avr/Makefile.objs34
-rw-r--r--target/avr/cpu-param.h36
-rw-r--r--target/avr/cpu-qom.h53
-rw-r--r--target/avr/cpu.c366
-rw-r--r--target/avr/cpu.h256
-rw-r--r--target/avr/disas.c245
-rw-r--r--target/avr/gdbstub.c84
-rw-r--r--target/avr/helper.c348
-rw-r--r--target/avr/helper.h29
-rw-r--r--target/avr/insn.decode187
-rw-r--r--target/avr/machine.c119
-rw-r--r--target/avr/translate.c3061
-rw-r--r--target/i386/Makefile.objs1
-rw-r--r--target/i386/cpu.c13
-rw-r--r--target/i386/cpu.h10
-rw-r--r--target/i386/excp_helper.c4
-rw-r--r--target/i386/fpu_helper.c37
-rw-r--r--target/i386/gdbstub.c1
-rw-r--r--target/i386/helper.c6
-rw-r--r--target/i386/helper.h1
-rw-r--r--target/i386/hvf/hvf.c137
-rw-r--r--target/i386/hvf/vmx.h17
-rw-r--r--target/i386/kvm.c53
-rw-r--r--target/i386/kvm_i386.h1
-rw-r--r--target/i386/machine.c31
-rw-r--r--target/i386/monitor.c10
-rw-r--r--target/i386/ops_sse.h28
-rw-r--r--target/i386/sev-stub.c3
-rw-r--r--target/i386/sev.c27
-rw-r--r--target/i386/sev_i386.h2
-rw-r--r--target/i386/svm.h1
-rw-r--r--target/i386/svm_helper.c7
-rw-r--r--target/i386/tcg-stub.c25
-rw-r--r--target/i386/translate.c36
-rw-r--r--tests/Makefile.include2
-rw-r--r--tests/acceptance/machine_avr6.py50
-rw-r--r--tests/qtest/Makefile.include2
-rw-r--r--tests/qtest/boot-serial-test.c11
-rw-r--r--tests/qtest/machine-none-test.c1
-rw-r--r--tests/qtest/qmp-cmd-test.c109
-rw-r--r--tests/tcg/i386/Makefile.target4
-rw-r--r--tests/tcg/i386/test-i386-sse-exceptions.c813
-rw-r--r--ui/cocoa.m1
-rw-r--r--util/qemu-error.c7
118 files changed, 8790 insertions, 442 deletions
diff --git a/Kconfig b/Kconfig
new file mode 100644
index 0000000000..bf694c42af
--- /dev/null
+++ b/Kconfig
@@ -0,0 +1,4 @@
+source Kconfig.host
+source backends/Kconfig
+source accel/Kconfig
+source hw/Kconfig
diff --git a/Kconfig.host b/Kconfig.host
index 55136e037d..a6d871c399 100644
--- a/Kconfig.host
+++ b/Kconfig.host
@@ -2,9 +2,6 @@
# down to Kconfig. See also MINIKCONF_ARGS in the Makefile:
# these two need to be kept in sync.
-config KVM
- bool
-
config LINUX
bool
@@ -31,10 +28,6 @@ config VHOST_KERNEL
bool
select VHOST
-config XEN
- bool
- select FSDEV_9P if VIRTFS
-
config VIRTFS
bool
diff --git a/MAINTAINERS b/MAINTAINERS
index 6aa54f7f8f..fe8139f367 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -115,7 +115,7 @@ Overall TCG CPUs
M: Richard Henderson <rth@twiddle.net>
R: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
-F: cpus.c
+F: softmmu/cpus.c
F: cpus-common.c
F: exec.c
F: accel/tcg/
@@ -167,6 +167,14 @@ S: Maintained
F: hw/arm/smmu*
F: include/hw/arm/smmu*
+AVR TCG CPUs
+M: Michael Rolnik <mrolnik@gmail.com>
+R: Sarah Harris <S.E.Harris@kent.ac.uk>
+S: Maintained
+F: gdb-xml/avr-cpu.xml
+F: target/avr/
+F: tests/acceptance/machine_avr6.py
+
CRIS TCG CPUs
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
S: Maintained
@@ -362,7 +370,7 @@ Overall KVM CPUs
M: Paolo Bonzini <pbonzini@redhat.com>
L: kvm@vger.kernel.org
S: Supported
-F: */kvm.*
+F: */*/kvm*
F: accel/kvm/
F: accel/stubs/kvm-stub.c
F: include/hw/kvm/
@@ -416,8 +424,21 @@ S: Supported
F: target/i386/kvm.c
F: scripts/kvm/vmxcap
+Guest CPU Cores (other accelerators)
+------------------------------------
+Overall
+M: Richard Henderson <rth@twiddle.net>
+R: Paolo Bonzini <pbonzini@redhat.com>
+S: Maintained
+F: include/sysemu/accel.h
+F: accel/accel.c
+F: accel/Makefile.objs
+F: accel/stubs/Makefile.objs
+
X86 HVF CPUs
+M: Cameron Esfahani <dirty@apple.com>
M: Roman Bolshakov <r.bolshakov@yadro.com>
+W: https://wiki.qemu.org/Features/HVF
S: Maintained
F: accel/stubs/hvf-stub.c
F: target/i386/hvf/
@@ -465,6 +486,7 @@ M: Colin Xu <colin.xu@intel.com>
L: haxm-team@intel.com
W: https://github.com/intel/haxm/issues
S: Maintained
+F: accel/stubs/hax-stub.c
F: include/sysemu/hax.h
F: target/i386/hax-*
@@ -968,6 +990,28 @@ F: include/hw/*/nrf51*.h
F: include/hw/*/microbit*.h
F: tests/qtest/microbit-test.c
+AVR Machines
+-------------
+
+AVR MCUs
+M: Michael Rolnik <mrolnik@gmail.com>
+R: Sarah Harris <S.E.Harris@kent.ac.uk>
+S: Maintained
+F: default-configs/avr-softmmu.mak
+F: hw/avr/
+F: include/hw/char/avr_usart.h
+F: hw/char/avr_usart.c
+F: include/hw/timer/avr_timer16.h
+F: hw/timer/avr_timer16.c
+F: include/hw/misc/avr_power.h
+F: hw/misc/avr_power.c
+
+Arduino
+M: Philippe Mathieu-Daudé <f4bug@amsat.org>
+R: Sarah Harris <S.E.Harris@kent.ac.uk>
+S: Maintained
+F: hw/avr/arduino.c
+
CRIS Machines
-------------
Axis Dev88
@@ -1710,7 +1754,7 @@ M: David Hildenbrand <david@redhat.com>
S: Maintained
F: hw/virtio/virtio-balloon*.c
F: include/hw/virtio/virtio-balloon.h
-F: balloon.c
+F: softmmu/balloon.c
F: include/sysemu/balloon.h
virtio-9p
@@ -2189,12 +2233,12 @@ Memory API
M: Paolo Bonzini <pbonzini@redhat.com>
S: Supported
F: include/exec/ioport.h
-F: ioport.c
F: include/exec/memop.h
F: include/exec/memory.h
F: include/exec/ram_addr.h
F: include/exec/ramblock.h
-F: memory.c
+F: softmmu/ioport.c
+F: softmmu/memory.c
F: include/exec/memory-internal.h
F: exec.c
F: scripts/coccinelle/memory-region-housekeeping.cocci
@@ -2226,13 +2270,14 @@ F: ui/cocoa.m
Main loop
M: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
-F: cpus.c
F: include/qemu/main-loop.h
F: include/sysemu/runstate.h
F: util/main-loop.c
F: util/qemu-timer.c
F: softmmu/vl.c
F: softmmu/main.c
+F: softmmu/cpus.c
+F: softmmu/cpu-throttle.c
F: qapi/run-state.json
Human Monitor (HMP)
@@ -2387,7 +2432,7 @@ M: Thomas Huth <thuth@redhat.com>
M: Laurent Vivier <lvivier@redhat.com>
R: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
-F: qtest.c
+F: softmmu/qtest.c
F: accel/qtest.c
F: tests/qtest/
X: tests/qtest/bios-tables-test-allowed-diff.h
diff --git a/Makefile b/Makefile
index b1b8a5a6d0..32345c610e 100644
--- a/Makefile
+++ b/Makefile
@@ -404,7 +404,8 @@ endif
# This has to be kept in sync with Kconfig.host.
MINIKCONF_ARGS = \
$(CONFIG_MINIKCONF_MODE) \
- $@ $*/config-devices.mak.d $< $(MINIKCONF_INPUTS) \
+ $@ $*/config-devices.mak.d $< $(SRC_PATH)/Kconfig \
+ CONFIG_TCG=$(CONFIG_TCG) \
CONFIG_KVM=$(CONFIG_KVM) \
CONFIG_SPICE=$(CONFIG_SPICE) \
CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \
@@ -418,12 +419,11 @@ MINIKCONF_ARGS = \
CONFIG_LINUX=$(CONFIG_LINUX) \
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
-MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/backends/Kconfig $(SRC_PATH)/hw/Kconfig
-MINIKCONF_DEPS = $(MINIKCONF_INPUTS) $(wildcard $(SRC_PATH)/hw/*/Kconfig)
-MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
+MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py
-$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_DEPS) $(BUILD_DIR)/config-host.mak
- $(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
+$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(SRC_PATH)/Kconfig $(BUILD_DIR)/config-host.mak
+ $(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) \
+ > $@.tmp, "GEN", "$@.tmp")
$(call quiet-command, if test -f $@; then \
if cmp -s $@.old $@; then \
mv $@.tmp $@; \
diff --git a/Makefile.target b/Makefile.target
index 02bd9d7117..ffa2657269 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -152,16 +152,13 @@ endif #CONFIG_BSD_USER
#########################################################
# System emulator target
ifdef CONFIG_SOFTMMU
-obj-y += arch_init.o cpus.o gdbstub.o balloon.o ioport.o
-obj-y += qtest.o
+obj-y += softmmu/
+obj-y += gdbstub.o
obj-y += dump/
obj-y += hw/
obj-y += monitor/
obj-y += qapi/
-obj-y += memory.o
-obj-y += memory_mapping.o
obj-y += migration/ram.o
-obj-y += softmmu/
LIBS := $(libs_softmmu) $(LIBS)
# Hardware support
diff --git a/accel/Kconfig b/accel/Kconfig
new file mode 100644
index 0000000000..2ad94a3839
--- /dev/null
+++ b/accel/Kconfig
@@ -0,0 +1,9 @@
+config TCG
+ bool
+
+config KVM
+ bool
+
+config XEN
+ bool
+ select FSDEV_9P if VIRTFS
diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c
index 677191a69c..e4bbf997aa 100644
--- a/accel/stubs/tcg-stub.c
+++ b/accel/stubs/tcg-stub.c
@@ -22,3 +22,10 @@ void tb_flush(CPUState *cpu)
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
{
}
+
+void *probe_access(CPUArchState *env, target_ulong addr, int size,
+ MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
+{
+ /* Handled by hardware accelerator. */
+ g_assert_not_reached();
+}
diff --git a/block/iscsi.c b/block/iscsi.c
index 6c2e353e1a..bd2122a3a4 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -241,9 +241,11 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
iTask->status = status;
iTask->do_retry = 0;
+ iTask->err_code = 0;
iTask->task = task;
if (status != SCSI_STATUS_GOOD) {
+ iTask->err_code = -EIO;
if (iTask->retries++ < ISCSI_CMD_RETRIES) {
if (status == SCSI_STATUS_BUSY ||
status == SCSI_STATUS_TIMEOUT ||
@@ -266,16 +268,16 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
timer_mod(&iTask->retry_timer,
qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + retry_time);
iTask->do_retry = 1;
- }
- } else if (status == SCSI_STATUS_CHECK_CONDITION) {
- int error = iscsi_translate_sense(&task->sense);
- if (error == EAGAIN) {
- error_report("iSCSI CheckCondition: %s",
- iscsi_get_error(iscsi));
- iTask->do_retry = 1;
- } else {
- iTask->err_code = -error;
- iTask->err_str = g_strdup(iscsi_get_error(iscsi));
+ } else if (status == SCSI_STATUS_CHECK_CONDITION) {
+ int error = iscsi_translate_sense(&task->sense);
+ if (error == EAGAIN) {
+ error_report("iSCSI CheckCondition: %s",
+ iscsi_get_error(iscsi));
+ iTask->do_retry = 1;
+ } else {
+ iTask->err_code = -error;
+ iTask->err_str = g_strdup(iscsi_get_error(iscsi));
+ }
}
}
}
diff --git a/configure b/configure
index 1e977601a4..814ed81279 100755
--- a/configure
+++ b/configure
@@ -8172,6 +8172,10 @@ case "$target_name" in
mttcg="yes"
gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml arm-m-profile.xml"
;;
+ avr)
+ gdb_xml_files="avr-cpu.xml"
+ target_compiler=$cross_cc_avr
+ ;;
cris)
;;
hppa)
@@ -8416,6 +8420,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do
disas_config "ARM_A64"
fi
;;
+ avr)
+ disas_config "AVR"
+ ;;
cris)
disas_config "CRIS"
;;
diff --git a/cpus-common.c b/cpus-common.c
index 8f5512b3d7..34044f4e4c 100644
--- a/cpus-common.c
+++ b/cpus-common.c
@@ -72,6 +72,8 @@ static int cpu_get_free_index(void)
return max_cpu_index;
}
+CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
+
void cpu_list_add(CPUState *cpu)
{
QEMU_LOCK_GUARD(&qemu_cpu_list_lock);
@@ -96,6 +98,22 @@ void cpu_list_remove(CPUState *cpu)
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
}
+CPUState *qemu_get_cpu(int index)
+{
+ CPUState *cpu;
+
+ CPU_FOREACH(cpu) {
+ if (cpu->cpu_index == index) {
+ return cpu;
+ }
+ }
+
+ return NULL;
+}
+
+/* current CPU in the current thread. It is only valid inside cpu_exec() */
+__thread CPUState *current_cpu;
+
struct qemu_work_item {
QSIMPLEQ_ENTRY(qemu_work_item) node;
run_on_cpu_func func;
diff --git a/default-configs/avr-softmmu.mak b/default-configs/avr-softmmu.mak
new file mode 100644
index 0000000000..80218add98
--- /dev/null
+++ b/default-configs/avr-softmmu.mak
@@ -0,0 +1,5 @@
+# Default configuration for avr-softmmu
+
+# Boards:
+#
+CONFIG_ARDUINO=y
diff --git a/docs/system/target-avr.rst b/docs/system/target-avr.rst
new file mode 100644
index 0000000000..dc99afc895
--- /dev/null
+++ b/docs/system/target-avr.rst
@@ -0,0 +1,37 @@
+.. _AVR-System-emulator:
+
+AVR System emulator
+-------------------
+
+Use the executable ``qemu-system-avr`` to emulate a AVR 8 bit based machine.
+These can have one of the following cores: avr1, avr2, avr25, avr3, avr31,
+avr35, avr4, avr5, avr51, avr6, avrtiny, xmega2, xmega3, xmega4, xmega5,
+xmega6 and xmega7.
+
+As for now it supports few Arduino boards for educational and testing purposes.
+These boards use a ATmega controller, which model is limited to USART & 16-bit
+timer devices, enought to run FreeRTOS based applications (like
+https://github.com/seharris/qemu-avr-tests/blob/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf
+).
+
+Following are examples of possible usages, assuming demo.elf is compiled for
+AVR cpu
+
+ - Continuous non interrupted execution:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf``
+
+ - Continuous non interrupted execution with serial output into telnet window:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf -serial
+ tcp::5678,server,nowait -nographic``
+ and then in another shell
+ ``telnet localhost 5678``
+
+ - Debugging wit GDB debugger:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf -s -S``
+ and then in another shell
+ ``avr-gdb demo.elf``
+ and then within GDB shell
+ ``target remote :1234``
+
+ - Print out executed instructions:
+ ``qemu-system-avr -machine mega2560 -bios demo.elf -d in_asm``
diff --git a/docs/system/targets.rst b/docs/system/targets.rst
index 99435a3eba..560783644d 100644
--- a/docs/system/targets.rst
+++ b/docs/system/targets.rst
@@ -19,3 +19,4 @@ Contents:
target-xtensa
target-s390x
target-rx
+ target-avr
diff --git a/exec.c b/exec.c
index 893636176e..6f381f98e2 100644
--- a/exec.c
+++ b/exec.c
@@ -98,12 +98,6 @@ AddressSpace address_space_memory;
static MemoryRegion io_mem_unassigned;
#endif
-CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus);
-
-/* current CPU in the current thread. It is only valid inside
- cpu_exec() */
-__thread CPUState *current_cpu;
-
uintptr_t qemu_host_page_size;
intptr_t qemu_host_page_mask;
@@ -832,22 +826,6 @@ const VMStateDescription vmstate_cpu_common = {
}
};
-#endif
-
-CPUState *qemu_get_cpu(int index)
-{
- CPUState *cpu;
-
- CPU_FOREACH(cpu) {
- if (cpu->cpu_index == index) {
- return cpu;
- }
- }
-
- return NULL;
-}
-
-#if !defined(CONFIG_USER_ONLY)
void cpu_address_space_init(CPUState *cpu, int asidx,
const char *prefix, MemoryRegion *mr)
{
diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c
index a9e069c0c7..3da64e9f72 100644
--- a/fsdev/qemu-fsdev.c
+++ b/fsdev/qemu-fsdev.c
@@ -78,6 +78,7 @@ static FsDriverTable FsDrivers[] = {
"throttling.iops-read-max-length",
"throttling.iops-write-max-length",
"throttling.iops-size",
+ NULL
},
},
{
@@ -85,6 +86,7 @@ static FsDriverTable FsDrivers[] = {
.ops = &synth_ops,
.opts = (const char * []) {
COMMON_FS_DRIVER_OPTIONS,
+ NULL
},
},
{
@@ -95,6 +97,7 @@ static FsDriverTable FsDrivers[] = {
"socket",
"sock_fd",
"writeout",
+ NULL
},
},
};
diff --git a/gdb-xml/avr-cpu.xml b/gdb-xml/avr-cpu.xml
new file mode 100644
index 0000000000..c4747f5b40
--- /dev/null
+++ b/gdb-xml/avr-cpu.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2018-2019 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!-- Register numbers are hard-coded in order to maintain backward
+ compatibility with older versions of tools that didn't use xml
+ register descriptions. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.riscv.cpu">
+ <reg name="r0" bitsize="8" type="int" regnum="0"/>
+ <reg name="r1" bitsize="8" type="int"/>
+ <reg name="r2" bitsize="8" type="int"/>
+ <reg name="r3" bitsize="8" type="int"/>
+ <reg name="r4" bitsize="8" type="int"/>
+ <reg name="r5" bitsize="8" type="int"/>
+ <reg name="r6" bitsize="8" type="int"/>
+ <reg name="r7" bitsize="8" type="int"/>
+ <reg name="r8" bitsize="8" type="int"/>
+ <reg name="r9" bitsize="8" type="int"/>
+ <reg name="r10" bitsize="8" type="int"/>
+ <reg name="r11" bitsize="8" type="int"/>
+ <reg name="r12" bitsize="8" type="int"/>
+ <reg name="r13" bitsize="8" type="int"/>
+ <reg name="r14" bitsize="8" type="int"/>
+ <reg name="r15" bitsize="8" type="int"/>
+ <reg name="r16" bitsize="8" type="int"/>
+ <reg name="r17" bitsize="8" type="int"/>
+ <reg name="r18" bitsize="8" type="int"/>
+ <reg name="r19" bitsize="8" type="int"/>
+ <reg name="r20" bitsize="8" type="int"/>
+ <reg name="r21" bitsize="8" type="int"/>
+ <reg name="r22" bitsize="8" type="int"/>
+ <reg name="r23" bitsize="8" type="int"/>
+ <reg name="r24" bitsize="8" type="int"/>
+ <reg name="r25" bitsize="8" type="int"/>
+ <reg name="r26" bitsize="8" type="int"/>
+ <reg name="r27" bitsize="8" type="int"/>
+ <reg name="r28" bitsize="8" type="int"/>
+ <reg name="r29" bitsize="8" type="int"/>
+ <reg name="r30" bitsize="8" type="int"/>
+ <reg name="r31" bitsize="8" type="int"/>
+ <reg name="sreg" bitsize="8" type="int"/>
+ <reg name="sp" bitsize="8" type="int"/>
+ <reg name="pc" bitsize="8" type="int"/>
+</feature>
diff --git a/hw/Kconfig b/hw/Kconfig
index 62f9ebdc22..4de1797ffd 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -43,6 +43,7 @@ source watchdog/Kconfig
# arch Kconfig
source arm/Kconfig
source alpha/Kconfig
+source avr/Kconfig
source cris/Kconfig
source hppa/Kconfig
source i386/Kconfig
diff --git a/hw/avr/Kconfig b/hw/avr/Kconfig
new file mode 100644
index 0000000000..d31298c3cc
--- /dev/null
+++ b/hw/avr/Kconfig
@@ -0,0 +1,9 @@
+config AVR_ATMEGA_MCU
+ bool
+ select AVR_TIMER16
+ select AVR_USART
+ select AVR_POWER
+
+config ARDUINO
+ select AVR_ATMEGA_MCU
+ select UNIMP
diff --git a/hw/avr/Makefile.objs b/hw/avr/Makefile.objs
new file mode 100644
index 0000000000..4dca064bfc
--- /dev/null
+++ b/hw/avr/Makefile.objs
@@ -0,0 +1,3 @@
+obj-y += boot.o
+obj-$(CONFIG_AVR_ATMEGA_MCU) += atmega.o
+obj-$(CONFIG_ARDUINO) += arduino.o
diff --git a/hw/avr/arduino.c b/hw/avr/arduino.c
new file mode 100644
index 0000000000..65093ab6fd
--- /dev/null
+++ b/hw/avr/arduino.c
@@ -0,0 +1,149 @@
+/*
+ * QEMU Arduino boards
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/* TODO: Implement the use of EXTRAM */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "atmega.h"
+#include "boot.h"
+
+typedef struct ArduinoMachineState {
+ /*< private >*/
+ MachineState parent_obj;
+ /*< public >*/
+ AtmegaMcuState mcu;
+} ArduinoMachineState;
+
+typedef struct ArduinoMachineClass {
+ /*< private >*/
+ MachineClass parent_class;
+ /*< public >*/
+ const char *mcu_type;
+ uint64_t xtal_hz;
+} ArduinoMachineClass;
+
+#define TYPE_ARDUINO_MACHINE \
+ MACHINE_TYPE_NAME("arduino")
+#define ARDUINO_MACHINE(obj) \
+ OBJECT_CHECK(ArduinoMachineState, (obj), TYPE_ARDUINO_MACHINE)
+#define ARDUINO_MACHINE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(ArduinoMachineClass, (klass), TYPE_ARDUINO_MACHINE)
+#define ARDUINO_MACHINE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(ArduinoMachineClass, (obj), TYPE_ARDUINO_MACHINE)
+
+static void arduino_machine_init(MachineState *machine)
+{
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_GET_CLASS(machine);
+ ArduinoMachineState *ams = ARDUINO_MACHINE(machine);
+
+ object_initialize_child(OBJECT(machine), "mcu", &ams->mcu, amc->mcu_type);
+ object_property_set_uint(OBJECT(&ams->mcu), "xtal-frequency-hz",
+ amc->xtal_hz, &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(&ams->mcu), &error_abort);
+
+ if (machine->firmware) {
+ if (!avr_load_firmware(&ams->mcu.cpu, machine,
+ &ams->mcu.flash, machine->firmware)) {
+ exit(1);
+ }
+ }
+}
+
+static void arduino_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = arduino_machine_init;
+ mc->default_cpus = 1;
+ mc->min_cpus = mc->default_cpus;
+ mc->max_cpus = mc->default_cpus;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_parallel = 1;
+}
+
+static void arduino_duemilanove_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://www.arduino.cc/en/Main/ArduinoBoardDuemilanove */
+ mc->desc = "Arduino Duemilanove (ATmega168)",
+ mc->alias = "2009";
+ amc->mcu_type = TYPE_ATMEGA168_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000;
+};
+
+static void arduino_uno_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://store.arduino.cc/arduino-uno-rev3 */
+ mc->desc = "Arduino UNO (ATmega328P)";
+ mc->alias = "uno";
+ amc->mcu_type = TYPE_ATMEGA328_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000;
+};
+
+static void arduino_mega_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://www.arduino.cc/en/Main/ArduinoBoardMega */
+ mc->desc = "Arduino Mega (ATmega1280)";
+ mc->alias = "mega";
+ amc->mcu_type = TYPE_ATMEGA1280_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000;
+};
+
+static void arduino_mega2560_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc);
+
+ /* https://store.arduino.cc/arduino-mega-2560-rev3 */
+ mc->desc = "Arduino Mega 2560 (ATmega2560)";
+ mc->alias = "mega2560";
+ amc->mcu_type = TYPE_ATMEGA2560_MCU;
+ amc->xtal_hz = 16 * 1000 * 1000; /* CSTCE16M0V53-R0 */
+};
+
+static const TypeInfo arduino_machine_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("arduino-duemilanove"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_duemilanove_class_init,
+ }, {
+ .name = MACHINE_TYPE_NAME("arduino-uno"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_uno_class_init,
+ }, {
+ .name = MACHINE_TYPE_NAME("arduino-mega"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_mega_class_init,
+ }, {
+ .name = MACHINE_TYPE_NAME("arduino-mega-2560-v3"),
+ .parent = TYPE_ARDUINO_MACHINE,
+ .class_init = arduino_mega2560_class_init,
+ }, {
+ .name = TYPE_ARDUINO_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(ArduinoMachineState),
+ .class_size = sizeof(ArduinoMachineClass),
+ .class_init = arduino_machine_class_init,
+ .abstract = true,
+ }
+};
+
+DEFINE_TYPES(arduino_machine_types)
diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c
new file mode 100644
index 0000000000..7131224431
--- /dev/null
+++ b/hw/avr/atmega.c
@@ -0,0 +1,458 @@
+/*
+ * QEMU ATmega MCU
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "qapi/error.h"
+#include "exec/memory.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "hw/boards.h" /* FIXME memory_region_allocate_system_memory for sram */
+#include "hw/misc/unimp.h"
+#include "atmega.h"
+
+enum AtmegaPeripheral {
+ POWER0, POWER1,
+ GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF,
+ GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK, GPIOL,
+ USART0, USART1, USART2, USART3,
+ TIMER0, TIMER1, TIMER2, TIMER3, TIMER4, TIMER5,
+ PERIFMAX
+};
+
+#define GPIO(n) (n + GPIOA)
+#define USART(n) (n + USART0)
+#define TIMER(n) (n + TIMER0)
+#define POWER(n) (n + POWER0)
+
+typedef struct {
+ uint16_t addr;
+ enum AtmegaPeripheral power_index;
+ uint8_t power_bit;
+ /* timer specific */
+ uint16_t intmask_addr;
+ uint16_t intflag_addr;
+ bool is_timer16;
+} peripheral_cfg;
+
+typedef struct AtmegaMcuClass {
+ /*< private >*/
+ SysBusDeviceClass parent_class;
+ /*< public >*/
+ const char *uc_name;
+ const char *cpu_type;
+ size_t flash_size;
+ size_t eeprom_size;
+ size_t sram_size;
+ size_t io_size;
+ size_t gpio_count;
+ size_t adc_count;
+ const uint8_t *irq;
+ const peripheral_cfg *dev;
+} AtmegaMcuClass;
+
+#define ATMEGA_MCU_CLASS(klass) \
+ OBJECT_CLASS_CHECK(AtmegaMcuClass, (klass), TYPE_ATMEGA_MCU)
+#define ATMEGA_MCU_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(AtmegaMcuClass, (obj), TYPE_ATMEGA_MCU)
+
+static const peripheral_cfg dev168_328[PERIFMAX] = {
+ [USART0] = { 0xc0, POWER0, 1 },
+ [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false },
+ [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true },
+ [POWER0] = { 0x64 },
+ [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false },
+ [GPIOD] = { 0x29 },
+ [GPIOC] = { 0x26 },
+ [GPIOB] = { 0x23 },
+}, dev1280_2560[PERIFMAX] = {
+ [USART3] = { 0x130, POWER1, 2 },
+ [TIMER5] = { 0x120, POWER1, 5, 0x73, 0x3a, true },
+ [GPIOL] = { 0x109 },
+ [GPIOK] = { 0x106 },
+ [GPIOJ] = { 0x103 },
+ [GPIOH] = { 0x100 },
+ [USART2] = { 0xd0, POWER1, 1 },
+ [USART1] = { 0xc8, POWER1, 0 },
+ [USART0] = { 0xc0, POWER0, 1 },
+ [TIMER2] = { 0xb0, POWER0, 6, 0x70, 0x37, false }, /* TODO async */
+ [TIMER4] = { 0xa0, POWER1, 4, 0x72, 0x39, true },
+ [TIMER3] = { 0x90, POWER1, 3, 0x71, 0x38, true },
+ [TIMER1] = { 0x80, POWER0, 3, 0x6f, 0x36, true },
+ [POWER1] = { 0x65 },
+ [POWER0] = { 0x64 },
+ [TIMER0] = { 0x44, POWER0, 5, 0x6e, 0x35, false },
+ [GPIOG] = { 0x32 },
+ [GPIOF] = { 0x2f },
+ [GPIOE] = { 0x2c },
+ [GPIOD] = { 0x29 },
+ [GPIOC] = { 0x26 },
+ [GPIOB] = { 0x23 },
+ [GPIOA] = { 0x20 },
+};
+
+enum AtmegaIrq {
+ USART0_RXC_IRQ, USART0_DRE_IRQ, USART0_TXC_IRQ,
+ USART1_RXC_IRQ, USART1_DRE_IRQ, USART1_TXC_IRQ,
+ USART2_RXC_IRQ, USART2_DRE_IRQ, USART2_TXC_IRQ,
+ USART3_RXC_IRQ, USART3_DRE_IRQ, USART3_TXC_IRQ,
+ TIMER0_CAPT_IRQ, TIMER0_COMPA_IRQ, TIMER0_COMPB_IRQ,
+ TIMER0_COMPC_IRQ, TIMER0_OVF_IRQ,
+ TIMER1_CAPT_IRQ, TIMER1_COMPA_IRQ, TIMER1_COMPB_IRQ,
+ TIMER1_COMPC_IRQ, TIMER1_OVF_IRQ,
+ TIMER2_CAPT_IRQ, TIMER2_COMPA_IRQ, TIMER2_COMPB_IRQ,
+ TIMER2_COMPC_IRQ, TIMER2_OVF_IRQ,
+ TIMER3_CAPT_IRQ, TIMER3_COMPA_IRQ, TIMER3_COMPB_IRQ,
+ TIMER3_COMPC_IRQ, TIMER3_OVF_IRQ,
+ TIMER4_CAPT_IRQ, TIMER4_COMPA_IRQ, TIMER4_COMPB_IRQ,
+ TIMER4_COMPC_IRQ, TIMER4_OVF_IRQ,
+ TIMER5_CAPT_IRQ, TIMER5_COMPA_IRQ, TIMER5_COMPB_IRQ,
+ TIMER5_COMPC_IRQ, TIMER5_OVF_IRQ,
+ IRQ_COUNT
+};
+
+#define USART_IRQ_COUNT 3
+#define USART_RXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_RXC_IRQ)
+#define USART_DRE_IRQ(n) (n * USART_IRQ_COUNT + USART0_DRE_IRQ)
+#define USART_TXC_IRQ(n) (n * USART_IRQ_COUNT + USART0_TXC_IRQ)
+#define TIMER_IRQ_COUNT 5
+#define TIMER_CAPT_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_CAPT_IRQ)
+#define TIMER_COMPA_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPA_IRQ)
+#define TIMER_COMPB_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPB_IRQ)
+#define TIMER_COMPC_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_COMPC_IRQ)
+#define TIMER_OVF_IRQ(n) (n * TIMER_IRQ_COUNT + TIMER0_OVF_IRQ)
+
+static const uint8_t irq168_328[IRQ_COUNT] = {
+ [TIMER2_COMPA_IRQ] = 8,
+ [TIMER2_COMPB_IRQ] = 9,
+ [TIMER2_OVF_IRQ] = 10,
+ [TIMER1_CAPT_IRQ] = 11,
+ [TIMER1_COMPA_IRQ] = 12,
+ [TIMER1_COMPB_IRQ] = 13,
+ [TIMER1_OVF_IRQ] = 14,
+ [TIMER0_COMPA_IRQ] = 15,
+ [TIMER0_COMPB_IRQ] = 16,
+ [TIMER0_OVF_IRQ] = 17,
+ [USART0_RXC_IRQ] = 19,
+ [USART0_DRE_IRQ] = 20,
+ [USART0_TXC_IRQ] = 21,
+}, irq1280_2560[IRQ_COUNT] = {
+ [TIMER2_COMPA_IRQ] = 14,
+ [TIMER2_COMPB_IRQ] = 15,
+ [TIMER2_OVF_IRQ] = 16,
+ [TIMER1_CAPT_IRQ] = 17,
+ [TIMER1_COMPA_IRQ] = 18,
+ [TIMER1_COMPB_IRQ] = 19,
+ [TIMER1_COMPC_IRQ] = 20,
+ [TIMER1_OVF_IRQ] = 21,
+ [TIMER0_COMPA_IRQ] = 22,
+ [TIMER0_COMPB_IRQ] = 23,
+ [TIMER0_OVF_IRQ] = 24,
+ [USART0_RXC_IRQ] = 26,
+ [USART0_DRE_IRQ] = 27,
+ [USART0_TXC_IRQ] = 28,
+ [TIMER3_CAPT_IRQ] = 32,
+ [TIMER3_COMPA_IRQ] = 33,
+ [TIMER3_COMPB_IRQ] = 34,
+ [TIMER3_COMPC_IRQ] = 35,
+ [TIMER3_OVF_IRQ] = 36,
+ [USART1_RXC_IRQ] = 37,
+ [USART1_DRE_IRQ] = 38,
+ [USART1_TXC_IRQ] = 39,
+ [TIMER4_CAPT_IRQ] = 42,
+ [TIMER4_COMPA_IRQ] = 43,
+ [TIMER4_COMPB_IRQ] = 44,
+ [TIMER4_COMPC_IRQ] = 45,
+ [TIMER4_OVF_IRQ] = 46,
+ [TIMER5_CAPT_IRQ] = 47,
+ [TIMER5_COMPA_IRQ] = 48,
+ [TIMER5_COMPB_IRQ] = 49,
+ [TIMER5_COMPC_IRQ] = 50,
+ [TIMER5_OVF_IRQ] = 51,
+ [USART2_RXC_IRQ] = 52,
+ [USART2_DRE_IRQ] = 53,
+ [USART2_TXC_IRQ] = 54,
+ [USART3_RXC_IRQ] = 55,
+ [USART3_DRE_IRQ] = 56,
+ [USART3_TXC_IRQ] = 57,
+};
+
+static void connect_peripheral_irq(const AtmegaMcuClass *k,
+ SysBusDevice *dev, int dev_irqn,
+ DeviceState *cpu,
+ unsigned peripheral_index)
+{
+ int cpu_irq = k->irq[peripheral_index];
+
+ if (!cpu_irq) {
+ return;
+ }
+ /* FIXME move that to avr_cpu_set_int() once 'sample' board is removed */
+ assert(cpu_irq >= 2);
+ cpu_irq -= 2;
+
+ sysbus_connect_irq(dev, dev_irqn, qdev_get_gpio_in(cpu, cpu_irq));
+}
+
+static void connect_power_reduction_gpio(AtmegaMcuState *s,
+ const AtmegaMcuClass *k,
+ DeviceState *cpu,
+ unsigned peripheral_index)
+{
+ unsigned power_index = k->dev[peripheral_index].power_index;
+ assert(k->dev[power_index].addr);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pwr[power_index - POWER0]),
+ k->dev[peripheral_index].power_bit,
+ qdev_get_gpio_in(cpu, 0));
+}
+
+static void atmega_realize(DeviceState *dev, Error **errp)
+{
+ AtmegaMcuState *s = ATMEGA_MCU(dev);
+ const AtmegaMcuClass *mc = ATMEGA_MCU_GET_CLASS(dev);
+ DeviceState *cpudev;
+ SysBusDevice *sbd;
+ char *devname;
+ size_t i;
+
+ assert(mc->io_size <= 0x200);
+
+ if (!s->xtal_freq_hz) {
+ error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
+ return;
+ }
+
+ /* CPU */
+ object_initialize_child(OBJECT(dev), "cpu", &s->cpu, mc->cpu_type);
+ object_property_set_bool(OBJECT(&s->cpu), "realized", true, &error_abort);
+ cpudev = DEVICE(&s->cpu);
+
+ /* SRAM */
+ memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size,
+ &error_abort);
+ memory_region_add_subregion(get_system_memory(),
+ OFFSET_DATA + mc->io_size, &s->sram);
+
+ /* Flash */
+ memory_region_init_rom(&s->flash, OBJECT(dev),
+ "flash", mc->flash_size, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), OFFSET_CODE, &s->flash);
+
+ /*
+ * I/O
+ *
+ * 0x00 - 0x1f: Registers
+ * 0x20 - 0x5f: I/O memory
+ * 0x60 - 0xff: Extended I/O
+ */
+ s->io = qdev_new(TYPE_UNIMPLEMENTED_DEVICE);
+ qdev_prop_set_string(s->io, "name", "I/O");
+ qdev_prop_set_uint64(s->io, "size", mc->io_size);
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(s->io), &error_fatal);
+ sysbus_mmio_map_overlap(SYS_BUS_DEVICE(s->io), 0, OFFSET_DATA, -1234);
+
+ /* Power Reduction */
+ for (i = 0; i < POWER_MAX; i++) {
+ int idx = POWER(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ devname = g_strdup_printf("power%zu", i);
+ object_initialize_child(OBJECT(dev), devname, &s->pwr[i],
+ TYPE_AVR_MASK);
+ sysbus_realize(SYS_BUS_DEVICE(&s->pwr[i]), &error_abort);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->pwr[i]), 0,
+ OFFSET_DATA + mc->dev[idx].addr);
+ g_free(devname);
+ }
+
+ /* GPIO */
+ for (i = 0; i < GPIO_MAX; i++) {
+ int idx = GPIO(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ devname = g_strdup_printf("atmega-gpio-%c", 'a' + (char)i);
+ create_unimplemented_device(devname,
+ OFFSET_DATA + mc->dev[idx].addr, 3);
+ g_free(devname);
+ }
+
+ /* USART */
+ for (i = 0; i < USART_MAX; i++) {
+ int idx = USART(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ devname = g_strdup_printf("usart%zu", i);
+ object_initialize_child(OBJECT(dev), devname, &s->usart[i],
+ TYPE_AVR_USART);
+ qdev_prop_set_chr(DEVICE(&s->usart[i]), "chardev", serial_hd(i));
+ sbd = SYS_BUS_DEVICE(&s->usart[i]);
+ sysbus_realize(sbd, &error_abort);
+ sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[USART(i)].addr);
+ connect_peripheral_irq(mc, sbd, 0, cpudev, USART_RXC_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 1, cpudev, USART_DRE_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 2, cpudev, USART_TXC_IRQ(i));
+ connect_power_reduction_gpio(s, mc, DEVICE(&s->usart[i]), idx);
+ g_free(devname);
+ }
+
+ /* Timer */
+ for (i = 0; i < TIMER_MAX; i++) {
+ int idx = TIMER(i);
+ if (!mc->dev[idx].addr) {
+ continue;
+ }
+ if (!mc->dev[idx].is_timer16) {
+ create_unimplemented_device("avr-timer8",
+ OFFSET_DATA + mc->dev[idx].addr, 5);
+ create_unimplemented_device("avr-timer8-intmask",
+ OFFSET_DATA
+ + mc->dev[idx].intmask_addr, 1);
+ create_unimplemented_device("avr-timer8-intflag",
+ OFFSET_DATA
+ + mc->dev[idx].intflag_addr, 1);
+ continue;
+ }
+ devname = g_strdup_printf("timer%zu", i);
+ object_initialize_child(OBJECT(dev), devname, &s->timer[i],
+ TYPE_AVR_TIMER16);
+ object_property_set_uint(OBJECT(&s->timer[i]), "cpu-frequency-hz",
+ s->xtal_freq_hz, &error_abort);
+ sbd = SYS_BUS_DEVICE(&s->timer[i]);
+ sysbus_realize(sbd, &error_abort);
+ sysbus_mmio_map(sbd, 0, OFFSET_DATA + mc->dev[idx].addr);
+ sysbus_mmio_map(sbd, 1, OFFSET_DATA + mc->dev[idx].intmask_addr);
+ sysbus_mmio_map(sbd, 2, OFFSET_DATA + mc->dev[idx].intflag_addr);
+ connect_peripheral_irq(mc, sbd, 0, cpudev, TIMER_CAPT_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 1, cpudev, TIMER_COMPA_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 2, cpudev, TIMER_COMPB_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 3, cpudev, TIMER_COMPC_IRQ(i));
+ connect_peripheral_irq(mc, sbd, 4, cpudev, TIMER_OVF_IRQ(i));
+ connect_power_reduction_gpio(s, mc, DEVICE(&s->timer[i]), idx);
+ g_free(devname);
+ }
+
+ create_unimplemented_device("avr-twi", OFFSET_DATA + 0x0b8, 6);
+ create_unimplemented_device("avr-adc", OFFSET_DATA + 0x078, 8);
+ create_unimplemented_device("avr-ext-mem-ctrl", OFFSET_DATA + 0x074, 2);
+ create_unimplemented_device("avr-watchdog", OFFSET_DATA + 0x060, 1);
+ create_unimplemented_device("avr-spi", OFFSET_DATA + 0x04c, 3);
+ create_unimplemented_device("avr-eeprom", OFFSET_DATA + 0x03f, 3);
+}
+
+static Property atmega_props[] = {
+ DEFINE_PROP_UINT64("xtal-frequency-hz", AtmegaMcuState,
+ xtal_freq_hz, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
+
+static void atmega_class_init(ObjectClass *oc, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(oc);
+
+ dc->realize = atmega_realize;
+ device_class_set_props(dc, atmega_props);
+ /* Reason: Mapped at fixed location on the system bus */
+ dc->user_creatable = false;
+}
+
+static void atmega168_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr5");
+ amc->flash_size = 16 * KiB;
+ amc->eeprom_size = 512;
+ amc->sram_size = 1 * KiB;
+ amc->io_size = 256;
+ amc->gpio_count = 23;
+ amc->adc_count = 6;
+ amc->irq = irq168_328;
+ amc->dev = dev168_328;
+};
+
+static void atmega328_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr5");
+ amc->flash_size = 32 * KiB;
+ amc->eeprom_size = 1 * KiB;
+ amc->sram_size = 2 * KiB;
+ amc->io_size = 256;
+ amc->gpio_count = 23;
+ amc->adc_count = 6;
+ amc->irq = irq168_328;
+ amc->dev = dev168_328;
+};
+
+static void atmega1280_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr6");
+ amc->flash_size = 128 * KiB;
+ amc->eeprom_size = 4 * KiB;
+ amc->sram_size = 8 * KiB;
+ amc->io_size = 512;
+ amc->gpio_count = 86;
+ amc->adc_count = 16;
+ amc->irq = irq1280_2560;
+ amc->dev = dev1280_2560;
+};
+
+static void atmega2560_class_init(ObjectClass *oc, void *data)
+{
+ AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc);
+
+ amc->cpu_type = AVR_CPU_TYPE_NAME("avr6");
+ amc->flash_size = 256 * KiB;
+ amc->eeprom_size = 4 * KiB;
+ amc->sram_size = 8 * KiB;
+ amc->io_size = 512;
+ amc->gpio_count = 54;
+ amc->adc_count = 16;
+ amc->irq = irq1280_2560;
+ amc->dev = dev1280_2560;
+};
+
+static const TypeInfo atmega_mcu_types[] = {
+ {
+ .name = TYPE_ATMEGA168_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega168_class_init,
+ }, {
+ .name = TYPE_ATMEGA328_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega328_class_init,
+ }, {
+ .name = TYPE_ATMEGA1280_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega1280_class_init,
+ }, {
+ .name = TYPE_ATMEGA2560_MCU,
+ .parent = TYPE_ATMEGA_MCU,
+ .class_init = atmega2560_class_init,
+ }, {
+ .name = TYPE_ATMEGA_MCU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AtmegaMcuState),
+ .class_size = sizeof(AtmegaMcuClass),
+ .class_init = atmega_class_init,
+ .abstract = true,
+ }
+};
+
+DEFINE_TYPES(atmega_mcu_types)
diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h
new file mode 100644
index 0000000000..0928cb0ce6
--- /dev/null
+++ b/hw/avr/atmega.h
@@ -0,0 +1,48 @@
+/*
+ * QEMU ATmega MCU
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_AVR_ATMEGA_H
+#define HW_AVR_ATMEGA_H
+
+#include "hw/char/avr_usart.h"
+#include "hw/timer/avr_timer16.h"
+#include "hw/misc/avr_power.h"
+#include "target/avr/cpu.h"
+
+#define TYPE_ATMEGA_MCU "ATmega"
+#define TYPE_ATMEGA168_MCU "ATmega168"
+#define TYPE_ATMEGA328_MCU "ATmega328"
+#define TYPE_ATMEGA1280_MCU "ATmega1280"
+#define TYPE_ATMEGA2560_MCU "ATmega2560"
+
+#define ATMEGA_MCU(obj) OBJECT_CHECK(AtmegaMcuState, (obj), TYPE_ATMEGA_MCU)
+
+#define POWER_MAX 2
+#define USART_MAX 4
+#define TIMER_MAX 6
+#define GPIO_MAX 12
+
+typedef struct AtmegaMcuState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ AVRCPU cpu;
+ MemoryRegion flash;
+ MemoryRegion eeprom;
+ MemoryRegion sram;
+ DeviceState *io;
+ AVRMaskState pwr[POWER_MAX];
+ AVRUsartState usart[USART_MAX];
+ AVRTimer16State timer[TIMER_MAX];
+ uint64_t xtal_freq_hz;
+} AtmegaMcuState;
+
+#endif /* HW_AVR_ATMEGA_H */
diff --git a/hw/avr/boot.c b/hw/avr/boot.c
new file mode 100644
index 0000000000..6fbcde4061
--- /dev/null
+++ b/hw/avr/boot.c
@@ -0,0 +1,115 @@
+/*
+ * AVR loader helpers
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "hw/loader.h"
+#include "elf.h"
+#include "boot.h"
+#include "qemu/error-report.h"
+
+static const char *avr_elf_e_flags_to_cpu_type(uint32_t flags)
+{
+ switch (flags & EF_AVR_MACH) {
+ case bfd_mach_avr1:
+ return AVR_CPU_TYPE_NAME("avr1");
+ case bfd_mach_avr2:
+ return AVR_CPU_TYPE_NAME("avr2");
+ case bfd_mach_avr25:
+ return AVR_CPU_TYPE_NAME("avr25");
+ case bfd_mach_avr3:
+ return AVR_CPU_TYPE_NAME("avr3");
+ case bfd_mach_avr31:
+ return AVR_CPU_TYPE_NAME("avr31");
+ case bfd_mach_avr35:
+ return AVR_CPU_TYPE_NAME("avr35");
+ case bfd_mach_avr4:
+ return AVR_CPU_TYPE_NAME("avr4");
+ case bfd_mach_avr5:
+ return AVR_CPU_TYPE_NAME("avr5");
+ case bfd_mach_avr51:
+ return AVR_CPU_TYPE_NAME("avr51");
+ case bfd_mach_avr6:
+ return AVR_CPU_TYPE_NAME("avr6");
+ case bfd_mach_avrtiny:
+ return AVR_CPU_TYPE_NAME("avrtiny");
+ case bfd_mach_avrxmega2:
+ return AVR_CPU_TYPE_NAME("xmega2");
+ case bfd_mach_avrxmega3:
+ return AVR_CPU_TYPE_NAME("xmega3");
+ case bfd_mach_avrxmega4:
+ return AVR_CPU_TYPE_NAME("xmega4");
+ case bfd_mach_avrxmega5:
+ return AVR_CPU_TYPE_NAME("xmega5");
+ case bfd_mach_avrxmega6:
+ return AVR_CPU_TYPE_NAME("xmega6");
+ case bfd_mach_avrxmega7:
+ return AVR_CPU_TYPE_NAME("xmega7");
+ default:
+ return NULL;
+ }
+}
+
+bool avr_load_firmware(AVRCPU *cpu, MachineState *ms,
+ MemoryRegion *program_mr, const char *firmware)
+{
+ const char *filename;
+ int bytes_loaded;
+ uint64_t entry;
+ uint32_t e_flags;
+
+ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, firmware);
+ if (filename == NULL) {
+ error_report("Unable to find %s", firmware);
+ return false;
+ }
+
+ bytes_loaded = load_elf_ram_sym(filename,
+ NULL, NULL, NULL,
+ &entry, NULL, NULL,
+ &e_flags, 0, EM_AVR, 0, 0,
+ NULL, true, NULL);
+ if (bytes_loaded >= 0) {
+ /* If ELF file is provided, determine CPU type reading ELF e_flags. */
+ const char *elf_cpu = avr_elf_e_flags_to_cpu_type(e_flags);
+ const char *mcu_cpu_type = object_get_typename(OBJECT(cpu));
+ int cpu_len = strlen(mcu_cpu_type) - strlen(AVR_CPU_TYPE_SUFFIX);
+
+ if (entry) {
+ error_report("BIOS entry_point must be 0x0000 "
+ "(ELF image '%s' has entry_point 0x%04" PRIx64 ")",
+ firmware, entry);
+ return false;
+ }
+ if (!elf_cpu) {
+ warn_report("Could not determine CPU type for ELF image '%s', "
+ "assuming '%.*s' CPU",
+ firmware, cpu_len, mcu_cpu_type);
+ return true;
+ }
+ if (strcmp(elf_cpu, mcu_cpu_type)) {
+ error_report("Current machine: %s with '%.*s' CPU",
+ MACHINE_GET_CLASS(ms)->desc, cpu_len, mcu_cpu_type);
+ error_report("ELF image '%s' is for '%.*s' CPU",
+ firmware,
+ (int)(strlen(elf_cpu) - strlen(AVR_CPU_TYPE_SUFFIX)),
+ elf_cpu);
+ return false;
+ }
+ } else {
+ bytes_loaded = load_image_mr(filename, program_mr);
+ }
+ if (bytes_loaded < 0) {
+ error_report("Unable to load firmware image %s as ELF or raw binary",
+ firmware);
+ return false;
+ }
+ return true;
+}
diff --git a/hw/avr/boot.h b/hw/avr/boot.h
new file mode 100644
index 0000000000..684d553322
--- /dev/null
+++ b/hw/avr/boot.h
@@ -0,0 +1,33 @@
+/*
+ * AVR loader helpers
+ *
+ * Copyright (c) 2019-2020 Philippe Mathieu-Daudé
+ *
+ * This work is licensed under the terms of the GNU GPLv2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_AVR_BOOT_H
+#define HW_AVR_BOOT_H
+
+#include "hw/boards.h"
+#include "cpu.h"
+
+/**
+ * avr_load_firmware: load an image into a memory region
+ *
+ * @cpu: Handle a AVR CPU object
+ * @ms: A MachineState
+ * @mr: Memory Region to load into
+ * @firmware: Path to the firmware file (raw binary or ELF format)
+ *
+ * Load a firmware supplied by the machine or by the user with the
+ * '-bios' command line option, and put it in target memory.
+ *
+ * Returns: true on success, false on error.
+ */
+bool avr_load_firmware(AVRCPU *cpu, MachineState *ms,
+ MemoryRegion *mr, const char *firmware);
+
+#endif
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 874627520c..b7e0e4d5fa 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -49,3 +49,6 @@ config TERMINAL3270
config RENESAS_SCI
bool
+
+config AVR_USART
+ bool
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index 8306c4a789..bf177ac41d 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -22,6 +22,7 @@ common-obj-$(CONFIG_DIGIC) += digic-uart.o
common-obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
common-obj-$(CONFIG_RASPI) += bcm2835_aux.o
common-obj-$(CONFIG_RENESAS_SCI) += renesas_sci.o
+common-obj-$(CONFIG_AVR_USART) += avr_usart.o
common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c
new file mode 100644
index 0000000000..fbe2a112b7
--- /dev/null
+++ b/hw/char/avr_usart.c
@@ -0,0 +1,320 @@
+/*
+ * AVR USART
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Sarah Harris
+ *
+ * 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/char/avr_usart.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+
+static int avr_usart_can_receive(void *opaque)
+{
+ AVRUsartState *usart = opaque;
+
+ if (usart->data_valid || !(usart->csrb & USART_CSRB_RXEN)) {
+ return 0;
+ }
+ return 1;
+}
+
+static void avr_usart_receive(void *opaque, const uint8_t *buffer, int size)
+{
+ AVRUsartState *usart = opaque;
+ assert(size == 1);
+ assert(!usart->data_valid);
+ usart->data = buffer[0];
+ usart->data_valid = true;
+ usart->csra |= USART_CSRA_RXC;
+ if (usart->csrb & USART_CSRB_RXCIE) {
+ qemu_set_irq(usart->rxc_irq, 1);
+ }
+}
+
+static void update_char_mask(AVRUsartState *usart)
+{
+ uint8_t mode = ((usart->csrc & USART_CSRC_CSZ0) ? 1 : 0) |
+ ((usart->csrc & USART_CSRC_CSZ1) ? 2 : 0) |
+ ((usart->csrb & USART_CSRB_CSZ2) ? 4 : 0);
+ switch (mode) {
+ case 0:
+ usart->char_mask = 0b11111;
+ break;
+ case 1:
+ usart->char_mask = 0b111111;
+ break;
+ case 2:
+ usart->char_mask = 0b1111111;
+ break;
+ case 3:
+ usart->char_mask = 0b11111111;
+ break;
+ case 4:
+ /* Fallthrough. */
+ case 5:
+ /* Fallthrough. */
+ case 6:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Reserved character size 0x%x\n",
+ __func__,
+ mode);
+ break;
+ case 7:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Nine bit character size not supported (forcing eight)\n",
+ __func__);
+ usart->char_mask = 0b11111111;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void avr_usart_reset(DeviceState *dev)
+{
+ AVRUsartState *usart = AVR_USART(dev);
+ usart->data_valid = false;
+ usart->csra = 0b00100000;
+ usart->csrb = 0b00000000;
+ usart->csrc = 0b00000110;
+ usart->brrl = 0;
+ usart->brrh = 0;
+ update_char_mask(usart);
+ qemu_set_irq(usart->rxc_irq, 0);
+ qemu_set_irq(usart->txc_irq, 0);
+ qemu_set_irq(usart->dre_irq, 0);
+}
+
+static uint64_t avr_usart_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ AVRUsartState *usart = opaque;
+ uint8_t data;
+ assert(size == 1);
+
+ if (!usart->enabled) {
+ return 0;
+ }
+
+ switch (addr) {
+ case USART_DR:
+ if (!(usart->csrb & USART_CSRB_RXEN)) {
+ /* Receiver disabled, ignore. */
+ return 0;
+ }
+ if (usart->data_valid) {
+ data = usart->data & usart->char_mask;
+ usart->data_valid = false;
+ } else {
+ data = 0;
+ }
+ usart->csra &= 0xff ^ USART_CSRA_RXC;
+ qemu_set_irq(usart->rxc_irq, 0);
+ qemu_chr_fe_accept_input(&usart->chr);
+ return data;
+ case USART_CSRA:
+ return usart->csra;
+ case USART_CSRB:
+ return usart->csrb;
+ case USART_CSRC:
+ return usart->csrc;
+ case USART_BRRL:
+ return usart->brrl;
+ case USART_BRRH:
+ return usart->brrh;
+ default:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+ __func__,
+ addr);
+ }
+ return 0;
+}
+
+static void avr_usart_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned int size)
+{
+ AVRUsartState *usart = opaque;
+ uint8_t mask;
+ uint8_t data;
+ assert((value & 0xff) == value);
+ assert(size == 1);
+
+ if (!usart->enabled) {
+ return;
+ }
+
+ switch (addr) {
+ case USART_DR:
+ if (!(usart->csrb & USART_CSRB_TXEN)) {
+ /* Transmitter disabled, ignore. */
+ return;
+ }
+ usart->csra |= USART_CSRA_TXC;
+ usart->csra |= USART_CSRA_DRE;
+ if (usart->csrb & USART_CSRB_TXCIE) {
+ qemu_set_irq(usart->txc_irq, 1);
+ usart->csra &= 0xff ^ USART_CSRA_TXC;
+ }
+ if (usart->csrb & USART_CSRB_DREIE) {
+ qemu_set_irq(usart->dre_irq, 1);
+ }
+ data = value;
+ qemu_chr_fe_write_all(&usart->chr, &data, 1);
+ break;
+ case USART_CSRA:
+ mask = 0b01000011;
+ /* Mask read-only bits. */
+ value = (value & mask) | (usart->csra & (0xff ^ mask));
+ usart->csra = value;
+ if (value & USART_CSRA_TXC) {
+ usart->csra ^= USART_CSRA_TXC;
+ qemu_set_irq(usart->txc_irq, 0);
+ }
+ if (value & USART_CSRA_MPCM) {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: MPCM not supported by USART\n",
+ __func__);
+ }
+ break;
+ case USART_CSRB:
+ mask = 0b11111101;
+ /* Mask read-only bits. */
+ value = (value & mask) | (usart->csrb & (0xff ^ mask));
+ usart->csrb = value;
+ if (!(value & USART_CSRB_RXEN)) {
+ /* Receiver disabled, flush input buffer. */
+ usart->data_valid = false;
+ }
+ qemu_set_irq(usart->rxc_irq,
+ ((value & USART_CSRB_RXCIE) &&
+ (usart->csra & USART_CSRA_RXC)) ? 1 : 0);
+ qemu_set_irq(usart->txc_irq,
+ ((value & USART_CSRB_TXCIE) &&
+ (usart->csra & USART_CSRA_TXC)) ? 1 : 0);
+ qemu_set_irq(usart->dre_irq,
+ ((value & USART_CSRB_DREIE) &&
+ (usart->csra & USART_CSRA_DRE)) ? 1 : 0);
+ update_char_mask(usart);
+ break;
+ case USART_CSRC:
+ usart->csrc = value;
+ if ((value & USART_CSRC_MSEL1) && (value & USART_CSRC_MSEL0)) {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: SPI mode not supported by USART\n",
+ __func__);
+ }
+ if ((value & USART_CSRC_MSEL1) && !(value & USART_CSRC_MSEL0)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad USART mode\n", __func__);
+ }
+ if (!(value & USART_CSRC_PM1) && (value & USART_CSRC_PM0)) {
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Bad USART parity mode\n",
+ __func__);
+ }
+ update_char_mask(usart);
+ break;
+ case USART_BRRL:
+ usart->brrl = value;
+ break;
+ case USART_BRRH:
+ usart->brrh = value & 0b00001111;
+ break;
+ default:
+ qemu_log_mask(
+ LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%"HWADDR_PRIx"\n",
+ __func__,
+ addr);
+ }
+}
+
+static const MemoryRegionOps avr_usart_ops = {
+ .read = avr_usart_read,
+ .write = avr_usart_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.min_access_size = 1, .max_access_size = 1}
+};
+
+static Property avr_usart_properties[] = {
+ DEFINE_PROP_CHR("chardev", AVRUsartState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void avr_usart_pr(void *opaque, int irq, int level)
+{
+ AVRUsartState *s = AVR_USART(opaque);
+
+ s->enabled = !level;
+
+ if (!s->enabled) {
+ avr_usart_reset(DEVICE(s));
+ }
+}
+
+static void avr_usart_init(Object *obj)
+{
+ AVRUsartState *s = AVR_USART(obj);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->rxc_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->dre_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->txc_irq);
+ memory_region_init_io(&s->mmio, obj, &avr_usart_ops, s, TYPE_AVR_USART, 7);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+ qdev_init_gpio_in(DEVICE(s), avr_usart_pr, 1);
+ s->enabled = true;
+}
+
+static void avr_usart_realize(DeviceState *dev, Error **errp)
+{
+ AVRUsartState *s = AVR_USART(dev);
+ qemu_chr_fe_set_handlers(&s->chr, avr_usart_can_receive,
+ avr_usart_receive, NULL, NULL,
+ s, NULL, true);
+ avr_usart_reset(dev);
+}
+
+static void avr_usart_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = avr_usart_reset;
+ device_class_set_props(dc, avr_usart_properties);
+ dc->realize = avr_usart_realize;
+}
+
+static const TypeInfo avr_usart_info = {
+ .name = TYPE_AVR_USART,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AVRUsartState),
+ .instance_init = avr_usart_init,
+ .class_init = avr_usart_class_init,
+};
+
+static void avr_usart_register_types(void)
+{
+ type_register_static(&avr_usart_info);
+}
+
+type_init(avr_usart_register_types)
diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c
index cb47d9d4f8..7e693523d7 100644
--- a/hw/core/null-machine.c
+++ b/hw/core/null-machine.c
@@ -50,6 +50,11 @@ static void machine_none_machine_init(MachineClass *mc)
mc->max_cpus = 1;
mc->default_ram_size = 0;
mc->default_ram_id = "ram";
+ mc->no_serial = 1;
+ mc->no_parallel = 1;
+ mc->no_floppy = 1;
+ mc->no_cdrom = 1;
+ mc->no_sdcard = 1;
}
DEFINE_MACHINE("none", machine_none_machine_init)
diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c
index 0ff47a4b59..b6c0822fe3 100644
--- a/hw/i386/pc_sysfw.c
+++ b/hw/i386/pc_sysfw.c
@@ -93,6 +93,11 @@ static PFlashCFI01 *pc_pflash_create(PCMachineState *pcms,
object_property_add_child(OBJECT(pcms), name, OBJECT(dev));
object_property_add_alias(OBJECT(pcms), alias_prop_name,
OBJECT(dev), "drive");
+ /*
+ * The returned reference is tied to the child property and
+ * will be removed with object_unparent.
+ */
+ object_unref(OBJECT(dev));
return PFLASH_CFI01(dev);
}
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 6b46839ef4..38aabd60cd 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -615,24 +615,6 @@ int apic_accept_pic_intr(DeviceState *dev)
return 0;
}
-static uint32_t apic_get_current_count(APICCommonState *s)
-{
- int64_t d;
- uint32_t val;
- d = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->initial_count_load_time) >>
- s->count_shift;
- if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
- /* periodic */
- val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
- } else {
- if (d >= s->initial_count)
- val = 0;
- else
- val = s->initial_count - d;
- }
- return val;
-}
-
static void apic_timer_update(APICCommonState *s, int64_t current_time)
{
if (apic_next_timer(s, current_time)) {
diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c
index dc070343c0..81addd6390 100644
--- a/hw/intc/apic_common.c
+++ b/hw/intc/apic_common.c
@@ -189,6 +189,25 @@ bool apic_next_timer(APICCommonState *s, int64_t current_time)
return true;
}
+uint32_t apic_get_current_count(APICCommonState *s)
+{
+ int64_t d;
+ uint32_t val;
+ d = (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - s->initial_count_load_time) >>
+ s->count_shift;
+ if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+ /* periodic */
+ val = s->initial_count - (d % ((uint64_t)s->initial_count + 1));
+ } else {
+ if (d >= s->initial_count) {
+ val = 0;
+ } else {
+ val = s->initial_count - d;
+ }
+ }
+ return val;
+}
+
void apic_init_reset(DeviceState *dev)
{
APICCommonState *s;
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index bdd77d8020..92c397ca07 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -131,4 +131,7 @@ config MAC_VIA
select MOS6522
select ADB
+config AVR_POWER
+ bool
+
source macio/Kconfig
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 5aaca8a039..6be3d255ab 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -91,3 +91,5 @@ common-obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o
obj-$(CONFIG_MAC_VIA) += mac_via.o
common-obj-$(CONFIG_GRLIB) += grlib_ahb_apb_pnp.o
+
+obj-$(CONFIG_AVR_POWER) += avr_power.o
diff --git a/hw/misc/avr_power.c b/hw/misc/avr_power.c
new file mode 100644
index 0000000000..a5412f2cfe
--- /dev/null
+++ b/hw/misc/avr_power.c
@@ -0,0 +1,113 @@
+/*
+ * AVR Power Reduction Management
+ *
+ * Copyright (c) 2019-2020 Michael Rolnik
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/avr_power.h"
+#include "qemu/log.h"
+#include "hw/qdev-properties.h"
+#include "hw/irq.h"
+#include "trace.h"
+
+static void avr_mask_reset(DeviceState *dev)
+{
+ AVRMaskState *s = AVR_MASK(dev);
+
+ s->val = 0x00;
+
+ for (int i = 0; i < 8; i++) {
+ qemu_set_irq(s->irq[i], 0);
+ }
+}
+
+static uint64_t avr_mask_read(void *opaque, hwaddr offset, unsigned size)
+{
+ assert(size == 1);
+ assert(offset == 0);
+ AVRMaskState *s = opaque;
+
+ trace_avr_power_read(s->val);
+
+ return (uint64_t)s->val;
+}
+
+static void avr_mask_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ assert(offset == 0);
+ AVRMaskState *s = opaque;
+ uint8_t val8 = val64;
+
+ trace_avr_power_write(val8);
+ s->val = val8;
+ for (int i = 0; i < 8; i++) {
+ qemu_set_irq(s->irq[i], (val8 & (1 << i)) != 0);
+ }
+}
+
+static const MemoryRegionOps avr_mask_ops = {
+ .read = avr_mask_read,
+ .write = avr_mask_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .max_access_size = 1,
+ },
+};
+
+static void avr_mask_init(Object *dev)
+{
+ AVRMaskState *s = AVR_MASK(dev);
+ SysBusDevice *busdev = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, dev, &avr_mask_ops, s, TYPE_AVR_MASK,
+ 0x01);
+ sysbus_init_mmio(busdev, &s->iomem);
+
+ for (int i = 0; i < 8; i++) {
+ sysbus_init_irq(busdev, &s->irq[i]);
+ }
+ s->val = 0x00;
+}
+
+static void avr_mask_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = avr_mask_reset;
+}
+
+static const TypeInfo avr_mask_info = {
+ .name = TYPE_AVR_MASK,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AVRMaskState),
+ .class_init = avr_mask_class_init,
+ .instance_init = avr_mask_init,
+};
+
+static void avr_mask_register_types(void)
+{
+ type_register_static(&avr_mask_info);
+}
+
+type_init(avr_mask_register_types)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index ebea53735c..066752aa90 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -19,6 +19,10 @@ allwinner_h3_dramphy_write(uint64_t offset, uint64_t data, unsigned size) "write
allwinner_sid_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
allwinner_sid_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %" PRIu32
+# avr_power.c
+avr_power_read(uint8_t value) "power_reduc read value:%u"
+avr_power_write(uint8_t value) "power_reduc write value:%u"
+
# eccmemctl.c
ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x"
ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x"
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index 59a667c503..8749edfb6a 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -41,3 +41,6 @@ config RENESAS_TMR
config RENESAS_CMT
bool
+
+config AVR_TIMER16
+ bool
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index a39f6ec0c2..1303b13e0d 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -37,3 +37,5 @@ common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o
common-obj-$(CONFIG_MSF2) += mss-timer.o
common-obj-$(CONFIG_RASPI) += bcm2835_systmr.o
+
+obj-$(CONFIG_AVR_TIMER16) += avr_timer16.o
diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c
new file mode 100644
index 0000000000..c48555da52
--- /dev/null
+++ b/hw/timer/avr_timer16.c
@@ -0,0 +1,621 @@
+/*
+ * AVR 16-bit timer
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Ed Robbins
+ *
+ * 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>
+ */
+
+/*
+ * Driver for 16 bit timers on 8 bit AVR devices.
+ * Note:
+ * ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 bit
+ */
+
+/*
+ * XXX TODO: Power Reduction Register support
+ * prescaler pause support
+ * PWM modes, GPIO, output capture pins, input compare pin
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/timer/avr_timer16.h"
+#include "trace.h"
+
+/* Register offsets */
+#define T16_CRA 0x0
+#define T16_CRB 0x1
+#define T16_CRC 0x2
+#define T16_CNTL 0x4
+#define T16_CNTH 0x5
+#define T16_ICRL 0x6
+#define T16_ICRH 0x7
+#define T16_OCRAL 0x8
+#define T16_OCRAH 0x9
+#define T16_OCRBL 0xa
+#define T16_OCRBH 0xb
+#define T16_OCRCL 0xc
+#define T16_OCRCH 0xd
+
+/* Field masks */
+#define T16_CRA_WGM01 0x3
+#define T16_CRA_COMC 0xc
+#define T16_CRA_COMB 0x30
+#define T16_CRA_COMA 0xc0
+#define T16_CRA_OC_CONF \
+ (T16_CRA_COMA | T16_CRA_COMB | T16_CRA_COMC)
+
+#define T16_CRB_CS 0x7
+#define T16_CRB_WGM23 0x18
+#define T16_CRB_ICES 0x40
+#define T16_CRB_ICNC 0x80
+
+#define T16_CRC_FOCC 0x20
+#define T16_CRC_FOCB 0x40
+#define T16_CRC_FOCA 0x80
+
+/* Fields masks both TIMSK and TIFR (interrupt mask/flag registers) */
+#define T16_INT_TOV 0x1 /* Timer overflow */
+#define T16_INT_OCA 0x2 /* Output compare A */
+#define T16_INT_OCB 0x4 /* Output compare B */
+#define T16_INT_OCC 0x8 /* Output compare C */
+#define T16_INT_IC 0x20 /* Input capture */
+
+/* Clock source values */
+#define T16_CLKSRC_STOPPED 0
+#define T16_CLKSRC_DIV1 1
+#define T16_CLKSRC_DIV8 2
+#define T16_CLKSRC_DIV64 3
+#define T16_CLKSRC_DIV256 4
+#define T16_CLKSRC_DIV1024 5
+#define T16_CLKSRC_EXT_FALLING 6
+#define T16_CLKSRC_EXT_RISING 7
+
+/* Timer mode values (not including PWM modes) */
+#define T16_MODE_NORMAL 0
+#define T16_MODE_CTC_OCRA 4
+#define T16_MODE_CTC_ICR 12
+
+/* Accessors */
+#define CLKSRC(t16) (t16->crb & T16_CRB_CS)
+#define MODE(t16) (((t16->crb & T16_CRB_WGM23) >> 1) | \
+ (t16->cra & T16_CRA_WGM01))
+#define CNT(t16) VAL16(t16->cntl, t16->cnth)
+#define OCRA(t16) VAL16(t16->ocral, t16->ocrah)
+#define OCRB(t16) VAL16(t16->ocrbl, t16->ocrbh)
+#define OCRC(t16) VAL16(t16->ocrcl, t16->ocrch)
+#define ICR(t16) VAL16(t16->icrl, t16->icrh)
+
+/* Helper macros */
+#define VAL16(l, h) ((h << 8) | l)
+#define DB_PRINT(fmt, args...) /* Nothing */
+
+static inline int64_t avr_timer16_ns_to_ticks(AVRTimer16State *t16, int64_t t)
+{
+ if (t16->period_ns == 0) {
+ return 0;
+ }
+ return t / t16->period_ns;
+}
+
+static void avr_timer16_update_cnt(AVRTimer16State *t16)
+{
+ uint16_t cnt;
+ cnt = avr_timer16_ns_to_ticks(t16, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
+ t16->reset_time_ns);
+ t16->cntl = (uint8_t)(cnt & 0xff);
+ t16->cnth = (uint8_t)((cnt & 0xff00) >> 8);
+}
+
+static inline void avr_timer16_recalc_reset_time(AVRTimer16State *t16)
+{
+ t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) -
+ CNT(t16) * t16->period_ns;
+}
+
+static void avr_timer16_clock_reset(AVRTimer16State *t16)
+{
+ t16->cntl = 0;
+ t16->cnth = 0;
+ t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void avr_timer16_clksrc_update(AVRTimer16State *t16)
+{
+ uint16_t divider = 0;
+ switch (CLKSRC(t16)) {
+ case T16_CLKSRC_EXT_FALLING:
+ case T16_CLKSRC_EXT_RISING:
+ qemu_log_mask(LOG_UNIMP, "%s: external clock source unsupported\n",
+ __func__);
+ break;
+ case T16_CLKSRC_STOPPED:
+ break;
+ case T16_CLKSRC_DIV1:
+ divider = 1;
+ break;
+ case T16_CLKSRC_DIV8:
+ divider = 8;
+ break;
+ case T16_CLKSRC_DIV64:
+ divider = 64;
+ break;
+ case T16_CLKSRC_DIV256:
+ divider = 256;
+ break;
+ case T16_CLKSRC_DIV1024:
+ divider = 1024;
+ break;
+ default:
+ break;
+ }
+ if (divider) {
+ t16->freq_hz = t16->cpu_freq_hz / divider;
+ t16->period_ns = NANOSECONDS_PER_SECOND / t16->freq_hz;
+ trace_avr_timer16_clksrc_update(t16->freq_hz, t16->period_ns,
+ (uint64_t)(1e6 / t16->freq_hz));
+ }
+}
+
+static void avr_timer16_set_alarm(AVRTimer16State *t16)
+{
+ if (CLKSRC(t16) == T16_CLKSRC_EXT_FALLING ||
+ CLKSRC(t16) == T16_CLKSRC_EXT_RISING ||
+ CLKSRC(t16) == T16_CLKSRC_STOPPED) {
+ /* Timer is disabled or set to external clock source (unsupported) */
+ return;
+ }
+
+ uint64_t alarm_offset = 0xffff;
+ enum NextInterrupt next_interrupt = OVERFLOW;
+
+ switch (MODE(t16)) {
+ case T16_MODE_NORMAL:
+ /* Normal mode */
+ if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCA)) {
+ alarm_offset = OCRA(t16);
+ next_interrupt = COMPA;
+ }
+ break;
+ case T16_MODE_CTC_OCRA:
+ /* CTC mode, top = ocra */
+ if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16)) {
+ alarm_offset = OCRA(t16);
+ next_interrupt = COMPA;
+ }
+ break;
+ case T16_MODE_CTC_ICR:
+ /* CTC mode, top = icr */
+ if (ICR(t16) < alarm_offset && ICR(t16) > CNT(t16)) {
+ alarm_offset = ICR(t16);
+ next_interrupt = CAPT;
+ }
+ if (OCRA(t16) < alarm_offset && OCRA(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCA)) {
+ alarm_offset = OCRA(t16);
+ next_interrupt = COMPA;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: pwm modes are unsupported\n",
+ __func__);
+ return;
+ }
+ if (OCRB(t16) < alarm_offset && OCRB(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCB)) {
+ alarm_offset = OCRB(t16);
+ next_interrupt = COMPB;
+ }
+ if (OCRC(t16) < alarm_offset && OCRB(t16) > CNT(t16) &&
+ (t16->imsk & T16_INT_OCC)) {
+ alarm_offset = OCRB(t16);
+ next_interrupt = COMPC;
+ }
+ alarm_offset -= CNT(t16);
+
+ t16->next_interrupt = next_interrupt;
+ uint64_t alarm_ns =
+ t16->reset_time_ns + ((CNT(t16) + alarm_offset) * t16->period_ns);
+ timer_mod(t16->timer, alarm_ns);
+
+ trace_avr_timer16_next_alarm(alarm_offset * t16->period_ns);
+}
+
+static void avr_timer16_interrupt(void *opaque)
+{
+ AVRTimer16State *t16 = opaque;
+ uint8_t mode = MODE(t16);
+
+ avr_timer16_update_cnt(t16);
+
+ if (CLKSRC(t16) == T16_CLKSRC_EXT_FALLING ||
+ CLKSRC(t16) == T16_CLKSRC_EXT_RISING ||
+ CLKSRC(t16) == T16_CLKSRC_STOPPED) {
+ /* Timer is disabled or set to external clock source (unsupported) */
+ return;
+ }
+
+ trace_avr_timer16_interrupt_count(CNT(t16));
+
+ /* Counter overflow */
+ if (t16->next_interrupt == OVERFLOW) {
+ trace_avr_timer16_interrupt_overflow("counter 0xffff");
+ avr_timer16_clock_reset(t16);
+ if (t16->imsk & T16_INT_TOV) {
+ t16->ifr |= T16_INT_TOV;
+ qemu_set_irq(t16->ovf_irq, 1);
+ }
+ }
+ /* Check for ocra overflow in CTC mode */
+ if (mode == T16_MODE_CTC_OCRA && t16->next_interrupt == COMPA) {
+ trace_avr_timer16_interrupt_overflow("CTC OCRA");
+ avr_timer16_clock_reset(t16);
+ }
+ /* Check for icr overflow in CTC mode */
+ if (mode == T16_MODE_CTC_ICR && t16->next_interrupt == CAPT) {
+ trace_avr_timer16_interrupt_overflow("CTC ICR");
+ avr_timer16_clock_reset(t16);
+ if (t16->imsk & T16_INT_IC) {
+ t16->ifr |= T16_INT_IC;
+ qemu_set_irq(t16->capt_irq, 1);
+ }
+ }
+ /* Check for output compare interrupts */
+ if (t16->imsk & T16_INT_OCA && t16->next_interrupt == COMPA) {
+ t16->ifr |= T16_INT_OCA;
+ qemu_set_irq(t16->compa_irq, 1);
+ }
+ if (t16->imsk & T16_INT_OCB && t16->next_interrupt == COMPB) {
+ t16->ifr |= T16_INT_OCB;
+ qemu_set_irq(t16->compb_irq, 1);
+ }
+ if (t16->imsk & T16_INT_OCC && t16->next_interrupt == COMPC) {
+ t16->ifr |= T16_INT_OCC;
+ qemu_set_irq(t16->compc_irq, 1);
+ }
+ avr_timer16_set_alarm(t16);
+}
+
+static void avr_timer16_reset(DeviceState *dev)
+{
+ AVRTimer16State *t16 = AVR_TIMER16(dev);
+
+ avr_timer16_clock_reset(t16);
+ avr_timer16_clksrc_update(t16);
+ avr_timer16_set_alarm(t16);
+
+ qemu_set_irq(t16->capt_irq, 0);
+ qemu_set_irq(t16->compa_irq, 0);
+ qemu_set_irq(t16->compb_irq, 0);
+ qemu_set_irq(t16->compc_irq, 0);
+ qemu_set_irq(t16->ovf_irq, 0);
+}
+
+static uint64_t avr_timer16_read(void *opaque, hwaddr offset, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ uint8_t retval = 0;
+
+ switch (offset) {
+ case T16_CRA:
+ retval = t16->cra;
+ break;
+ case T16_CRB:
+ retval = t16->crb;
+ break;
+ case T16_CRC:
+ retval = t16->crc;
+ break;
+ case T16_CNTL:
+ avr_timer16_update_cnt(t16);
+ t16->rtmp = t16->cnth;
+ retval = t16->cntl;
+ break;
+ case T16_CNTH:
+ retval = t16->rtmp;
+ break;
+ case T16_ICRL:
+ /*
+ * The timer copies cnt to icr when the input capture pin changes
+ * state or when the analog comparator has a match. We don't
+ * emulate this behaviour. We do support it's use for defining a
+ * TOP value in T16_MODE_CTC_ICR
+ */
+ t16->rtmp = t16->icrh;
+ retval = t16->icrl;
+ break;
+ case T16_ICRH:
+ retval = t16->rtmp;
+ break;
+ case T16_OCRAL:
+ retval = t16->ocral;
+ break;
+ case T16_OCRAH:
+ retval = t16->ocrah;
+ break;
+ case T16_OCRBL:
+ retval = t16->ocrbl;
+ break;
+ case T16_OCRBH:
+ retval = t16->ocrbh;
+ break;
+ case T16_OCRCL:
+ retval = t16->ocrcl;
+ break;
+ case T16_OCRCH:
+ retval = t16->ocrch;
+ break;
+ default:
+ break;
+ }
+ trace_avr_timer16_read(offset, retval);
+
+ return (uint64_t)retval;
+}
+
+static void avr_timer16_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ uint8_t val8 = (uint8_t)val64;
+ uint8_t prev_clk_src = CLKSRC(t16);
+
+ trace_avr_timer16_write(offset, val8);
+
+ switch (offset) {
+ case T16_CRA:
+ t16->cra = val8;
+ if (t16->cra & T16_CRA_OC_CONF) {
+ qemu_log_mask(LOG_UNIMP, "%s: output compare pins unsupported\n",
+ __func__);
+ }
+ break;
+ case T16_CRB:
+ t16->crb = val8;
+ if (t16->crb & T16_CRB_ICNC) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: input capture noise canceller unsupported\n",
+ __func__);
+ }
+ if (t16->crb & T16_CRB_ICES) {
+ qemu_log_mask(LOG_UNIMP, "%s: input capture unsupported\n",
+ __func__);
+ }
+ if (CLKSRC(t16) != prev_clk_src) {
+ avr_timer16_clksrc_update(t16);
+ if (prev_clk_src == T16_CLKSRC_STOPPED) {
+ t16->reset_time_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ }
+ }
+ break;
+ case T16_CRC:
+ t16->crc = val8;
+ qemu_log_mask(LOG_UNIMP, "%s: output compare pins unsupported\n",
+ __func__);
+ break;
+ case T16_CNTL:
+ /*
+ * CNT is the 16-bit counter value, it must be read/written via
+ * a temporary register (rtmp) to make the read/write atomic.
+ */
+ /* ICR also has this behaviour, and shares rtmp */
+ /*
+ * Writing CNT blocks compare matches for one clock cycle.
+ * Writing CNT to TOP or to an OCR value (if in use) will
+ * skip the relevant interrupt
+ */
+ t16->cntl = val8;
+ t16->cnth = t16->rtmp;
+ avr_timer16_recalc_reset_time(t16);
+ break;
+ case T16_CNTH:
+ t16->rtmp = val8;
+ break;
+ case T16_ICRL:
+ /* ICR can only be written in mode T16_MODE_CTC_ICR */
+ if (MODE(t16) == T16_MODE_CTC_ICR) {
+ t16->icrl = val8;
+ t16->icrh = t16->rtmp;
+ }
+ break;
+ case T16_ICRH:
+ if (MODE(t16) == T16_MODE_CTC_ICR) {
+ t16->rtmp = val8;
+ }
+ break;
+ case T16_OCRAL:
+ /*
+ * OCRn cause the relevant output compare flag to be raised, and
+ * trigger an interrupt, when CNT is equal to the value here
+ */
+ t16->ocral = val8;
+ break;
+ case T16_OCRAH:
+ t16->ocrah = val8;
+ break;
+ case T16_OCRBL:
+ t16->ocrbl = val8;
+ break;
+ case T16_OCRBH:
+ t16->ocrbh = val8;
+ break;
+ case T16_OCRCL:
+ t16->ocrcl = val8;
+ break;
+ case T16_OCRCH:
+ t16->ocrch = val8;
+ break;
+ default:
+ break;
+ }
+ avr_timer16_set_alarm(t16);
+}
+
+static uint64_t avr_timer16_imsk_read(void *opaque,
+ hwaddr offset,
+ unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_read_imsk(offset ? 0 : t16->imsk);
+ if (offset != 0) {
+ return 0;
+ }
+ return t16->imsk;
+}
+
+static void avr_timer16_imsk_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_write_imsk(val64);
+ if (offset != 0) {
+ return;
+ }
+ t16->imsk = (uint8_t)val64;
+}
+
+static uint64_t avr_timer16_ifr_read(void *opaque,
+ hwaddr offset,
+ unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_read_ifr(offset ? 0 : t16->ifr);
+ if (offset != 0) {
+ return 0;
+ }
+ return t16->ifr;
+}
+
+static void avr_timer16_ifr_write(void *opaque, hwaddr offset,
+ uint64_t val64, unsigned size)
+{
+ assert(size == 1);
+ AVRTimer16State *t16 = opaque;
+ trace_avr_timer16_write_imsk(val64);
+ if (offset != 0) {
+ return;
+ }
+ t16->ifr = (uint8_t)val64;
+}
+
+static const MemoryRegionOps avr_timer16_ops = {
+ .read = avr_timer16_read,
+ .write = avr_timer16_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.max_access_size = 1}
+};
+
+static const MemoryRegionOps avr_timer16_imsk_ops = {
+ .read = avr_timer16_imsk_read,
+ .write = avr_timer16_imsk_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.max_access_size = 1}
+};
+
+static const MemoryRegionOps avr_timer16_ifr_ops = {
+ .read = avr_timer16_ifr_read,
+ .write = avr_timer16_ifr_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {.max_access_size = 1}
+};
+
+static Property avr_timer16_properties[] = {
+ DEFINE_PROP_UINT8("id", struct AVRTimer16State, id, 0),
+ DEFINE_PROP_UINT64("cpu-frequency-hz", struct AVRTimer16State,
+ cpu_freq_hz, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void avr_timer16_pr(void *opaque, int irq, int level)
+{
+ AVRTimer16State *s = AVR_TIMER16(opaque);
+
+ s->enabled = !level;
+
+ if (!s->enabled) {
+ avr_timer16_reset(DEVICE(s));
+ }
+}
+
+static void avr_timer16_init(Object *obj)
+{
+ AVRTimer16State *s = AVR_TIMER16(obj);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->capt_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compa_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compb_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->compc_irq);
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->ovf_irq);
+
+ memory_region_init_io(&s->iomem, obj, &avr_timer16_ops,
+ s, "avr-timer16", 0xe);
+ memory_region_init_io(&s->imsk_iomem, obj, &avr_timer16_imsk_ops,
+ s, "avr-timer16-intmask", 0x1);
+ memory_region_init_io(&s->ifr_iomem, obj, &avr_timer16_ifr_ops,
+ s, "avr-timer16-intflag", 0x1);
+
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->imsk_iomem);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->ifr_iomem);
+ qdev_init_gpio_in(DEVICE(s), avr_timer16_pr, 1);
+}
+
+static void avr_timer16_realize(DeviceState *dev, Error **errp)
+{
+ AVRTimer16State *s = AVR_TIMER16(dev);
+
+ if (s->cpu_freq_hz == 0) {
+ error_setg(errp, "AVR timer16: cpu-frequency-hz property must be set");
+ return;
+ }
+
+ s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, avr_timer16_interrupt, s);
+ s->enabled = true;
+}
+
+static void avr_timer16_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = avr_timer16_reset;
+ dc->realize = avr_timer16_realize;
+ device_class_set_props(dc, avr_timer16_properties);
+}
+
+static const TypeInfo avr_timer16_info = {
+ .name = TYPE_AVR_TIMER16,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AVRTimer16State),
+ .instance_init = avr_timer16_init,
+ .class_init = avr_timer16_class_init,
+};
+
+static void avr_timer16_register_types(void)
+{
+ type_register_static(&avr_timer16_info);
+}
+
+type_init(avr_timer16_register_types)
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 866c9f546a..447b7c405b 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -75,3 +75,15 @@ nrf51_timer_set_count(uint8_t timer_id, uint8_t counter_id, uint32_t value) "tim
bcm2835_systmr_irq(bool enable) "timer irq state %u"
bcm2835_systmr_read(uint64_t offset, uint64_t data) "timer read: offset 0x%" PRIx64 " data 0x%" PRIx64
bcm2835_systmr_write(uint64_t offset, uint64_t data) "timer write: offset 0x%" PRIx64 " data 0x%" PRIx64
+
+# avr_timer16.c
+avr_timer16_read(uint8_t addr, uint8_t value) "timer16 read addr:%u value:%u"
+avr_timer16_read_ifr(uint8_t value) "timer16 read addr:ifr value:%u"
+avr_timer16_read_imsk(uint8_t value) "timer16 read addr:imsk value:%u"
+avr_timer16_write(uint8_t addr, uint8_t value) "timer16 write addr:%u value:%u"
+avr_timer16_write_ifr(uint8_t value) "timer16 write addr:ifr value:%u"
+avr_timer16_write_imsk(uint8_t value) "timer16 write addr:imsk value:%u"
+avr_timer16_interrupt_count(uint8_t cnt) "count: %u"
+avr_timer16_interrupt_overflow(const char *reason) "overflow: %s"
+avr_timer16_next_alarm(uint64_t delay_ns) "next alarm: %" PRIu64 " ns from now"
+avr_timer16_clksrc_update(uint64_t freq_hz, uint64_t period_ns, uint64_t delay_s) "timer frequency: %" PRIu64 " Hz, period: %" PRIu64 " ns (%" PRId64 " us)"
diff --git a/include/disas/dis-asm.h b/include/disas/dis-asm.h
index c5f9fa08ab..9856bf7921 100644
--- a/include/disas/dis-asm.h
+++ b/include/disas/dis-asm.h
@@ -211,6 +211,25 @@ enum bfd_architecture
#define bfd_mach_m32r 0 /* backwards compatibility */
bfd_arch_mn10200, /* Matsushita MN10200 */
bfd_arch_mn10300, /* Matsushita MN10300 */
+ bfd_arch_avr, /* AVR microcontrollers */
+#define bfd_mach_avr1 1
+#define bfd_mach_avr2 2
+#define bfd_mach_avr25 25
+#define bfd_mach_avr3 3
+#define bfd_mach_avr31 31
+#define bfd_mach_avr35 35
+#define bfd_mach_avr4 4
+#define bfd_mach_avr5 5
+#define bfd_mach_avr51 51
+#define bfd_mach_avr6 6
+#define bfd_mach_avrtiny 100
+#define bfd_mach_avrxmega1 101
+#define bfd_mach_avrxmega2 102
+#define bfd_mach_avrxmega3 103
+#define bfd_mach_avrxmega4 104
+#define bfd_mach_avrxmega5 105
+#define bfd_mach_avrxmega6 106
+#define bfd_mach_avrxmega7 107
bfd_arch_cris, /* Axis CRIS */
#define bfd_mach_cris_v0_v10 255
#define bfd_mach_cris_v32 32
diff --git a/include/elf.h b/include/elf.h
index 8fbfe60e09..5b06b55f28 100644
--- a/include/elf.h
+++ b/include/elf.h
@@ -160,6 +160,8 @@ typedef struct mips_elf_abiflags_v0 {
#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
+#define EM_AVR 83 /* AVR 8-bit microcontroller */
+
#define EM_V850 87 /* NEC v850 */
#define EM_H8_300H 47 /* Hitachi H8/300H */
@@ -202,6 +204,8 @@ typedef struct mips_elf_abiflags_v0 {
#define EM_MOXIE 223 /* Moxie processor family */
#define EM_MOXIE_OLD 0xFEED
+#define EF_AVR_MACH 0x7F /* Mask for AVR e_flags to get core type */
+
/* This is the info that is needed to parse the dynamic section of the file */
#define DT_NULL 0
#define DT_NEEDED 1
diff --git a/include/hw/char/avr_usart.h b/include/hw/char/avr_usart.h
new file mode 100644
index 0000000000..5739aaf26f
--- /dev/null
+++ b/include/hw/char/avr_usart.h
@@ -0,0 +1,93 @@
+/*
+ * AVR USART
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Sarah Harris
+ *
+ * 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 HW_CHAR_AVR_USART_H
+#define HW_CHAR_AVR_USART_H
+
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+#include "hw/hw.h"
+
+/* Offsets of registers. */
+#define USART_DR 0x06
+#define USART_CSRA 0x00
+#define USART_CSRB 0x01
+#define USART_CSRC 0x02
+#define USART_BRRH 0x05
+#define USART_BRRL 0x04
+
+/* Relevant bits in regiters. */
+#define USART_CSRA_RXC (1 << 7)
+#define USART_CSRA_TXC (1 << 6)
+#define USART_CSRA_DRE (1 << 5)
+#define USART_CSRA_MPCM (1 << 0)
+
+#define USART_CSRB_RXCIE (1 << 7)
+#define USART_CSRB_TXCIE (1 << 6)
+#define USART_CSRB_DREIE (1 << 5)
+#define USART_CSRB_RXEN (1 << 4)
+#define USART_CSRB_TXEN (1 << 3)
+#define USART_CSRB_CSZ2 (1 << 2)
+#define USART_CSRB_RXB8 (1 << 1)
+#define USART_CSRB_TXB8 (1 << 0)
+
+#define USART_CSRC_MSEL1 (1 << 7)
+#define USART_CSRC_MSEL0 (1 << 6)
+#define USART_CSRC_PM1 (1 << 5)
+#define USART_CSRC_PM0 (1 << 4)
+#define USART_CSRC_CSZ1 (1 << 2)
+#define USART_CSRC_CSZ0 (1 << 1)
+
+#define TYPE_AVR_USART "avr-usart"
+#define AVR_USART(obj) \
+ OBJECT_CHECK(AVRUsartState, (obj), TYPE_AVR_USART)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion mmio;
+
+ CharBackend chr;
+
+ bool enabled;
+
+ uint8_t data;
+ bool data_valid;
+ uint8_t char_mask;
+ /* Control and Status Registers */
+ uint8_t csra;
+ uint8_t csrb;
+ uint8_t csrc;
+ /* Baud Rate Registers (low/high byte) */
+ uint8_t brrh;
+ uint8_t brrl;
+
+ /* Receive Complete */
+ qemu_irq rxc_irq;
+ /* Transmit Complete */
+ qemu_irq txc_irq;
+ /* Data Register Empty */
+ qemu_irq dre_irq;
+} AVRUsartState;
+
+#endif /* HW_CHAR_AVR_USART_H */
diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h
index b3f4b79318..5542577d2b 100644
--- a/include/hw/core/cpu.h
+++ b/include/hw/core/cpu.h
@@ -822,43 +822,6 @@ bool cpu_exists(int64_t id);
*/
CPUState *cpu_by_arch_id(int64_t id);
-/**
- * cpu_throttle_set:
- * @new_throttle_pct: Percent of sleep time. Valid range is 1 to 99.
- *
- * Throttles all vcpus by forcing them to sleep for the given percentage of
- * time. A throttle_percentage of 25 corresponds to a 75% duty cycle roughly.
- * (example: 10ms sleep for every 30ms awake).
- *
- * cpu_throttle_set can be called as needed to adjust new_throttle_pct.
- * Once the throttling starts, it will remain in effect until cpu_throttle_stop
- * is called.
- */
-void cpu_throttle_set(int new_throttle_pct);
-
-/**
- * cpu_throttle_stop:
- *
- * Stops the vcpu throttling started by cpu_throttle_set.
- */
-void cpu_throttle_stop(void);
-
-/**
- * cpu_throttle_active:
- *
- * Returns: %true if the vcpus are currently being throttled, %false otherwise.
- */
-bool cpu_throttle_active(void);
-
-/**
- * cpu_throttle_get_percentage:
- *
- * Returns the vcpu throttle percentage. See cpu_throttle_set for details.
- *
- * Returns: The throttle percentage in range 1 to 99.
- */
-int cpu_throttle_get_percentage(void);
-
#ifndef CONFIG_USER_ONLY
typedef void (*CPUInterruptHandler)(CPUState *, int);
diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h
index b04bdd947f..2597000e03 100644
--- a/include/hw/i386/apic_internal.h
+++ b/include/hw/i386/apic_internal.h
@@ -211,6 +211,7 @@ void vapic_report_tpr_access(DeviceState *dev, CPUState *cpu, target_ulong ip,
TPRAccess access);
int apic_get_ppr(APICCommonState *s);
+uint32_t apic_get_current_count(APICCommonState *s);
static inline void apic_set_bit(uint32_t *tab, int index)
{
diff --git a/include/hw/misc/avr_power.h b/include/hw/misc/avr_power.h
new file mode 100644
index 0000000000..e08e44f629
--- /dev/null
+++ b/include/hw/misc/avr_power.h
@@ -0,0 +1,46 @@
+/*
+ * AVR Power Reduction Management
+ *
+ * Copyright (c) 2019-2020 Michael Rolnik
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef HW_MISC_AVR_POWER_H
+#define HW_MISC_AVR_POWER_H
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+
+
+#define TYPE_AVR_MASK "avr-power"
+#define AVR_MASK(obj) OBJECT_CHECK(AVRMaskState, (obj), TYPE_AVR_MASK)
+
+typedef struct {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion iomem;
+
+ uint8_t val;
+ qemu_irq irq[8];
+} AVRMaskState;
+
+#endif /* HW_MISC_AVR_POWER_H */
diff --git a/include/hw/timer/avr_timer16.h b/include/hw/timer/avr_timer16.h
new file mode 100644
index 0000000000..982019d242
--- /dev/null
+++ b/include/hw/timer/avr_timer16.h
@@ -0,0 +1,94 @@
+/*
+ * AVR 16-bit timer
+ *
+ * Copyright (c) 2018 University of Kent
+ * Author: Ed Robbins
+ *
+ * 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>
+ */
+
+/*
+ * Driver for 16 bit timers on 8 bit AVR devices.
+ * Note:
+ * On ATmega640/V-1280/V-1281/V-2560/V-2561/V timers 1, 3, 4 and 5 are 16 bit
+ */
+
+#ifndef HW_TIMER_AVR_TIMER16_H
+#define HW_TIMER_AVR_TIMER16_H
+
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+#include "hw/hw.h"
+
+enum NextInterrupt {
+ OVERFLOW,
+ COMPA,
+ COMPB,
+ COMPC,
+ CAPT
+};
+
+#define TYPE_AVR_TIMER16 "avr-timer16"
+#define AVR_TIMER16(obj) \
+ OBJECT_CHECK(AVRTimer16State, (obj), TYPE_AVR_TIMER16)
+
+typedef struct AVRTimer16State {
+ /* <private> */
+ SysBusDevice parent_obj;
+
+ /* <public> */
+ MemoryRegion iomem;
+ MemoryRegion imsk_iomem;
+ MemoryRegion ifr_iomem;
+ QEMUTimer *timer;
+ qemu_irq capt_irq;
+ qemu_irq compa_irq;
+ qemu_irq compb_irq;
+ qemu_irq compc_irq;
+ qemu_irq ovf_irq;
+
+ bool enabled;
+
+ /* registers */
+ uint8_t cra;
+ uint8_t crb;
+ uint8_t crc;
+ uint8_t cntl;
+ uint8_t cnth;
+ uint8_t icrl;
+ uint8_t icrh;
+ uint8_t ocral;
+ uint8_t ocrah;
+ uint8_t ocrbl;
+ uint8_t ocrbh;
+ uint8_t ocrcl;
+ uint8_t ocrch;
+ /*
+ * Reads and writes to CNT and ICR utilise a bizarre temporary
+ * register, which we emulate
+ */
+ uint8_t rtmp;
+ uint8_t imsk;
+ uint8_t ifr;
+
+ uint8_t id;
+ uint64_t cpu_freq_hz;
+ uint64_t freq_hz;
+ uint64_t period_ns;
+ uint64_t reset_time_ns;
+ enum NextInterrupt next_interrupt;
+} AVRTimer16State;
+
+#endif /* HW_TIMER_AVR_TIMER16_H */
diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h
index 87532d8596..a5ad95ff1b 100644
--- a/include/qemu/error-report.h
+++ b/include/qemu/error-report.h
@@ -75,5 +75,7 @@ void error_init(const char *argv0);
const char *error_get_progname(void);
extern bool error_with_timestamp;
+extern bool error_with_guestname;
+extern const char *error_guest_name;
#endif
diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h
index a6d20b0719..8e98613656 100644
--- a/include/qemu/main-loop.h
+++ b/include/qemu/main-loop.h
@@ -303,6 +303,11 @@ void qemu_mutex_unlock_iothread(void);
*/
void qemu_cond_wait_iothread(QemuCond *cond);
+/*
+ * qemu_cond_timedwait_iothread: like the previous, but with timeout
+ */
+void qemu_cond_timedwait_iothread(QemuCond *cond, int ms);
+
/* internal interfaces */
void qemu_fd_register(int fd);
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index 0d26a1b9bd..0fc206ae61 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -250,7 +250,8 @@ extern int daemon(int, int);
* Note that neither form is usable as an #if condition; if you truly
* need to write conditional code that depends on a minimum or maximum
* determined by the pre-processor instead of the compiler, you'll
- * have to open-code it.
+ * have to open-code it. Sadly, Coverity is severely confused by the
+ * constant variants, so we have to dumb things down there.
*/
#undef MIN
#define MIN(a, b) \
@@ -258,22 +259,28 @@ extern int daemon(int, int);
typeof(1 ? (a) : (b)) _a = (a), _b = (b); \
_a < _b ? _a : _b; \
})
-#define MIN_CONST(a, b) \
- __builtin_choose_expr( \
- __builtin_constant_p(a) && __builtin_constant_p(b), \
- (a) < (b) ? (a) : (b), \
- ((void)0))
#undef MAX
#define MAX(a, b) \
({ \
typeof(1 ? (a) : (b)) _a = (a), _b = (b); \
_a > _b ? _a : _b; \
})
-#define MAX_CONST(a, b) \
+
+#ifdef __COVERITY__
+# define MIN_CONST(a, b) ((a) < (b) ? (a) : (b))
+# define MAX_CONST(a, b) ((a) > (b) ? (a) : (b))
+#else
+# define MIN_CONST(a, b) \
+ __builtin_choose_expr( \
+ __builtin_constant_p(a) && __builtin_constant_p(b), \
+ (a) < (b) ? (a) : (b), \
+ ((void)0))
+# define MAX_CONST(a, b) \
__builtin_choose_expr( \
__builtin_constant_p(a) && __builtin_constant_p(b), \
(a) > (b) ? (a) : (b), \
((void)0))
+#endif
/*
* Minimum function that returns zero only if both values are zero.
diff --git a/include/qom/object.h b/include/qom/object.h
index 10fd4a2d4c..79c8f838b6 100644
--- a/include/qom/object.h
+++ b/include/qom/object.h
@@ -1047,7 +1047,7 @@ Object *object_ref(Object *obj);
void object_unref(Object *obj);
/**
- * object_property_add:
+ * object_property_try_add:
* @obj: the object to add a property to
* @name: the name of the property. This can contain any character except for
* a forward slash. In general, you should use hyphens '-' instead of
@@ -1064,10 +1064,23 @@ void object_unref(Object *obj);
* meant to allow a property to free its opaque upon object
* destruction. This may be NULL.
* @opaque: an opaque pointer to pass to the callbacks for the property
+ * @errp: pointer to error object
*
* Returns: The #ObjectProperty; this can be used to set the @resolve
* callback for child and link properties.
*/
+ObjectProperty *object_property_try_add(Object *obj, const char *name,
+ const char *type,
+ ObjectPropertyAccessor *get,
+ ObjectPropertyAccessor *set,
+ ObjectPropertyRelease *release,
+ void *opaque, Error **errp);
+
+/**
+ * object_property_add:
+ * Same as object_property_try_add() with @errp hardcoded to
+ * &error_abort.
+ */
ObjectProperty *object_property_add(Object *obj, const char *name,
const char *type,
ObjectPropertyAccessor *get,
@@ -1518,10 +1531,11 @@ Object *object_resolve_path_type(const char *path, const char *typename,
Object *object_resolve_path_component(Object *parent, const char *part);
/**
- * object_property_add_child:
+ * object_property_try_add_child:
* @obj: the object to add a property to
* @name: the name of the property
* @child: the child object
+ * @errp: pointer to error object
*
* Child properties form the composition tree. All objects need to be a child
* of another object. Objects can only be a child of one object.
@@ -1535,6 +1549,14 @@ Object *object_resolve_path_component(Object *parent, const char *part);
*
* Returns: The newly added property on success, or %NULL on failure.
*/
+ObjectProperty *object_property_try_add_child(Object *obj, const char *name,
+ Object *child, Error **errp);
+
+/**
+ * object_property_add_child:
+ * Same as object_property_try_add_child() with @errp hardcoded to
+ * &error_abort
+ */
ObjectProperty *object_property_add_child(Object *obj, const char *name,
Object *child);
diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h
index 71a7a285ee..54f069d491 100644
--- a/include/sysemu/arch_init.h
+++ b/include/sysemu/arch_init.h
@@ -25,6 +25,7 @@ enum {
QEMU_ARCH_HPPA = (1 << 18),
QEMU_ARCH_RISCV = (1 << 19),
QEMU_ARCH_RX = (1 << 20),
+ QEMU_ARCH_AVR = (1 << 21),
QEMU_ARCH_NONE = (1 << 31),
};
diff --git a/include/sysemu/cpu-throttle.h b/include/sysemu/cpu-throttle.h
new file mode 100644
index 0000000000..d65bdef6d0
--- /dev/null
+++ b/include/sysemu/cpu-throttle.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2012 SUSE LINUX Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see
+ * <http://www.gnu.org/licenses/gpl-2.0.html>
+ */
+
+#ifndef SYSEMU_CPU_THROTTLE_H
+#define SYSEMU_CPU_THROTTLE_H
+
+#include "qemu/timer.h"
+
+/**
+ * cpu_throttle_init:
+ *
+ * Initialize the CPU throttling API.
+ */
+void cpu_throttle_init(void);
+
+/**
+ * cpu_throttle_set:
+ * @new_throttle_pct: Percent of sleep time. Valid range is 1 to 99.
+ *
+ * Throttles all vcpus by forcing them to sleep for the given percentage of
+ * time. A throttle_percentage of 25 corresponds to a 75% duty cycle roughly.
+ * (example: 10ms sleep for every 30ms awake).
+ *
+ * cpu_throttle_set can be called as needed to adjust new_throttle_pct.
+ * Once the throttling starts, it will remain in effect until cpu_throttle_stop
+ * is called.
+ */
+void cpu_throttle_set(int new_throttle_pct);
+
+/**
+ * cpu_throttle_stop:
+ *
+ * Stops the vcpu throttling started by cpu_throttle_set.
+ */
+void cpu_throttle_stop(void);
+
+/**
+ * cpu_throttle_active:
+ *
+ * Returns: %true if the vcpus are currently being throttled, %false otherwise.
+ */
+bool cpu_throttle_active(void);
+
+/**
+ * cpu_throttle_get_percentage:
+ *
+ * Returns the vcpu throttle percentage. See cpu_throttle_set for details.
+ *
+ * Returns: The throttle percentage in range 1 to 99.
+ */
+int cpu_throttle_get_percentage(void);
+
+#endif /* SYSEMU_CPU_THROTTLE_H */
diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h
index 5214ed5202..6d3ee4fdb7 100644
--- a/include/sysemu/hvf.h
+++ b/include/sysemu/hvf.h
@@ -28,8 +28,8 @@ int hvf_vcpu_exec(CPUState *);
void hvf_cpu_synchronize_state(CPUState *);
void hvf_cpu_synchronize_post_reset(CPUState *);
void hvf_cpu_synchronize_post_init(CPUState *);
+void hvf_cpu_synchronize_pre_loadvm(CPUState *);
void hvf_vcpu_destroy(CPUState *);
-void hvf_reset_vcpu(CPUState *);
#define TYPE_HVF_ACCEL ACCEL_CLASS_NAME("hvf")
diff --git a/include/sysemu/hw_accel.h b/include/sysemu/hw_accel.h
index 0ec2372477..e128f8b06b 100644
--- a/include/sysemu/hw_accel.h
+++ b/include/sysemu/hw_accel.h
@@ -14,6 +14,7 @@
#include "hw/core/cpu.h"
#include "sysemu/hax.h"
#include "sysemu/kvm.h"
+#include "sysemu/hvf.h"
#include "sysemu/whpx.h"
static inline void cpu_synchronize_state(CPUState *cpu)
@@ -24,6 +25,9 @@ static inline void cpu_synchronize_state(CPUState *cpu)
if (hax_enabled()) {
hax_cpu_synchronize_state(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_state(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_state(cpu);
}
@@ -37,6 +41,9 @@ static inline void cpu_synchronize_post_reset(CPUState *cpu)
if (hax_enabled()) {
hax_cpu_synchronize_post_reset(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_post_reset(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_post_reset(cpu);
}
@@ -50,6 +57,9 @@ static inline void cpu_synchronize_post_init(CPUState *cpu)
if (hax_enabled()) {
hax_cpu_synchronize_post_init(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_post_init(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_post_init(cpu);
}
@@ -63,6 +73,9 @@ static inline void cpu_synchronize_pre_loadvm(CPUState *cpu)
if (hax_enabled()) {
hax_cpu_synchronize_pre_loadvm(cpu);
}
+ if (hvf_enabled()) {
+ hvf_cpu_synchronize_pre_loadvm(cpu);
+ }
if (whpx_enabled()) {
whpx_cpu_synchronize_pre_loadvm(cpu);
}
diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h
index 23de64e44c..57cd1f21db 100644
--- a/linux-headers/asm-arm/unistd-common.h
+++ b/linux-headers/asm-arm/unistd-common.h
@@ -392,5 +392,6 @@
#define __NR_clone3 (__NR_SYSCALL_BASE + 435)
#define __NR_openat2 (__NR_SYSCALL_BASE + 437)
#define __NR_pidfd_getfd (__NR_SYSCALL_BASE + 438)
+#define __NR_faccessat2 (__NR_SYSCALL_BASE + 439)
#endif /* _ASM_ARM_UNISTD_COMMON_H */
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index 17c5a038f4..0780f97c18 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -408,14 +408,15 @@ struct kvm_vmx_nested_state_data {
};
struct kvm_vmx_nested_state_hdr {
- __u32 flags;
__u64 vmxon_pa;
__u64 vmcs12_pa;
- __u64 preemption_timer_deadline;
struct {
__u16 flags;
} smm;
+
+ __u32 flags;
+ __u64 preemption_timer_deadline;
};
struct kvm_svm_nested_state_data {
diff --git a/migration/migration.c b/migration/migration.c
index 92e44e021e..4e658c397e 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -23,6 +23,7 @@
#include "socket.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
+#include "sysemu/cpu-throttle.h"
#include "rdma.h"
#include "ram.h"
#include "migration/global_state.h"
diff --git a/migration/ram.c b/migration/ram.c
index 5554a7d2d8..76d4fee5d5 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -52,6 +52,7 @@
#include "migration/colo.h"
#include "block.h"
#include "sysemu/sysemu.h"
+#include "sysemu/cpu-throttle.h"
#include "savevm.h"
#include "qemu/iov.h"
#include "multifd.h"
diff --git a/qapi/machine.json b/qapi/machine.json
index ff7b5032e3..f59144023c 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -17,6 +17,7 @@
# being.
#
# @rx: since 5.0
+# @avr: since 5.1
#
# Notes: The resulting QMP strings can be appended to the "qemu-system-"
# prefix to produce the corresponding QEMU executable name. This
@@ -25,7 +26,7 @@
# Since: 3.0
##
{ 'enum' : 'SysEmuTarget',
- 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32',
+ 'data' : [ 'aarch64', 'alpha', 'arm', 'avr', 'cris', 'hppa', 'i386', 'lm32',
'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64',
'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc',
'ppc64', 'riscv32', 'riscv64', 'rx', 's390x', 'sh4',
diff --git a/qemu-options.hx b/qemu-options.hx
index c6edb4047b..d2c1e95bcf 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4303,16 +4303,22 @@ HXCOMM Deprecated by -accel tcg
DEF("no-kvm", 0, QEMU_OPTION_no_kvm, "", QEMU_ARCH_I386)
DEF("msg", HAS_ARG, QEMU_OPTION_msg,
- "-msg timestamp[=on|off]\n"
+ "-msg [timestamp[=on|off]][,guest-name=[on|off]]\n"
" control error message format\n"
- " timestamp=on enables timestamps (default: off)\n",
+ " timestamp=on enables timestamps (default: off)\n"
+ " guest-name=on enables guest name prefix but only if\n"
+ " -name guest option is set (default: off)\n",
QEMU_ARCH_ALL)
SRST
-``-msg timestamp[=on|off]``
+``-msg [timestamp[=on|off]][,guest-name[=on|off]]``
Control error message format.
``timestamp=on|off``
Prefix messages with a timestamp. Default is off.
+
+ ``guest-name=on|off``
+ Prefix messages with guest name but only if -name guest option is set
+ otherwise the option is ignored. Default is off.
ERST
DEF("dump-vmstate", HAS_ARG, QEMU_OPTION_dump_vmstate,
diff --git a/qom/object.c b/qom/object.c
index 4c91de8183..76f5f75239 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1146,7 +1146,7 @@ void object_unref(Object *obj)
}
}
-static ObjectProperty *
+ObjectProperty *
object_property_try_add(Object *obj, const char *name, const char *type,
ObjectPropertyAccessor *get,
ObjectPropertyAccessor *set,
@@ -1675,8 +1675,8 @@ static void object_finalize_child_property(Object *obj, const char *name,
}
ObjectProperty *
-object_property_add_child(Object *obj, const char *name,
- Object *child)
+object_property_try_add_child(Object *obj, const char *name,
+ Object *child, Error **errp)
{
g_autofree char *type = NULL;
ObjectProperty *op;
@@ -1685,14 +1685,25 @@ object_property_add_child(Object *obj, const char *name,
type = g_strdup_printf("child<%s>", object_get_typename(child));
- op = object_property_add(obj, name, type, object_get_child_property, NULL,
- object_finalize_child_property, child);
+ op = object_property_try_add(obj, name, type, object_get_child_property,
+ NULL, object_finalize_child_property,
+ child, errp);
+ if (!op) {
+ return NULL;
+ }
op->resolve = object_resolve_child_property;
object_ref(child);
child->parent = obj;
return op;
}
+ObjectProperty *
+object_property_add_child(Object *obj, const char *name,
+ Object *child)
+{
+ return object_property_try_add_child(obj, name, child, &error_abort);
+}
+
void object_property_allow_set_link(const Object *obj, const char *name,
Object *val, Error **errp)
{
diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c
index 15fff66c3c..e8e1523960 100644
--- a/qom/object_interfaces.c
+++ b/qom/object_interfaces.c
@@ -83,8 +83,11 @@ Object *user_creatable_add_type(const char *type, const char *id,
}
if (id != NULL) {
- object_property_add_child(object_get_objects_root(),
- id, obj);
+ object_property_try_add_child(object_get_objects_root(),
+ id, obj, &local_err);
+ if (local_err) {
+ goto out;
+ }
}
if (!user_creatable_complete(USER_CREATABLE(obj), &local_err)) {
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 2d2e922d89..bd3faa154c 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -49,7 +49,7 @@ Version: $V
Options:
-q, --quiet quiet
- --no-tree run without a kernel tree
+ --no-tree run without a qemu tree
--no-signoff do not check for 'Signed-off-by' line
--patch treat FILE as patchfile
--branch treat args as GIT revision list
@@ -57,7 +57,7 @@ Options:
--terse one line per report
-f, --file treat FILE as regular source file
--strict fail if only warnings are found
- --root=PATH PATH to the kernel tree root
+ --root=PATH PATH to the qemu tree root
--no-summary suppress the per-file summary
--mailback only produce a report in case of warnings/errors
--summary-file include the filename in summary
@@ -203,7 +203,7 @@ if ($tree) {
}
if (!defined $root) {
- print "Must be run from the top-level dir. of a kernel tree\n";
+ print "Must be run from the top-level dir. of a qemu tree\n";
exit(2);
}
}
diff --git a/scripts/tap-driver.pl b/scripts/tap-driver.pl
index 6621a5cd67..b1d3880c50 100755
--- a/scripts/tap-driver.pl
+++ b/scripts/tap-driver.pl
@@ -217,7 +217,7 @@ sub report ($;$)
sub testsuite_error ($)
{
- report "ERROR", "- $_[0]";
+ report "ERROR", "$test_name - $_[0]";
}
sub handle_tap_result ($)
diff --git a/softmmu/Makefile.objs b/softmmu/Makefile.objs
index dd15c24346..a414a74c50 100644
--- a/softmmu/Makefile.objs
+++ b/softmmu/Makefile.objs
@@ -1,3 +1,14 @@
softmmu-main-y = softmmu/main.o
+
+obj-y += arch_init.o
+obj-y += cpus.o
+obj-y += cpu-throttle.o
+obj-y += balloon.o
+obj-y += ioport.o
+obj-y += memory.o
+obj-y += memory_mapping.o
+
+obj-y += qtest.o
+
obj-y += vl.o
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
diff --git a/arch_init.c b/softmmu/arch_init.c
index 8afea4748b..7fd5c09b2b 100644
--- a/arch_init.c
+++ b/softmmu/arch_init.c
@@ -90,6 +90,8 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_XTENSA)
#define QEMU_ARCH QEMU_ARCH_XTENSA
+#elif defined(TARGET_AVR)
+#define QEMU_ARCH QEMU_ARCH_AVR
#endif
const uint32_t arch_type = QEMU_ARCH;
diff --git a/balloon.c b/softmmu/balloon.c
index 354408c6ea..354408c6ea 100644
--- a/balloon.c
+++ b/softmmu/balloon.c
diff --git a/softmmu/cpu-throttle.c b/softmmu/cpu-throttle.c
new file mode 100644
index 0000000000..4e6b2818ca
--- /dev/null
+++ b/softmmu/cpu-throttle.c
@@ -0,0 +1,122 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2003-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/thread.h"
+#include "hw/core/cpu.h"
+#include "qemu/main-loop.h"
+#include "sysemu/cpus.h"
+#include "sysemu/cpu-throttle.h"
+
+/* vcpu throttling controls */
+static QEMUTimer *throttle_timer;
+static unsigned int throttle_percentage;
+
+#define CPU_THROTTLE_PCT_MIN 1
+#define CPU_THROTTLE_PCT_MAX 99
+#define CPU_THROTTLE_TIMESLICE_NS 10000000
+
+static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque)
+{
+ double pct;
+ double throttle_ratio;
+ int64_t sleeptime_ns, endtime_ns;
+
+ if (!cpu_throttle_get_percentage()) {
+ return;
+ }
+
+ pct = (double)cpu_throttle_get_percentage() / 100;
+ throttle_ratio = pct / (1 - pct);
+ /* Add 1ns to fix double's rounding error (like 0.9999999...) */
+ sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1);
+ endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns;
+ while (sleeptime_ns > 0 && !cpu->stop) {
+ if (sleeptime_ns > SCALE_MS) {
+ qemu_cond_timedwait_iothread(cpu->halt_cond,
+ sleeptime_ns / SCALE_MS);
+ } else {
+ qemu_mutex_unlock_iothread();
+ g_usleep(sleeptime_ns / SCALE_US);
+ qemu_mutex_lock_iothread();
+ }
+ sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ }
+ atomic_set(&cpu->throttle_thread_scheduled, 0);
+}
+
+static void cpu_throttle_timer_tick(void *opaque)
+{
+ CPUState *cpu;
+ double pct;
+
+ /* Stop the timer if needed */
+ if (!cpu_throttle_get_percentage()) {
+ return;
+ }
+ CPU_FOREACH(cpu) {
+ if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) {
+ async_run_on_cpu(cpu, cpu_throttle_thread,
+ RUN_ON_CPU_NULL);
+ }
+ }
+
+ pct = (double)cpu_throttle_get_percentage() / 100;
+ timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
+ CPU_THROTTLE_TIMESLICE_NS / (1 - pct));
+}
+
+void cpu_throttle_set(int new_throttle_pct)
+{
+ /* Ensure throttle percentage is within valid range */
+ new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX);
+ new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN);
+
+ atomic_set(&throttle_percentage, new_throttle_pct);
+
+ timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
+ CPU_THROTTLE_TIMESLICE_NS);
+}
+
+void cpu_throttle_stop(void)
+{
+ atomic_set(&throttle_percentage, 0);
+}
+
+bool cpu_throttle_active(void)
+{
+ return (cpu_throttle_get_percentage() != 0);
+}
+
+int cpu_throttle_get_percentage(void)
+{
+ return atomic_read(&throttle_percentage);
+}
+
+void cpu_throttle_init(void)
+{
+ throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
+ cpu_throttle_timer_tick, NULL);
+}
diff --git a/cpus.c b/softmmu/cpus.c
index 41d1c5099f..a802e899ab 100644
--- a/cpus.c
+++ b/softmmu/cpus.c
@@ -61,6 +61,8 @@
#include "hw/boards.h"
#include "hw/hw.h"
+#include "sysemu/cpu-throttle.h"
+
#ifdef CONFIG_LINUX
#include <sys/prctl.h>
@@ -84,14 +86,6 @@ static QemuMutex qemu_global_mutex;
int64_t max_delay;
int64_t max_advance;
-/* vcpu throttling controls */
-static QEMUTimer *throttle_timer;
-static unsigned int throttle_percentage;
-
-#define CPU_THROTTLE_PCT_MIN 1
-#define CPU_THROTTLE_PCT_MAX 99
-#define CPU_THROTTLE_TIMESLICE_NS 10000000
-
bool cpu_is_stopped(CPUState *cpu)
{
return cpu->stopped || !runstate_is_running();
@@ -738,90 +732,12 @@ static const VMStateDescription vmstate_timers = {
}
};
-static void cpu_throttle_thread(CPUState *cpu, run_on_cpu_data opaque)
-{
- double pct;
- double throttle_ratio;
- int64_t sleeptime_ns, endtime_ns;
-
- if (!cpu_throttle_get_percentage()) {
- return;
- }
-
- pct = (double)cpu_throttle_get_percentage()/100;
- throttle_ratio = pct / (1 - pct);
- /* Add 1ns to fix double's rounding error (like 0.9999999...) */
- sleeptime_ns = (int64_t)(throttle_ratio * CPU_THROTTLE_TIMESLICE_NS + 1);
- endtime_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + sleeptime_ns;
- while (sleeptime_ns > 0 && !cpu->stop) {
- if (sleeptime_ns > SCALE_MS) {
- qemu_cond_timedwait(cpu->halt_cond, &qemu_global_mutex,
- sleeptime_ns / SCALE_MS);
- } else {
- qemu_mutex_unlock_iothread();
- g_usleep(sleeptime_ns / SCALE_US);
- qemu_mutex_lock_iothread();
- }
- sleeptime_ns = endtime_ns - qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- }
- atomic_set(&cpu->throttle_thread_scheduled, 0);
-}
-
-static void cpu_throttle_timer_tick(void *opaque)
-{
- CPUState *cpu;
- double pct;
-
- /* Stop the timer if needed */
- if (!cpu_throttle_get_percentage()) {
- return;
- }
- CPU_FOREACH(cpu) {
- if (!atomic_xchg(&cpu->throttle_thread_scheduled, 1)) {
- async_run_on_cpu(cpu, cpu_throttle_thread,
- RUN_ON_CPU_NULL);
- }
- }
-
- pct = (double)cpu_throttle_get_percentage()/100;
- timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
- CPU_THROTTLE_TIMESLICE_NS / (1-pct));
-}
-
-void cpu_throttle_set(int new_throttle_pct)
-{
- /* Ensure throttle percentage is within valid range */
- new_throttle_pct = MIN(new_throttle_pct, CPU_THROTTLE_PCT_MAX);
- new_throttle_pct = MAX(new_throttle_pct, CPU_THROTTLE_PCT_MIN);
-
- atomic_set(&throttle_percentage, new_throttle_pct);
-
- timer_mod(throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL_RT) +
- CPU_THROTTLE_TIMESLICE_NS);
-}
-
-void cpu_throttle_stop(void)
-{
- atomic_set(&throttle_percentage, 0);
-}
-
-bool cpu_throttle_active(void)
-{
- return (cpu_throttle_get_percentage() != 0);
-}
-
-int cpu_throttle_get_percentage(void)
-{
- return atomic_read(&throttle_percentage);
-}
-
void cpu_ticks_init(void)
{
seqlock_init(&timers_state.vm_clock_seqlock);
qemu_spin_init(&timers_state.vm_clock_lock);
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
- throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
- cpu_throttle_timer_tick, NULL);
+ cpu_throttle_init();
}
void configure_icount(QemuOpts *opts, Error **errp)
@@ -1017,10 +933,6 @@ void cpu_synchronize_all_states(void)
CPU_FOREACH(cpu) {
cpu_synchronize_state(cpu);
- /* TODO: move to cpu_synchronize_state() */
- if (hvf_enabled()) {
- hvf_cpu_synchronize_state(cpu);
- }
}
}
@@ -1030,10 +942,6 @@ void cpu_synchronize_all_post_reset(void)
CPU_FOREACH(cpu) {
cpu_synchronize_post_reset(cpu);
- /* TODO: move to cpu_synchronize_post_reset() */
- if (hvf_enabled()) {
- hvf_cpu_synchronize_post_reset(cpu);
- }
}
}
@@ -1043,10 +951,6 @@ void cpu_synchronize_all_post_init(void)
CPU_FOREACH(cpu) {
cpu_synchronize_post_init(cpu);
- /* TODO: move to cpu_synchronize_post_init() */
- if (hvf_enabled()) {
- hvf_cpu_synchronize_post_init(cpu);
- }
}
}
@@ -1891,6 +1795,11 @@ void qemu_cond_wait_iothread(QemuCond *cond)
qemu_cond_wait(cond, &qemu_global_mutex);
}
+void qemu_cond_timedwait_iothread(QemuCond *cond, int ms)
+{
+ qemu_cond_timedwait(cond, &qemu_global_mutex, ms);
+}
+
static bool all_vcpus_paused(void)
{
CPUState *cpu;
diff --git a/ioport.c b/softmmu/ioport.c
index 04e360e79a..04e360e79a 100644
--- a/ioport.c
+++ b/softmmu/ioport.c
diff --git a/memory.c b/softmmu/memory.c
index 9200b20130..9200b20130 100644
--- a/memory.c
+++ b/softmmu/memory.c
diff --git a/memory_mapping.c b/softmmu/memory_mapping.c
index 18d0b8067c..18d0b8067c 100644
--- a/memory_mapping.c
+++ b/softmmu/memory_mapping.c
diff --git a/qtest.c b/softmmu/qtest.c
index 5672b75c35..5672b75c35 100644
--- a/qtest.c
+++ b/softmmu/qtest.c
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 96417a7c3a..f476ef89ed 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -389,6 +389,12 @@ static QemuOptsList qemu_msg_opts = {
.name = "timestamp",
.type = QEMU_OPT_BOOL,
},
+ {
+ .name = "guest-name",
+ .type = QEMU_OPT_BOOL,
+ .help = "Prepends guest name for error messages but only if "
+ "-name guest is set otherwise option is ignored\n",
+ },
{ /* end of list */ }
},
};
@@ -1114,6 +1120,7 @@ static void realtime_init(void)
static void configure_msg(QemuOpts *opts)
{
error_with_timestamp = qemu_opt_get_bool(opts, "timestamp", false);
+ error_with_guestname = qemu_opt_get_bool(opts, "guest-name", false);
}
@@ -3499,11 +3506,6 @@ void qemu_init(int argc, char **argv, char **envp)
g_slist_free(accel_list);
exit(0);
}
- if (optarg && strchr(optarg, ':')) {
- error_report("Don't use ':' with -accel, "
- "use -M accel=... for now instead");
- exit(1);
- }
break;
case QEMU_OPTION_usb:
olist = qemu_find_opts("machine");
@@ -3592,6 +3594,8 @@ void qemu_init(int argc, char **argv, char **envp)
if (!opts) {
exit(1);
}
+ /* Capture guest name if -msg guest-name is used later */
+ error_guest_name = qemu_opt_get(opts, "guest");
break;
case QEMU_OPTION_prom_env:
if (nb_prom_envs >= MAX_PROM_ENVS) {
diff --git a/target/avr/Makefile.objs b/target/avr/Makefile.objs
new file mode 100644
index 0000000000..6e35ba2c5c
--- /dev/null
+++ b/target/avr/Makefile.objs
@@ -0,0 +1,34 @@
+#
+# QEMU AVR
+#
+# Copyright (c) 2016-2020 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>
+#
+
+DECODETREE = $(SRC_PATH)/scripts/decodetree.py
+decode-y = $(SRC_PATH)/target/avr/insn.decode
+
+target/avr/decode_insn.inc.c: $(decode-y) $(DECODETREE)
+ $(call quiet-command, \
+ $(PYTHON) $(DECODETREE) -o $@ --decode decode_insn --insnwidth 16 $<, \
+ "GEN", $(TARGET_DIR)$@)
+
+target/avr/translate.o: target/avr/decode_insn.inc.c
+
+obj-y += translate.o cpu.o helper.o
+obj-y += gdbstub.o
+obj-y += disas.o
+obj-$(CONFIG_SOFTMMU) += machine.o
diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h
new file mode 100644
index 0000000000..7ef4e7c679
--- /dev/null
+++ b/target/avr/cpu-param.h
@@ -0,0 +1,36 @@
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 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 AVR_CPU_PARAM_H
+#define AVR_CPU_PARAM_H
+
+#define TARGET_LONG_BITS 32
+/*
+ * 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
+
+#endif
diff --git a/target/avr/cpu-qom.h b/target/avr/cpu-qom.h
new file mode 100644
index 0000000000..d23ad43a99
--- /dev/null
+++ b/target/avr/cpu-qom.h
@@ -0,0 +1,53 @@
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 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_QOM_H
+#define QEMU_AVR_QOM_H
+
+#include "hw/core/cpu.h"
+
+#define TYPE_AVR_CPU "avr-cpu"
+
+#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 {
+ /*< private >*/
+ CPUClass parent_class;
+ /*< public >*/
+ DeviceRealize parent_realize;
+ DeviceReset parent_reset;
+} AVRCPUClass;
+
+typedef struct AVRCPU AVRCPU;
+
+#endif /* !defined (QEMU_AVR_CPU_QOM_H) */
diff --git a/target/avr/cpu.c b/target/avr/cpu.c
new file mode 100644
index 0000000000..5d9c4ad5bf
--- /dev/null
+++ b/target/avr/cpu.c
@@ -0,0 +1,366 @@
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2019-2020 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 "qemu/qemu-print.h"
+#include "exec/exec-all.h"
+#include "cpu.h"
+#include "disas/dis-asm.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(DeviceState *ds)
+{
+ CPUState *cs = CPU(ds);
+ AVRCPU *cpu = AVR_CPU(cs);
+ AVRCPUClass *mcc = AVR_CPU_GET_CLASS(cpu);
+ CPUAVRState *env = &cpu->env;
+
+ mcc->parent_reset(ds);
+
+ 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;
+
+ env->skip = 0;
+
+ memset(env->r, 0, sizeof(env->r));
+}
+
+static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info)
+{
+ info->mach = bfd_arch_avr;
+ info->print_insn = avr_print_insn;
+}
+
+static void avr_cpu_realizefn(DeviceState *dev, Error **errp)
+{
+ CPUState *cs = CPU(dev);
+ AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev);
+ Error *local_err = NULL;
+
+ cpu_exec_realizefn(cs, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ 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)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+
+ cpu_set_cpustate_pointers(cpu);
+
+ /* Set the number of interrupts supported by the CPU. */
+ qdev_init_gpio_in(DEVICE(cpu), avr_cpu_set_int,
+ sizeof(cpu->env.intsrc) * 8);
+}
+
+static ObjectClass *avr_cpu_class_by_name(const char *cpu_model)
+{
+ ObjectClass *oc;
+
+ oc = object_class_by_name(cpu_model);
+ if (object_class_dynamic_cast(oc, TYPE_AVR_CPU) == NULL ||
+ object_class_is_abstract(oc)) {
+ oc = NULL;
+ }
+ return oc;
+}
+
+static void avr_cpu_dump_state(CPUState *cs, FILE *f, int flags)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+ int i;
+
+ qemu_fprintf(f, "\n");
+ qemu_fprintf(f, "PC: %06x\n", env->pc_w * 2); /* PC points to words */
+ qemu_fprintf(f, "SP: %04x\n", env->sp);
+ qemu_fprintf(f, "rampD: %02x\n", env->rampD >> 16);
+ qemu_fprintf(f, "rampX: %02x\n", env->rampX >> 16);
+ qemu_fprintf(f, "rampY: %02x\n", env->rampY >> 16);
+ qemu_fprintf(f, "rampZ: %02x\n", env->rampZ >> 16);
+ qemu_fprintf(f, "EIND: %02x\n", env->eind >> 16);
+ qemu_fprintf(f, "X: %02x%02x\n", env->r[27], env->r[26]);
+ qemu_fprintf(f, "Y: %02x%02x\n", env->r[29], env->r[28]);
+ qemu_fprintf(f, "Z: %02x%02x\n", env->r[31], env->r[30]);
+ qemu_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' : '-');
+ qemu_fprintf(f, "SKIP: %02x\n", env->skip);
+
+ qemu_fprintf(f, "\n");
+ for (i = 0; i < ARRAY_SIZE(env->r); i++) {
+ qemu_fprintf(f, "R[%02d]: %02x ", i, env->r[i]);
+
+ if ((i % 8) == 7) {
+ qemu_fprintf(f, "\n");
+ }
+ }
+ qemu_fprintf(f, "\n");
+}
+
+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;
+
+ device_class_set_parent_reset(dc, avr_cpu_reset, &mcc->parent_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;
+ cc->memory_rw_debug = avr_cpu_memory_rw_debug;
+ cc->get_phys_page_debug = avr_cpu_get_phys_page_debug;
+ cc->tlb_fill = avr_cpu_tlb_fill;
+ cc->vmsd = &vms_avr_cpu;
+ cc->disas_set_info = avr_cpu_disas_set_info;
+ cc->tcg_initialize = avr_cpu_tcg_init;
+ 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;
+ cc->gdb_core_xml_file = "avr-cpu.xml";
+}
+
+/*
+ * Setting features of AVR core type avr5
+ * --------------------------------------
+ *
+ * This type of AVR core is present in the following AVR MCUs:
+ *
+ * ata5702m322, ata5782, ata5790, ata5790n, ata5791, ata5795, ata5831, ata6613c,
+ * ata6614q, ata8210, ata8510, atmega16, atmega16a, atmega161, atmega162,
+ * atmega163, atmega164a, atmega164p, atmega164pa, atmega165, atmega165a,
+ * atmega165p, atmega165pa, atmega168, atmega168a, atmega168p, atmega168pa,
+ * atmega168pb, atmega169, atmega169a, atmega169p, atmega169pa, atmega16hvb,
+ * atmega16hvbrevb, atmega16m1, atmega16u4, atmega32a, atmega32, atmega323,
+ * atmega324a, atmega324p, atmega324pa, atmega325, atmega325a, atmega325p,
+ * atmega325pa, atmega3250, atmega3250a, atmega3250p, atmega3250pa, atmega328,
+ * atmega328p, atmega328pb, atmega329, atmega329a, atmega329p, atmega329pa,
+ * atmega3290, atmega3290a, atmega3290p, atmega3290pa, atmega32c1, atmega32m1,
+ * atmega32u4, atmega32u6, atmega406, atmega64, atmega64a, atmega640, atmega644,
+ * atmega644a, atmega644p, atmega644pa, atmega645, atmega645a, atmega645p,
+ * atmega6450, atmega6450a, atmega6450p, atmega649, atmega649a, atmega649p,
+ * atmega6490, atmega16hva, atmega16hva2, atmega32hvb, atmega6490a, atmega6490p,
+ * atmega64c1, atmega64m1, atmega64hve, atmega64hve2, atmega64rfr2,
+ * atmega644rfr2, atmega32hvbrevb, at90can32, at90can64, at90pwm161, at90pwm216,
+ * at90pwm316, at90scr100, at90usb646, at90usb647, at94k, m3000
+ */
+static void avr_avr5_initfn(Object *obj)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+ CPUAVRState *env = &cpu->env;
+
+ set_avr_feature(env, AVR_FEATURE_LPM);
+ set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
+ set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
+ set_avr_feature(env, AVR_FEATURE_SRAM);
+ set_avr_feature(env, AVR_FEATURE_BREAK);
+
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_PC);
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
+ set_avr_feature(env, AVR_FEATURE_JMP_CALL);
+ set_avr_feature(env, AVR_FEATURE_LPMX);
+ set_avr_feature(env, AVR_FEATURE_MOVW);
+ set_avr_feature(env, AVR_FEATURE_MUL);
+}
+
+/*
+ * Setting features of AVR core type avr51
+ * --------------------------------------
+ *
+ * This type of AVR core is present in the following AVR MCUs:
+ *
+ * atmega128, atmega128a, atmega1280, atmega1281, atmega1284, atmega1284p,
+ * atmega128rfa1, atmega128rfr2, atmega1284rfr2, at90can128, at90usb1286,
+ * at90usb1287
+ */
+static void avr_avr51_initfn(Object *obj)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+ CPUAVRState *env = &cpu->env;
+
+ set_avr_feature(env, AVR_FEATURE_LPM);
+ set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
+ set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
+ set_avr_feature(env, AVR_FEATURE_SRAM);
+ set_avr_feature(env, AVR_FEATURE_BREAK);
+
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_PC);
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
+ set_avr_feature(env, AVR_FEATURE_RAMPZ);
+ set_avr_feature(env, AVR_FEATURE_ELPMX);
+ set_avr_feature(env, AVR_FEATURE_ELPM);
+ set_avr_feature(env, AVR_FEATURE_JMP_CALL);
+ set_avr_feature(env, AVR_FEATURE_LPMX);
+ set_avr_feature(env, AVR_FEATURE_MOVW);
+ set_avr_feature(env, AVR_FEATURE_MUL);
+}
+
+/*
+ * Setting features of AVR core type avr6
+ * --------------------------------------
+ *
+ * This type of AVR core is present in the following AVR MCUs:
+ *
+ * atmega2560, atmega2561, atmega256rfr2, atmega2564rfr2
+ */
+static void avr_avr6_initfn(Object *obj)
+{
+ AVRCPU *cpu = AVR_CPU(obj);
+ CPUAVRState *env = &cpu->env;
+
+ set_avr_feature(env, AVR_FEATURE_LPM);
+ set_avr_feature(env, AVR_FEATURE_IJMP_ICALL);
+ set_avr_feature(env, AVR_FEATURE_ADIW_SBIW);
+ set_avr_feature(env, AVR_FEATURE_SRAM);
+ set_avr_feature(env, AVR_FEATURE_BREAK);
+
+ set_avr_feature(env, AVR_FEATURE_3_BYTE_PC);
+ set_avr_feature(env, AVR_FEATURE_2_BYTE_SP);
+ set_avr_feature(env, AVR_FEATURE_RAMPZ);
+ set_avr_feature(env, AVR_FEATURE_EIJMP_EICALL);
+ set_avr_feature(env, AVR_FEATURE_ELPMX);
+ set_avr_feature(env, AVR_FEATURE_ELPM);
+ set_avr_feature(env, AVR_FEATURE_JMP_CALL);
+ set_avr_feature(env, AVR_FEATURE_LPMX);
+ set_avr_feature(env, AVR_FEATURE_MOVW);
+ set_avr_feature(env, AVR_FEATURE_MUL);
+}
+
+typedef struct AVRCPUInfo {
+ const char *name;
+ void (*initfn)(Object *obj);
+} AVRCPUInfo;
+
+
+static void avr_cpu_list_entry(gpointer data, gpointer user_data)
+{
+ const char *typename = object_class_get_name(OBJECT_CLASS(data));
+
+ qemu_printf("%s\n", typename);
+}
+
+void avr_cpu_list(void)
+{
+ GSList *list;
+ list = object_class_get_list_sorted(TYPE_AVR_CPU, false);
+ g_slist_foreach(list, avr_cpu_list_entry, NULL);
+ g_slist_free(list);
+}
+
+#define DEFINE_AVR_CPU_TYPE(model, initfn) \
+ { \
+ .parent = TYPE_AVR_CPU, \
+ .instance_init = initfn, \
+ .name = AVR_CPU_TYPE_NAME(model), \
+ }
+
+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,
+ },
+ DEFINE_AVR_CPU_TYPE("avr5", avr_avr5_initfn),
+ DEFINE_AVR_CPU_TYPE("avr51", avr_avr51_initfn),
+ DEFINE_AVR_CPU_TYPE("avr6", avr_avr6_initfn),
+};
+
+DEFINE_TYPES(avr_cpu_type_info)
diff --git a/target/avr/cpu.h b/target/avr/cpu.h
new file mode 100644
index 0000000000..d148e8c75a
--- /dev/null
+++ b/target/avr/cpu.h
@@ -0,0 +1,256 @@
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 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_H
+#define QEMU_AVR_CPU_H
+
+#include "cpu-qom.h"
+#include "exec/cpu-defs.h"
+
+#ifdef CONFIG_USER_ONLY
+#error "AVR 8-bit does not support user mode"
+#endif
+
+#define AVR_CPU_TYPE_SUFFIX "-" TYPE_AVR_CPU
+#define AVR_CPU_TYPE_NAME(name) (name AVR_CPU_TYPE_SUFFIX)
+#define CPU_RESOLVING_TYPE TYPE_AVR_CPU
+
+#define TCG_GUEST_DEFAULT_MO 0
+
+/*
+ * AVR has two memory spaces, data & code.
+ * e.g. both have 0 address
+ * ST/LD instructions access data space
+ * LPM/SPM and instruction fetching access code memory space
+ */
+#define MMU_CODE_IDX 0
+#define MMU_DATA_IDX 1
+
+#define EXCP_RESET 1
+#define EXCP_INT(n) (EXCP_RESET + (n) + 1)
+
+/* Number of CPU registers */
+#define NUMBER_OF_CPU_REGISTERS 32
+/* Number of IO registers accessible by ld/st/in/out */
+#define NUMBER_OF_IO_REGISTERS 64
+
+/*
+ * Offsets of AVR memory regions in host memory space.
+ *
+ * This is needed because the AVR has separate code and data address
+ * spaces that both have start from zero but have to go somewhere in
+ * host memory.
+ *
+ * It's also useful to know where some things are, like the IO registers.
+ */
+/* Flash program memory */
+#define OFFSET_CODE 0x00000000
+/* CPU registers, IO registers, and SRAM */
+#define OFFSET_DATA 0x00800000
+/* CPU registers specifically, these are mapped at the start of data */
+#define OFFSET_CPU_REGISTERS OFFSET_DATA
+/*
+ * IO registers, including status register, stack pointer, and memory
+ * mapped peripherals, mapped just after CPU registers
+ */
+#define OFFSET_IO_REGISTERS (OFFSET_DATA + NUMBER_OF_CPU_REGISTERS)
+
+typedef enum AVRFeature {
+ AVR_FEATURE_SRAM,
+
+ AVR_FEATURE_1_BYTE_PC,
+ AVR_FEATURE_2_BYTE_PC,
+ AVR_FEATURE_3_BYTE_PC,
+
+ AVR_FEATURE_1_BYTE_SP,
+ AVR_FEATURE_2_BYTE_SP,
+
+ AVR_FEATURE_BREAK,
+ AVR_FEATURE_DES,
+ AVR_FEATURE_RMW, /* Read Modify Write - XCH LAC LAS LAT */
+
+ AVR_FEATURE_EIJMP_EICALL,
+ AVR_FEATURE_IJMP_ICALL,
+ AVR_FEATURE_JMP_CALL,
+
+ AVR_FEATURE_ADIW_SBIW,
+
+ AVR_FEATURE_SPM,
+ AVR_FEATURE_SPMX,
+
+ AVR_FEATURE_ELPMX,
+ AVR_FEATURE_ELPM,
+ AVR_FEATURE_LPMX,
+ AVR_FEATURE_LPM,
+
+ AVR_FEATURE_MOVW,
+ AVR_FEATURE_MUL,
+ AVR_FEATURE_RAMPD,
+ AVR_FEATURE_RAMPX,
+ AVR_FEATURE_RAMPY,
+ AVR_FEATURE_RAMPZ,
+} AVRFeature;
+
+typedef struct CPUAVRState CPUAVRState;
+
+struct CPUAVRState {
+ uint32_t pc_w; /* 0x003fffff up to 22 bits */
+
+ uint32_t sregC; /* 0x00000001 1 bit */
+ uint32_t sregZ; /* 0x00000001 1 bit */
+ uint32_t sregN; /* 0x00000001 1 bit */
+ uint32_t sregV; /* 0x00000001 1 bit */
+ uint32_t sregS; /* 0x00000001 1 bit */
+ uint32_t sregH; /* 0x00000001 1 bit */
+ uint32_t sregT; /* 0x00000001 1 bit */
+ uint32_t sregI; /* 0x00000001 1 bit */
+
+ 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 r[NUMBER_OF_CPU_REGISTERS]; /* 8 bits each */
+ uint32_t sp; /* 16 bits */
+
+ uint32_t skip; /* if set skip instruction */
+
+ uint64_t intsrc; /* interrupt sources */
+ bool fullacc; /* CPU/MEM if true MEM only otherwise */
+
+ uint64_t features;
+};
+
+/**
+ * AVRCPU:
+ * @env: #CPUAVRState
+ *
+ * A AVR CPU.
+ */
+typedef struct AVRCPU {
+ /*< private >*/
+ CPUState parent_obj;
+ /*< public >*/
+
+ CPUNegativeOffsetState neg;
+ CPUAVRState env;
+} AVRCPU;
+
+extern const struct VMStateDescription vms_avr_cpu;
+
+void avr_cpu_do_interrupt(CPUState *cpu);
+bool avr_cpu_exec_interrupt(CPUState *cpu, int int_req);
+hwaddr avr_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
+int avr_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg);
+int avr_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
+int avr_print_insn(bfd_vma addr, disassemble_info *info);
+
+static inline int avr_feature(CPUAVRState *env, AVRFeature feature)
+{
+ return (env->features & (1U << feature)) != 0;
+}
+
+static inline void set_avr_feature(CPUAVRState *env, int feature)
+{
+ env->features |= (1U << feature);
+}
+
+#define cpu_list avr_cpu_list
+#define cpu_signal_handler cpu_avr_signal_handler
+#define cpu_mmu_index avr_cpu_mmu_index
+
+static inline int avr_cpu_mmu_index(CPUAVRState *env, bool ifetch)
+{
+ return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX;
+}
+
+void avr_cpu_tcg_init(void);
+
+void avr_cpu_list(void);
+int cpu_avr_exec(CPUState *cpu);
+int cpu_avr_signal_handler(int host_signum, void *pinfo, void *puc);
+int avr_cpu_memory_rw_debug(CPUState *cs, vaddr address, uint8_t *buf,
+ int len, bool is_write);
+
+enum {
+ TB_FLAGS_FULL_ACCESS = 1,
+ TB_FLAGS_SKIP = 2,
+};
+
+static inline void cpu_get_tb_cpu_state(CPUAVRState *env, target_ulong *pc,
+ target_ulong *cs_base, uint32_t *pflags)
+{
+ uint32_t flags = 0;
+
+ *pc = env->pc_w * 2;
+ *cs_base = 0;
+
+ if (env->fullacc) {
+ flags |= TB_FLAGS_FULL_ACCESS;
+ }
+ if (env->skip) {
+ flags |= TB_FLAGS_SKIP;
+ }
+
+ *pflags = flags;
+}
+
+static inline int cpu_interrupts_enabled(CPUAVRState *env)
+{
+ return env->sregI != 0;
+}
+
+static inline uint8_t cpu_get_sreg(CPUAVRState *env)
+{
+ uint8_t sreg;
+ sreg = (env->sregC) << 0
+ | (env->sregZ) << 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;
+ 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;
+}
+
+bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr);
+
+typedef CPUAVRState CPUArchState;
+typedef AVRCPU ArchCPU;
+
+#include "exec/cpu-all.h"
+
+#endif /* !defined (QEMU_AVR_CPU_H) */
diff --git a/target/avr/disas.c b/target/avr/disas.c
new file mode 100644
index 0000000000..8e1bac4d76
--- /dev/null
+++ b/target/avr/disas.c
@@ -0,0 +1,245 @@
+/*
+ * AVR disassembler
+ *
+ * Copyright (c) 2019-2020 Richard Henderson <rth@twiddle.net>
+ * Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+typedef struct {
+ disassemble_info *info;
+ uint16_t next_word;
+ bool next_word_used;
+} DisasContext;
+
+static int to_regs_16_31_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 16);
+}
+
+static int to_regs_16_23_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 8);
+}
+
+static int to_regs_24_30_by_two(DisasContext *ctx, int indx)
+{
+ return 24 + (indx % 4) * 2;
+}
+
+static int to_regs_00_30_by_two(DisasContext *ctx, int indx)
+{
+ return (indx % 16) * 2;
+}
+
+static uint16_t next_word(DisasContext *ctx)
+{
+ ctx->next_word_used = true;
+ return ctx->next_word;
+}
+
+static int append_16(DisasContext *ctx, int x)
+{
+ return x << 16 | next_word(ctx);
+}
+
+/* Include the auto-generated decoder. */
+static bool decode_insn(DisasContext *ctx, uint16_t insn);
+#include "decode_insn.inc.c"
+
+#define output(mnemonic, format, ...) \
+ (pctx->info->fprintf_func(pctx->info->stream, "%-9s " format, \
+ mnemonic, ##__VA_ARGS__))
+
+int avr_print_insn(bfd_vma addr, disassemble_info *info)
+{
+ DisasContext ctx;
+ DisasContext *pctx = &ctx;
+ bfd_byte buffer[4];
+ uint16_t insn;
+ int status;
+
+ ctx.info = info;
+
+ status = info->read_memory_func(addr, buffer, 4, info);
+ if (status != 0) {
+ info->memory_error_func(status, addr, info);
+ return -1;
+ }
+ insn = bfd_getl16(buffer);
+ ctx.next_word = bfd_getl16(buffer + 2);
+ ctx.next_word_used = false;
+
+ if (!decode_insn(&ctx, insn)) {
+ output(".db", "0x%02x, 0x%02x", buffer[0], buffer[1]);
+ }
+
+ return ctx.next_word_used ? 4 : 2;
+}
+
+
+#define INSN(opcode, format, ...) \
+static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
+{ \
+ output(#opcode, format, ##__VA_ARGS__); \
+ return true; \
+}
+
+#define INSN_MNEMONIC(opcode, mnemonic, format, ...) \
+static bool trans_##opcode(DisasContext *pctx, arg_##opcode * a) \
+{ \
+ output(mnemonic, format, ##__VA_ARGS__); \
+ return true; \
+}
+
+/*
+ * C Z N V S H T I
+ * 0 1 2 3 4 5 6 7
+ */
+static const char brbc[][5] = {
+ "BRCC", "BRNE", "BRPL", "BRVC", "BRGE", "BRHC", "BRTC", "BRID"
+};
+
+static const char brbs[][5] = {
+ "BRCS", "BREQ", "BRMI", "BRVS", "BRLT", "BRHS", "BRTS", "BRIE"
+};
+
+static const char bset[][4] = {
+ "SEC", "SEZ", "SEN", "SEZ", "SES", "SEH", "SET", "SEI"
+};
+
+static const char bclr[][4] = {
+ "CLC", "CLZ", "CLN", "CLZ", "CLS", "CLH", "CLT", "CLI"
+};
+
+/*
+ * Arithmetic Instructions
+ */
+INSN(ADD, "r%d, r%d", a->rd, a->rr)
+INSN(ADC, "r%d, r%d", a->rd, a->rr)
+INSN(ADIW, "r%d:r%d, %d", a->rd + 1, a->rd, a->imm)
+INSN(SUB, "r%d, r%d", a->rd, a->rr)
+INSN(SUBI, "r%d, %d", a->rd, a->imm)
+INSN(SBC, "r%d, r%d", a->rd, a->rr)
+INSN(SBCI, "r%d, %d", a->rd, a->imm)
+INSN(SBIW, "r%d:r%d, %d", a->rd + 1, a->rd, a->imm)
+INSN(AND, "r%d, r%d", a->rd, a->rr)
+INSN(ANDI, "r%d, %d", a->rd, a->imm)
+INSN(OR, "r%d, r%d", a->rd, a->rr)
+INSN(ORI, "r%d, %d", a->rd, a->imm)
+INSN(EOR, "r%d, r%d", a->rd, a->rr)
+INSN(COM, "r%d", a->rd)
+INSN(NEG, "r%d", a->rd)
+INSN(INC, "r%d", a->rd)
+INSN(DEC, "r%d", a->rd)
+INSN(MUL, "r%d, r%d", a->rd, a->rr)
+INSN(MULS, "r%d, r%d", a->rd, a->rr)
+INSN(MULSU, "r%d, r%d", a->rd, a->rr)
+INSN(FMUL, "r%d, r%d", a->rd, a->rr)
+INSN(FMULS, "r%d, r%d", a->rd, a->rr)
+INSN(FMULSU, "r%d, r%d", a->rd, a->rr)
+INSN(DES, "%d", a->imm)
+
+/*
+ * Branch Instructions
+ */
+INSN(RJMP, ".%+d", a->imm * 2)
+INSN(IJMP, "")
+INSN(EIJMP, "")
+INSN(JMP, "0x%x", a->imm * 2)
+INSN(RCALL, ".%+d", a->imm * 2)
+INSN(ICALL, "")
+INSN(EICALL, "")
+INSN(CALL, "0x%x", a->imm * 2)
+INSN(RET, "")
+INSN(RETI, "")
+INSN(CPSE, "r%d, r%d", a->rd, a->rr)
+INSN(CP, "r%d, r%d", a->rd, a->rr)
+INSN(CPC, "r%d, r%d", a->rd, a->rr)
+INSN(CPI, "r%d, %d", a->rd, a->imm)
+INSN(SBRC, "r%d, %d", a->rr, a->bit)
+INSN(SBRS, "r%d, %d", a->rr, a->bit)
+INSN(SBIC, "$%d, %d", a->reg, a->bit)
+INSN(SBIS, "$%d, %d", a->reg, a->bit)
+INSN_MNEMONIC(BRBS, brbs[a->bit], ".%+d", a->imm * 2)
+INSN_MNEMONIC(BRBC, brbc[a->bit], ".%+d", a->imm * 2)
+
+/*
+ * Data Transfer Instructions
+ */
+INSN(MOV, "r%d, r%d", a->rd, a->rr)
+INSN(MOVW, "r%d:r%d, r%d:r%d", a->rd + 1, a->rd, a->rr + 1, a->rr)
+INSN(LDI, "r%d, %d", a->rd, a->imm)
+INSN(LDS, "r%d, %d", a->rd, a->imm)
+INSN(LDX1, "r%d, X", a->rd)
+INSN(LDX2, "r%d, X+", a->rd)
+INSN(LDX3, "r%d, -X", a->rd)
+INSN(LDY2, "r%d, Y+", a->rd)
+INSN(LDY3, "r%d, -Y", a->rd)
+INSN(LDZ2, "r%d, Z+", a->rd)
+INSN(LDZ3, "r%d, -Z", a->rd)
+INSN(LDDY, "r%d, Y+%d", a->rd, a->imm)
+INSN(LDDZ, "r%d, Z+%d", a->rd, a->imm)
+INSN(STS, "%d, r%d", a->imm, a->rd)
+INSN(STX1, "X, r%d", a->rr)
+INSN(STX2, "X+, r%d", a->rr)
+INSN(STX3, "-X, r%d", a->rr)
+INSN(STY2, "Y+, r%d", a->rd)
+INSN(STY3, "-Y, r%d", a->rd)
+INSN(STZ2, "Z+, r%d", a->rd)
+INSN(STZ3, "-Z, r%d", a->rd)
+INSN(STDY, "Y+%d, r%d", a->imm, a->rd)
+INSN(STDZ, "Z+%d, r%d", a->imm, a->rd)
+INSN(LPM1, "")
+INSN(LPM2, "r%d, Z", a->rd)
+INSN(LPMX, "r%d, Z+", a->rd)
+INSN(ELPM1, "")
+INSN(ELPM2, "r%d, Z", a->rd)
+INSN(ELPMX, "r%d, Z+", a->rd)
+INSN(SPM, "")
+INSN(SPMX, "Z+")
+INSN(IN, "r%d, $%d", a->rd, a->imm)
+INSN(OUT, "$%d, r%d", a->imm, a->rd)
+INSN(PUSH, "r%d", a->rd)
+INSN(POP, "r%d", a->rd)
+INSN(XCH, "Z, r%d", a->rd)
+INSN(LAC, "Z, r%d", a->rd)
+INSN(LAS, "Z, r%d", a->rd)
+INSN(LAT, "Z, r%d", a->rd)
+
+/*
+ * Bit and Bit-test Instructions
+ */
+INSN(LSR, "r%d", a->rd)
+INSN(ROR, "r%d", a->rd)
+INSN(ASR, "r%d", a->rd)
+INSN(SWAP, "r%d", a->rd)
+INSN(SBI, "$%d, %d", a->reg, a->bit)
+INSN(CBI, "%d, %d", a->reg, a->bit)
+INSN(BST, "r%d, %d", a->rd, a->bit)
+INSN(BLD, "r%d, %d", a->rd, a->bit)
+INSN_MNEMONIC(BSET, bset[a->bit], "")
+INSN_MNEMONIC(BCLR, bclr[a->bit], "")
+
+/*
+ * MCU Control Instructions
+ */
+INSN(BREAK, "")
+INSN(NOP, "")
+INSN(SLEEP, "")
+INSN(WDR, "")
diff --git a/target/avr/gdbstub.c b/target/avr/gdbstub.c
new file mode 100644
index 0000000000..c28ed67efe
--- /dev/null
+++ b/target/avr/gdbstub.c
@@ -0,0 +1,84 @@
+/*
+ * QEMU AVR gdbstub
+ *
+ * Copyright (c) 2016-2020 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 "exec/gdbstub.h"
+
+int avr_cpu_gdb_read_register(CPUState *cs, GByteArray *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;
+
+ /* R */
+ if (n < 32) {
+ env->r[n] = *mem_buf;
+ return 1;
+ }
+
+ /* SREG */
+ if (n == 32) {
+ cpu_set_sreg(env, *mem_buf);
+ return 1;
+ }
+
+ /* SP */
+ if (n == 33) {
+ env->sp = lduw_p(mem_buf);
+ return 2;
+ }
+
+ /* PC */
+ if (n == 34) {
+ env->pc_w = ldl_p(mem_buf) / 2;
+ return 4;
+ }
+
+ return 0;
+}
diff --git a/target/avr/helper.c b/target/avr/helper.c
new file mode 100644
index 0000000000..d96d14372b
--- /dev/null
+++ b/target/avr/helper.c
@@ -0,0 +1,348 @@
+/*
+ * QEMU AVR CPU helpers
+ *
+ * Copyright (c) 2016-2020 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 "exec/address-spaces.h"
+#include "exec/helper-proto.h"
+
+bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
+{
+ bool ret = false;
+ CPUClass *cc = CPU_GET_CLASS(cs);
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ if (interrupt_request & CPU_INTERRUPT_RESET) {
+ if (cpu_interrupts_enabled(env)) {
+ cs->exception_index = EXCP_RESET;
+ cc->do_interrupt(cs);
+
+ cs->interrupt_request &= ~CPU_INTERRUPT_RESET;
+
+ ret = true;
+ }
+ }
+ if (interrupt_request & CPU_INTERRUPT_HARD) {
+ if (cpu_interrupts_enabled(env) && env->intsrc != 0) {
+ int index = ctz32(env->intsrc);
+ cs->exception_index = EXCP_INT(index);
+ cc->do_interrupt(cs);
+
+ env->intsrc &= env->intsrc - 1; /* clear the interrupt */
+ cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
+
+ ret = true;
+ }
+ }
+ return ret;
+}
+
+void avr_cpu_do_interrupt(CPUState *cs)
+{
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+
+ uint32_t ret = env->pc_w;
+ int vector = 0;
+ int size = avr_feature(env, AVR_FEATURE_JMP_CALL) ? 2 : 1;
+ int base = 0;
+
+ if (cs->exception_index == EXCP_RESET) {
+ vector = 0;
+ } else if (env->intsrc != 0) {
+ vector = ctz32(env->intsrc) + 1;
+ }
+
+ if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) {
+ cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
+ cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8);
+ cpu_stb_data(env, env->sp--, (ret & 0xff0000) >> 16);
+ } else if (avr_feature(env, AVR_FEATURE_2_BYTE_PC)) {
+ cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
+ cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8);
+ } else {
+ cpu_stb_data(env, env->sp--, (ret & 0x0000ff));
+ }
+
+ env->pc_w = base + vector * size;
+ env->sregI = 0; /* clear Global Interrupt Flag */
+
+ cs->exception_index = -1;
+}
+
+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 */
+}
+
+bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
+ MMUAccessType access_type, int mmu_idx,
+ bool probe, uintptr_t retaddr)
+{
+ int prot = 0;
+ MemTxAttrs attrs = {};
+ uint32_t paddr;
+
+ address &= TARGET_PAGE_MASK;
+
+ if (mmu_idx == MMU_CODE_IDX) {
+ /* access to code in flash */
+ paddr = OFFSET_CODE + address;
+ prot = PAGE_READ | PAGE_EXEC;
+ if (paddr + TARGET_PAGE_SIZE > OFFSET_DATA) {
+ error_report("execution left flash memory");
+ abort();
+ }
+ } else if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
+ /*
+ * access to CPU registers, exit and rebuilt this TB to use full access
+ * incase it touches specially handled registers like SREG or SP
+ */
+ AVRCPU *cpu = AVR_CPU(cs);
+ CPUAVRState *env = &cpu->env;
+ env->fullacc = 1;
+ cpu_loop_exit_restore(cs, retaddr);
+ } else {
+ /* access to memory. nothing special */
+ paddr = OFFSET_DATA + address;
+ prot = PAGE_READ | PAGE_WRITE;
+ }
+
+ tlb_set_page_with_attrs(cs, address, paddr, attrs, prot,
+ mmu_idx, TARGET_PAGE_SIZE);
+
+ return true;
+}
+
+/*
+ * helpers
+ */
+
+void helper_sleep(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ cs->exception_index = EXCP_HLT;
+ cpu_loop_exit(cs);
+}
+
+void helper_unsupported(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ /*
+ * I count not find what happens on the real platform, so
+ * it's EXCP_DEBUG for meanwhile
+ */
+ cs->exception_index = EXCP_DEBUG;
+ if (qemu_loglevel_mask(LOG_UNIMP)) {
+ qemu_log("UNSUPPORTED\n");
+ cpu_dump_state(cs, stderr, 0);
+ }
+ cpu_loop_exit(cs);
+}
+
+void helper_debug(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ cs->exception_index = EXCP_DEBUG;
+ cpu_loop_exit(cs);
+}
+
+void helper_break(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ cs->exception_index = EXCP_DEBUG;
+ cpu_loop_exit(cs);
+}
+
+void helper_wdr(CPUAVRState *env)
+{
+ CPUState *cs = env_cpu(env);
+
+ /* WD is not implemented yet, placeholder */
+ cs->exception_index = EXCP_DEBUG;
+ cpu_loop_exit(cs);
+}
+
+/*
+ * This function implements IN instruction
+ *
+ * It does the following
+ * a. if an IO register belongs to CPU, its value is read and returned
+ * b. otherwise io address is translated to mem address and physical memory
+ * is read.
+ * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation
+ *
+ */
+target_ulong helper_inb(CPUAVRState *env, uint32_t port)
+{
+ target_ulong data = 0;
+
+ switch (port) {
+ case 0x38: /* RAMPD */
+ data = 0xff & (env->rampD >> 16);
+ break;
+ case 0x39: /* RAMPX */
+ data = 0xff & (env->rampX >> 16);
+ break;
+ case 0x3a: /* RAMPY */
+ data = 0xff & (env->rampY >> 16);
+ break;
+ case 0x3b: /* RAMPZ */
+ data = 0xff & (env->rampZ >> 16);
+ break;
+ case 0x3c: /* EIND */
+ data = 0xff & (env->eind >> 16);
+ break;
+ case 0x3d: /* SPL */
+ data = env->sp & 0x00ff;
+ break;
+ case 0x3e: /* SPH */
+ data = env->sp >> 8;
+ break;
+ case 0x3f: /* SREG */
+ data = cpu_get_sreg(env);
+ break;
+ default:
+ /* not a special register, pass to normal memory access */
+ data = address_space_ldub(&address_space_memory,
+ OFFSET_IO_REGISTERS + port,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+
+ return data;
+}
+
+/*
+ * This function implements OUT instruction
+ *
+ * It does the following
+ * a. if an IO register belongs to CPU, its value is written into the register
+ * b. otherwise io address is translated to mem address and physical memory
+ * is written.
+ * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation
+ *
+ */
+void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data)
+{
+ data &= 0x000000ff;
+
+ switch (port) {
+ case 0x38: /* RAMPD */
+ if (avr_feature(env, AVR_FEATURE_RAMPD)) {
+ env->rampD = (data & 0xff) << 16;
+ }
+ break;
+ case 0x39: /* RAMPX */
+ if (avr_feature(env, AVR_FEATURE_RAMPX)) {
+ env->rampX = (data & 0xff) << 16;
+ }
+ break;
+ case 0x3a: /* RAMPY */
+ if (avr_feature(env, AVR_FEATURE_RAMPY)) {
+ env->rampY = (data & 0xff) << 16;
+ }
+ break;
+ case 0x3b: /* RAMPZ */
+ if (avr_feature(env, AVR_FEATURE_RAMPZ)) {
+ env->rampZ = (data & 0xff) << 16;
+ }
+ break;
+ case 0x3c: /* EIDN */
+ env->eind = (data & 0xff) << 16;
+ break;
+ case 0x3d: /* SPL */
+ env->sp = (env->sp & 0xff00) | (data);
+ break;
+ case 0x3e: /* SPH */
+ if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) {
+ env->sp = (env->sp & 0x00ff) | (data << 8);
+ }
+ break;
+ case 0x3f: /* SREG */
+ cpu_set_sreg(env, data);
+ break;
+ default:
+ /* not a special register, pass to normal memory access */
+ address_space_stb(&address_space_memory, OFFSET_IO_REGISTERS + port,
+ data, MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+}
+
+/*
+ * this function implements LD instruction when there is a posibility to read
+ * from a CPU register
+ */
+target_ulong helper_fullrd(CPUAVRState *env, uint32_t addr)
+{
+ uint8_t data;
+
+ env->fullacc = false;
+
+ if (addr < NUMBER_OF_CPU_REGISTERS) {
+ /* CPU registers */
+ data = env->r[addr];
+ } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
+ /* IO registers */
+ data = helper_inb(env, addr - NUMBER_OF_CPU_REGISTERS);
+ } else {
+ /* memory */
+ data = address_space_ldub(&address_space_memory, OFFSET_DATA + addr,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+ return data;
+}
+
+/*
+ * this function implements ST instruction when there is a posibility to write
+ * into a CPU register
+ */
+void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr)
+{
+ env->fullacc = false;
+
+ /* Following logic assumes this: */
+ assert(OFFSET_CPU_REGISTERS == OFFSET_DATA);
+ assert(OFFSET_IO_REGISTERS == OFFSET_CPU_REGISTERS +
+ NUMBER_OF_CPU_REGISTERS);
+
+ if (addr < NUMBER_OF_CPU_REGISTERS) {
+ /* CPU registers */
+ env->r[addr] = data;
+ } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) {
+ /* IO registers */
+ helper_outb(env, addr - NUMBER_OF_CPU_REGISTERS, data);
+ } else {
+ /* memory */
+ address_space_stb(&address_space_memory, OFFSET_DATA + addr, data,
+ MEMTXATTRS_UNSPECIFIED, NULL);
+ }
+}
diff --git a/target/avr/helper.h b/target/avr/helper.h
new file mode 100644
index 0000000000..8e1ae7fda0
--- /dev/null
+++ b/target/avr/helper.h
@@ -0,0 +1,29 @@
+/*
+ * QEMU AVR CPU helpers
+ *
+ * Copyright (c) 2016-2020 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(wdr, void, env)
+DEF_HELPER_1(debug, void, env)
+DEF_HELPER_1(break, void, env)
+DEF_HELPER_1(sleep, void, env)
+DEF_HELPER_1(unsupported, void, env)
+DEF_HELPER_3(outb, void, env, i32, i32)
+DEF_HELPER_2(inb, tl, env, i32)
+DEF_HELPER_3(fullwr, void, env, i32, i32)
+DEF_HELPER_2(fullrd, tl, env, i32)
diff --git a/target/avr/insn.decode b/target/avr/insn.decode
new file mode 100644
index 0000000000..482c23ad0c
--- /dev/null
+++ b/target/avr/insn.decode
@@ -0,0 +1,187 @@
+#
+# AVR instruction decode definitions.
+#
+# Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
+#
+# 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/>.
+#
+
+#
+# regs_16_31_by_one = [16 .. 31]
+# regs_16_23_by_one = [16 .. 23]
+# regs_24_30_by_two = [24, 26, 28, 30]
+# regs_00_30_by_two = [0, 2, 4, 6, 8, .. 30]
+
+%rd 4:5
+%rr 9:1 0:4
+
+%rd_a 4:4 !function=to_regs_16_31_by_one
+%rd_b 4:3 !function=to_regs_16_23_by_one
+%rd_c 4:2 !function=to_regs_24_30_by_two
+%rr_a 0:4 !function=to_regs_16_31_by_one
+%rr_b 0:3 !function=to_regs_16_23_by_one
+
+%imm6 6:2 0:4
+%imm8 8:4 0:4
+
+%io_imm 9:2 0:4
+%ldst_d_imm 13:1 10:2 0:3
+
+
+&rd_rr rd rr
+&rd_imm rd imm
+
+@op_rd_rr .... .. . ..... .... &rd_rr rd=%rd rr=%rr
+@op_rd_imm6 .... .... .. .. .... &rd_imm rd=%rd_c imm=%imm6
+@op_rd_imm8 .... .... .... .... &rd_imm rd=%rd_a imm=%imm8
+@fmul .... .... . ... . ... &rd_rr rd=%rd_b rr=%rr_b
+
+#
+# Arithmetic Instructions
+#
+ADD 0000 11 . ..... .... @op_rd_rr
+ADC 0001 11 . ..... .... @op_rd_rr
+ADIW 1001 0110 .. .. .... @op_rd_imm6
+SUB 0001 10 . ..... .... @op_rd_rr
+SUBI 0101 .... .... .... @op_rd_imm8
+SBC 0000 10 . ..... .... @op_rd_rr
+SBCI 0100 .... .... .... @op_rd_imm8
+SBIW 1001 0111 .. .. .... @op_rd_imm6
+AND 0010 00 . ..... .... @op_rd_rr
+ANDI 0111 .... .... .... @op_rd_imm8
+OR 0010 10 . ..... .... @op_rd_rr
+ORI 0110 .... .... .... @op_rd_imm8
+EOR 0010 01 . ..... .... @op_rd_rr
+COM 1001 010 rd:5 0000
+NEG 1001 010 rd:5 0001
+INC 1001 010 rd:5 0011
+DEC 1001 010 rd:5 1010
+MUL 1001 11 . ..... .... @op_rd_rr
+MULS 0000 0010 .... .... &rd_rr rd=%rd_a rr=%rr_a
+MULSU 0000 0011 0 ... 0 ... @fmul
+FMUL 0000 0011 0 ... 1 ... @fmul
+FMULS 0000 0011 1 ... 0 ... @fmul
+FMULSU 0000 0011 1 ... 1 ... @fmul
+DES 1001 0100 imm:4 1011
+
+#
+# Branch Instructions
+#
+
+# The 22-bit immediate is partially in the opcode word,
+# and partially in the next. Use append_16 to build the
+# complete 22-bit value.
+%imm_call 4:5 0:1 !function=append_16
+
+@op_bit .... .... . bit:3 ....
+@op_bit_imm .... .. imm:s7 bit:3
+
+RJMP 1100 imm:s12
+IJMP 1001 0100 0000 1001
+EIJMP 1001 0100 0001 1001
+JMP 1001 010 ..... 110 . imm=%imm_call
+RCALL 1101 imm:s12
+ICALL 1001 0101 0000 1001
+EICALL 1001 0101 0001 1001
+CALL 1001 010 ..... 111 . imm=%imm_call
+RET 1001 0101 0000 1000
+RETI 1001 0101 0001 1000
+CPSE 0001 00 . ..... .... @op_rd_rr
+CP 0001 01 . ..... .... @op_rd_rr
+CPC 0000 01 . ..... .... @op_rd_rr
+CPI 0011 .... .... .... @op_rd_imm8
+SBRC 1111 110 rr:5 0 bit:3
+SBRS 1111 111 rr:5 0 bit:3
+SBIC 1001 1001 reg:5 bit:3
+SBIS 1001 1011 reg:5 bit:3
+BRBS 1111 00 ....... ... @op_bit_imm
+BRBC 1111 01 ....... ... @op_bit_imm
+
+#
+# Data Transfer Instructions
+#
+
+%rd_d 4:4 !function=to_regs_00_30_by_two
+%rr_d 0:4 !function=to_regs_00_30_by_two
+
+@io_rd_imm .... . .. ..... .... &rd_imm rd=%rd imm=%io_imm
+@ldst_d .. . . .. . rd:5 . ... &rd_imm imm=%ldst_d_imm
+
+# The 16-bit immediate is completely in the next word.
+# Fields cannot be defined with no bits, so we cannot play
+# the same trick and append to a zero-bit value.
+# Defer reading the immediate until trans_{LDS,STS}.
+@ldst_s .... ... rd:5 .... imm=0
+
+MOV 0010 11 . ..... .... @op_rd_rr
+MOVW 0000 0001 .... .... &rd_rr rd=%rd_d rr=%rr_d
+LDI 1110 .... .... .... @op_rd_imm8
+LDS 1001 000 ..... 0000 @ldst_s
+LDX1 1001 000 rd:5 1100
+LDX2 1001 000 rd:5 1101
+LDX3 1001 000 rd:5 1110
+LDY2 1001 000 rd:5 1001
+LDY3 1001 000 rd:5 1010
+LDZ2 1001 000 rd:5 0001
+LDZ3 1001 000 rd:5 0010
+LDDY 10 . 0 .. 0 ..... 1 ... @ldst_d
+LDDZ 10 . 0 .. 0 ..... 0 ... @ldst_d
+STS 1001 001 ..... 0000 @ldst_s
+STX1 1001 001 rr:5 1100
+STX2 1001 001 rr:5 1101
+STX3 1001 001 rr:5 1110
+STY2 1001 001 rd:5 1001
+STY3 1001 001 rd:5 1010
+STZ2 1001 001 rd:5 0001
+STZ3 1001 001 rd:5 0010
+STDY 10 . 0 .. 1 ..... 1 ... @ldst_d
+STDZ 10 . 0 .. 1 ..... 0 ... @ldst_d
+LPM1 1001 0101 1100 1000
+LPM2 1001 000 rd:5 0100
+LPMX 1001 000 rd:5 0101
+ELPM1 1001 0101 1101 1000
+ELPM2 1001 000 rd:5 0110
+ELPMX 1001 000 rd:5 0111
+SPM 1001 0101 1110 1000
+SPMX 1001 0101 1111 1000
+IN 1011 0 .. ..... .... @io_rd_imm
+OUT 1011 1 .. ..... .... @io_rd_imm
+PUSH 1001 001 rd:5 1111
+POP 1001 000 rd:5 1111
+XCH 1001 001 rd:5 0100
+LAC 1001 001 rd:5 0110
+LAS 1001 001 rd:5 0101
+LAT 1001 001 rd:5 0111
+
+#
+# Bit and Bit-test Instructions
+#
+LSR 1001 010 rd:5 0110
+ROR 1001 010 rd:5 0111
+ASR 1001 010 rd:5 0101
+SWAP 1001 010 rd:5 0010
+SBI 1001 1010 reg:5 bit:3
+CBI 1001 1000 reg:5 bit:3
+BST 1111 101 rd:5 0 bit:3
+BLD 1111 100 rd:5 0 bit:3
+BSET 1001 0100 0 bit:3 1000
+BCLR 1001 0100 1 bit:3 1000
+
+#
+# MCU Control Instructions
+#
+BREAK 1001 0101 1001 1000
+NOP 0000 0000 0000 0000
+SLEEP 1001 0101 1000 1000
+WDR 1001 0101 1010 1000
diff --git a/target/avr/machine.c b/target/avr/machine.c
new file mode 100644
index 0000000000..e315442787
--- /dev/null
+++ b/target/avr/machine.c
@@ -0,0 +1,119 @@
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2016-2020 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 "migration/cpu.h"
+
+static int get_sreg(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field)
+{
+ CPUAVRState *env = opaque;
+ uint8_t sreg;
+
+ sreg = qemu_get_byte(f);
+ cpu_set_sreg(env, sreg);
+ return 0;
+}
+
+static int put_sreg(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field, QJSON *vmdesc)
+{
+ CPUAVRState *env = opaque;
+ uint8_t sreg = cpu_get_sreg(env);
+
+ qemu_put_byte(f, sreg);
+ return 0;
+}
+
+static const VMStateInfo vms_sreg = {
+ .name = "sreg",
+ .get = get_sreg,
+ .put = put_sreg,
+};
+
+static int get_segment(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field)
+{
+ uint32_t *ramp = opaque;
+ uint8_t temp;
+
+ temp = qemu_get_byte(f);
+ *ramp = ((uint32_t)temp) << 16;
+ return 0;
+}
+
+static int put_segment(QEMUFile *f, void *opaque, size_t size,
+ const VMStateField *field, QJSON *vmdesc)
+{
+ uint32_t *ramp = opaque;
+ uint8_t temp = *ramp >> 16;
+
+ qemu_put_byte(f, temp);
+ return 0;
+}
+
+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(env.skip, AVRCPU),
+
+ VMSTATE_UINT32_ARRAY(env.r, AVRCPU, NUMBER_OF_CPU_REGISTERS),
+
+ VMSTATE_SINGLE(env, AVRCPU, 0, vms_sreg, CPUAVRState),
+ VMSTATE_SINGLE(env.rampD, AVRCPU, 0, vms_rampD, uint32_t),
+ VMSTATE_SINGLE(env.rampX, AVRCPU, 0, vms_rampX, uint32_t),
+ VMSTATE_SINGLE(env.rampY, AVRCPU, 0, vms_rampY, uint32_t),
+ VMSTATE_SINGLE(env.rampZ, AVRCPU, 0, vms_rampZ, uint32_t),
+ VMSTATE_SINGLE(env.eind, AVRCPU, 0, vms_eind, uint32_t),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/target/avr/translate.c b/target/avr/translate.c
new file mode 100644
index 0000000000..648dcd5c3e
--- /dev/null
+++ b/target/avr/translate.c
@@ -0,0 +1,3061 @@
+/*
+ * QEMU AVR CPU
+ *
+ * Copyright (c) 2019-2020 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/qemu-print.h"
+#include "tcg/tcg.h"
+#include "cpu.h"
+#include "exec/exec-all.h"
+#include "tcg/tcg-op.h"
+#include "exec/cpu_ldst.h"
+#include "exec/helper-proto.h"
+#include "exec/helper-gen.h"
+#include "exec/log.h"
+#include "exec/translator.h"
+#include "exec/gen-icount.h"
+
+/*
+ * Define if you want a BREAK instruction translated to a breakpoint
+ * Active debugging connection is assumed
+ * This is for
+ * https://github.com/seharris/qemu-avr-tests/tree/master/instruction-tests
+ * tests
+ */
+#undef BREAKPOINT_ON_BREAK
+
+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_r[NUMBER_OF_CPU_REGISTERS];
+static TCGv cpu_eind;
+static TCGv cpu_sp;
+
+static TCGv cpu_skip;
+
+static const char reg_names[NUMBER_OF_CPU_REGISTERS][8] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
+ "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
+};
+#define REG(x) (cpu_r[x])
+
+enum {
+ DISAS_EXIT = DISAS_TARGET_0, /* We want return to the cpu main loop. */
+ DISAS_LOOKUP = DISAS_TARGET_1, /* We have a variable condition exit. */
+ DISAS_CHAIN = DISAS_TARGET_2, /* We have a single condition exit. */
+};
+
+typedef struct DisasContext DisasContext;
+
+/* This is the state at translation time. */
+struct DisasContext {
+ TranslationBlock *tb;
+
+ CPUAVRState *env;
+ CPUState *cs;
+
+ target_long npc;
+ uint32_t opcode;
+
+ /* Routine used to access memory */
+ int memidx;
+ int bstate;
+ int singlestep;
+
+ /*
+ * some AVR instructions can make the following instruction to be skipped
+ * Let's name those instructions
+ * A - instruction that can skip the next one
+ * B - instruction that can be skipped. this depends on execution of A
+ * there are two scenarios
+ * 1. A and B belong to the same translation block
+ * 2. A is the last instruction in the translation block and B is the last
+ *
+ * following variables are used to simplify the skipping logic, they are
+ * used in the following manner (sketch)
+ *
+ * TCGLabel *skip_label = NULL;
+ * if (ctx.skip_cond != TCG_COND_NEVER) {
+ * skip_label = gen_new_label();
+ * tcg_gen_brcond_tl(skip_cond, skip_var0, skip_var1, skip_label);
+ * }
+ *
+ * if (free_skip_var0) {
+ * tcg_temp_free(skip_var0);
+ * free_skip_var0 = false;
+ * }
+ *
+ * translate(&ctx);
+ *
+ * if (skip_label) {
+ * gen_set_label(skip_label);
+ * }
+ */
+ TCGv skip_var0;
+ TCGv skip_var1;
+ TCGCond skip_cond;
+ bool free_skip_var0;
+};
+
+void avr_cpu_tcg_init(void)
+{
+ int i;
+
+#define AVR_REG_OFFS(x) offsetof(CPUAVRState, x)
+ 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");
+ cpu_skip = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(skip), "skip");
+
+ for (i = 0; i < NUMBER_OF_CPU_REGISTERS; i++) {
+ cpu_r[i] = tcg_global_mem_new_i32(cpu_env, AVR_REG_OFFS(r[i]),
+ reg_names[i]);
+ }
+#undef AVR_REG_OFFS
+}
+
+static int to_regs_16_31_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 16);
+}
+
+static int to_regs_16_23_by_one(DisasContext *ctx, int indx)
+{
+ return 16 + (indx % 8);
+}
+
+static int to_regs_24_30_by_two(DisasContext *ctx, int indx)
+{
+ return 24 + (indx % 4) * 2;
+}
+
+static int to_regs_00_30_by_two(DisasContext *ctx, int indx)
+{
+ return (indx % 16) * 2;
+}
+
+static uint16_t next_word(DisasContext *ctx)
+{
+ return cpu_lduw_code(ctx->env, ctx->npc++ * 2);
+}
+
+static int append_16(DisasContext *ctx, int x)
+{
+ return x << 16 | next_word(ctx);
+}
+
+static bool avr_have_feature(DisasContext *ctx, int feature)
+{
+ if (!avr_feature(ctx->env, feature)) {
+ gen_helper_unsupported(cpu_env);
+ ctx->bstate = DISAS_NORETURN;
+ return false;
+ }
+ return true;
+}
+
+static bool decode_insn(DisasContext *ctx, uint16_t insn);
+#include "decode_insn.inc.c"
+
+/*
+ * Arithmetic Instructions
+ */
+
+/*
+ * Utility functions for updating status registers:
+ *
+ * - gen_add_CHf()
+ * - gen_add_Vf()
+ * - gen_sub_CHf()
+ * - gen_sub_Vf()
+ * - gen_NSf()
+ * - gen_ZNSf()
+ *
+ */
+
+static void gen_add_CHf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+ TCGv t3 = tcg_temp_new_i32();
+
+ tcg_gen_and_tl(t1, Rd, Rr); /* t1 = Rd & Rr */
+ tcg_gen_andc_tl(t2, Rd, R); /* t2 = Rd & ~R */
+ tcg_gen_andc_tl(t3, Rr, R); /* t3 = Rr & ~R */
+ tcg_gen_or_tl(t1, t1, t2); /* t1 = t1 | t2 | t3 */
+ tcg_gen_or_tl(t1, t1, t3);
+
+ tcg_gen_shri_tl(cpu_Cf, t1, 7); /* Cf = t1(7) */
+ tcg_gen_shri_tl(cpu_Hf, t1, 3); /* Hf = t1(3) */
+ tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1);
+
+ tcg_temp_free_i32(t3);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_add_Vf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+
+ /* t1 = Rd & Rr & ~R | ~Rd & ~Rr & R */
+ /* = (Rd ^ R) & ~(Rd ^ Rr) */
+ tcg_gen_xor_tl(t1, Rd, R);
+ tcg_gen_xor_tl(t2, Rd, Rr);
+ tcg_gen_andc_tl(t1, t1, t2);
+
+ tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf = t1(7) */
+
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_sub_CHf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+ TCGv t3 = tcg_temp_new_i32();
+
+ tcg_gen_not_tl(t1, Rd); /* t1 = ~Rd */
+ tcg_gen_and_tl(t2, t1, Rr); /* t2 = ~Rd & Rr */
+ tcg_gen_or_tl(t3, t1, Rr); /* t3 = (~Rd | Rr) & R */
+ tcg_gen_and_tl(t3, t3, R);
+ tcg_gen_or_tl(t2, t2, t3); /* t2 = ~Rd & Rr | ~Rd & R | R & Rr */
+
+ tcg_gen_shri_tl(cpu_Cf, t2, 7); /* Cf = t2(7) */
+ tcg_gen_shri_tl(cpu_Hf, t2, 3); /* Hf = t2(3) */
+ tcg_gen_andi_tl(cpu_Hf, cpu_Hf, 1);
+
+ tcg_temp_free_i32(t3);
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_sub_Vf(TCGv R, TCGv Rd, TCGv Rr)
+{
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv t2 = tcg_temp_new_i32();
+
+ /* t1 = Rd & ~Rr & ~R | ~Rd & Rr & R */
+ /* = (Rd ^ R) & (Rd ^ R) */
+ tcg_gen_xor_tl(t1, Rd, R);
+ tcg_gen_xor_tl(t2, Rd, Rr);
+ tcg_gen_and_tl(t1, t1, t2);
+
+ tcg_gen_shri_tl(cpu_Vf, t1, 7); /* Vf = t1(7) */
+
+ tcg_temp_free_i32(t2);
+ tcg_temp_free_i32(t1);
+}
+
+static void gen_NSf(TCGv R)
+{
+ tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf = R(7) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+}
+
+static void gen_ZNSf(TCGv R)
+{
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf = R(7) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+}
+
+/*
+ * Adds two registers without the C Flag and places the result in the
+ * destination register Rd.
+ */
+static bool trans_ADD(DisasContext *ctx, arg_ADD *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_add_tl(R, Rd, Rr); /* Rd = Rd + Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_add_CHf(R, Rd, Rr);
+ gen_add_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Adds two registers and the contents of the C Flag and places the result in
+ * the destination register Rd.
+ */
+static bool trans_ADC(DisasContext *ctx, arg_ADC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_add_tl(R, Rd, Rr); /* R = Rd + Rr + Cf */
+ tcg_gen_add_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_add_CHf(R, Rd, Rr);
+ gen_add_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Adds an immediate value (0 - 63) to a register pair and places the result
+ * in the register pair. This instruction operates on the upper four register
+ * pairs, and is well suited for operations on the pointer registers. This
+ * instruction is not available in all devices. Refer to the device specific
+ * instruction set summary.
+ */
+static bool trans_ADIW(DisasContext *ctx, arg_ADIW *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ADIW_SBIW)) {
+ return true;
+ }
+
+ TCGv RdL = cpu_r[a->rd];
+ TCGv RdH = cpu_r[a->rd + 1];
+ int Imm = (a->imm);
+ TCGv R = tcg_temp_new_i32();
+ TCGv Rd = tcg_temp_new_i32();
+
+ tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd = RdH:RdL */
+ tcg_gen_addi_tl(R, Rd, Imm); /* R = Rd + Imm */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_andc_tl(cpu_Cf, Rd, R); /* Cf = Rd & ~R */
+ tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15);
+ tcg_gen_andc_tl(cpu_Vf, R, Rd); /* Vf = R & ~Rd */
+ tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15);
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf = R(15) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf);/* Sf = Nf ^ Vf */
+
+ /* update output registers */
+ tcg_gen_andi_tl(RdL, R, 0xff);
+ tcg_gen_shri_tl(RdH, R, 8);
+
+ tcg_temp_free_i32(Rd);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Subtracts two registers and places the result in the destination
+ * register Rd.
+ */
+static bool trans_SUB(DisasContext *ctx, arg_SUB *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ tcg_gen_andc_tl(cpu_Cf, Rd, R); /* Cf = Rd & ~R */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Subtracts a register and a constant and places the result in the
+ * destination register Rd. This instruction is working on Register R16 to R31
+ * and is very well suited for operations on the X, Y, and Z-pointers.
+ */
+static bool trans_SUBI(DisasContext *ctx, arg_SUBI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = tcg_const_i32(a->imm);
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Imm */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+ tcg_temp_free_i32(Rr);
+
+ return true;
+}
+
+/*
+ * Subtracts two registers and subtracts with the C Flag and places the
+ * result in the destination register Rd.
+ */
+static bool trans_SBC(DisasContext *ctx, arg_SBC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv zero = tcg_const_i32(0);
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */
+ tcg_gen_sub_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_NSf(R);
+
+ /*
+ * Previous value remains unchanged when the result is zero;
+ * cleared otherwise.
+ */
+ tcg_gen_movcond_tl(TCG_COND_EQ, cpu_Zf, R, zero, cpu_Zf, zero);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(zero);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * SBCI -- Subtract Immediate with Carry
+ */
+static bool trans_SBCI(DisasContext *ctx, arg_SBCI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = tcg_const_i32(a->imm);
+ TCGv R = tcg_temp_new_i32();
+ TCGv zero = tcg_const_i32(0);
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */
+ tcg_gen_sub_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_NSf(R);
+
+ /*
+ * Previous value remains unchanged when the result is zero;
+ * cleared otherwise.
+ */
+ tcg_gen_movcond_tl(TCG_COND_EQ, cpu_Zf, R, zero, cpu_Zf, zero);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(zero);
+ tcg_temp_free_i32(R);
+ tcg_temp_free_i32(Rr);
+
+ return true;
+}
+
+/*
+ * Subtracts an immediate value (0-63) from a register pair and places the
+ * result in the register pair. This instruction operates on the upper four
+ * register pairs, and is well suited for operations on the Pointer Registers.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_SBIW(DisasContext *ctx, arg_SBIW *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ADIW_SBIW)) {
+ return true;
+ }
+
+ TCGv RdL = cpu_r[a->rd];
+ TCGv RdH = cpu_r[a->rd + 1];
+ int Imm = (a->imm);
+ TCGv R = tcg_temp_new_i32();
+ TCGv Rd = tcg_temp_new_i32();
+
+ tcg_gen_deposit_tl(Rd, RdL, RdH, 8, 8); /* Rd = RdH:RdL */
+ tcg_gen_subi_tl(R, Rd, Imm); /* R = Rd - Imm */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_andc_tl(cpu_Cf, R, Rd);
+ tcg_gen_shri_tl(cpu_Cf, cpu_Cf, 15); /* Cf = R & ~Rd */
+ tcg_gen_andc_tl(cpu_Vf, Rd, R);
+ tcg_gen_shri_tl(cpu_Vf, cpu_Vf, 15); /* Vf = Rd & ~R */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ tcg_gen_shri_tl(cpu_Nf, R, 15); /* Nf = R(15) */
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+
+ /* update output registers */
+ tcg_gen_andi_tl(RdL, R, 0xff);
+ tcg_gen_shri_tl(RdH, R, 8);
+
+ tcg_temp_free_i32(Rd);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Performs the logical AND between the contents of register Rd and register
+ * Rr and places the result in the destination register Rd.
+ */
+static bool trans_AND(DisasContext *ctx, arg_AND *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_and_tl(R, Rd, Rr); /* Rd = Rd and Rr */
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0); /* Vf = 0 */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Performs the logical AND between the contents of register Rd and a constant
+ * and places the result in the destination register Rd.
+ */
+static bool trans_ANDI(DisasContext *ctx, arg_ANDI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int Imm = (a->imm);
+
+ tcg_gen_andi_tl(Rd, Rd, Imm); /* Rd = Rd & Imm */
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf = 0 */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Performs the logical OR between the contents of register Rd and register
+ * Rr and places the result in the destination register Rd.
+ */
+static bool trans_OR(DisasContext *ctx, arg_OR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_or_tl(R, Rd, Rr);
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Performs the logical OR between the contents of register Rd and a
+ * constant and places the result in the destination register Rd.
+ */
+static bool trans_ORI(DisasContext *ctx, arg_ORI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int Imm = (a->imm);
+
+ tcg_gen_ori_tl(Rd, Rd, Imm); /* Rd = Rd | Imm */
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0x00); /* Vf = 0 */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Performs the logical EOR between the contents of register Rd and
+ * register Rr and places the result in the destination register Rd.
+ */
+static bool trans_EOR(DisasContext *ctx, arg_EOR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+
+ tcg_gen_xor_tl(Rd, Rd, Rr);
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Vf, 0);
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Clears the specified bits in register Rd. Performs the logical AND
+ * between the contents of register Rd and the complement of the constant mask
+ * K. The result will be placed in register Rd.
+ */
+static bool trans_COM(DisasContext *ctx, arg_COM *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_xori_tl(Rd, Rd, 0xff);
+
+ /* update status register */
+ tcg_gen_movi_tl(cpu_Cf, 1); /* Cf = 1 */
+ tcg_gen_movi_tl(cpu_Vf, 0); /* Vf = 0 */
+ gen_ZNSf(Rd);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Replaces the contents of register Rd with its two's complement; the
+ * value $80 is left unchanged.
+ */
+static bool trans_NEG(DisasContext *ctx, arg_NEG *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_const_i32(0);
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, t0, Rd); /* R = 0 - Rd */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, t0, Rd);
+ gen_sub_Vf(R, t0, Rd);
+ gen_ZNSf(R);
+
+ /* update output registers */
+ tcg_gen_mov_tl(Rd, R);
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * Adds one -1- to the contents of register Rd and places the result in the
+ * destination register Rd. The C Flag in SREG is not affected by the
+ * operation, thus allowing the INC instruction to be used on a loop counter in
+ * multiple-precision computations. When operating on unsigned numbers, only
+ * BREQ and BRNE branches can be expected to perform consistently. When
+ * operating on two's complement values, all signed branches are available.
+ */
+static bool trans_INC(DisasContext *ctx, arg_INC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_addi_tl(Rd, Rd, 1);
+ tcg_gen_andi_tl(Rd, Rd, 0xff);
+
+ /* update status register */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x80); /* Vf = Rd == 0x80 */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * Subtracts one -1- from the contents of register Rd and places the result
+ * in the destination register Rd. The C Flag in SREG is not affected by the
+ * operation, thus allowing the DEC instruction to be used on a loop counter in
+ * multiple-precision computations. When operating on unsigned values, only
+ * BREQ and BRNE branches can be expected to perform consistently. When
+ * operating on two's complement values, all signed branches are available.
+ */
+static bool trans_DEC(DisasContext *ctx, arg_DEC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_subi_tl(Rd, Rd, 1); /* Rd = Rd - 1 */
+ tcg_gen_andi_tl(Rd, Rd, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Vf, Rd, 0x7f); /* Vf = Rd == 0x7f */
+ gen_ZNSf(Rd);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit unsigned multiplication.
+ */
+static bool trans_MUL(DisasContext *ctx, arg_MUL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_mul_tl(R, Rd, Rr); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication.
+ */
+static bool trans_MULS(DisasContext *ctx, arg_MULS *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, t1); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit multiplication of a
+ * signed and an unsigned number.
+ */
+static bool trans_MULSU(DisasContext *ctx, arg_MULSU *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, Rr); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make R 16 bits */
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit unsigned
+ * multiplication and shifts the result one bit left.
+ */
+static bool trans_FMUL(DisasContext *ctx, arg_FMUL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_mul_tl(R, Rd, Rr); /* R = Rd * Rr */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update output registers */
+ tcg_gen_shli_tl(R, R, 1);
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+ tcg_gen_andi_tl(R1, R1, 0xff);
+
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication
+ * and shifts the result one bit left.
+ */
+static bool trans_FMULS(DisasContext *ctx, arg_FMULS *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_ext8s_tl(t1, Rr); /* make Rr full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, t1); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update output registers */
+ tcg_gen_shli_tl(R, R, 1);
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+ tcg_gen_andi_tl(R1, R1, 0xff);
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs 8-bit x 8-bit -> 16-bit signed multiplication
+ * and shifts the result one bit left.
+ */
+static bool trans_FMULSU(DisasContext *ctx, arg_FMULSU *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MUL)) {
+ return true;
+ }
+
+ TCGv R0 = cpu_r[0];
+ TCGv R1 = cpu_r[1];
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv t0 = tcg_temp_new_i32();
+
+ tcg_gen_ext8s_tl(t0, Rd); /* make Rd full 32 bit signed */
+ tcg_gen_mul_tl(R, t0, Rr); /* R = Rd * Rr */
+ tcg_gen_andi_tl(R, R, 0xffff); /* make it 16 bits */
+
+ /* update status register */
+ tcg_gen_shri_tl(cpu_Cf, R, 15); /* Cf = R(15) */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+
+ /* update output registers */
+ tcg_gen_shli_tl(R, R, 1);
+ tcg_gen_andi_tl(R0, R, 0xff);
+ tcg_gen_shri_tl(R1, R, 8);
+ tcg_gen_andi_tl(R1, R1, 0xff);
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * The module is an instruction set extension to the AVR CPU, performing
+ * DES iterations. The 64-bit data block (plaintext or ciphertext) is placed in
+ * the CPU register file, registers R0-R7, where LSB of data is placed in LSB
+ * of R0 and MSB of data is placed in MSB of R7. The full 64-bit key (including
+ * parity bits) is placed in registers R8- R15, organized in the register file
+ * with LSB of key in LSB of R8 and MSB of key in MSB of R15. Executing one DES
+ * instruction performs one round in the DES algorithm. Sixteen rounds must be
+ * executed in increasing order to form the correct DES ciphertext or
+ * plaintext. Intermediate results are stored in the register file (R0-R15)
+ * after each DES instruction. The instruction's operand (K) determines which
+ * round is executed, and the half carry flag (H) determines whether encryption
+ * or decryption is performed. The DES algorithm is described in
+ * "Specifications for the Data Encryption Standard" (Federal Information
+ * Processing Standards Publication 46). Intermediate results in this
+ * implementation differ from the standard because the initial permutation and
+ * the inverse initial permutation are performed each iteration. This does not
+ * affect the result in the final ciphertext or plaintext, but reduces
+ * execution time.
+ */
+static bool trans_DES(DisasContext *ctx, arg_DES *a)
+{
+ /* TODO */
+ if (!avr_have_feature(ctx, AVR_FEATURE_DES)) {
+ return true;
+ }
+
+ qemu_log_mask(LOG_UNIMP, "%s: not implemented\n", __func__);
+
+ return true;
+}
+
+/*
+ * Branch Instructions
+ */
+static void gen_jmp_ez(DisasContext *ctx)
+{
+ tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8);
+ tcg_gen_or_tl(cpu_pc, cpu_pc, cpu_eind);
+ ctx->bstate = DISAS_LOOKUP;
+}
+
+static void gen_jmp_z(DisasContext *ctx)
+{
+ tcg_gen_deposit_tl(cpu_pc, cpu_r[30], cpu_r[31], 8, 8);
+ ctx->bstate = DISAS_LOOKUP;
+}
+
+static void gen_push_ret(DisasContext *ctx, int ret)
+{
+ if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) {
+
+ TCGv t0 = tcg_const_i32((ret & 0x0000ff));
+
+ tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_UB);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ tcg_temp_free_i32(t0);
+ } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) {
+
+ TCGv t0 = tcg_const_i32((ret & 0x00ffff));
+
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_st_tl(t0, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ tcg_temp_free_i32(t0);
+
+ } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) {
+
+ TCGv lo = tcg_const_i32((ret & 0x0000ff));
+ TCGv hi = tcg_const_i32((ret & 0xffff00) >> 8);
+
+ tcg_gen_qemu_st_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 2);
+ tcg_gen_qemu_st_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ tcg_temp_free_i32(lo);
+ tcg_temp_free_i32(hi);
+ }
+}
+
+static void gen_pop_ret(DisasContext *ctx, TCGv ret)
+{
+ if (avr_feature(ctx->env, AVR_FEATURE_1_BYTE_PC)) {
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_UB);
+ } else if (avr_feature(ctx->env, AVR_FEATURE_2_BYTE_PC)) {
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_ld_tl(ret, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ } else if (avr_feature(ctx->env, AVR_FEATURE_3_BYTE_PC)) {
+ TCGv lo = tcg_temp_new_i32();
+ TCGv hi = tcg_temp_new_i32();
+
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ tcg_gen_qemu_ld_tl(hi, cpu_sp, MMU_DATA_IDX, MO_BEUW);
+
+ tcg_gen_addi_tl(cpu_sp, cpu_sp, 2);
+ tcg_gen_qemu_ld_tl(lo, cpu_sp, MMU_DATA_IDX, MO_UB);
+
+ tcg_gen_deposit_tl(ret, lo, hi, 8, 16);
+
+ tcg_temp_free_i32(lo);
+ tcg_temp_free_i32(hi);
+ }
+}
+
+static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb = ctx->tb;
+
+ if (ctx->singlestep == 0) {
+ tcg_gen_goto_tb(n);
+ tcg_gen_movi_i32(cpu_pc, dest);
+ tcg_gen_exit_tb(tb, n);
+ } else {
+ tcg_gen_movi_i32(cpu_pc, dest);
+ gen_helper_debug(cpu_env);
+ tcg_gen_exit_tb(NULL, 0);
+ }
+ ctx->bstate = DISAS_NORETURN;
+}
+
+/*
+ * Relative jump to an address within PC - 2K +1 and PC + 2K (words). For
+ * AVR microcontrollers with Program memory not exceeding 4K words (8KB) this
+ * instruction can address the entire memory from every address location. See
+ * also JMP.
+ */
+static bool trans_RJMP(DisasContext *ctx, arg_RJMP *a)
+{
+ int dst = ctx->npc + a->imm;
+
+ gen_goto_tb(ctx, 0, dst);
+
+ return true;
+}
+
+/*
+ * Indirect jump to the address pointed to by the Z (16 bits) Pointer
+ * Register in the Register File. The Z-pointer Register is 16 bits wide and
+ * allows jump within the lowest 64K words (128KB) section of Program memory.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_IJMP(DisasContext *ctx, arg_IJMP *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_IJMP_ICALL)) {
+ return true;
+ }
+
+ gen_jmp_z(ctx);
+
+ return true;
+}
+
+/*
+ * Indirect jump to the address pointed to by the Z (16 bits) Pointer
+ * Register in the Register File and the EIND Register in the I/O space. This
+ * instruction allows for indirect jumps to the entire 4M (words) Program
+ * memory space. See also IJMP. This instruction is not available in all
+ * devices. Refer to the device specific instruction set summary.
+ */
+static bool trans_EIJMP(DisasContext *ctx, arg_EIJMP *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_EIJMP_EICALL)) {
+ return true;
+ }
+
+ gen_jmp_ez(ctx);
+ return true;
+}
+
+/*
+ * Jump to an address within the entire 4M (words) Program memory. See also
+ * RJMP. This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.0
+ */
+static bool trans_JMP(DisasContext *ctx, arg_JMP *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_JMP_CALL)) {
+ return true;
+ }
+
+ gen_goto_tb(ctx, 0, a->imm);
+
+ return true;
+}
+
+/*
+ * Relative call to an address within PC - 2K + 1 and PC + 2K (words). The
+ * return address (the instruction after the RCALL) is stored onto the Stack.
+ * See also CALL. For AVR microcontrollers with Program memory not exceeding 4K
+ * words (8KB) this instruction can address the entire memory from every
+ * address location. The Stack Pointer uses a post-decrement scheme during
+ * RCALL.
+ */
+static bool trans_RCALL(DisasContext *ctx, arg_RCALL *a)
+{
+ int ret = ctx->npc;
+ int dst = ctx->npc + a->imm;
+
+ gen_push_ret(ctx, ret);
+ gen_goto_tb(ctx, 0, dst);
+
+ return true;
+}
+
+/*
+ * Calls to a subroutine within the entire 4M (words) Program memory. The
+ * return address (to the instruction after the CALL) will be stored onto the
+ * Stack. See also RCALL. The Stack Pointer uses a post-decrement scheme during
+ * CALL. This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_ICALL(DisasContext *ctx, arg_ICALL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_IJMP_ICALL)) {
+ return true;
+ }
+
+ int ret = ctx->npc;
+
+ gen_push_ret(ctx, ret);
+ gen_jmp_z(ctx);
+
+ return true;
+}
+
+/*
+ * Indirect call of a subroutine pointed to by the Z (16 bits) Pointer
+ * Register in the Register File and the EIND Register in the I/O space. This
+ * instruction allows for indirect calls to the entire 4M (words) Program
+ * memory space. See also ICALL. The Stack Pointer uses a post-decrement scheme
+ * during EICALL. This instruction is not available in all devices. Refer to
+ * the device specific instruction set summary.
+ */
+static bool trans_EICALL(DisasContext *ctx, arg_EICALL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_EIJMP_EICALL)) {
+ return true;
+ }
+
+ int ret = ctx->npc;
+
+ gen_push_ret(ctx, ret);
+ gen_jmp_ez(ctx);
+ return true;
+}
+
+/*
+ * Calls to a subroutine within the entire Program memory. The return
+ * address (to the instruction after the CALL) will be stored onto the Stack.
+ * (See also RCALL). The Stack Pointer uses a post-decrement scheme during
+ * CALL. This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_CALL(DisasContext *ctx, arg_CALL *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_JMP_CALL)) {
+ return true;
+ }
+
+ int Imm = a->imm;
+ int ret = ctx->npc;
+
+ gen_push_ret(ctx, ret);
+ gen_goto_tb(ctx, 0, Imm);
+
+ return true;
+}
+
+/*
+ * Returns from subroutine. The return address is loaded from the STACK.
+ * The Stack Pointer uses a preincrement scheme during RET.
+ */
+static bool trans_RET(DisasContext *ctx, arg_RET *a)
+{
+ gen_pop_ret(ctx, cpu_pc);
+
+ ctx->bstate = DISAS_LOOKUP;
+ return true;
+}
+
+/*
+ * Returns from interrupt. The return address is loaded from the STACK and
+ * the Global Interrupt Flag is set. Note that the Status Register is not
+ * automatically stored when entering an interrupt routine, and it is not
+ * restored when returning from an interrupt routine. This must be handled by
+ * the application program. The Stack Pointer uses a pre-increment scheme
+ * during RETI.
+ */
+static bool trans_RETI(DisasContext *ctx, arg_RETI *a)
+{
+ gen_pop_ret(ctx, cpu_pc);
+ tcg_gen_movi_tl(cpu_If, 1);
+
+ /* Need to return to main loop to re-evaluate interrupts. */
+ ctx->bstate = DISAS_EXIT;
+ return true;
+}
+
+/*
+ * This instruction performs a compare between two registers Rd and Rr, and
+ * skips the next instruction if Rd = Rr.
+ */
+static bool trans_CPSE(DisasContext *ctx, arg_CPSE *a)
+{
+ ctx->skip_cond = TCG_COND_EQ;
+ ctx->skip_var0 = cpu_r[a->rd];
+ ctx->skip_var1 = cpu_r[a->rr];
+ return true;
+}
+
+/*
+ * This instruction performs a compare between two registers Rd and Rr.
+ * None of the registers are changed. All conditional branches can be used
+ * after this instruction.
+ */
+static bool trans_CP(DisasContext *ctx, arg_CP *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs a compare between two registers Rd and Rr and
+ * also takes into account the previous carry. None of the registers are
+ * changed. All conditional branches can be used after this instruction.
+ */
+static bool trans_CPC(DisasContext *ctx, arg_CPC *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+ TCGv R = tcg_temp_new_i32();
+ TCGv zero = tcg_const_i32(0);
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr - Cf */
+ tcg_gen_sub_tl(R, R, cpu_Cf);
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_NSf(R);
+
+ /*
+ * Previous value remains unchanged when the result is zero;
+ * cleared otherwise.
+ */
+ tcg_gen_movcond_tl(TCG_COND_EQ, cpu_Zf, R, zero, cpu_Zf, zero);
+
+ tcg_temp_free_i32(zero);
+ tcg_temp_free_i32(R);
+
+ return true;
+}
+
+/*
+ * This instruction performs a compare between register Rd and a constant.
+ * The register is not changed. All conditional branches can be used after this
+ * instruction.
+ */
+static bool trans_CPI(DisasContext *ctx, arg_CPI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int Imm = a->imm;
+ TCGv Rr = tcg_const_i32(Imm);
+ TCGv R = tcg_temp_new_i32();
+
+ tcg_gen_sub_tl(R, Rd, Rr); /* R = Rd - Rr */
+ tcg_gen_andi_tl(R, R, 0xff); /* make it 8 bits */
+
+ /* update status register */
+ gen_sub_CHf(R, Rd, Rr);
+ gen_sub_Vf(R, Rd, Rr);
+ gen_ZNSf(R);
+
+ tcg_temp_free_i32(R);
+ tcg_temp_free_i32(Rr);
+
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in a register and skips the next
+ * instruction if the bit is cleared.
+ */
+static bool trans_SBRC(DisasContext *ctx, arg_SBRC *a)
+{
+ TCGv Rr = cpu_r[a->rr];
+
+ ctx->skip_cond = TCG_COND_EQ;
+ ctx->skip_var0 = tcg_temp_new();
+ ctx->free_skip_var0 = true;
+
+ tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit);
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in a register and skips the next
+ * instruction if the bit is set.
+ */
+static bool trans_SBRS(DisasContext *ctx, arg_SBRS *a)
+{
+ TCGv Rr = cpu_r[a->rr];
+
+ ctx->skip_cond = TCG_COND_NE;
+ ctx->skip_var0 = tcg_temp_new();
+ ctx->free_skip_var0 = true;
+
+ tcg_gen_andi_tl(ctx->skip_var0, Rr, 1 << a->bit);
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in an I/O Register and skips the
+ * next instruction if the bit is cleared. This instruction operates on the
+ * lower 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a)
+{
+ TCGv temp = tcg_const_i32(a->reg);
+
+ gen_helper_inb(temp, cpu_env, temp);
+ tcg_gen_andi_tl(temp, temp, 1 << a->bit);
+ ctx->skip_cond = TCG_COND_EQ;
+ ctx->skip_var0 = temp;
+ ctx->free_skip_var0 = true;
+
+ return true;
+}
+
+/*
+ * This instruction tests a single bit in an I/O Register and skips the
+ * next instruction if the bit is set. This instruction operates on the lower
+ * 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a)
+{
+ TCGv temp = tcg_const_i32(a->reg);
+
+ gen_helper_inb(temp, cpu_env, temp);
+ tcg_gen_andi_tl(temp, temp, 1 << a->bit);
+ ctx->skip_cond = TCG_COND_NE;
+ ctx->skip_var0 = temp;
+ ctx->free_skip_var0 = true;
+
+ return true;
+}
+
+/*
+ * Conditional relative branch. Tests a single bit in SREG and branches
+ * relatively to PC if the bit is cleared. This instruction branches relatively
+ * to PC in either direction (PC - 63 < = destination <= PC + 64). The
+ * parameter k is the offset from PC and is represented in two's complement
+ * form.
+ */
+static bool trans_BRBC(DisasContext *ctx, arg_BRBC *a)
+{
+ TCGLabel *not_taken = gen_new_label();
+
+ TCGv var;
+
+ switch (a->bit) {
+ case 0x00:
+ var = cpu_Cf;
+ break;
+ case 0x01:
+ var = cpu_Zf;
+ break;
+ case 0x02:
+ var = cpu_Nf;
+ break;
+ case 0x03:
+ var = cpu_Vf;
+ break;
+ case 0x04:
+ var = cpu_Sf;
+ break;
+ case 0x05:
+ var = cpu_Hf;
+ break;
+ case 0x06:
+ var = cpu_Tf;
+ break;
+ case 0x07:
+ var = cpu_If;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_gen_brcondi_i32(TCG_COND_NE, var, 0, not_taken);
+ gen_goto_tb(ctx, 0, ctx->npc + a->imm);
+ gen_set_label(not_taken);
+
+ ctx->bstate = DISAS_CHAIN;
+ return true;
+}
+
+/*
+ * Conditional relative branch. Tests a single bit in SREG and branches
+ * relatively to PC if the bit is set. This instruction branches relatively to
+ * PC in either direction (PC - 63 < = destination <= PC + 64). The parameter k
+ * is the offset from PC and is represented in two's complement form.
+ */
+static bool trans_BRBS(DisasContext *ctx, arg_BRBS *a)
+{
+ TCGLabel *not_taken = gen_new_label();
+
+ TCGv var;
+
+ switch (a->bit) {
+ case 0x00:
+ var = cpu_Cf;
+ break;
+ case 0x01:
+ var = cpu_Zf;
+ break;
+ case 0x02:
+ var = cpu_Nf;
+ break;
+ case 0x03:
+ var = cpu_Vf;
+ break;
+ case 0x04:
+ var = cpu_Sf;
+ break;
+ case 0x05:
+ var = cpu_Hf;
+ break;
+ case 0x06:
+ var = cpu_Tf;
+ break;
+ case 0x07:
+ var = cpu_If;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_gen_brcondi_i32(TCG_COND_EQ, var, 0, not_taken);
+ gen_goto_tb(ctx, 0, ctx->npc + a->imm);
+ gen_set_label(not_taken);
+
+ ctx->bstate = DISAS_CHAIN;
+ return true;
+}
+
+/*
+ * Data Transfer Instructions
+ */
+
+/*
+ * in the gen_set_addr & gen_get_addr functions
+ * H assumed to be in 0x00ff0000 format
+ * M assumed to be in 0x000000ff format
+ * L assumed to be in 0x000000ff format
+ */
+static void gen_set_addr(TCGv addr, TCGv H, TCGv M, TCGv L)
+{
+
+ tcg_gen_andi_tl(L, addr, 0x000000ff);
+
+ tcg_gen_andi_tl(M, addr, 0x0000ff00);
+ tcg_gen_shri_tl(M, M, 8);
+
+ tcg_gen_andi_tl(H, addr, 0x00ff0000);
+}
+
+static void gen_set_xaddr(TCGv addr)
+{
+ gen_set_addr(addr, cpu_rampX, cpu_r[27], cpu_r[26]);
+}
+
+static void gen_set_yaddr(TCGv addr)
+{
+ gen_set_addr(addr, cpu_rampY, cpu_r[29], cpu_r[28]);
+}
+
+static void gen_set_zaddr(TCGv addr)
+{
+ gen_set_addr(addr, cpu_rampZ, cpu_r[31], cpu_r[30]);
+}
+
+static TCGv gen_get_addr(TCGv H, TCGv M, TCGv L)
+{
+ TCGv addr = tcg_temp_new_i32();
+
+ tcg_gen_deposit_tl(addr, M, H, 8, 8);
+ tcg_gen_deposit_tl(addr, L, addr, 8, 16);
+
+ return addr;
+}
+
+static TCGv gen_get_xaddr(void)
+{
+ return gen_get_addr(cpu_rampX, cpu_r[27], cpu_r[26]);
+}
+
+static TCGv gen_get_yaddr(void)
+{
+ return gen_get_addr(cpu_rampY, cpu_r[29], cpu_r[28]);
+}
+
+static TCGv gen_get_zaddr(void)
+{
+ return gen_get_addr(cpu_rampZ, cpu_r[31], cpu_r[30]);
+}
+
+/*
+ * Load one byte indirect from data space to register and stores an clear
+ * the bits in data space specified by the register. The instruction can only
+ * be used towards internal SRAM. The data location is pointed to by the Z (16
+ * bits) Pointer Register in the Register File. Memory access is limited to the
+ * current data segment of 64KB. To access another data segment in devices with
+ * more than 64KB data space, the RAMPZ in register in the I/O area has to be
+ * changed. The Z-pointer Register is left unchanged by the operation. This
+ * instruction is especially suited for clearing status bits stored in SRAM.
+ */
+static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr)
+{
+ if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) {
+ gen_helper_fullwr(cpu_env, data, addr);
+ } else {
+ tcg_gen_qemu_st8(data, addr, MMU_DATA_IDX); /* mem[addr] = data */
+ }
+}
+
+static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr)
+{
+ if (ctx->tb->flags & TB_FLAGS_FULL_ACCESS) {
+ gen_helper_fullrd(data, cpu_env, addr);
+ } else {
+ tcg_gen_qemu_ld8u(data, addr, MMU_DATA_IDX); /* data = mem[addr] */
+ }
+}
+
+/*
+ * This instruction makes a copy of one register into another. The source
+ * register Rr is left unchanged, while the destination register Rd is loaded
+ * with a copy of Rr.
+ */
+static bool trans_MOV(DisasContext *ctx, arg_MOV *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv Rr = cpu_r[a->rr];
+
+ tcg_gen_mov_tl(Rd, Rr);
+
+ return true;
+}
+
+/*
+ * This instruction makes a copy of one register pair into another register
+ * pair. The source register pair Rr+1:Rr is left unchanged, while the
+ * destination register pair Rd+1:Rd is loaded with a copy of Rr + 1:Rr. This
+ * instruction is not available in all devices. Refer to the device specific
+ * instruction set summary.
+ */
+static bool trans_MOVW(DisasContext *ctx, arg_MOVW *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_MOVW)) {
+ return true;
+ }
+
+ TCGv RdL = cpu_r[a->rd];
+ TCGv RdH = cpu_r[a->rd + 1];
+ TCGv RrL = cpu_r[a->rr];
+ TCGv RrH = cpu_r[a->rr + 1];
+
+ tcg_gen_mov_tl(RdH, RrH);
+ tcg_gen_mov_tl(RdL, RrL);
+
+ return true;
+}
+
+/*
+ * Loads an 8 bit constant directly to register 16 to 31.
+ */
+static bool trans_LDI(DisasContext *ctx, arg_LDI *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ int imm = a->imm;
+
+ tcg_gen_movi_tl(Rd, imm);
+
+ return true;
+}
+
+/*
+ * Loads one byte from the data space to a register. For parts with SRAM,
+ * the data space consists of the Register File, I/O memory and internal SRAM
+ * (and external SRAM if applicable). For parts without SRAM, the data space
+ * consists of the register file only. The EEPROM has a separate address space.
+ * A 16-bit address must be supplied. Memory access is limited to the current
+ * data segment of 64KB. The LDS instruction uses the RAMPD Register to access
+ * memory above 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPD in register in the I/O area has to be changed.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_LDS(DisasContext *ctx, arg_LDS *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_rampD;
+ a->imm = next_word(ctx);
+
+ tcg_gen_mov_tl(addr, H); /* addr = H:M:L */
+ tcg_gen_shli_tl(addr, addr, 16);
+ tcg_gen_ori_tl(addr, addr, a->imm);
+
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte indirect from the data space to a register. For parts
+ * with SRAM, the data space consists of the Register File, I/O memory and
+ * internal SRAM (and external SRAM if applicable). For parts without SRAM, the
+ * data space consists of the Register File only. In some parts the Flash
+ * Memory has been mapped to the data space and can be read using this command.
+ * The EEPROM has a separate address space. The data location is pointed to by
+ * the X (16 bits) Pointer Register in the Register File. Memory access is
+ * limited to the current data segment of 64KB. To access another data segment
+ * in devices with more than 64KB data space, the RAMPX in register in the I/O
+ * area has to be changed. The X-pointer Register can either be left unchanged
+ * by the operation, or it can be post-incremented or predecremented. These
+ * features are especially suited for accessing arrays, tables, and Stack
+ * Pointer usage of the X-pointer Register. Note that only the low byte of the
+ * X-pointer is updated in devices with no more than 256 bytes data space. For
+ * such devices, the high byte of the pointer is not used by this instruction
+ * and can be used for other purposes. The RAMPX Register in the I/O area is
+ * updated in parts with more than 64KB data space or more than 64KB Program
+ * memory, and the increment/decrement is added to the entire 24-bit address on
+ * such devices. Not all variants of this instruction is available in all
+ * devices. Refer to the device specific instruction set summary. In the
+ * Reduced Core tinyAVR the LD instruction can be used to achieve the same
+ * operation as LPM since the program memory is mapped to the data memory
+ * space.
+ */
+static bool trans_LDX1(DisasContext *ctx, arg_LDX1 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDX2(DisasContext *ctx, arg_LDX2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_load(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDX3(DisasContext *ctx, arg_LDX3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_xaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_load(ctx, Rd, addr);
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte indirect with or without displacement from the data space
+ * to a register. For parts with SRAM, the data space consists of the Register
+ * File, I/O memory and internal SRAM (and external SRAM if applicable). For
+ * parts without SRAM, the data space consists of the Register File only. In
+ * some parts the Flash Memory has been mapped to the data space and can be
+ * read using this command. The EEPROM has a separate address space. The data
+ * location is pointed to by the Y (16 bits) Pointer Register in the Register
+ * File. Memory access is limited to the current data segment of 64KB. To
+ * access another data segment in devices with more than 64KB data space, the
+ * RAMPY in register in the I/O area has to be changed. The Y-pointer Register
+ * can either be left unchanged by the operation, or it can be post-incremented
+ * or predecremented. These features are especially suited for accessing
+ * arrays, tables, and Stack Pointer usage of the Y-pointer Register. Note that
+ * only the low byte of the Y-pointer is updated in devices with no more than
+ * 256 bytes data space. For such devices, the high byte of the pointer is not
+ * used by this instruction and can be used for other purposes. The RAMPY
+ * Register in the I/O area is updated in parts with more than 64KB data space
+ * or more than 64KB Program memory, and the increment/decrement/displacement
+ * is added to the entire 24-bit address on such devices. Not all variants of
+ * this instruction is available in all devices. Refer to the device specific
+ * instruction set summary. In the Reduced Core tinyAVR the LD instruction can
+ * be used to achieve the same operation as LPM since the program memory is
+ * mapped to the data memory space.
+ */
+static bool trans_LDY2(DisasContext *ctx, arg_LDY2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ gen_data_load(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDY3(DisasContext *ctx, arg_LDY3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_load(ctx, Rd, addr);
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDDY(DisasContext *ctx, arg_LDDY *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte indirect with or without displacement from the data space
+ * to a register. For parts with SRAM, the data space consists of the Register
+ * File, I/O memory and internal SRAM (and external SRAM if applicable). For
+ * parts without SRAM, the data space consists of the Register File only. In
+ * some parts the Flash Memory has been mapped to the data space and can be
+ * read using this command. The EEPROM has a separate address space. The data
+ * location is pointed to by the Z (16 bits) Pointer Register in the Register
+ * File. Memory access is limited to the current data segment of 64KB. To
+ * access another data segment in devices with more than 64KB data space, the
+ * RAMPZ in register in the I/O area has to be changed. The Z-pointer Register
+ * can either be left unchanged by the operation, or it can be post-incremented
+ * or predecremented. These features are especially suited for Stack Pointer
+ * usage of the Z-pointer Register, however because the Z-pointer Register can
+ * be used for indirect subroutine calls, indirect jumps and table lookup, it
+ * is often more convenient to use the X or Y-pointer as a dedicated Stack
+ * Pointer. Note that only the low byte of the Z-pointer is updated in devices
+ * with no more than 256 bytes data space. For such devices, the high byte of
+ * the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPZ Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the
+ * increment/decrement/displacement is added to the entire 24-bit address on
+ * such devices. Not all variants of this instruction is available in all
+ * devices. Refer to the device specific instruction set summary. In the
+ * Reduced Core tinyAVR the LD instruction can be used to achieve the same
+ * operation as LPM since the program memory is mapped to the data memory
+ * space. For using the Z-pointer for table lookup in Program memory see the
+ * LPM and ELPM instructions.
+ */
+static bool trans_LDZ2(DisasContext *ctx, arg_LDZ2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ gen_data_load(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDZ3(DisasContext *ctx, arg_LDZ3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_load(ctx, Rd, addr);
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LDDZ(DisasContext *ctx, arg_LDDZ *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_load(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte from a Register to the data space. For parts with SRAM,
+ * the data space consists of the Register File, I/O memory and internal SRAM
+ * (and external SRAM if applicable). For parts without SRAM, the data space
+ * consists of the Register File only. The EEPROM has a separate address space.
+ * A 16-bit address must be supplied. Memory access is limited to the current
+ * data segment of 64KB. The STS instruction uses the RAMPD Register to access
+ * memory above 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPD in register in the I/O area has to be changed.
+ * This instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ */
+static bool trans_STS(DisasContext *ctx, arg_STS *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_rampD;
+ a->imm = next_word(ctx);
+
+ tcg_gen_mov_tl(addr, H); /* addr = H:M:L */
+ tcg_gen_shli_tl(addr, addr, 16);
+ tcg_gen_ori_tl(addr, addr, a->imm);
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte indirect from a register to data space. For parts with SRAM,
+ * the data space consists of the Register File, I/O memory, and internal SRAM
+ * (and external SRAM if applicable). For parts without SRAM, the data space
+ * consists of the Register File only. The EEPROM has a separate address space.
+ *
+ * The data location is pointed to by the X (16 bits) Pointer Register in the
+ * Register File. Memory access is limited to the current data segment of 64KB.
+ * To access another data segment in devices with more than 64KB data space, the
+ * RAMPX in register in the I/O area has to be changed.
+ *
+ * The X-pointer Register can either be left unchanged by the operation, or it
+ * can be post-incremented or pre-decremented. These features are especially
+ * suited for accessing arrays, tables, and Stack Pointer usage of the
+ * X-pointer Register. Note that only the low byte of the X-pointer is updated
+ * in devices with no more than 256 bytes data space. For such devices, the high
+ * byte of the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPX Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the increment /
+ * decrement is added to the entire 24-bit address on such devices.
+ */
+static bool trans_STX1(DisasContext *ctx, arg_STX1 *a)
+{
+ TCGv Rd = cpu_r[a->rr];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STX2(DisasContext *ctx, arg_STX2 *a)
+{
+ TCGv Rd = cpu_r[a->rr];
+ TCGv addr = gen_get_xaddr();
+
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STX3(DisasContext *ctx, arg_STX3 *a)
+{
+ TCGv Rd = cpu_r[a->rr];
+ TCGv addr = gen_get_xaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_store(ctx, Rd, addr);
+ gen_set_xaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte indirect with or without displacement from a register to data
+ * space. For parts with SRAM, the data space consists of the Register File, I/O
+ * memory, and internal SRAM (and external SRAM if applicable). For parts
+ * without SRAM, the data space consists of the Register File only. The EEPROM
+ * has a separate address space.
+ *
+ * The data location is pointed to by the Y (16 bits) Pointer Register in the
+ * Register File. Memory access is limited to the current data segment of 64KB.
+ * To access another data segment in devices with more than 64KB data space, the
+ * RAMPY in register in the I/O area has to be changed.
+ *
+ * The Y-pointer Register can either be left unchanged by the operation, or it
+ * can be post-incremented or pre-decremented. These features are especially
+ * suited for accessing arrays, tables, and Stack Pointer usage of the Y-pointer
+ * Register. Note that only the low byte of the Y-pointer is updated in devices
+ * with no more than 256 bytes data space. For such devices, the high byte of
+ * the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPY Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the increment /
+ * decrement / displacement is added to the entire 24-bit address on such
+ * devices.
+ */
+static bool trans_STY2(DisasContext *ctx, arg_STY2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STY3(DisasContext *ctx, arg_STY3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_store(ctx, Rd, addr);
+ gen_set_yaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STDY(DisasContext *ctx, arg_STDY *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_yaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Stores one byte indirect with or without displacement from a register to data
+ * space. For parts with SRAM, the data space consists of the Register File, I/O
+ * memory, and internal SRAM (and external SRAM if applicable). For parts
+ * without SRAM, the data space consists of the Register File only. The EEPROM
+ * has a separate address space.
+ *
+ * The data location is pointed to by the Y (16 bits) Pointer Register in the
+ * Register File. Memory access is limited to the current data segment of 64KB.
+ * To access another data segment in devices with more than 64KB data space, the
+ * RAMPY in register in the I/O area has to be changed.
+ *
+ * The Y-pointer Register can either be left unchanged by the operation, or it
+ * can be post-incremented or pre-decremented. These features are especially
+ * suited for accessing arrays, tables, and Stack Pointer usage of the Y-pointer
+ * Register. Note that only the low byte of the Y-pointer is updated in devices
+ * with no more than 256 bytes data space. For such devices, the high byte of
+ * the pointer is not used by this instruction and can be used for other
+ * purposes. The RAMPY Register in the I/O area is updated in parts with more
+ * than 64KB data space or more than 64KB Program memory, and the increment /
+ * decrement / displacement is added to the entire 24-bit address on such
+ * devices.
+ */
+static bool trans_STZ2(DisasContext *ctx, arg_STZ2 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STZ3(DisasContext *ctx, arg_STZ3 *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_subi_tl(addr, addr, 1); /* addr = addr - 1 */
+ gen_data_store(ctx, Rd, addr);
+
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_STDZ(DisasContext *ctx, arg_STDZ *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_addi_tl(addr, addr, a->imm); /* addr = addr + q */
+ gen_data_store(ctx, Rd, addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte pointed to by the Z-register into the destination
+ * register Rd. This instruction features a 100% space effective constant
+ * initialization or constant data fetch. The Program memory is organized in
+ * 16-bit words while the Z-pointer is a byte address. Thus, the least
+ * significant bit of the Z-pointer selects either low byte (ZLSB = 0) or high
+ * byte (ZLSB = 1). This instruction can address the first 64KB (32K words) of
+ * Program memory. The Zpointer Register can either be left unchanged by the
+ * operation, or it can be incremented. The incrementation does not apply to
+ * the RAMPZ Register.
+ *
+ * Devices with Self-Programming capability can use the LPM instruction to read
+ * the Fuse and Lock bit values.
+ */
+static bool trans_LPM1(DisasContext *ctx, arg_LPM1 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_LPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[0];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_r[31];
+ TCGv L = cpu_r[30];
+
+ tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */
+ tcg_gen_or_tl(addr, addr, L);
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LPM2(DisasContext *ctx, arg_LPM2 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_LPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_r[31];
+ TCGv L = cpu_r[30];
+
+ tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */
+ tcg_gen_or_tl(addr, addr, L);
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_LPMX(DisasContext *ctx, arg_LPMX *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_LPMX)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = tcg_temp_new_i32();
+ TCGv H = cpu_r[31];
+ TCGv L = cpu_r[30];
+
+ tcg_gen_shli_tl(addr, H, 8); /* addr = H:L */
+ tcg_gen_or_tl(addr, addr, L);
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ tcg_gen_andi_tl(L, addr, 0xff);
+ tcg_gen_shri_tl(addr, addr, 8);
+ tcg_gen_andi_tl(H, addr, 0xff);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Loads one byte pointed to by the Z-register and the RAMPZ Register in
+ * the I/O space, and places this byte in the destination register Rd. This
+ * instruction features a 100% space effective constant initialization or
+ * constant data fetch. The Program memory is organized in 16-bit words while
+ * the Z-pointer is a byte address. Thus, the least significant bit of the
+ * Z-pointer selects either low byte (ZLSB = 0) or high byte (ZLSB = 1). This
+ * instruction can address the entire Program memory space. The Z-pointer
+ * Register can either be left unchanged by the operation, or it can be
+ * incremented. The incrementation applies to the entire 24-bit concatenation
+ * of the RAMPZ and Z-pointer Registers.
+ *
+ * Devices with Self-Programming capability can use the ELPM instruction to
+ * read the Fuse and Lock bit value.
+ */
+static bool trans_ELPM1(DisasContext *ctx, arg_ELPM1 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ELPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[0];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_ELPM2(DisasContext *ctx, arg_ELPM2 *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ELPM)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+static bool trans_ELPMX(DisasContext *ctx, arg_ELPMX *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_ELPMX)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+
+ tcg_gen_qemu_ld8u(Rd, addr, MMU_CODE_IDX); /* Rd = mem[addr] */
+ tcg_gen_addi_tl(addr, addr, 1); /* addr = addr + 1 */
+ gen_set_zaddr(addr);
+
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * SPM can be used to erase a page in the Program memory, to write a page
+ * in the Program memory (that is already erased), and to set Boot Loader Lock
+ * bits. In some devices, the Program memory can be written one word at a time,
+ * in other devices an entire page can be programmed simultaneously after first
+ * filling a temporary page buffer. In all cases, the Program memory must be
+ * erased one page at a time. When erasing the Program memory, the RAMPZ and
+ * Z-register are used as page address. When writing the Program memory, the
+ * RAMPZ and Z-register are used as page or word address, and the R1:R0
+ * register pair is used as data(1). When setting the Boot Loader Lock bits,
+ * the R1:R0 register pair is used as data. Refer to the device documentation
+ * for detailed description of SPM usage. This instruction can address the
+ * entire Program memory.
+ *
+ * The SPM instruction is not available in all devices. Refer to the device
+ * specific instruction set summary.
+ *
+ * Note: 1. R1 determines the instruction high byte, and R0 determines the
+ * instruction low byte.
+ */
+static bool trans_SPM(DisasContext *ctx, arg_SPM *a)
+{
+ /* TODO */
+ if (!avr_have_feature(ctx, AVR_FEATURE_SPM)) {
+ return true;
+ }
+
+ return true;
+}
+
+static bool trans_SPMX(DisasContext *ctx, arg_SPMX *a)
+{
+ /* TODO */
+ if (!avr_have_feature(ctx, AVR_FEATURE_SPMX)) {
+ return true;
+ }
+
+ return true;
+}
+
+/*
+ * Loads data from the I/O Space (Ports, Timers, Configuration Registers,
+ * etc.) into register Rd in the Register File.
+ */
+static bool trans_IN(DisasContext *ctx, arg_IN *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv port = tcg_const_i32(a->imm);
+
+ gen_helper_inb(Rd, cpu_env, port);
+
+ tcg_temp_free_i32(port);
+
+ return true;
+}
+
+/*
+ * Stores data from register Rr in the Register File to I/O Space (Ports,
+ * Timers, Configuration Registers, etc.).
+ */
+static bool trans_OUT(DisasContext *ctx, arg_OUT *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv port = tcg_const_i32(a->imm);
+
+ gen_helper_outb(cpu_env, port, Rd);
+
+ tcg_temp_free_i32(port);
+
+ return true;
+}
+
+/*
+ * This instruction stores the contents of register Rr on the STACK. The
+ * Stack Pointer is post-decremented by 1 after the PUSH. This instruction is
+ * not available in all devices. Refer to the device specific instruction set
+ * summary.
+ */
+static bool trans_PUSH(DisasContext *ctx, arg_PUSH *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ gen_data_store(ctx, Rd, cpu_sp);
+ tcg_gen_subi_tl(cpu_sp, cpu_sp, 1);
+
+ return true;
+}
+
+/*
+ * This instruction loads register Rd with a byte from the STACK. The Stack
+ * Pointer is pre-incremented by 1 before the POP. This instruction is not
+ * available in all devices. Refer to the device specific instruction set
+ * summary.
+ */
+static bool trans_POP(DisasContext *ctx, arg_POP *a)
+{
+ /*
+ * Using a temp to work around some strange behaviour:
+ * tcg_gen_addi_tl(cpu_sp, cpu_sp, 1);
+ * gen_data_load(ctx, Rd, cpu_sp);
+ * seems to cause the add to happen twice.
+ * This doesn't happen if either the add or the load is removed.
+ */
+ TCGv t1 = tcg_temp_new_i32();
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_addi_tl(t1, cpu_sp, 1);
+ gen_data_load(ctx, Rd, t1);
+ tcg_gen_mov_tl(cpu_sp, t1);
+
+ return true;
+}
+
+/*
+ * Exchanges one byte indirect between register and data space. The data
+ * location is pointed to by the Z (16 bits) Pointer Register in the Register
+ * File. Memory access is limited to the current data segment of 64KB. To
+ * access another data segment in devices with more than 64KB data space, the
+ * RAMPZ in register in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for writing/reading status bits stored in SRAM.
+ */
+static bool trans_XCH(DisasContext *ctx, arg_XCH *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv addr = gen_get_zaddr();
+
+ gen_data_load(ctx, t0, addr);
+ gen_data_store(ctx, Rd, addr);
+ tcg_gen_mov_tl(Rd, t0);
+
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Load one byte indirect from data space to register and set bits in data
+ * space specified by the register. The instruction can only be used towards
+ * internal SRAM. The data location is pointed to by the Z (16 bits) Pointer
+ * Register in the Register File. Memory access is limited to the current data
+ * segment of 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPZ in register in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for setting status bits stored in SRAM.
+ */
+static bool trans_LAS(DisasContext *ctx, arg_LAS *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rr = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ gen_data_load(ctx, t0, addr); /* t0 = mem[addr] */
+ tcg_gen_or_tl(t1, t0, Rr);
+ tcg_gen_mov_tl(Rr, t0); /* Rr = t0 */
+ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Load one byte indirect from data space to register and stores and clear
+ * the bits in data space specified by the register. The instruction can
+ * only be used towards internal SRAM. The data location is pointed to by
+ * the Z (16 bits) Pointer Register in the Register File. Memory access is
+ * limited to the current data segment of 64KB. To access another data
+ * segment in devices with more than 64KB data space, the RAMPZ in register
+ * in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for clearing status bits stored in SRAM.
+ */
+static bool trans_LAC(DisasContext *ctx, arg_LAC *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rr = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ gen_data_load(ctx, t0, addr); /* t0 = mem[addr] */
+ tcg_gen_andc_tl(t1, t0, Rr); /* t1 = t0 & (0xff - Rr) = t0 & ~Rr */
+ tcg_gen_mov_tl(Rr, t0); /* Rr = t0 */
+ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+
+/*
+ * Load one byte indirect from data space to register and toggles bits in
+ * the data space specified by the register. The instruction can only be used
+ * towards SRAM. The data location is pointed to by the Z (16 bits) Pointer
+ * Register in the Register File. Memory access is limited to the current data
+ * segment of 64KB. To access another data segment in devices with more than
+ * 64KB data space, the RAMPZ in register in the I/O area has to be changed.
+ *
+ * The Z-pointer Register is left unchanged by the operation. This instruction
+ * is especially suited for changing status bits stored in SRAM.
+ */
+static bool trans_LAT(DisasContext *ctx, arg_LAT *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_RMW)) {
+ return true;
+ }
+
+ TCGv Rd = cpu_r[a->rd];
+ TCGv addr = gen_get_zaddr();
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ gen_data_load(ctx, t0, addr); /* t0 = mem[addr] */
+ tcg_gen_xor_tl(t1, t0, Rd);
+ tcg_gen_mov_tl(Rd, t0); /* Rd = t0 */
+ gen_data_store(ctx, t1, addr); /* mem[addr] = t1 */
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+ tcg_temp_free_i32(addr);
+
+ return true;
+}
+
+/*
+ * Bit and Bit-test Instructions
+ */
+static void gen_rshift_ZNVSf(TCGv R)
+{
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, R, 0); /* Zf = R == 0 */
+ tcg_gen_shri_tl(cpu_Nf, R, 7); /* Nf = R(7) */
+ tcg_gen_xor_tl(cpu_Vf, cpu_Nf, cpu_Cf);
+ tcg_gen_xor_tl(cpu_Sf, cpu_Nf, cpu_Vf); /* Sf = Nf ^ Vf */
+}
+
+/*
+ * Shifts all bits in Rd one place to the right. Bit 7 is cleared. Bit 0 is
+ * loaded into the C Flag of the SREG. This operation effectively divides an
+ * unsigned value by two. The C Flag can be used to round the result.
+ */
+static bool trans_LSR(DisasContext *ctx, arg_LSR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_andi_tl(cpu_Cf, Rd, 1);
+ tcg_gen_shri_tl(Rd, Rd, 1);
+
+ /* update status register */
+ tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_Zf, Rd, 0); /* Zf = Rd == 0 */
+ tcg_gen_movi_tl(cpu_Nf, 0);
+ tcg_gen_mov_tl(cpu_Vf, cpu_Cf);
+ tcg_gen_mov_tl(cpu_Sf, cpu_Vf);
+
+ return true;
+}
+
+/*
+ * Shifts all bits in Rd one place to the right. The C Flag is shifted into
+ * bit 7 of Rd. Bit 0 is shifted into the C Flag. This operation, combined
+ * with ASR, effectively divides multi-byte signed values by two. Combined with
+ * LSR it effectively divides multi-byte unsigned values by two. The Carry Flag
+ * can be used to round the result.
+ */
+static bool trans_ROR(DisasContext *ctx, arg_ROR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+
+ tcg_gen_shli_tl(t0, cpu_Cf, 7);
+
+ /* update status register */
+ tcg_gen_andi_tl(cpu_Cf, Rd, 1);
+
+ /* update output register */
+ tcg_gen_shri_tl(Rd, Rd, 1);
+ tcg_gen_or_tl(Rd, Rd, t0);
+
+ /* update status register */
+ gen_rshift_ZNVSf(Rd);
+
+ tcg_temp_free_i32(t0);
+
+ return true;
+}
+
+/*
+ * Shifts all bits in Rd one place to the right. Bit 7 is held constant. Bit 0
+ * is loaded into the C Flag of the SREG. This operation effectively divides a
+ * signed value by two without changing its sign. The Carry Flag can be used to
+ * round the result.
+ */
+static bool trans_ASR(DisasContext *ctx, arg_ASR *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+
+ /* update status register */
+ tcg_gen_andi_tl(cpu_Cf, Rd, 1); /* Cf = Rd(0) */
+
+ /* update output register */
+ tcg_gen_andi_tl(t0, Rd, 0x80); /* Rd = (Rd & 0x80) | (Rd >> 1) */
+ tcg_gen_shri_tl(Rd, Rd, 1);
+ tcg_gen_or_tl(Rd, Rd, t0);
+
+ /* update status register */
+ gen_rshift_ZNVSf(Rd);
+
+ tcg_temp_free_i32(t0);
+
+ return true;
+}
+
+/*
+ * Swaps high and low nibbles in a register.
+ */
+static bool trans_SWAP(DisasContext *ctx, arg_SWAP *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t0 = tcg_temp_new_i32();
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_andi_tl(t0, Rd, 0x0f);
+ tcg_gen_shli_tl(t0, t0, 4);
+ tcg_gen_andi_tl(t1, Rd, 0xf0);
+ tcg_gen_shri_tl(t1, t1, 4);
+ tcg_gen_or_tl(Rd, t0, t1);
+
+ tcg_temp_free_i32(t1);
+ tcg_temp_free_i32(t0);
+
+ return true;
+}
+
+/*
+ * Sets a specified bit in an I/O Register. This instruction operates on
+ * the lower 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_SBI(DisasContext *ctx, arg_SBI *a)
+{
+ TCGv data = tcg_temp_new_i32();
+ TCGv port = tcg_const_i32(a->reg);
+
+ gen_helper_inb(data, cpu_env, port);
+ tcg_gen_ori_tl(data, data, 1 << a->bit);
+ gen_helper_outb(cpu_env, port, data);
+
+ tcg_temp_free_i32(port);
+ tcg_temp_free_i32(data);
+
+ return true;
+}
+
+/*
+ * Clears a specified bit in an I/O Register. This instruction operates on
+ * the lower 32 I/O Registers -- addresses 0-31.
+ */
+static bool trans_CBI(DisasContext *ctx, arg_CBI *a)
+{
+ TCGv data = tcg_temp_new_i32();
+ TCGv port = tcg_const_i32(a->reg);
+
+ gen_helper_inb(data, cpu_env, port);
+ tcg_gen_andi_tl(data, data, ~(1 << a->bit));
+ gen_helper_outb(cpu_env, port, data);
+
+ tcg_temp_free_i32(data);
+ tcg_temp_free_i32(port);
+
+ return true;
+}
+
+/*
+ * Stores bit b from Rd to the T Flag in SREG (Status Register).
+ */
+static bool trans_BST(DisasContext *ctx, arg_BST *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+
+ tcg_gen_andi_tl(cpu_Tf, Rd, 1 << a->bit);
+ tcg_gen_shri_tl(cpu_Tf, cpu_Tf, a->bit);
+
+ return true;
+}
+
+/*
+ * Copies the T Flag in the SREG (Status Register) to bit b in register Rd.
+ */
+static bool trans_BLD(DisasContext *ctx, arg_BLD *a)
+{
+ TCGv Rd = cpu_r[a->rd];
+ TCGv t1 = tcg_temp_new_i32();
+
+ tcg_gen_andi_tl(Rd, Rd, ~(1u << a->bit)); /* clear bit */
+ tcg_gen_shli_tl(t1, cpu_Tf, a->bit); /* create mask */
+ tcg_gen_or_tl(Rd, Rd, t1);
+
+ tcg_temp_free_i32(t1);
+
+ return true;
+}
+
+/*
+ * Sets a single Flag or bit in SREG.
+ */
+static bool trans_BSET(DisasContext *ctx, arg_BSET *a)
+{
+ switch (a->bit) {
+ case 0x00:
+ tcg_gen_movi_tl(cpu_Cf, 0x01);
+ break;
+ case 0x01:
+ tcg_gen_movi_tl(cpu_Zf, 0x01);
+ break;
+ case 0x02:
+ tcg_gen_movi_tl(cpu_Nf, 0x01);
+ break;
+ case 0x03:
+ tcg_gen_movi_tl(cpu_Vf, 0x01);
+ break;
+ case 0x04:
+ tcg_gen_movi_tl(cpu_Sf, 0x01);
+ break;
+ case 0x05:
+ tcg_gen_movi_tl(cpu_Hf, 0x01);
+ break;
+ case 0x06:
+ tcg_gen_movi_tl(cpu_Tf, 0x01);
+ break;
+ case 0x07:
+ tcg_gen_movi_tl(cpu_If, 0x01);
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * Clears a single Flag in SREG.
+ */
+static bool trans_BCLR(DisasContext *ctx, arg_BCLR *a)
+{
+ switch (a->bit) {
+ case 0x00:
+ tcg_gen_movi_tl(cpu_Cf, 0x00);
+ break;
+ case 0x01:
+ tcg_gen_movi_tl(cpu_Zf, 0x00);
+ break;
+ case 0x02:
+ tcg_gen_movi_tl(cpu_Nf, 0x00);
+ break;
+ case 0x03:
+ tcg_gen_movi_tl(cpu_Vf, 0x00);
+ break;
+ case 0x04:
+ tcg_gen_movi_tl(cpu_Sf, 0x00);
+ break;
+ case 0x05:
+ tcg_gen_movi_tl(cpu_Hf, 0x00);
+ break;
+ case 0x06:
+ tcg_gen_movi_tl(cpu_Tf, 0x00);
+ break;
+ case 0x07:
+ tcg_gen_movi_tl(cpu_If, 0x00);
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * MCU Control Instructions
+ */
+
+/*
+ * The BREAK instruction is used by the On-chip Debug system, and is
+ * normally not used in the application software. When the BREAK instruction is
+ * executed, the AVR CPU is set in the Stopped Mode. This gives the On-chip
+ * Debugger access to internal resources. If any Lock bits are set, or either
+ * the JTAGEN or OCDEN Fuses are unprogrammed, the CPU will treat the BREAK
+ * instruction as a NOP and will not enter the Stopped mode. This instruction
+ * is not available in all devices. Refer to the device specific instruction
+ * set summary.
+ */
+static bool trans_BREAK(DisasContext *ctx, arg_BREAK *a)
+{
+ if (!avr_have_feature(ctx, AVR_FEATURE_BREAK)) {
+ return true;
+ }
+
+#ifdef BREAKPOINT_ON_BREAK
+ tcg_gen_movi_tl(cpu_pc, ctx->npc - 1);
+ gen_helper_debug(cpu_env);
+ ctx->bstate = DISAS_EXIT;
+#else
+ /* NOP */
+#endif
+
+ return true;
+}
+
+/*
+ * This instruction performs a single cycle No Operation.
+ */
+static bool trans_NOP(DisasContext *ctx, arg_NOP *a)
+{
+
+ /* NOP */
+
+ return true;
+}
+
+/*
+ * This instruction sets the circuit in sleep mode defined by the MCU
+ * Control Register.
+ */
+static bool trans_SLEEP(DisasContext *ctx, arg_SLEEP *a)
+{
+ gen_helper_sleep(cpu_env);
+ ctx->bstate = DISAS_NORETURN;
+ return true;
+}
+
+/*
+ * This instruction resets the Watchdog Timer. This instruction must be
+ * executed within a limited time given by the WD prescaler. See the Watchdog
+ * Timer hardware specification.
+ */
+static bool trans_WDR(DisasContext *ctx, arg_WDR *a)
+{
+ gen_helper_wdr(cpu_env);
+
+ return true;
+}
+
+/*
+ * Core translation mechanism functions:
+ *
+ * - translate()
+ * - canonicalize_skip()
+ * - gen_intermediate_code()
+ * - restore_state_to_opc()
+ *
+ */
+static void translate(DisasContext *ctx)
+{
+ uint32_t opcode = next_word(ctx);
+
+ if (!decode_insn(ctx, opcode)) {
+ gen_helper_unsupported(cpu_env);
+ ctx->bstate = DISAS_NORETURN;
+ }
+}
+
+/* Standardize the cpu_skip condition to NE. */
+static bool canonicalize_skip(DisasContext *ctx)
+{
+ switch (ctx->skip_cond) {
+ case TCG_COND_NEVER:
+ /* Normal case: cpu_skip is known to be false. */
+ return false;
+
+ case TCG_COND_ALWAYS:
+ /*
+ * Breakpoint case: cpu_skip is known to be true, via TB_FLAGS_SKIP.
+ * The breakpoint is on the instruction being skipped, at the start
+ * of the TranslationBlock. No need to update.
+ */
+ return false;
+
+ case TCG_COND_NE:
+ if (ctx->skip_var1 == NULL) {
+ tcg_gen_mov_tl(cpu_skip, ctx->skip_var0);
+ } else {
+ tcg_gen_xor_tl(cpu_skip, ctx->skip_var0, ctx->skip_var1);
+ ctx->skip_var1 = NULL;
+ }
+ break;
+
+ default:
+ /* Convert to a NE condition vs 0. */
+ if (ctx->skip_var1 == NULL) {
+ tcg_gen_setcondi_tl(ctx->skip_cond, cpu_skip, ctx->skip_var0, 0);
+ } else {
+ tcg_gen_setcond_tl(ctx->skip_cond, cpu_skip,
+ ctx->skip_var0, ctx->skip_var1);
+ ctx->skip_var1 = NULL;
+ }
+ ctx->skip_cond = TCG_COND_NE;
+ break;
+ }
+ if (ctx->free_skip_var0) {
+ tcg_temp_free(ctx->skip_var0);
+ ctx->free_skip_var0 = false;
+ }
+ ctx->skip_var0 = cpu_skip;
+ return true;
+}
+
+void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns)
+{
+ CPUAVRState *env = cs->env_ptr;
+ DisasContext ctx = {
+ .tb = tb,
+ .cs = cs,
+ .env = env,
+ .memidx = 0,
+ .bstate = DISAS_NEXT,
+ .skip_cond = TCG_COND_NEVER,
+ .singlestep = cs->singlestep_enabled,
+ };
+ target_ulong pc_start = tb->pc / 2;
+ int num_insns = 0;
+
+ if (tb->flags & TB_FLAGS_FULL_ACCESS) {
+ /*
+ * This flag is set by ST/LD instruction we will regenerate it ONLY
+ * with mem/cpu memory access instead of mem access
+ */
+ max_insns = 1;
+ }
+ if (ctx.singlestep) {
+ max_insns = 1;
+ }
+
+ gen_tb_start(tb);
+
+ ctx.npc = pc_start;
+ if (tb->flags & TB_FLAGS_SKIP) {
+ ctx.skip_cond = TCG_COND_ALWAYS;
+ ctx.skip_var0 = cpu_skip;
+ }
+
+ do {
+ TCGLabel *skip_label = NULL;
+
+ /* translate current instruction */
+ tcg_gen_insn_start(ctx.npc);
+ num_insns++;
+
+ /*
+ * this is due to some strange GDB behavior
+ * let's assume main has address 0x100
+ * b main - sets breakpoint at address 0x00000100 (code)
+ * b *0x100 - sets breakpoint at address 0x00800100 (data)
+ */
+ if (unlikely(!ctx.singlestep &&
+ (cpu_breakpoint_test(cs, OFFSET_CODE + ctx.npc * 2, BP_ANY) ||
+ cpu_breakpoint_test(cs, OFFSET_DATA + ctx.npc * 2, BP_ANY)))) {
+ canonicalize_skip(&ctx);
+ tcg_gen_movi_tl(cpu_pc, ctx.npc);
+ gen_helper_debug(cpu_env);
+ goto done_generating;
+ }
+
+ /* Conditionally skip the next instruction, if indicated. */
+ if (ctx.skip_cond != TCG_COND_NEVER) {
+ skip_label = gen_new_label();
+ if (ctx.skip_var0 == cpu_skip) {
+ /*
+ * Copy cpu_skip so that we may zero it before the branch.
+ * This ensures that cpu_skip is non-zero after the label
+ * if and only if the skipped insn itself sets a skip.
+ */
+ ctx.free_skip_var0 = true;
+ ctx.skip_var0 = tcg_temp_new();
+ tcg_gen_mov_tl(ctx.skip_var0, cpu_skip);
+ tcg_gen_movi_tl(cpu_skip, 0);
+ }
+ if (ctx.skip_var1 == NULL) {
+ tcg_gen_brcondi_tl(ctx.skip_cond, ctx.skip_var0, 0, skip_label);
+ } else {
+ tcg_gen_brcond_tl(ctx.skip_cond, ctx.skip_var0,
+ ctx.skip_var1, skip_label);
+ ctx.skip_var1 = NULL;
+ }
+ if (ctx.free_skip_var0) {
+ tcg_temp_free(ctx.skip_var0);
+ ctx.free_skip_var0 = false;
+ }
+ ctx.skip_cond = TCG_COND_NEVER;
+ ctx.skip_var0 = NULL;
+ }
+
+ translate(&ctx);
+
+ if (skip_label) {
+ canonicalize_skip(&ctx);
+ gen_set_label(skip_label);
+ if (ctx.bstate == DISAS_NORETURN) {
+ ctx.bstate = DISAS_CHAIN;
+ }
+ }
+ } while (ctx.bstate == DISAS_NEXT
+ && num_insns < max_insns
+ && (ctx.npc - pc_start) * 2 < TARGET_PAGE_SIZE - 4
+ && !tcg_op_buf_full());
+
+ if (tb->cflags & CF_LAST_IO) {
+ gen_io_end();
+ }
+
+ bool nonconst_skip = canonicalize_skip(&ctx);
+
+ switch (ctx.bstate) {
+ case DISAS_NORETURN:
+ assert(!nonconst_skip);
+ break;
+ case DISAS_NEXT:
+ case DISAS_TOO_MANY:
+ case DISAS_CHAIN:
+ if (!nonconst_skip) {
+ /* Note gen_goto_tb checks singlestep. */
+ gen_goto_tb(&ctx, 1, ctx.npc);
+ break;
+ }
+ tcg_gen_movi_tl(cpu_pc, ctx.npc);
+ /* fall through */
+ case DISAS_LOOKUP:
+ if (!ctx.singlestep) {
+ tcg_gen_lookup_and_goto_ptr();
+ break;
+ }
+ /* fall through */
+ case DISAS_EXIT:
+ if (ctx.singlestep) {
+ gen_helper_debug(cpu_env);
+ } else {
+ tcg_gen_exit_tb(NULL, 0);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+done_generating:
+ gen_tb_end(tb, num_insns);
+
+ tb->size = (ctx.npc - pc_start) * 2;
+ tb->icount = num_insns;
+
+#ifdef DEBUG_DISAS
+ if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
+ && qemu_log_in_addr_range(tb->pc)) {
+ FILE *fd;
+ fd = qemu_log_lock();
+ qemu_log("IN: %s\n", lookup_symbol(tb->pc));
+ log_target_disas(cs, tb->pc, tb->size);
+ qemu_log("\n");
+ qemu_log_unlock(fd);
+ }
+#endif
+}
+
+void restore_state_to_opc(CPUAVRState *env, TranslationBlock *tb,
+ target_ulong *data)
+{
+ env->pc_w = data[0];
+}
diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs
index 48e0c28434..0b93143e27 100644
--- a/target/i386/Makefile.objs
+++ b/target/i386/Makefile.objs
@@ -3,6 +3,7 @@ obj-$(CONFIG_TCG) += translate.o
obj-$(CONFIG_TCG) += bpt_helper.o cc_helper.o excp_helper.o fpu_helper.o
obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o
obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o
+obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
ifeq ($(CONFIG_SOFTMMU),y)
obj-y += machine.o arch_memory_mapping.o arch_dump.o monitor.o
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index e46ab8f774..1e5123251d 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -986,8 +986,8 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
NULL, NULL, "avx512-4vnniw", "avx512-4fmaps",
NULL, NULL, NULL, NULL,
"avx512-vp2intersect", NULL, "md-clear", NULL,
- NULL, NULL, NULL, NULL,
- NULL, NULL, NULL /* pconfig */, NULL,
+ NULL, NULL, "serialize", NULL,
+ "tsx-ldtrk", NULL, NULL /* pconfig */, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, "spec-ctrl", "stibp",
NULL, "arch-capabilities", "core-capability", "ssbd",
@@ -5968,6 +5968,7 @@ static void x86_cpu_reset(DeviceState *dev)
/* init to reset state */
env->hflags2 |= HF2_GIF_MASK;
+ env->hflags &= ~HF_GUEST_MASK;
cpu_x86_update_cr0(env, 0x60000010);
env->a20_mask = ~0x0;
@@ -6079,9 +6080,6 @@ static void x86_cpu_reset(DeviceState *dev)
if (kvm_enabled()) {
kvm_arch_reset_vcpu(cpu);
}
- else if (hvf_enabled()) {
- hvf_reset_vcpu(s);
- }
#endif
}
@@ -6400,7 +6398,7 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp)
} else if (cpu->env.cpuid_min_level < 0x14) {
mark_unavailable_features(cpu, FEAT_7_0_EBX,
CPUID_7_0_EBX_INTEL_PT,
- "Intel PT need CPUID leaf 0x14, please set by \"-cpu ...,+intel-pt,level=0x14\"");
+ "Intel PT need CPUID leaf 0x14, please set by \"-cpu ...,+intel-pt,min-level=0x14\"");
}
}
@@ -6511,6 +6509,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx,
&cpu->mwait.ecx, &cpu->mwait.edx);
env->features[FEAT_1_ECX] |= CPUID_EXT_MONITOR;
+ if (kvm_enabled() && kvm_has_waitpkg()) {
+ env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG;
+ }
}
if (kvm_enabled() && cpu->ucode_rev == 0) {
cpu->ucode_rev = kvm_arch_get_supported_msr_feature(kvm_state,
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 7d77efd9e4..37fffa5cac 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -777,6 +777,10 @@ typedef uint64_t FeatureWordArray[FEATURE_WORDS];
#define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3)
/* AVX512 Vector Pair Intersection to a Pair of Mask Registers */
#define CPUID_7_0_EDX_AVX512_VP2INTERSECT (1U << 8)
+/* SERIALIZE instruction */
+#define CPUID_7_0_EDX_SERIALIZE (1U << 14)
+/* TSX Suspend Load Address Tracking instruction */
+#define CPUID_7_0_EDX_TSX_LDTRK (1U << 16)
/* Speculation Control */
#define CPUID_7_0_EDX_SPEC_CTRL (1U << 26)
/* Single Thread Indirect Branch Predictors */
@@ -2118,6 +2122,11 @@ static inline bool cpu_has_vmx(CPUX86State *env)
return env->features[FEAT_1_ECX] & CPUID_EXT_VMX;
}
+static inline bool cpu_has_svm(CPUX86State *env)
+{
+ return env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM;
+}
+
/*
* In order for a vCPU to enter VMX operation it must have CR4.VMXE set.
* Since it was set, CR4.VMXE must remain set as long as vCPU is in
@@ -2143,6 +2152,7 @@ static inline bool cpu_vmx_maybe_enabled(CPUX86State *env)
/* fpu_helper.c */
void update_fp_status(CPUX86State *env);
void update_mxcsr_status(CPUX86State *env);
+void update_mxcsr_from_sse_status(CPUX86State *env);
static inline void cpu_set_mxcsr(CPUX86State *env, uint32_t mxcsr)
{
diff --git a/target/i386/excp_helper.c b/target/i386/excp_helper.c
index 1447bda7a9..b10c7ecbcc 100644
--- a/target/i386/excp_helper.c
+++ b/target/i386/excp_helper.c
@@ -262,8 +262,8 @@ static hwaddr get_hphys(CPUState *cs, hwaddr gphys, MMUAccessType access_type,
}
ptep = pde | PG_NX_MASK;
- /* if PSE bit is set, then we use a 4MB page */
- if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) {
+ /* if host cr4 PSE bit is set, then we use a 4MB page */
+ if ((pde & PG_PSE_MASK) && (env->nested_pg_mode & SVM_NPT_PSE)) {
page_size = 4096 * 1024;
pte_addr = pde_addr;
diff --git a/target/i386/fpu_helper.c b/target/i386/fpu_helper.c
index 71cec3962f..f5e6c4b88d 100644
--- a/target/i386/fpu_helper.c
+++ b/target/i386/fpu_helper.c
@@ -2539,6 +2539,7 @@ static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra)
static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra)
{
+ update_mxcsr_from_sse_status(env);
cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr), env->mxcsr, ra);
cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr_mask), 0x0000ffff, ra);
}
@@ -2968,11 +2969,45 @@ void update_mxcsr_status(CPUX86State *env)
}
set_float_rounding_mode(rnd_type, &env->sse_status);
+ /* Set exception flags. */
+ set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) |
+ (mxcsr & FPUS_ZE ? float_flag_divbyzero : 0) |
+ (mxcsr & FPUS_OE ? float_flag_overflow : 0) |
+ (mxcsr & FPUS_UE ? float_flag_underflow : 0) |
+ (mxcsr & FPUS_PE ? float_flag_inexact : 0),
+ &env->sse_status);
+
/* set denormals are zero */
set_flush_inputs_to_zero((mxcsr & SSE_DAZ) ? 1 : 0, &env->sse_status);
/* set flush to zero */
- set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->fp_status);
+ set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->sse_status);
+}
+
+void update_mxcsr_from_sse_status(CPUX86State *env)
+{
+ if (tcg_enabled()) {
+ uint8_t flags = get_float_exception_flags(&env->sse_status);
+ /*
+ * The MXCSR denormal flag has opposite semantics to
+ * float_flag_input_denormal (the softfloat code sets that flag
+ * only when flushing input denormals to zero, but SSE sets it
+ * only when not flushing them to zero), so is not converted
+ * here.
+ */
+ env->mxcsr |= ((flags & float_flag_invalid ? FPUS_IE : 0) |
+ (flags & float_flag_divbyzero ? FPUS_ZE : 0) |
+ (flags & float_flag_overflow ? FPUS_OE : 0) |
+ (flags & float_flag_underflow ? FPUS_UE : 0) |
+ (flags & float_flag_inexact ? FPUS_PE : 0) |
+ (flags & float_flag_output_denormal ? FPUS_UE | FPUS_PE :
+ 0));
+ }
+}
+
+void helper_update_mxcsr(CPUX86State *env)
+{
+ update_mxcsr_from_sse_status(env);
}
void helper_ldmxcsr(CPUX86State *env, uint32_t val)
diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c
index b98a99500a..9ae43bda0f 100644
--- a/target/i386/gdbstub.c
+++ b/target/i386/gdbstub.c
@@ -184,6 +184,7 @@ int x86_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
return gdb_get_reg32(mem_buf, 0); /* fop */
case IDX_MXCSR_REG:
+ update_mxcsr_from_sse_status(env);
return gdb_get_reg32(mem_buf, env->mxcsr);
case IDX_CTL_CR0_REG:
diff --git a/target/i386/helper.c b/target/i386/helper.c
index c3a6e4fabe..70be53e2c3 100644
--- a/target/i386/helper.c
+++ b/target/i386/helper.c
@@ -370,10 +370,11 @@ void x86_cpu_dump_local_apic_state(CPUState *cs, int flags)
dump_apic_lvt("LVTTHMR", lvt[APIC_LVT_THERMAL], false);
dump_apic_lvt("LVTT", lvt[APIC_LVT_TIMER], true);
- qemu_printf("Timer\t DCR=0x%x (divide by %u) initial_count = %u\n",
+ qemu_printf("Timer\t DCR=0x%x (divide by %u) initial_count = %u"
+ " current_count = %u\n",
s->divide_conf & APIC_DCR_MASK,
divider_conf(s->divide_conf),
- s->initial_count);
+ s->initial_count, apic_get_current_count(s));
qemu_printf("SPIV\t 0x%08x APIC %s, focus=%s, spurious vec %u\n",
s->spurious_vec,
@@ -544,6 +545,7 @@ void x86_cpu_dump_state(CPUState *cs, FILE *f, int flags)
for(i = 0; i < 8; i++) {
fptag |= ((!env->fptags[i]) << i);
}
+ update_mxcsr_from_sse_status(env);
qemu_fprintf(f, "FCW=%04x FSW=%04x [ST=%d] FTW=%02x MXCSR=%08x\n",
env->fpuc,
(env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11,
diff --git a/target/i386/helper.h b/target/i386/helper.h
index 8f9e1905c3..c2ae2f7e61 100644
--- a/target/i386/helper.h
+++ b/target/i386/helper.h
@@ -207,6 +207,7 @@ DEF_HELPER_FLAGS_2(pext, TCG_CALL_NO_RWG_SE, tl, tl, tl)
/* MMX/SSE */
DEF_HELPER_2(ldmxcsr, void, env, i32)
+DEF_HELPER_1(update_mxcsr, void, env)
DEF_HELPER_1(enter_mmx, void, env)
DEF_HELPER_1(emms, void, env)
DEF_HELPER_3(movq, void, env, ptr, ptr)
diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c
index be016b951a..d81f569aed 100644
--- a/target/i386/hvf/hvf.c
+++ b/target/i386/hvf/hvf.c
@@ -282,47 +282,54 @@ void hvf_handle_io(CPUArchState *env, uint16_t port, void *buffer,
}
}
-/* TODO: synchronize vcpu state */
static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
{
- CPUState *cpu_state = cpu;
- if (cpu_state->vcpu_dirty == 0) {
- hvf_get_registers(cpu_state);
+ if (!cpu->vcpu_dirty) {
+ hvf_get_registers(cpu);
+ cpu->vcpu_dirty = true;
}
-
- cpu_state->vcpu_dirty = 1;
}
-void hvf_cpu_synchronize_state(CPUState *cpu_state)
+void hvf_cpu_synchronize_state(CPUState *cpu)
{
- if (cpu_state->vcpu_dirty == 0) {
- run_on_cpu(cpu_state, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
+ if (!cpu->vcpu_dirty) {
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL);
}
}
-static void do_hvf_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg)
+static void do_hvf_cpu_synchronize_post_reset(CPUState *cpu,
+ run_on_cpu_data arg)
{
- CPUState *cpu_state = cpu;
- hvf_put_registers(cpu_state);
- cpu_state->vcpu_dirty = false;
+ hvf_put_registers(cpu);
+ cpu->vcpu_dirty = false;
}
-void hvf_cpu_synchronize_post_reset(CPUState *cpu_state)
+void hvf_cpu_synchronize_post_reset(CPUState *cpu)
{
- run_on_cpu(cpu_state, do_hvf_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
}
static void do_hvf_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
- CPUState *cpu_state = cpu;
- hvf_put_registers(cpu_state);
- cpu_state->vcpu_dirty = false;
+ hvf_put_registers(cpu);
+ cpu->vcpu_dirty = false;
+}
+
+void hvf_cpu_synchronize_post_init(CPUState *cpu)
+{
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
+}
+
+static void do_hvf_cpu_synchronize_pre_loadvm(CPUState *cpu,
+ run_on_cpu_data arg)
+{
+ cpu->vcpu_dirty = true;
}
-void hvf_cpu_synchronize_post_init(CPUState *cpu_state)
+void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu)
{
- run_on_cpu(cpu_state, do_hvf_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
+ run_on_cpu(cpu, do_hvf_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
}
static bool ept_emulation_fault(hvf_slot *slot, uint64_t gpa, uint64_t ept_qual)
@@ -441,96 +448,6 @@ static MemoryListener hvf_memory_listener = {
.log_sync = hvf_log_sync,
};
-void hvf_reset_vcpu(CPUState *cpu) {
- uint64_t pdpte[4] = {0, 0, 0, 0};
- int i;
-
- /* TODO: this shouldn't be needed; there is already a call to
- * cpu_synchronize_all_post_reset in vl.c
- */
- wvmcs(cpu->hvf_fd, VMCS_ENTRY_CTLS, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_IA32_EFER, 0);
-
- /* Initialize PDPTE */
- for (i = 0; i < 4; i++) {
- wvmcs(cpu->hvf_fd, VMCS_GUEST_PDPTE0 + i * 2, pdpte[i]);
- }
-
- macvm_set_cr0(cpu->hvf_fd, 0x60000010);
-
- wvmcs(cpu->hvf_fd, VMCS_CR4_MASK, CR4_VMXE_MASK);
- wvmcs(cpu->hvf_fd, VMCS_CR4_SHADOW, 0x0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CR4, CR4_VMXE_MASK);
-
- /* set VMCS guest state fields */
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_SELECTOR, 0xf000);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_ACCESS_RIGHTS, 0x9b);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CS_BASE, 0xffff0000);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_DS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_ES_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_FS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_LIMIT, 0xffff);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_ACCESS_RIGHTS, 0x93);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_SS_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_ACCESS_RIGHTS, 0x10000);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_LDTR_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_SELECTOR, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_ACCESS_RIGHTS, 0x83);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_TR_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_GDTR_BASE, 0);
-
- wvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_LIMIT, 0);
- wvmcs(cpu->hvf_fd, VMCS_GUEST_IDTR_BASE, 0);
-
- /*wvmcs(cpu->hvf_fd, VMCS_GUEST_CR2, 0x0);*/
- wvmcs(cpu->hvf_fd, VMCS_GUEST_CR3, 0x0);
-
- wreg(cpu->hvf_fd, HV_X86_RIP, 0xfff0);
- wreg(cpu->hvf_fd, HV_X86_RDX, 0x623);
- wreg(cpu->hvf_fd, HV_X86_RFLAGS, 0x2);
- wreg(cpu->hvf_fd, HV_X86_RSP, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RAX, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RBX, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RCX, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RSI, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RDI, 0x0);
- wreg(cpu->hvf_fd, HV_X86_RBP, 0x0);
-
- for (int i = 0; i < 8; i++) {
- wreg(cpu->hvf_fd, HV_X86_R8 + i, 0x0);
- }
-
- hv_vcpu_invalidate_tlb(cpu->hvf_fd);
- hv_vcpu_flush(cpu->hvf_fd);
-}
-
void hvf_vcpu_destroy(CPUState *cpu)
{
X86CPU *x86_cpu = X86_CPU(cpu);
diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h
index ce2a1532d5..75ba1e2a5f 100644
--- a/target/i386/hvf/vmx.h
+++ b/target/i386/hvf/vmx.h
@@ -121,7 +121,9 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0)
uint64_t pdpte[4] = {0, 0, 0, 0};
uint64_t efer = rvmcs(vcpu, VMCS_GUEST_IA32_EFER);
uint64_t old_cr0 = rvmcs(vcpu, VMCS_GUEST_CR0);
+ uint64_t changed_cr0 = old_cr0 ^ cr0;
uint64_t mask = CR0_PG | CR0_CD | CR0_NW | CR0_NE | CR0_ET;
+ uint64_t entry_ctls;
if ((cr0 & CR0_PG) && (rvmcs(vcpu, VMCS_GUEST_CR4) & CR4_PAE) &&
!(efer & MSR_EFER_LME)) {
@@ -138,12 +140,16 @@ static inline void macvm_set_cr0(hv_vcpuid_t vcpu, uint64_t cr0)
wvmcs(vcpu, VMCS_CR0_SHADOW, cr0);
if (efer & MSR_EFER_LME) {
- if (!(old_cr0 & CR0_PG) && (cr0 & CR0_PG)) {
- enter_long_mode(vcpu, cr0, efer);
- }
- if (/*(old_cr0 & CR0_PG) &&*/ !(cr0 & CR0_PG)) {
- exit_long_mode(vcpu, cr0, efer);
+ if (changed_cr0 & CR0_PG) {
+ if (cr0 & CR0_PG) {
+ enter_long_mode(vcpu, cr0, efer);
+ } else {
+ exit_long_mode(vcpu, cr0, efer);
+ }
}
+ } else {
+ entry_ctls = rvmcs(vcpu, VMCS_ENTRY_CTLS);
+ wvmcs(vcpu, VMCS_ENTRY_CTLS, entry_ctls & ~VM_ENTRY_GUEST_LMA);
}
/* Filter new CR0 after we are finished examining it above. */
@@ -173,6 +179,7 @@ static inline void macvm_set_rip(CPUState *cpu, uint64_t rip)
/* BUG, should take considering overlap.. */
wreg(cpu->hvf_fd, HV_X86_RIP, rip);
+ env->eip = rip;
/* after moving forward in rip, we need to clean INTERRUPTABILITY */
val = rvmcs(cpu->hvf_fd, VMCS_GUEST_INTERRUPTIBILITY);
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index 6adbff3d74..b8455c89ed 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -411,12 +411,6 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function,
if (host_tsx_blacklisted()) {
ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE);
}
- } else if (function == 7 && index == 0 && reg == R_ECX) {
- if (enable_cpu_pm) {
- ret |= CPUID_7_0_ECX_WAITPKG;
- } else {
- ret &= ~CPUID_7_0_ECX_WAITPKG;
- }
} else if (function == 7 && index == 0 && reg == R_EDX) {
/*
* Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts.
@@ -1840,16 +1834,18 @@ int kvm_arch_init_vcpu(CPUState *cs)
if (max_nested_state_len > 0) {
assert(max_nested_state_len >= offsetof(struct kvm_nested_state, data));
- if (cpu_has_vmx(env)) {
+ if (cpu_has_vmx(env) || cpu_has_svm(env)) {
struct kvm_vmx_nested_state_hdr *vmx_hdr;
env->nested_state = g_malloc0(max_nested_state_len);
env->nested_state->size = max_nested_state_len;
env->nested_state->format = KVM_STATE_NESTED_FORMAT_VMX;
- vmx_hdr = &env->nested_state->hdr.vmx;
- vmx_hdr->vmxon_pa = -1ull;
- vmx_hdr->vmcs12_pa = -1ull;
+ if (cpu_has_vmx(env)) {
+ vmx_hdr = &env->nested_state->hdr.vmx;
+ vmx_hdr->vmxon_pa = -1ull;
+ vmx_hdr->vmcs12_pa = -1ull;
+ }
}
}
@@ -3873,6 +3869,20 @@ static int kvm_put_nested_state(X86CPU *cpu)
return 0;
}
+ /*
+ * Copy flags that are affected by reset from env->hflags and env->hflags2.
+ */
+ if (env->hflags & HF_GUEST_MASK) {
+ env->nested_state->flags |= KVM_STATE_NESTED_GUEST_MODE;
+ } else {
+ env->nested_state->flags &= ~KVM_STATE_NESTED_GUEST_MODE;
+ }
+ if (env->hflags2 & HF2_GIF_MASK) {
+ env->nested_state->flags |= KVM_STATE_NESTED_GIF_SET;
+ } else {
+ env->nested_state->flags &= ~KVM_STATE_NESTED_GIF_SET;
+ }
+
assert(env->nested_state->size <= max_nested_state_len);
return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_NESTED_STATE, env->nested_state);
}
@@ -3901,11 +3911,19 @@ static int kvm_get_nested_state(X86CPU *cpu)
return ret;
}
+ /*
+ * Copy flags that are affected by reset to env->hflags and env->hflags2.
+ */
if (env->nested_state->flags & KVM_STATE_NESTED_GUEST_MODE) {
env->hflags |= HF_GUEST_MASK;
} else {
env->hflags &= ~HF_GUEST_MASK;
}
+ if (env->nested_state->flags & KVM_STATE_NESTED_GIF_SET) {
+ env->hflags2 |= HF2_GIF_MASK;
+ } else {
+ env->hflags2 &= ~HF2_GIF_MASK;
+ }
return ret;
}
@@ -3917,6 +3935,12 @@ int kvm_arch_put_registers(CPUState *cpu, int level)
assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
+ /* must be before kvm_put_nested_state so that EFER.SVME is set */
+ ret = kvm_put_sregs(x86_cpu);
+ if (ret < 0) {
+ return ret;
+ }
+
if (level >= KVM_PUT_RESET_STATE) {
ret = kvm_put_nested_state(x86_cpu);
if (ret < 0) {
@@ -3950,10 +3974,6 @@ int kvm_arch_put_registers(CPUState *cpu, int level)
if (ret < 0) {
return ret;
}
- ret = kvm_put_sregs(x86_cpu);
- if (ret < 0) {
- return ret;
- }
/* must be before kvm_put_msrs */
ret = kvm_inject_mce_oldstyle(x86_cpu);
if (ret < 0) {
@@ -4704,3 +4724,8 @@ int kvm_arch_msi_data_to_gsi(uint32_t data)
{
abort();
}
+
+bool kvm_has_waitpkg(void)
+{
+ return has_msr_umwait;
+}
diff --git a/target/i386/kvm_i386.h b/target/i386/kvm_i386.h
index 00bde7acaf..064b8798a2 100644
--- a/target/i386/kvm_i386.h
+++ b/target/i386/kvm_i386.h
@@ -44,6 +44,7 @@ void kvm_put_apicbase(X86CPU *cpu, uint64_t value);
bool kvm_enable_x2apic(void);
bool kvm_has_x2apic_api(void);
+bool kvm_has_waitpkg(void);
bool kvm_hv_vpindex_settable(void);
diff --git a/target/i386/machine.c b/target/i386/machine.c
index 0c96531a56..b1acf7d0ef 100644
--- a/target/i386/machine.c
+++ b/target/i386/machine.c
@@ -1071,13 +1071,41 @@ static const VMStateDescription vmstate_vmx_nested_state = {
}
};
+static bool svm_nested_state_needed(void *opaque)
+{
+ struct kvm_nested_state *nested_state = opaque;
+
+ /*
+ * HF_GUEST_MASK and HF2_GIF_MASK are already serialized
+ * via hflags and hflags2, all that's left is the opaque
+ * nested state blob.
+ */
+ return (nested_state->format == KVM_STATE_NESTED_FORMAT_SVM &&
+ nested_state->size > offsetof(struct kvm_nested_state, data));
+}
+
+static const VMStateDescription vmstate_svm_nested_state = {
+ .name = "cpu/kvm_nested_state/svm",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = svm_nested_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_U64(hdr.svm.vmcb_pa, struct kvm_nested_state),
+ VMSTATE_UINT8_ARRAY(data.svm[0].vmcb12,
+ struct kvm_nested_state,
+ KVM_STATE_NESTED_SVM_VMCB_SIZE),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static bool nested_state_needed(void *opaque)
{
X86CPU *cpu = opaque;
CPUX86State *env = &cpu->env;
return (env->nested_state &&
- vmx_nested_state_needed(env->nested_state));
+ (vmx_nested_state_needed(env->nested_state) ||
+ svm_nested_state_needed(env->nested_state)));
}
static int nested_state_post_load(void *opaque, int version_id)
@@ -1139,6 +1167,7 @@ static const VMStateDescription vmstate_kvm_nested_state = {
},
.subsections = (const VMStateDescription*[]) {
&vmstate_vmx_nested_state,
+ &vmstate_svm_nested_state,
NULL
}
};
diff --git a/target/i386/monitor.c b/target/i386/monitor.c
index 27ebfa3ad2..7abae3c8df 100644
--- a/target/i386/monitor.c
+++ b/target/i386/monitor.c
@@ -726,13 +726,5 @@ SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp)
SevCapability *qmp_query_sev_capabilities(Error **errp)
{
- SevCapability *data;
-
- data = sev_get_capabilities();
- if (!data) {
- error_setg(errp, "SEV feature is not available");
- return NULL;
- }
-
- return data;
+ return sev_get_capabilities(errp);
}
diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h
index 14f2b16abd..c7614f8b0b 100644
--- a/target/i386/ops_sse.h
+++ b/target/i386/ops_sse.h
@@ -843,6 +843,7 @@ int64_t helper_cvttsd2sq(CPUX86State *env, ZMMReg *s)
void helper_rsqrtps(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one,
float32_sqrt(s->ZMM_S(0), &env->sse_status),
&env->sse_status);
@@ -855,26 +856,33 @@ void helper_rsqrtps(CPUX86State *env, ZMMReg *d, ZMMReg *s)
d->ZMM_S(3) = float32_div(float32_one,
float32_sqrt(s->ZMM_S(3), &env->sse_status),
&env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one,
float32_sqrt(s->ZMM_S(0), &env->sse_status),
&env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
void helper_rcpps(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status);
d->ZMM_S(1) = float32_div(float32_one, s->ZMM_S(1), &env->sse_status);
d->ZMM_S(2) = float32_div(float32_one, s->ZMM_S(2), &env->sse_status);
d->ZMM_S(3) = float32_div(float32_one, s->ZMM_S(3), &env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *s)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status);
+ set_float_exception_flags(old_flags, &env->sse_status);
}
static inline uint64_t helper_extrq(uint64_t src, int shift, int len)
@@ -1764,6 +1772,7 @@ void glue(helper_phminposuw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s)
void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
@@ -1789,19 +1798,18 @@ void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
d->ZMM_S(2) = float32_round_to_int(s->ZMM_S(2), &env->sse_status);
d->ZMM_S(3) = float32_round_to_int(s->ZMM_S(3), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
@@ -1825,19 +1833,18 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status);
d->ZMM_D(1) = float64_round_to_int(s->ZMM_D(1), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
@@ -1860,19 +1867,18 @@ void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
d->ZMM_S(0) = float32_round_to_int(s->ZMM_S(0), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
uint32_t mode)
{
+ uint8_t old_flags = get_float_exception_flags(&env->sse_status);
signed char prev_rounding_mode;
prev_rounding_mode = env->sse_status.float_rounding_mode;
@@ -1895,13 +1901,11 @@ void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s,
d->ZMM_D(0) = float64_round_to_int(s->ZMM_D(0), &env->sse_status);
-#if 0 /* TODO */
- if (mode & (1 << 3)) {
+ if (mode & (1 << 3) && !(old_flags & float_flag_inexact)) {
set_float_exception_flags(get_float_exception_flags(&env->sse_status) &
~float_flag_inexact,
&env->sse_status);
}
-#endif
env->sse_status.float_rounding_mode = prev_rounding_mode;
}
diff --git a/target/i386/sev-stub.c b/target/i386/sev-stub.c
index e5ee13309c..88e3f39a1e 100644
--- a/target/i386/sev-stub.c
+++ b/target/i386/sev-stub.c
@@ -44,7 +44,8 @@ char *sev_get_launch_measurement(void)
return NULL;
}
-SevCapability *sev_get_capabilities(void)
+SevCapability *sev_get_capabilities(Error **errp)
{
+ error_setg(errp, "SEV is not available in this QEMU");
return NULL;
}
diff --git a/target/i386/sev.c b/target/i386/sev.c
index f100a53231..c3ecf86704 100644
--- a/target/i386/sev.c
+++ b/target/i386/sev.c
@@ -399,7 +399,7 @@ sev_get_info(void)
static int
sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
- size_t *cert_chain_len)
+ size_t *cert_chain_len, Error **errp)
{
guchar *pdh_data = NULL;
guchar *cert_chain_data = NULL;
@@ -410,8 +410,8 @@ sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
if (r < 0) {
if (err != SEV_RET_INVALID_LEN) {
- error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
- r, err, fw_error_to_str(err));
+ error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
+ r, err, fw_error_to_str(err));
return 1;
}
}
@@ -423,8 +423,8 @@ sev_get_pdh_info(int fd, guchar **pdh, size_t *pdh_len, guchar **cert_chain,
r = sev_platform_ioctl(fd, SEV_PDH_CERT_EXPORT, &export, &err);
if (r < 0) {
- error_report("failed to export PDH cert ret=%d fw_err=%d (%s)",
- r, err, fw_error_to_str(err));
+ error_setg(errp, "failed to export PDH cert ret=%d fw_err=%d (%s)",
+ r, err, fw_error_to_str(err));
goto e_free;
}
@@ -441,7 +441,7 @@ e_free:
}
SevCapability *
-sev_get_capabilities(void)
+sev_get_capabilities(Error **errp)
{
SevCapability *cap = NULL;
guchar *pdh_data = NULL;
@@ -450,15 +450,24 @@ sev_get_capabilities(void)
uint32_t ebx;
int fd;
+ if (!kvm_enabled()) {
+ error_setg(errp, "KVM not enabled");
+ return NULL;
+ }
+ if (kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, NULL) < 0) {
+ error_setg(errp, "SEV is not enabled in KVM");
+ return NULL;
+ }
+
fd = open(DEFAULT_SEV_DEVICE, O_RDWR);
if (fd < 0) {
- error_report("%s: Failed to open %s '%s'", __func__,
- DEFAULT_SEV_DEVICE, strerror(errno));
+ error_setg_errno(errp, errno, "Failed to open %s",
+ DEFAULT_SEV_DEVICE);
return NULL;
}
if (sev_get_pdh_info(fd, &pdh_data, &pdh_len,
- &cert_chain_data, &cert_chain_len)) {
+ &cert_chain_data, &cert_chain_len, errp)) {
goto out;
}
diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h
index 8eb7de1bef..4db6960f60 100644
--- a/target/i386/sev_i386.h
+++ b/target/i386/sev_i386.h
@@ -34,6 +34,6 @@ extern SevInfo *sev_get_info(void);
extern uint32_t sev_get_cbit_position(void);
extern uint32_t sev_get_reduced_phys_bits(void);
extern char *sev_get_launch_measurement(void);
-extern SevCapability *sev_get_capabilities(void);
+extern SevCapability *sev_get_capabilities(Error **errp);
#endif
diff --git a/target/i386/svm.h b/target/i386/svm.h
index 23a3a040b8..ae30fc6f79 100644
--- a/target/i386/svm.h
+++ b/target/i386/svm.h
@@ -135,6 +135,7 @@
#define SVM_NPT_PAE (1 << 0)
#define SVM_NPT_LMA (1 << 1)
#define SVM_NPT_NXE (1 << 2)
+#define SVM_NPT_PSE (1 << 3)
#define SVM_NPTEXIT_P (1ULL << 0)
#define SVM_NPTEXIT_RW (1ULL << 1)
diff --git a/target/i386/svm_helper.c b/target/i386/svm_helper.c
index 7b8105a1c3..6224387eab 100644
--- a/target/i386/svm_helper.c
+++ b/target/i386/svm_helper.c
@@ -209,16 +209,21 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend)
nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb,
control.nested_ctl));
+
+ env->nested_pg_mode = 0;
+
if (nested_ctl & SVM_NPT_ENABLED) {
env->nested_cr3 = x86_ldq_phys(cs,
env->vm_vmcb + offsetof(struct vmcb,
control.nested_cr3));
env->hflags2 |= HF2_NPT_MASK;
- env->nested_pg_mode = 0;
if (env->cr[4] & CR4_PAE_MASK) {
env->nested_pg_mode |= SVM_NPT_PAE;
}
+ if (env->cr[4] & CR4_PSE_MASK) {
+ env->nested_pg_mode |= SVM_NPT_PSE;
+ }
if (env->hflags & HF_LMA_MASK) {
env->nested_pg_mode |= SVM_NPT_LMA;
}
diff --git a/target/i386/tcg-stub.c b/target/i386/tcg-stub.c
new file mode 100644
index 0000000000..b00e23d606
--- /dev/null
+++ b/target/i386/tcg-stub.c
@@ -0,0 +1,25 @@
+/*
+ * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * 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 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+
+void update_mxcsr_from_sse_status(CPUX86State *env)
+{
+}
diff --git a/target/i386/translate.c b/target/i386/translate.c
index 5e5dbb41b0..a1d31f09c1 100644
--- a/target/i386/translate.c
+++ b/target/i386/translate.c
@@ -1128,9 +1128,6 @@ static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot)
static inline void gen_ins(DisasContext *s, MemOp ot)
{
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_start();
- }
gen_string_movl_A0_EDI(s);
/* Note: we must do this dummy write first to be restartable in
case of page fault. */
@@ -1143,16 +1140,10 @@ static inline void gen_ins(DisasContext *s, MemOp ot)
gen_op_movl_T0_Dshift(s, ot);
gen_op_add_reg_T0(s, s->aflag, R_EDI);
gen_bpt_io(s, s->tmp2_i32, ot);
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
- }
}
static inline void gen_outs(DisasContext *s, MemOp ot)
{
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_start();
- }
gen_string_movl_A0_ESI(s);
gen_op_ld_v(s, ot, s->T0, s->A0);
@@ -1163,9 +1154,6 @@ static inline void gen_outs(DisasContext *s, MemOp ot)
gen_op_movl_T0_Dshift(s, ot);
gen_op_add_reg_T0(s, s->aflag, R_ESI);
gen_bpt_io(s, s->tmp2_i32, ot);
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
- }
}
/* same method as Valgrind : we generate jumps to current or next
@@ -6400,8 +6388,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4);
+ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ gen_io_start();
+ }
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ /* jump generated by gen_repz_ins */
} else {
gen_ins(s, ot);
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
@@ -6415,8 +6407,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]);
gen_check_io(s, ot, pc_start - s->cs_base,
svm_is_rep(prefixes) | 4);
+ if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ gen_io_start();
+ }
if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) {
gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base);
+ /* jump generated by gen_repz_outs */
} else {
gen_outs(s, ot);
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
@@ -7583,12 +7579,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
CASE_MODRM_OP(4): /* smsw */
gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0);
tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, cr[0]));
- if (CODE64(s)) {
- mod = (modrm >> 6) & 3;
- ot = (mod != 3 ? MO_16 : s->dflag);
- } else {
- ot = MO_16;
- }
+ /*
+ * In 32-bit mode, the higher 16 bits of the destination
+ * register are undefined. In practice CR0[31:0] is stored
+ * just like in 64-bit mode.
+ */
+ mod = (modrm >> 6) & 3;
+ ot = (mod != 3 ? MO_16 : s->dflag);
gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1);
break;
case 0xee: /* rdpkru */
@@ -8039,7 +8036,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
gen_helper_read_crN(s->T0, cpu_env, tcg_const_i32(reg));
gen_op_mov_reg_v(s, ot, rm, s->T0);
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
+ gen_jmp(s, s->pc - s->cs_base);
}
}
break;
@@ -8157,6 +8154,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu)
gen_exception(s, EXCP07_PREX, pc_start - s->cs_base);
break;
}
+ gen_helper_update_mxcsr(cpu_env);
gen_lea_modrm(env, s, modrm);
tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr));
gen_op_st_v(s, MO_32, s->T0, s->A0);
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 94b1cc8302..c7e4646ded 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -639,7 +639,7 @@ define do_test_tap
{ export MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2; \
$(foreach COMMAND, $1, \
$(COMMAND) -m=$(SPEED) -k --tap < /dev/null \
- | sed "s/^[a-z][a-z]* [0-9]* /&$(notdir $(COMMAND)) /" || true; ) } \
+ | sed "s/^\(not \)\?ok [0-9]* /&$(notdir $(COMMAND)) /" || true; ) } \
| ./scripts/tap-merge.pl | tee "$@" \
| ./scripts/tap-driver.pl $(if $(V),, --show-failures-only), \
"TAP","$@")
diff --git a/tests/acceptance/machine_avr6.py b/tests/acceptance/machine_avr6.py
new file mode 100644
index 0000000000..6baf4e9c7f
--- /dev/null
+++ b/tests/acceptance/machine_avr6.py
@@ -0,0 +1,50 @@
+#
+# QEMU AVR acceptance tests
+#
+# Copyright (c) 2019-2020 Michael Rolnik <mrolnik@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import time
+
+from avocado_qemu import Test
+
+class AVR6Machine(Test):
+ timeout = 5
+
+ def test_freertos(self):
+ """
+ :avocado: tags=arch:avr
+ :avocado: tags=machine:arduino-mega-2560-v3
+ """
+ """
+ https://github.com/seharris/qemu-avr-tests/raw/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf
+ constantly prints out 'ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX'
+ """
+ rom_url = ('https://github.com/seharris/qemu-avr-tests'
+ '/raw/36c3e67b8755dcf/free-rtos/Demo'
+ '/AVR_ATMega2560_GCC/demo.elf')
+ rom_hash = '7eb521f511ca8f2622e0a3c5e8dd686efbb911d4'
+ rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash)
+
+ self.vm.add_args('-bios', rom_path)
+ self.vm.add_args('-nographic')
+ self.vm.launch()
+
+ time.sleep(2)
+ self.vm.shutdown()
+
+ self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX',
+ self.vm.get_log())
diff --git a/tests/qtest/Makefile.include b/tests/qtest/Makefile.include
index 6a0276fd42..b0204e44f2 100644
--- a/tests/qtest/Makefile.include
+++ b/tests/qtest/Makefile.include
@@ -66,6 +66,8 @@ check-qtest-i386-y += numa-test
check-qtest-x86_64-y += $(check-qtest-i386-y)
+check-qtest-avr-y += boot-serial-test
+
check-qtest-alpha-y += boot-serial-test
check-qtest-alpha-$(CONFIG_VGA) += display-vga-test
diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c
index 85a3614286..bfe7624dc6 100644
--- a/tests/qtest/boot-serial-test.c
+++ b/tests/qtest/boot-serial-test.c
@@ -17,6 +17,15 @@
#include "libqtest.h"
#include "libqos/libqos-spapr.h"
+static const uint8_t bios_avr[] = {
+ 0x88, 0xe0, /* ldi r24, 0x08 */
+ 0x80, 0x93, 0xc1, 0x00, /* sts 0x00C1, r24 ; Enable tx */
+ 0x86, 0xe0, /* ldi r24, 0x06 */
+ 0x80, 0x93, 0xc2, 0x00, /* sts 0x00C2, r24 ; Set the data bits to 8 */
+ 0x84, 0xe5, /* ldi r24, 0x54 */
+ 0x80, 0x93, 0xc6, 0x00, /* sts 0x00C6, r24 ; Output 'T' */
+};
+
static const uint8_t kernel_mcf5208[] = {
0x41, 0xf9, 0xfc, 0x06, 0x00, 0x00, /* lea 0xfc060000,%a0 */
0x10, 0x3c, 0x00, 0x54, /* move.b #'T',%d0 */
@@ -104,6 +113,8 @@ typedef struct testdef {
static testdef_t tests[] = {
{ "alpha", "clipper", "", "PCI:" },
+ { "avr", "arduino-duemilanove", "", "T", sizeof(bios_avr), NULL, bios_avr },
+ { "avr", "arduino-mega-2560-v3", "", "T", sizeof(bios_avr), NULL, bios_avr},
{ "ppc", "ppce500", "", "U-Boot" },
{ "ppc", "40p", "-vga none -boot d", "Trying cd:," },
{ "ppc", "g3beige", "", "PowerPC,750" },
diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c
index 8b7abea8af..57107f1aec 100644
--- a/tests/qtest/machine-none-test.c
+++ b/tests/qtest/machine-none-test.c
@@ -27,6 +27,7 @@ static struct arch2cpu cpus_map[] = {
/* tested targets list */
{ "arm", "cortex-a15" },
{ "aarch64", "cortex-a57" },
+ { "avr", "avr6-avr-cpu" },
{ "x86_64", "qemu64,apic-id=0" },
{ "i386", "qemu32,apic-id=0" },
{ "alpha", "ev67" },
diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c
index 9f5228cd99..c68f99f659 100644
--- a/tests/qtest/qmp-cmd-test.c
+++ b/tests/qtest/qmp-cmd-test.c
@@ -200,16 +200,116 @@ static void add_query_tests(QmpSchema *schema)
}
}
-static void test_object_add_without_props(void)
+static void test_object_add_failure_modes(void)
{
QTestState *qts;
QDict *resp;
+ /* attempt to create an object without props */
qts = qtest_init(common_args);
resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
- " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }");
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }");
g_assert_nonnull(resp);
qmp_assert_error_class(resp, "GenericError");
+
+ /* attempt to create an object without qom-type */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* attempt to delete an object that does not exist */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* attempt to create 2 objects with duplicate id */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+
+ /* attempt to create an object with a property of a wrong type */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': '1048576' } } }");
+ g_assert_nonnull(resp);
+ /* now do it right */
+ qmp_assert_error_class(resp, "GenericError");
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+
+ /* attempt to create an object without the id */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+ /* now do it right */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+
+ /* attempt to set a non existing property */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'sized': 1048576 } } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+ /* now do it right */
+ resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':"
+ " {'qom-type': 'memory-backend-ram', 'id': 'ram1',"
+ " 'props': {'size': 1048576 } } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+
+ /* delete ram1 object without id */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'ida': 'ram1' } }");
+ g_assert_nonnull(resp);
+
+ /* delete ram1 object */
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ g_assert(qdict_haskey(resp, "return"));
+
+ /* delete ram1 object that does not exist anymore*/
+ resp = qtest_qmp(qts, "{'execute': 'object-del', 'arguments':"
+ " {'id': 'ram1' } }");
+ g_assert_nonnull(resp);
+ qmp_assert_error_class(resp, "GenericError");
+
qtest_quit(qts);
}
@@ -223,9 +323,8 @@ int main(int argc, char *argv[])
qmp_schema_init(&schema);
add_query_tests(&schema);
- qtest_add_func("qmp/object-add-without-props",
- test_object_add_without_props);
- /* TODO: add coverage of generic object-add failure modes */
+ qtest_add_func("qmp/object-add-failure-modes",
+ test_object_add_failure_modes);
ret = g_test_run();
diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target
index 1a6463a7dc..a66232a67d 100644
--- a/tests/tcg/i386/Makefile.target
+++ b/tests/tcg/i386/Makefile.target
@@ -10,6 +10,10 @@ ALL_X86_TESTS=$(I386_SRCS:.c=)
SKIP_I386_TESTS=test-i386-ssse3
X86_64_TESTS:=$(filter test-i386-ssse3, $(ALL_X86_TESTS))
+test-i386-sse-exceptions: CFLAGS += -msse4.1 -mfpmath=sse
+run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max
+run-plugin-test-i386-sse-exceptions-%: QEMU_OPTS += -cpu max
+
test-i386-pcmpistri: CFLAGS += -msse4.2
run-test-i386-pcmpistri: QEMU_OPTS += -cpu max
run-plugin-test-i386-pcmpistri-%: QEMU_OPTS += -cpu max
diff --git a/tests/tcg/i386/test-i386-sse-exceptions.c b/tests/tcg/i386/test-i386-sse-exceptions.c
new file mode 100644
index 0000000000..a104f46c11
--- /dev/null
+++ b/tests/tcg/i386/test-i386-sse-exceptions.c
@@ -0,0 +1,813 @@
+/* Test SSE exceptions. */
+
+#include <float.h>
+#include <stdint.h>
+#include <stdio.h>
+
+volatile float f_res;
+volatile double d_res;
+
+volatile float f_snan = __builtin_nansf("");
+volatile float f_half = 0.5f;
+volatile float f_third = 1.0f / 3.0f;
+volatile float f_nan = __builtin_nanl("");
+volatile float f_inf = __builtin_inff();
+volatile float f_ninf = -__builtin_inff();
+volatile float f_one = 1.0f;
+volatile float f_two = 2.0f;
+volatile float f_zero = 0.0f;
+volatile float f_nzero = -0.0f;
+volatile float f_min = FLT_MIN;
+volatile float f_true_min = 0x1p-149f;
+volatile float f_max = FLT_MAX;
+volatile float f_nmax = -FLT_MAX;
+
+volatile double d_snan = __builtin_nans("");
+volatile double d_half = 0.5;
+volatile double d_third = 1.0 / 3.0;
+volatile double d_nan = __builtin_nan("");
+volatile double d_inf = __builtin_inf();
+volatile double d_ninf = -__builtin_inf();
+volatile double d_one = 1.0;
+volatile double d_two = 2.0;
+volatile double d_zero = 0.0;
+volatile double d_nzero = -0.0;
+volatile double d_min = DBL_MIN;
+volatile double d_true_min = 0x1p-1074;
+volatile double d_max = DBL_MAX;
+volatile double d_nmax = -DBL_MAX;
+
+volatile int32_t i32_max = INT32_MAX;
+
+#define IE (1 << 0)
+#define ZE (1 << 2)
+#define OE (1 << 3)
+#define UE (1 << 4)
+#define PE (1 << 5)
+#define EXC (IE | ZE | OE | UE | PE)
+
+uint32_t mxcsr_default = 0x1f80;
+uint32_t mxcsr_ftz = 0x9f80;
+
+int main(void)
+{
+ uint32_t mxcsr;
+ int32_t i32_res;
+ int ret = 0;
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = f_snan;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: widen float snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: narrow float underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: narrow float overflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: narrow float inexact\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = d_snan;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: narrow float snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $4, %0, %0" : "=x" (f_res) : "0" (f_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: roundss min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $12, %0, %0" : "=x" (f_res) : "0" (f_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: roundss no-inexact min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $4, %0, %0" : "=x" (f_res) : "0" (f_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundss snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundss $12, %0, %0" : "=x" (f_res) : "0" (f_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundss no-inexact snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $4, %0, %0" : "=x" (d_res) : "0" (d_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: roundsd min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $12, %0, %0" : "=x" (d_res) : "0" (d_min));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: roundsd no-inexact min\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $4, %0, %0" : "=x" (d_res) : "0" (d_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundsd snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("roundsd $12, %0, %0" : "=x" (d_res) : "0" (d_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: roundsd no-inexact snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("comiss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: comiss nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomiss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: ucomiss nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomiss %1, %0" : : "x" (f_snan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: ucomiss snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("comisd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: comisd nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomisd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: ucomisd nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("ucomisd %1, %0" : : "x" (d_snan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: ucomisd snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max + f_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float add overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max + f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float add inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf + f_ninf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float add inf -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan + f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float add snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_true_min + f_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float add FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max + d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double add overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max + d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double add inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf + d_ninf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double add inf -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan + d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double add snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_true_min + d_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double add FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max - f_nmax;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float sub overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max - f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float sub inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf - f_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float sub inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan - f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float sub snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_min - f_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float sub FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max - d_nmax;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double sub overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max - d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double sub inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf - d_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double sub inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan - d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double sub snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_min - d_true_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double sub FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max * f_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float mul overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_third * f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float mul inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_min * f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float mul underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf * f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float mul inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan * f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float mul snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_min * f_half;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float mul FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max * d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double mul overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_third * d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double mul inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_min * d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double mul underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf * d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double mul inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan * d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double mul snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_min * d_half;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double mul FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_max / f_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: float div overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_one / f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: float div inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_min / f_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float div underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_one / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != ZE) {
+ printf("FAIL: float div 1 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: float div inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_nan / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: float div nan 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_zero / f_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float div 0 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_inf / f_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float div inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ f_res = f_snan / f_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: float div snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ f_res = f_min / f_two;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: float div FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_max / d_min;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (OE | PE)) {
+ printf("FAIL: double div overflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_one / d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: double div inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_min / d_max;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double div underflow\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_one / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != ZE) {
+ printf("FAIL: double div 1 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: double div inf 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_nan / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: double div nan 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_zero / d_zero;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double div 0 0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_inf / d_inf;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double div inf inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ d_res = d_snan / d_third;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: double div snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_ftz));
+ d_res = d_min / d_two;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != (UE | PE)) {
+ printf("FAIL: double div FTZ underflow\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: sqrtss inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_nmax));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtss -max\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_ninf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtss -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtss snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) : "0" (f_nzero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtss -0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtss %0, %0" : "=x" (f_res) :
+ "0" (-__builtin_nanf("")));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtss -nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: sqrtsd inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_nmax));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtsd -max\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_ninf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtsd -inf\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_snan));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: sqrtsd snan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) : "0" (d_nzero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtsd -0\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("sqrtsd %0, %0" : "=x" (d_res) :
+ "0" (-__builtin_nan("")));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: sqrtsd -nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("maxss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: maxss nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("minss %1, %0" : : "x" (f_nan), "x" (f_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: minss nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("maxsd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: maxsd nan\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("minsd %1, %0" : : "x" (d_nan), "x" (d_zero));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: minsd nan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsi2ss %1, %0" : "=x" (f_res) : "m" (i32_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvtsi2ss inexact\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsi2sd %1, %0" : "=x" (d_res) : "m" (i32_max));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: cvtsi2sd exact\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtss2si %1, %0" : "=r" (i32_res) : "x" (1.5f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvtss2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtss2si %1, %0" : "=r" (i32_res) : "x" (0x1p31f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtss2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtss2si %1, %0" : "=r" (i32_res) : "x" (f_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtss2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsd2si %1, %0" : "=r" (i32_res) : "x" (1.5));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvtsd2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsd2si %1, %0" : "=r" (i32_res) : "x" (0x1p31));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtsd2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvtsd2si %1, %0" : "=r" (i32_res) : "x" (d_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvtsd2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttss2si %1, %0" : "=r" (i32_res) : "x" (1.5f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvttss2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttss2si %1, %0" : "=r" (i32_res) : "x" (0x1p31f));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttss2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttss2si %1, %0" : "=r" (i32_res) : "x" (f_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttss2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttsd2si %1, %0" : "=r" (i32_res) : "x" (1.5));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != PE) {
+ printf("FAIL: cvttsd2si inexact\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttsd2si %1, %0" : "=r" (i32_res) : "x" (0x1p31));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttsd2si 0x1p31\n");
+ ret = 1;
+ }
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("cvttsd2si %1, %0" : "=r" (i32_res) : "x" (d_inf));
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != IE) {
+ printf("FAIL: cvttsd2si inf\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("rcpss %0, %0" : "=x" (f_res) : "0" (f_snan));
+ f_res += f_one;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: rcpss snan\n");
+ ret = 1;
+ }
+
+ __asm__ volatile ("ldmxcsr %0" : : "m" (mxcsr_default));
+ __asm__ volatile ("rsqrtss %0, %0" : "=x" (f_res) : "0" (f_snan));
+ f_res += f_one;
+ __asm__ volatile ("stmxcsr %0" : "=m" (mxcsr));
+ if ((mxcsr & EXC) != 0) {
+ printf("FAIL: rsqrtss snan\n");
+ ret = 1;
+ }
+
+ return ret;
+}
diff --git a/ui/cocoa.m b/ui/cocoa.m
index cb556e4e66..0910b4a716 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -32,6 +32,7 @@
#include "ui/input.h"
#include "sysemu/sysemu.h"
#include "sysemu/runstate.h"
+#include "sysemu/cpu-throttle.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-block.h"
#include "qapi/qapi-commands-misc.h"
diff --git a/util/qemu-error.c b/util/qemu-error.c
index dac7c7dc50..3ee41438e9 100644
--- a/util/qemu-error.c
+++ b/util/qemu-error.c
@@ -26,6 +26,8 @@ typedef enum {
/* Prepend timestamp to messages */
bool error_with_timestamp;
+bool error_with_guestname;
+const char *error_guest_name;
int error_printf(const char *fmt, ...)
{
@@ -213,6 +215,11 @@ static void vreport(report_type type, const char *fmt, va_list ap)
g_free(timestr);
}
+ /* Only prepend guest name if -msg guest-name and -name guest=... are set */
+ if (error_with_guestname && error_guest_name && !cur_mon) {
+ error_printf("%s ", error_guest_name);
+ }
+
print_loc();
switch (type) {