Merge remote-tracking branch 'remotes/armbru/tags/pull-qdev-2020-06-23' into staging
Qdev patches for 2020-06-23
# gpg: Signature made Tue 23 Jun 2020 15:08:28 BST
# gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg: issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653
* remotes/armbru/tags/pull-qdev-2020-06-23:
sd/milkymist-memcard: Fix error API violation
sd/pxa2xx_mmci: Don't crash on pxa2xx_mmci_init() error
arm/aspeed: Drop aspeed_board_init_flashes() parameter @errp
qdev: Make qdev_prop_set_drive() match the other helpers
qdev: Reject chardev property override
qdev: Reject drive property override
qdev: Improve netdev property override error a bit
qdev: Eliminate get_pointer(), set_pointer()
blockdev: Deprecate -drive with bogus interface type
docs/qdev-device-use.txt: Update section "Default Devices"
fdc: Deprecate configuring floppies with -global isa-fdc
fdc: Open-code fdctrl_init_isa()
fdc: Reject clash between -drive if=floppy and -global isa-fdc
iotests/172: Cover -global floppy.drive=...
iotests/172: Cover empty filename and multiple use of drives
iotests/172: Include "info block" in test output
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index f0cb1fd..1b40446 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -842,6 +842,7 @@
L: qemu-arm@nongnu.org
S: Maintained
F: hw/*/versatile*
+F: include/hw/i2c/arm_sbcon_i2c.h
F: hw/misc/arm_sysctl.c
F: docs/system/arm/versatile.rst
@@ -1256,6 +1257,15 @@
F: include/hw/char/ibex_uart.h
F: include/hw/intc/ibex_plic.h
+RX Machines
+-----------
+rx-gdbsim
+M: Yoshinori Sato <ysato@users.sourceforge.jp>
+S: Maintained
+F: docs/system/target-rx.rst
+F: hw/rx/rx-gdbsim.c
+F: tests/acceptance/machine_rx_gdbsim.py
+
SH4 Machines
------------
R2D
@@ -1264,13 +1274,15 @@
S: Maintained
F: hw/sh4/r2d.c
F: hw/intc/sh_intc.c
-F: hw/timer/sh_timer.c
+F: include/hw/sh4/sh_intc.h
Shix
M: Yoshinori Sato <ysato@users.sourceforge.jp>
R: Magnus Damm <magnus.damm@gmail.com>
S: Odd Fixes
F: hw/sh4/shix.c
+F: hw/intc/sh_intc.c
+F: include/hw/sh4/sh_intc.h
SPARC Machines
--------------
@@ -1965,6 +1977,26 @@
F: include/hw/*/*xive*
F: docs/*/*xive*
+Renesas peripherals
+M: Yoshinori Sato <ysato@users.sourceforge.jp>
+R: Magnus Damm <magnus.damm@gmail.com>
+S: Maintained
+F: hw/char/renesas_sci.c
+F: hw/char/sh_serial.c
+F: hw/timer/renesas_*.c
+F: hw/timer/sh_timer.c
+F: include/hw/char/renesas_sci.h
+F: include/hw/sh4/sh.h
+F: include/hw/timer/renesas_*.h
+
+Renesas RX peripherals
+M: Yoshinori Sato <ysato@users.sourceforge.jp>
+S: Maintained
+F: hw/intc/rx_icu.c
+F: hw/rx/
+F: include/hw/intc/rx_icu.h
+F: include/hw/rx/
+
Subsystems
----------
Audio
@@ -2399,7 +2431,7 @@
F: include/hw/acpi/tpm.h
F: include/sysemu/tpm*
F: qapi/tpm.json
-F: backends/tpm.c
+F: backends/tpm/
F: tests/qtest/*tpm*
T: git https://github.com/stefanberger/qemu-tpm.git tpm-next
diff --git a/Makefile b/Makefile
index 48f23aa..a009215 100644
--- a/Makefile
+++ b/Makefile
@@ -418,7 +418,7 @@
CONFIG_LINUX=$(CONFIG_LINUX) \
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
-MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig
+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 \
diff --git a/Makefile.objs b/Makefile.objs
index 7ce2588..9838397 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -125,6 +125,7 @@
trace-events-subdirs += accel/kvm
trace-events-subdirs += accel/tcg
trace-events-subdirs += backends
+trace-events-subdirs += backends/tpm
trace-events-subdirs += crypto
trace-events-subdirs += monitor
ifeq ($(CONFIG_USER_ONLY),y)
diff --git a/backends/Kconfig b/backends/Kconfig
new file mode 100644
index 0000000..f35abc1
--- /dev/null
+++ b/backends/Kconfig
@@ -0,0 +1 @@
+source tpm/Kconfig
diff --git a/backends/Makefile.objs b/backends/Makefile.objs
index 28a847c..22d204c 100644
--- a/backends/Makefile.objs
+++ b/backends/Makefile.objs
@@ -1,7 +1,7 @@
common-obj-y += rng.o rng-egd.o rng-builtin.o
common-obj-$(CONFIG_POSIX) += rng-random.o
-common-obj-$(CONFIG_TPM) += tpm.o
+common-obj-$(CONFIG_TPM) += tpm/
common-obj-y += hostmem.o hostmem-ram.o
common-obj-$(CONFIG_POSIX) += hostmem-file.o
diff --git a/backends/tpm/Kconfig b/backends/tpm/Kconfig
new file mode 100644
index 0000000..5d91eb8
--- /dev/null
+++ b/backends/tpm/Kconfig
@@ -0,0 +1,14 @@
+config TPM_BACKEND
+ bool
+ depends on TPM
+
+config TPM_PASSTHROUGH
+ bool
+ default y
+ # FIXME: should check for x86 host as well
+ depends on TPM_BACKEND && LINUX
+
+config TPM_EMULATOR
+ bool
+ default y
+ depends on TPM_BACKEND
diff --git a/backends/tpm/Makefile.objs b/backends/tpm/Makefile.objs
new file mode 100644
index 0000000..db2731f
--- /dev/null
+++ b/backends/tpm/Makefile.objs
@@ -0,0 +1,4 @@
+common-obj-y += tpm_backend.o
+common-obj-y += tpm_util.o
+common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
+common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
diff --git a/backends/tpm.c b/backends/tpm/tpm_backend.c
similarity index 100%
rename from backends/tpm.c
rename to backends/tpm/tpm_backend.c
diff --git a/hw/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c
similarity index 99%
rename from hw/tpm/tpm_emulator.c
rename to backends/tpm/tpm_emulator.c
index 3a0fc44..9605339 100644
--- a/hw/tpm/tpm_emulator.c
+++ b/backends/tpm/tpm_emulator.c
@@ -32,8 +32,8 @@
#include "qemu/sockets.h"
#include "io/channel-socket.h"
#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm_util.h"
#include "tpm_int.h"
-#include "tpm_util.h"
#include "tpm_ioctl.h"
#include "migration/blocker.h"
#include "migration/vmstate.h"
diff --git a/hw/tpm/tpm_int.h b/backends/tpm/tpm_int.h
similarity index 81%
rename from hw/tpm/tpm_int.h
rename to backends/tpm/tpm_int.h
index 3fb28a9..ba61093 100644
--- a/hw/tpm/tpm_int.h
+++ b/backends/tpm/tpm_int.h
@@ -9,8 +9,11 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
-#ifndef TPM_TPM_INT_H
-#define TPM_TPM_INT_H
+#ifndef BACKENDS_TPM_INT_H
+#define BACKENDS_TPM_INT_H
+
+#include "qemu/option.h"
+#include "sysemu/tpm.h"
#define TPM_STANDARD_CMDLINE_OPTS \
{ \
@@ -72,4 +75,14 @@
#define TPM_RC_FAILURE 0x101
#define TPM_RC_LOCALITY 0x907
-#endif /* TPM_TPM_INT_H */
+int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
+ size_t *buffersize);
+
+typedef struct TPMSizedBuffer {
+ uint32_t size;
+ uint8_t *buffer;
+} TPMSizedBuffer;
+
+void tpm_sized_buffer_reset(TPMSizedBuffer *tsb);
+
+#endif /* BACKENDS_TPM_INT_H */
diff --git a/hw/tpm/tpm_ioctl.h b/backends/tpm/tpm_ioctl.h
similarity index 100%
rename from hw/tpm/tpm_ioctl.h
rename to backends/tpm/tpm_ioctl.h
diff --git a/hw/tpm/tpm_passthrough.c b/backends/tpm/tpm_passthrough.c
similarity index 99%
rename from hw/tpm/tpm_passthrough.c
rename to backends/tpm/tpm_passthrough.c
index f67244b..7403807 100644
--- a/hw/tpm/tpm_passthrough.c
+++ b/backends/tpm/tpm_passthrough.c
@@ -28,10 +28,10 @@
#include "qemu/module.h"
#include "qemu/sockets.h"
#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm_util.h"
#include "tpm_int.h"
#include "qapi/clone-visitor.h"
#include "qapi/qapi-visit-tpm.h"
-#include "tpm_util.h"
#include "trace.h"
#define TYPE_TPM_PASSTHROUGH "tpm-passthrough"
diff --git a/hw/tpm/tpm_util.c b/backends/tpm/tpm_util.c
similarity index 98%
rename from hw/tpm/tpm_util.c
rename to backends/tpm/tpm_util.c
index c0a0f3d..cfc7572 100644
--- a/hw/tpm/tpm_util.c
+++ b/backends/tpm/tpm_util.c
@@ -23,11 +23,11 @@
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
-#include "tpm_util.h"
#include "tpm_int.h"
#include "exec/memory.h"
#include "hw/qdev-properties.h"
#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm_util.h"
#include "trace.h"
/* tpm backend property */
@@ -357,6 +357,9 @@
size_t len, i;
char *line_buffer, *p;
+ if (!trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
+ return;
+ }
len = MIN(tpm_cmd_get_size(buffer), buffer_size);
/*
diff --git a/backends/tpm/trace-events b/backends/tpm/trace-events
new file mode 100644
index 0000000..0a2591f
--- /dev/null
+++ b/backends/tpm/trace-events
@@ -0,0 +1,33 @@
+# See docs/devel/tracing.txt for syntax documentation.
+
+# tpm_passthrough.c
+tpm_passthrough_handle_request(void *cmd) "processing command %p"
+tpm_passthrough_reset(void) "reset"
+
+# tpm_util.c
+tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu"
+tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu"
+tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu"
+tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu"
+tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu"
+tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
+
+# tpm_emulator.c
+tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
+tpm_emulator_handle_request(void) "processing TPM command"
+tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64
+tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u"
+tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu"
+tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d"
+tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD"
+tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2"
+tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2"
+tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified"
+tpm_emulator_handle_device_opts_startup_error(void) "Startup error"
+tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x"
+tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x"
+tpm_emulator_set_state_blobs(void) "setting state blobs"
+tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s"
+tpm_emulator_set_state_blobs_done(void) "Done setting state blobs"
+tpm_emulator_pre_save(void) ""
+tpm_emulator_inst_init(void) ""
diff --git a/default-configs/rx-softmmu.mak b/default-configs/rx-softmmu.mak
index 7c4eb2c..df2b4e4 100644
--- a/default-configs/rx-softmmu.mak
+++ b/default-configs/rx-softmmu.mak
@@ -1,2 +1,3 @@
# Default configuration for rx-softmmu
+CONFIG_RX_GDBSIM=y
diff --git a/docs/specs/tpm.rst b/docs/specs/tpm.rst
index 5e61238..ed6c0d7 100644
--- a/docs/specs/tpm.rst
+++ b/docs/specs/tpm.rst
@@ -199,8 +199,8 @@
QEMU files related to TPM backends:
- ``backends/tpm.c``
+ - ``include/sysemu/tpm.h``
- ``include/sysemu/tpm_backend.h``
- - ``include/sysemu/tpm_backend_int.h``
The QEMU TPM passthrough device
-------------------------------
@@ -232,9 +232,9 @@
PCRs.
QEMU files related to the TPM passthrough device:
- - ``hw/tpm/tpm_passthrough.c``
- - ``hw/tpm/tpm_util.c``
- - ``hw/tpm/tpm_util.h``
+ - ``backends/tpm/tpm_passthrough.c``
+ - ``backends/tpm/tpm_util.c``
+ - ``include/sysemu/tpm_util.h``
Command line to start QEMU with the TPM passthrough device using the host's
@@ -292,9 +292,9 @@
command.
QEMU files related to the TPM emulator device:
- - ``hw/tpm/tpm_emulator.c``
- - ``hw/tpm/tpm_util.c``
- - ``hw/tpm/tpm_util.h``
+ - ``backends/tpm/tpm_emulator.c``
+ - ``backends/tpm/tpm_util.c``
+ - ``include/sysemu/tpm_util.h``
The following commands start the swtpm with a UnixIO control channel over
a socket interface. They do not need to be run as root.
diff --git a/docs/system/target-rx.rst b/docs/system/target-rx.rst
new file mode 100644
index 0000000..4a20a89
--- /dev/null
+++ b/docs/system/target-rx.rst
@@ -0,0 +1,36 @@
+.. _RX-System-emulator:
+
+RX System emulator
+--------------------
+
+Use the executable ``qemu-system-rx`` to simulate RX target (GDB simulator).
+This target emulated following devices.
+
+- R5F562N8 MCU
+
+ - On-chip memory (ROM 512KB, RAM 96KB)
+ - Interrupt Control Unit (ICUa)
+ - 8Bit Timer x 1CH (TMR0,1)
+ - Compare Match Timer x 2CH (CMT0,1)
+ - Serial Communication Interface x 1CH (SCI0)
+
+- External memory 16MByte
+
+Example of ``qemu-system-rx`` usage for RX is shown below:
+
+Download ``<u-boot_image_file>`` from
+https://osdn.net/users/ysato/pf/qemu/dl/u-boot.bin.gz
+
+Start emulation of rx-virt::
+ qemu-system-rx -M gdbsim-r5f562n8 -bios <u-boot_image_file>
+
+Download ``kernel_image_file`` from
+https://osdn.net/users/ysato/pf/qemu/dl/zImage
+
+Download ``device_tree_blob`` from
+https://osdn.net/users/ysato/pf/qemu/dl/rx-virt.dtb
+
+Start emulation of rx-virt::
+ qemu-system-rx -M gdbsim-r5f562n8 \
+ -kernel <kernel_image_file> -dtb <device_tree_blob> \
+ -append "earlycon"
diff --git a/docs/system/targets.rst b/docs/system/targets.rst
index 0d8f915..99435a3 100644
--- a/docs/system/targets.rst
+++ b/docs/system/targets.rst
@@ -18,3 +18,4 @@
target-m68k
target-xtensa
target-s390x
+ target-rx
diff --git a/hw/Kconfig b/hw/Kconfig
index ecf491b..62f9ebd 100644
--- a/hw/Kconfig
+++ b/hw/Kconfig
@@ -55,6 +55,7 @@
source openrisc/Kconfig
source ppc/Kconfig
source riscv/Kconfig
+source rx/Kconfig
source s390x/Kconfig
source sh4/Kconfig
source sparc/Kconfig
diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c
index 1cb3411..b8abdef 100644
--- a/hw/acpi/generic_event_device.c
+++ b/hw/acpi/generic_event_device.c
@@ -193,6 +193,33 @@
}
}
+static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+ if ((object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) &&
+ !(object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)))) {
+ acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug request for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ AcpiGedState *s = ACPI_GED(hotplug_dev);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ acpi_memory_unplug_cb(&s->memhp_state, dev, errp);
+ } else {
+ error_setg(errp, "acpi: device unplug for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev)
{
AcpiGedState *s = ACPI_GED(adev);
@@ -318,6 +345,8 @@
dc->vmsd = &vmstate_acpi_ged;
hc->plug = acpi_ged_device_plug_cb;
+ hc->unplug_request = acpi_ged_unplug_request_cb;
+ hc->unplug = acpi_ged_unplug_cb;
adevc->send_event = acpi_ged_send_event;
}
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 9afa6ee..4a224a6 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -59,7 +59,7 @@
select ARM_TIMER # sp804
select ARM_V7M
select PL011 # UART
- select PL022 # Serial port
+ select PL022 # SPI
select PL031 # RTC
select PL061 # GPIO
select PL310 # cache controller
@@ -222,7 +222,7 @@
select CMSDK_APB_WATCHDOG
select I2C
select PL011 # UART
- select PL022 # Serial port
+ select PL022 # SPI
select PL061 # GPIO
select SSD0303 # OLED display
select SSD0323 # OLED display
@@ -401,10 +401,12 @@
select MPS2_FPGAIO
select MPS2_SCC
select OR_IRQ
- select PL022 # Serial port
+ select PL022 # SPI
select PL080 # DMA controller
select SPLIT_IRQ
select UNIMP
+ select CMSDK_APB_WATCHDOG
+ select VERSATILE_I2C
config FSL_IMX7
bool
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
index 8155c35..a4fd5dd 100644
--- a/hw/arm/mps2-tz.c
+++ b/hw/arm/mps2-tz.c
@@ -58,6 +58,7 @@
#include "hw/arm/armsse.h"
#include "hw/dma/pl080.h"
#include "hw/ssi/pl022.h"
+#include "hw/i2c/arm_sbcon_i2c.h"
#include "hw/net/lan9118.h"
#include "net/net.h"
#include "hw/core/split-irq.h"
@@ -87,7 +88,7 @@
TZPPC ppc[5];
TZMPC ssram_mpc[3];
PL022State spi[5];
- UnimplementedDeviceState i2c[4];
+ ArmSbconI2CState i2c[4];
UnimplementedDeviceState i2s_audio;
UnimplementedDeviceState gpio[4];
UnimplementedDeviceState gfx;
@@ -365,6 +366,18 @@
return sysbus_mmio_get_region(s, 0);
}
+static MemoryRegion *make_i2c(MPS2TZMachineState *mms, void *opaque,
+ const char *name, hwaddr size)
+{
+ ArmSbconI2CState *i2c = opaque;
+ SysBusDevice *s;
+
+ object_initialize_child(OBJECT(mms), name, i2c, TYPE_ARM_SBCON_I2C);
+ s = SYS_BUS_DEVICE(i2c);
+ sysbus_realize(s, &error_fatal);
+ return sysbus_mmio_get_region(s, 0);
+}
+
static void mps2tz_common_init(MachineState *machine)
{
MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine);
@@ -499,10 +512,10 @@
{ "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 },
{ "uart3", make_uart, &mms->uart[3], 0x40203000, 0x1000 },
{ "uart4", make_uart, &mms->uart[4], 0x40204000, 0x1000 },
- { "i2c0", make_unimp_dev, &mms->i2c[0], 0x40207000, 0x1000 },
- { "i2c1", make_unimp_dev, &mms->i2c[1], 0x40208000, 0x1000 },
- { "i2c2", make_unimp_dev, &mms->i2c[2], 0x4020c000, 0x1000 },
- { "i2c3", make_unimp_dev, &mms->i2c[3], 0x4020d000, 0x1000 },
+ { "i2c0", make_i2c, &mms->i2c[0], 0x40207000, 0x1000 },
+ { "i2c1", make_i2c, &mms->i2c[1], 0x40208000, 0x1000 },
+ { "i2c2", make_i2c, &mms->i2c[2], 0x4020c000, 0x1000 },
+ { "i2c3", make_i2c, &mms->i2c[3], 0x4020d000, 0x1000 },
},
}, {
.name = "apb_ppcexp2",
diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c
index daa55f7..d1653a7 100644
--- a/hw/arm/mps2.c
+++ b/hw/arm/mps2.c
@@ -38,8 +38,12 @@
#include "hw/timer/cmsdk-apb-timer.h"
#include "hw/timer/cmsdk-apb-dualtimer.h"
#include "hw/misc/mps2-scc.h"
+#include "hw/misc/mps2-fpgaio.h"
+#include "hw/ssi/pl022.h"
+#include "hw/i2c/arm_sbcon_i2c.h"
#include "hw/net/lan9118.h"
#include "net/net.h"
+#include "hw/watchdog/cmsdk-apb-watchdog.h"
typedef enum MPS2FPGAType {
FPGA_AN385,
@@ -65,8 +69,12 @@
MemoryRegion blockram_m2;
MemoryRegion blockram_m3;
MemoryRegion sram;
+ /* FPGA APB subsystem */
MPS2SCC scc;
+ MPS2FPGAIO fpgaio;
+ /* CMSDK APB subsystem */
CMSDKAPBDualTimer dualtimer;
+ CMSDKAPBWatchdog watchdog;
} MPS2MachineState;
#define TYPE_MPS2_MACHINE "mps2"
@@ -111,6 +119,7 @@
MemoryRegion *system_memory = get_system_memory();
MachineClass *mc = MACHINE_GET_CLASS(machine);
DeviceState *armv7m, *sccdev;
+ int i;
if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
error_report("This board can only be used with CPU %s",
@@ -210,10 +219,11 @@
*/
create_unimplemented_device("CMSDK APB peripheral region @0x40000000",
0x40000000, 0x00010000);
- create_unimplemented_device("CMSDK peripheral region @0x40010000",
+ create_unimplemented_device("CMSDK AHB peripheral region @0x40010000",
0x40010000, 0x00010000);
create_unimplemented_device("Extra peripheral region @0x40020000",
0x40020000, 0x00010000);
+
create_unimplemented_device("RESERVED 4", 0x40030000, 0x001D0000);
create_unimplemented_device("VGA", 0x41000000, 0x0200000);
@@ -225,7 +235,6 @@
*/
Object *orgate;
DeviceState *orgate_dev;
- int i;
orgate = object_new(TYPE_OR_IRQ);
object_property_set_int(orgate, 6, "num-lines", &error_fatal);
@@ -262,7 +271,6 @@
*/
Object *orgate;
DeviceState *orgate_dev;
- int i;
orgate = object_new(TYPE_OR_IRQ);
object_property_set_int(orgate, 10, "num-lines", &error_fatal);
@@ -298,10 +306,15 @@
default:
g_assert_not_reached();
}
+ for (i = 0; i < 4; i++) {
+ static const hwaddr gpiobase[] = {0x40010000, 0x40011000,
+ 0x40012000, 0x40013000};
+ create_unimplemented_device("cmsdk-ahb-gpio", gpiobase[i], 0x1000);
+ }
+ /* CMSDK APB subsystem */
cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ);
cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ);
-
object_initialize_child(OBJECT(mms), "dualtimer", &mms->dualtimer,
TYPE_CMSDK_APB_DUALTIMER);
qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ);
@@ -309,7 +322,15 @@
sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0,
qdev_get_gpio_in(armv7m, 10));
sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000);
+ object_initialize_child(OBJECT(mms), "watchdog", &mms->watchdog,
+ TYPE_CMSDK_APB_WATCHDOG);
+ qdev_prop_set_uint32(DEVICE(&mms->watchdog), "wdogclk-frq", SYSCLK_FRQ);
+ sysbus_realize(SYS_BUS_DEVICE(&mms->watchdog), &error_fatal);
+ sysbus_connect_irq(SYS_BUS_DEVICE(&mms->watchdog), 0,
+ qdev_get_gpio_in_named(armv7m, "NMI", 0));
+ sysbus_mmio_map(SYS_BUS_DEVICE(&mms->watchdog), 0, 0x40008000);
+ /* FPGA APB subsystem */
object_initialize_child(OBJECT(mms), "scc", &mms->scc, TYPE_MPS2_SCC);
sccdev = DEVICE(&mms->scc);
qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2);
@@ -317,6 +338,42 @@
qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id);
sysbus_realize(SYS_BUS_DEVICE(&mms->scc), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000);
+ object_initialize_child(OBJECT(mms), "fpgaio",
+ &mms->fpgaio, TYPE_MPS2_FPGAIO);
+ qdev_prop_set_uint32(DEVICE(&mms->fpgaio), "prescale-clk", 25000000);
+ sysbus_realize(SYS_BUS_DEVICE(&mms->fpgaio), &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(&mms->fpgaio), 0, 0x40028000);
+ sysbus_create_simple(TYPE_PL022, 0x40025000, /* External ADC */
+ qdev_get_gpio_in(armv7m, 22));
+ for (i = 0; i < 2; i++) {
+ static const int spi_irqno[] = {11, 24};
+ static const hwaddr spibase[] = {0x40020000, /* APB */
+ 0x40021000, /* LCD */
+ 0x40026000, /* Shield0 */
+ 0x40027000}; /* Shield1 */
+ DeviceState *orgate_dev;
+ Object *orgate;
+ int j;
+
+ orgate = object_new(TYPE_OR_IRQ);
+ object_property_set_int(orgate, 2, "num-lines", &error_fatal);
+ orgate_dev = DEVICE(orgate);
+ qdev_realize(orgate_dev, NULL, &error_fatal);
+ qdev_connect_gpio_out(orgate_dev, 0,
+ qdev_get_gpio_in(armv7m, spi_irqno[i]));
+ for (j = 0; j < 2; j++) {
+ sysbus_create_simple(TYPE_PL022, spibase[2 * i + j],
+ qdev_get_gpio_in(orgate_dev, j));
+ }
+ }
+ for (i = 0; i < 4; i++) {
+ static const hwaddr i2cbase[] = {0x40022000, /* Touch */
+ 0x40023000, /* Audio */
+ 0x40029000, /* Shield0 */
+ 0x4002a000}; /* Shield1 */
+ sysbus_create_simple(TYPE_ARM_SBCON_I2C, i2cbase[i], NULL);
+ }
+ create_unimplemented_device("i2s", 0x40024000, 0x400);
/* In hardware this is a LAN9220; the LAN9118 is software compatible
* except that it doesn't support the checksum-offload feature.
diff --git a/hw/arm/realview.c b/hw/arm/realview.c
index f3c00fe..b6c0a1a 100644
--- a/hw/arm/realview.c
+++ b/hw/arm/realview.c
@@ -26,6 +26,7 @@
#include "hw/cpu/a9mpcore.h"
#include "hw/intc/realview_gic.h"
#include "hw/irq.h"
+#include "hw/i2c/arm_sbcon_i2c.h"
#define SMP_BOOT_ADDR 0xe0000000
#define SMP_BOOTREG_ADDR 0x10000030
@@ -282,7 +283,7 @@
}
}
- dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL);
+ dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL);
i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c");
i2c_create_slave(i2c, "ds1338", 0x68);
diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c
index 2ebdcbd..e596b81 100644
--- a/hw/arm/versatilepb.c
+++ b/hw/arm/versatilepb.c
@@ -18,6 +18,7 @@
#include "sysemu/sysemu.h"
#include "hw/pci/pci.h"
#include "hw/i2c/i2c.h"
+#include "hw/i2c/arm_sbcon_i2c.h"
#include "hw/irq.h"
#include "hw/boards.h"
#include "exec/address-spaces.h"
@@ -314,7 +315,7 @@
/* Add PL031 Real Time Clock. */
sysbus_create_simple("pl031", 0x101e8000, pic[10]);
- dev = sysbus_create_simple("versatile_i2c", 0x10002000, NULL);
+ dev = sysbus_create_simple(TYPE_VERSATILE_I2C, 0x10002000, NULL);
i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c");
i2c_create_slave(i2c, "ds1338", 0x68);
diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c
index 725d024..5bf9cff 100644
--- a/hw/arm/vexpress.c
+++ b/hw/arm/vexpress.c
@@ -42,6 +42,7 @@
#include "hw/char/pl011.h"
#include "hw/cpu/a9mpcore.h"
#include "hw/cpu/a15mpcore.h"
+#include "hw/i2c/arm_sbcon_i2c.h"
#define VEXPRESS_BOARD_ID 0x8e0
#define VEXPRESS_FLASH_SIZE (64 * 1024 * 1024)
@@ -639,7 +640,7 @@
sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]);
sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]);
- dev = sysbus_create_simple("versatile_i2c", map[VE_SERIALDVI], NULL);
+ dev = sysbus_create_simple(TYPE_VERSATILE_I2C, map[VE_SERIALDVI], NULL);
i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c");
i2c_create_slave(i2c, "sii9022", 0x39);
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index caceb1e..402c362 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -2177,11 +2177,68 @@
}
}
+static void virt_dimm_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ Error *local_err = NULL;
+
+ if (!vms->acpi_dev) {
+ error_setg(&local_err,
+ "memory hotplug is not enabled: missing acpi-ged device");
+ goto out;
+ }
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
+ error_setg(&local_err,
+ "nvdimm device hot unplug is not supported yet.");
+ goto out;
+ }
+
+ hotplug_handler_unplug_request(HOTPLUG_HANDLER(vms->acpi_dev), dev,
+ &local_err);
+out:
+ error_propagate(errp, local_err);
+}
+
+static void virt_dimm_unplug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(hotplug_dev);
+ Error *local_err = NULL;
+
+ hotplug_handler_unplug(HOTPLUG_HANDLER(vms->acpi_dev), dev, &local_err);
+ if (local_err) {
+ goto out;
+ }
+
+ pc_dimm_unplug(PC_DIMM(dev), MACHINE(vms));
+ qdev_unrealize(dev);
+
+out:
+ error_propagate(errp, local_err);
+}
+
static void virt_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
- error_setg(errp, "device unplug request for unsupported device"
- " type: %s", object_get_typename(OBJECT(dev)));
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ virt_dimm_unplug_request(hotplug_dev, dev, errp);
+ } else {
+ error_setg(errp, "device unplug request for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
+}
+
+static void virt_machine_device_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
+ virt_dimm_unplug(hotplug_dev, dev, errp);
+ } else {
+ error_setg(errp, "virt: device unplug for unsupported device"
+ " type: %s", object_get_typename(OBJECT(dev)));
+ }
}
static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine,
@@ -2262,6 +2319,7 @@
hc->pre_plug = virt_machine_device_pre_plug_cb;
hc->plug = virt_machine_device_plug_cb;
hc->unplug_request = virt_machine_device_unplug_request_cb;
+ hc->unplug = virt_machine_device_unplug_cb;
mc->numa_mem_supported = true;
mc->nvdimm_supported = true;
mc->auto_enable_numa_with_memhp = true;
@@ -2375,6 +2433,7 @@
static void virt_machine_5_0_options(MachineClass *mc)
{
virt_machine_5_1_options(mc);
+ compat_props_add(mc->compat_props, hw_compat_5_0, hw_compat_5_0_len);
}
DEFINE_VIRT_MACHINE(5, 0)
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 40e7a8b..8746275 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -46,3 +46,6 @@
config TERMINAL3270
bool
+
+config RENESAS_SCI
+ bool
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index 633996b..8306c4a 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -21,6 +21,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_CMSDK_APB_UART) += cmsdk-apb-uart.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c
new file mode 100644
index 0000000..5d7c6e6
--- /dev/null
+++ b/hw/char/renesas_sci.c
@@ -0,0 +1,350 @@
+/*
+ * Renesas Serial Communication Interface
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/char/renesas_sci.h"
+#include "migration/vmstate.h"
+
+/* SCI register map */
+REG8(SMR, 0)
+ FIELD(SMR, CKS, 0, 2)
+ FIELD(SMR, MP, 2, 1)
+ FIELD(SMR, STOP, 3, 1)
+ FIELD(SMR, PM, 4, 1)
+ FIELD(SMR, PE, 5, 1)
+ FIELD(SMR, CHR, 6, 1)
+ FIELD(SMR, CM, 7, 1)
+REG8(BRR, 1)
+REG8(SCR, 2)
+ FIELD(SCR, CKE, 0, 2)
+ FIELD(SCR, TEIE, 2, 1)
+ FIELD(SCR, MPIE, 3, 1)
+ FIELD(SCR, RE, 4, 1)
+ FIELD(SCR, TE, 5, 1)
+ FIELD(SCR, RIE, 6, 1)
+ FIELD(SCR, TIE, 7, 1)
+REG8(TDR, 3)
+REG8(SSR, 4)
+ FIELD(SSR, MPBT, 0, 1)
+ FIELD(SSR, MPB, 1, 1)
+ FIELD(SSR, TEND, 2, 1)
+ FIELD(SSR, ERR, 3, 3)
+ FIELD(SSR, PER, 3, 1)
+ FIELD(SSR, FER, 4, 1)
+ FIELD(SSR, ORER, 5, 1)
+ FIELD(SSR, RDRF, 6, 1)
+ FIELD(SSR, TDRE, 7, 1)
+REG8(RDR, 5)
+REG8(SCMR, 6)
+ FIELD(SCMR, SMIF, 0, 1)
+ FIELD(SCMR, SINV, 2, 1)
+ FIELD(SCMR, SDIR, 3, 1)
+ FIELD(SCMR, BCP2, 7, 1)
+REG8(SEMR, 7)
+ FIELD(SEMR, ACS0, 0, 1)
+ FIELD(SEMR, ABCS, 4, 1)
+
+static int can_receive(void *opaque)
+{
+ RSCIState *sci = RSCI(opaque);
+ if (sci->rx_next > qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)) {
+ return 0;
+ } else {
+ return FIELD_EX8(sci->scr, SCR, RE);
+ }
+}
+
+static void receive(void *opaque, const uint8_t *buf, int size)
+{
+ RSCIState *sci = RSCI(opaque);
+ sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime;
+ if (FIELD_EX8(sci->ssr, SSR, RDRF) || size > 1) {
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, ORER, 1);
+ if (FIELD_EX8(sci->scr, SCR, RIE)) {
+ qemu_set_irq(sci->irq[ERI], 1);
+ }
+ } else {
+ sci->rdr = buf[0];
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 1);
+ if (FIELD_EX8(sci->scr, SCR, RIE)) {
+ qemu_irq_pulse(sci->irq[RXI]);
+ }
+ }
+}
+
+static void send_byte(RSCIState *sci)
+{
+ if (qemu_chr_fe_backend_connected(&sci->chr)) {
+ qemu_chr_fe_write_all(&sci->chr, &sci->tdr, 1);
+ }
+ timer_mod(&sci->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + sci->trtime);
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 0);
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
+ qemu_set_irq(sci->irq[TEI], 0);
+ if (FIELD_EX8(sci->scr, SCR, TIE)) {
+ qemu_irq_pulse(sci->irq[TXI]);
+ }
+}
+
+static void txend(void *opaque)
+{
+ RSCIState *sci = RSCI(opaque);
+ if (!FIELD_EX8(sci->ssr, SSR, TDRE)) {
+ send_byte(sci);
+ } else {
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
+ if (FIELD_EX8(sci->scr, SCR, TEIE)) {
+ qemu_set_irq(sci->irq[TEI], 1);
+ }
+ }
+}
+
+static void update_trtime(RSCIState *sci)
+{
+ /* char per bits */
+ sci->trtime = 8 - FIELD_EX8(sci->smr, SMR, CHR);
+ sci->trtime += FIELD_EX8(sci->smr, SMR, PE);
+ sci->trtime += FIELD_EX8(sci->smr, SMR, STOP) + 1;
+ /* x bit transmit time (32 * divrate * brr) / base freq */
+ sci->trtime *= 32 * sci->brr;
+ sci->trtime *= 1 << (2 * FIELD_EX8(sci->smr, SMR, CKS));
+ sci->trtime *= NANOSECONDS_PER_SECOND;
+ sci->trtime /= sci->input_freq;
+}
+
+static bool sci_is_tr_enabled(RSCIState *sci)
+{
+ return FIELD_EX8(sci->scr, SCR, TE) || FIELD_EX8(sci->scr, SCR, RE);
+}
+
+static void sci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
+{
+ RSCIState *sci = RSCI(opaque);
+
+ switch (offset) {
+ case A_SMR:
+ if (!sci_is_tr_enabled(sci)) {
+ sci->smr = val;
+ update_trtime(sci);
+ }
+ break;
+ case A_BRR:
+ if (!sci_is_tr_enabled(sci)) {
+ sci->brr = val;
+ update_trtime(sci);
+ }
+ break;
+ case A_SCR:
+ sci->scr = val;
+ if (FIELD_EX8(sci->scr, SCR, TE)) {
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 1);
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TEND, 1);
+ if (FIELD_EX8(sci->scr, SCR, TIE)) {
+ qemu_irq_pulse(sci->irq[TXI]);
+ }
+ }
+ if (!FIELD_EX8(sci->scr, SCR, TEIE)) {
+ qemu_set_irq(sci->irq[TEI], 0);
+ }
+ if (!FIELD_EX8(sci->scr, SCR, RIE)) {
+ qemu_set_irq(sci->irq[ERI], 0);
+ }
+ break;
+ case A_TDR:
+ sci->tdr = val;
+ if (FIELD_EX8(sci->ssr, SSR, TEND)) {
+ send_byte(sci);
+ } else {
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, TDRE, 0);
+ }
+ break;
+ case A_SSR:
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, MPBT,
+ FIELD_EX8(val, SSR, MPBT));
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, ERR,
+ FIELD_EX8(val, SSR, ERR) & 0x07);
+ if (FIELD_EX8(sci->read_ssr, SSR, ERR) &&
+ FIELD_EX8(sci->ssr, SSR, ERR) == 0) {
+ qemu_set_irq(sci->irq[ERI], 0);
+ }
+ break;
+ case A_RDR:
+ qemu_log_mask(LOG_GUEST_ERROR, "reneas_sci: RDR is read only.\n");
+ break;
+ case A_SCMR:
+ sci->scmr = val; break;
+ case A_SEMR: /* SEMR */
+ sci->semr = val; break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX " "
+ "not implemented\n",
+ offset);
+ }
+}
+
+static uint64_t sci_read(void *opaque, hwaddr offset, unsigned size)
+{
+ RSCIState *sci = RSCI(opaque);
+
+ switch (offset) {
+ case A_SMR:
+ return sci->smr;
+ case A_BRR:
+ return sci->brr;
+ case A_SCR:
+ return sci->scr;
+ case A_TDR:
+ return sci->tdr;
+ case A_SSR:
+ sci->read_ssr = sci->ssr;
+ return sci->ssr;
+ case A_RDR:
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, RDRF, 0);
+ return sci->rdr;
+ case A_SCMR:
+ return sci->scmr;
+ case A_SEMR:
+ return sci->semr;
+ default:
+ qemu_log_mask(LOG_UNIMP, "renesas_sci: Register 0x%" HWADDR_PRIX
+ " not implemented.\n", offset);
+ }
+ return UINT64_MAX;
+}
+
+static const MemoryRegionOps sci_ops = {
+ .write = sci_write,
+ .read = sci_read,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl.max_access_size = 1,
+ .valid.max_access_size = 1,
+};
+
+static void rsci_reset(DeviceState *dev)
+{
+ RSCIState *sci = RSCI(dev);
+ sci->smr = sci->scr = 0x00;
+ sci->brr = 0xff;
+ sci->tdr = 0xff;
+ sci->rdr = 0x00;
+ sci->ssr = 0x84;
+ sci->scmr = 0x00;
+ sci->semr = 0x00;
+ sci->rx_next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void sci_event(void *opaque, QEMUChrEvent event)
+{
+ RSCIState *sci = RSCI(opaque);
+ if (event == CHR_EVENT_BREAK) {
+ sci->ssr = FIELD_DP8(sci->ssr, SSR, FER, 1);
+ if (FIELD_EX8(sci->scr, SCR, RIE)) {
+ qemu_set_irq(sci->irq[ERI], 1);
+ }
+ }
+}
+
+static void rsci_realize(DeviceState *dev, Error **errp)
+{
+ RSCIState *sci = RSCI(dev);
+
+ if (sci->input_freq == 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_sci: input-freq property must be set.");
+ return;
+ }
+ qemu_chr_fe_set_handlers(&sci->chr, can_receive, receive,
+ sci_event, NULL, sci, NULL, true);
+}
+
+static void rsci_init(Object *obj)
+{
+ SysBusDevice *d = SYS_BUS_DEVICE(obj);
+ RSCIState *sci = RSCI(obj);
+ int i;
+
+ memory_region_init_io(&sci->memory, OBJECT(sci), &sci_ops,
+ sci, "renesas-sci", 0x8);
+ sysbus_init_mmio(d, &sci->memory);
+
+ for (i = 0; i < SCI_NR_IRQ; i++) {
+ sysbus_init_irq(d, &sci->irq[i]);
+ }
+ timer_init_ns(&sci->timer, QEMU_CLOCK_VIRTUAL, txend, sci);
+}
+
+static const VMStateDescription vmstate_rsci = {
+ .name = "renesas-sci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(trtime, RSCIState),
+ VMSTATE_INT64(rx_next, RSCIState),
+ VMSTATE_UINT8(smr, RSCIState),
+ VMSTATE_UINT8(brr, RSCIState),
+ VMSTATE_UINT8(scr, RSCIState),
+ VMSTATE_UINT8(tdr, RSCIState),
+ VMSTATE_UINT8(ssr, RSCIState),
+ VMSTATE_UINT8(rdr, RSCIState),
+ VMSTATE_UINT8(scmr, RSCIState),
+ VMSTATE_UINT8(semr, RSCIState),
+ VMSTATE_UINT8(read_ssr, RSCIState),
+ VMSTATE_TIMER(timer, RSCIState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property rsci_properties[] = {
+ DEFINE_PROP_UINT64("input-freq", RSCIState, input_freq, 0),
+ DEFINE_PROP_CHR("chardev", RSCIState, chr),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rsci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = rsci_realize;
+ dc->vmsd = &vmstate_rsci;
+ dc->reset = rsci_reset;
+ device_class_set_props(dc, rsci_properties);
+}
+
+static const TypeInfo rsci_info = {
+ .name = TYPE_RENESAS_SCI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RSCIState),
+ .instance_init = rsci_init,
+ .class_init = rsci_class_init,
+};
+
+static void rsci_register_types(void)
+{
+ type_register_static(&rsci_info);
+}
+
+type_init(rsci_register_types)
diff --git a/hw/i2c/versatile_i2c.c b/hw/i2c/versatile_i2c.c
index 1ac2a6f..da8cda2 100644
--- a/hw/i2c/versatile_i2c.c
+++ b/hw/i2c/versatile_i2c.c
@@ -1,5 +1,6 @@
/*
- * ARM Versatile I2C controller
+ * ARM SBCon two-wire serial bus interface (I2C bitbang)
+ * a.k.a. ARM Versatile I2C controller
*
* Copyright (c) 2006-2007 CodeSourcery.
* Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com>
@@ -22,32 +23,33 @@
*/
#include "qemu/osdep.h"
-#include "hw/sysbus.h"
-#include "hw/i2c/bitbang_i2c.h"
+#include "hw/i2c/arm_sbcon_i2c.h"
+#include "hw/registerfields.h"
#include "qemu/log.h"
#include "qemu/module.h"
-#define TYPE_VERSATILE_I2C "versatile_i2c"
#define VERSATILE_I2C(obj) \
OBJECT_CHECK(VersatileI2CState, (obj), TYPE_VERSATILE_I2C)
-typedef struct VersatileI2CState {
- SysBusDevice parent_obj;
+typedef ArmSbconI2CState VersatileI2CState;
- MemoryRegion iomem;
- bitbang_i2c_interface bitbang;
- int out;
- int in;
-} VersatileI2CState;
+
+REG32(CONTROL_GET, 0)
+REG32(CONTROL_SET, 0)
+REG32(CONTROL_CLR, 4)
+
+#define SCL BIT(0)
+#define SDA BIT(1)
static uint64_t versatile_i2c_read(void *opaque, hwaddr offset,
unsigned size)
{
VersatileI2CState *s = (VersatileI2CState *)opaque;
- if (offset == 0) {
+ switch (offset) {
+ case A_CONTROL_SET:
return (s->out & 1) | (s->in << 1);
- } else {
+ default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
return -1;
@@ -60,18 +62,18 @@
VersatileI2CState *s = (VersatileI2CState *)opaque;
switch (offset) {
- case 0:
+ case A_CONTROL_SET:
s->out |= value & 3;
break;
- case 4:
+ case A_CONTROL_CLR:
s->out &= ~value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%x\n", __func__, (int)offset);
}
- bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & 1) != 0);
- s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & 2) != 0);
+ bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SCL, (s->out & SCL) != 0);
+ s->in = bitbang_i2c_set(&s->bitbang, BITBANG_I2C_SDA, (s->out & SDA) != 0);
}
static const MemoryRegionOps versatile_i2c_ops = {
@@ -90,7 +92,7 @@
bus = i2c_init_bus(dev, "i2c");
bitbang_i2c_init(&s->bitbang, bus);
memory_region_init_io(&s->iomem, obj, &versatile_i2c_ops, s,
- "versatile_i2c", 0x1000);
+ "arm_sbcon_i2c", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
}
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig
index a189d6f..f562342 100644
--- a/hw/intc/Kconfig
+++ b/hw/intc/Kconfig
@@ -61,3 +61,6 @@
config OMPIC
bool
+
+config RX_ICU
+ bool
diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs
index a61e672..a420263 100644
--- a/hw/intc/Makefile.objs
+++ b/hw/intc/Makefile.objs
@@ -20,6 +20,7 @@
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_redist.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_its_common.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
+common-obj-$(CONFIG_RX_ICU) += rx_icu.o
common-obj-y += intc.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c
new file mode 100644
index 0000000..df4b6a8
--- /dev/null
+++ b/hw/intc/rx_icu.c
@@ -0,0 +1,397 @@
+/*
+ * RX Interrupt Control Unit
+ *
+ * Warning: Only ICUa is supported.
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/irq.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/intc/rx_icu.h"
+#include "migration/vmstate.h"
+
+REG8(IR, 0)
+ FIELD(IR, IR, 0, 1)
+REG8(DTCER, 0x100)
+ FIELD(DTCER, DTCE, 0, 1)
+REG8(IER, 0x200)
+REG8(SWINTR, 0x2e0)
+ FIELD(SWINTR, SWINT, 0, 1)
+REG16(FIR, 0x2f0)
+ FIELD(FIR, FVCT, 0, 8)
+ FIELD(FIR, FIEN, 15, 1)
+REG8(IPR, 0x300)
+ FIELD(IPR, IPR, 0, 4)
+REG8(DMRSR, 0x400)
+REG8(IRQCR, 0x500)
+ FIELD(IRQCR, IRQMD, 2, 2)
+REG8(NMISR, 0x580)
+ FIELD(NMISR, NMIST, 0, 1)
+ FIELD(NMISR, LVDST, 1, 1)
+ FIELD(NMISR, OSTST, 2, 1)
+REG8(NMIER, 0x581)
+ FIELD(NMIER, NMIEN, 0, 1)
+ FIELD(NMIER, LVDEN, 1, 1)
+ FIELD(NMIER, OSTEN, 2, 1)
+REG8(NMICLR, 0x582)
+ FIELD(NMICLR, NMICLR, 0, 1)
+ FIELD(NMICLR, OSTCLR, 2, 1)
+REG8(NMICR, 0x583)
+ FIELD(NMICR, NMIMD, 3, 1)
+
+static void set_irq(RXICUState *icu, int n_IRQ, int req)
+{
+ if ((icu->fir & R_FIR_FIEN_MASK) &&
+ (icu->fir & R_FIR_FVCT_MASK) == n_IRQ) {
+ qemu_set_irq(icu->_fir, req);
+ } else {
+ qemu_set_irq(icu->_irq, req);
+ }
+}
+
+static uint16_t rxicu_level(RXICUState *icu, unsigned n)
+{
+ return (icu->ipr[icu->map[n]] << 8) | n;
+}
+
+static void rxicu_request(RXICUState *icu, int n_IRQ)
+{
+ int enable;
+
+ enable = icu->ier[n_IRQ / 8] & (1 << (n_IRQ & 7));
+ if (n_IRQ > 0 && enable != 0 && atomic_read(&icu->req_irq) < 0) {
+ atomic_set(&icu->req_irq, n_IRQ);
+ set_irq(icu, n_IRQ, rxicu_level(icu, n_IRQ));
+ }
+}
+
+static void rxicu_set_irq(void *opaque, int n_IRQ, int level)
+{
+ RXICUState *icu = opaque;
+ struct IRQSource *src;
+ int issue;
+
+ if (n_IRQ >= NR_IRQS) {
+ error_report("%s: IRQ %d out of range", __func__, n_IRQ);
+ return;
+ }
+
+ src = &icu->src[n_IRQ];
+
+ level = (level != 0);
+ switch (src->sense) {
+ case TRG_LEVEL:
+ /* level-sensitive irq */
+ issue = level;
+ src->level = level;
+ break;
+ case TRG_NEDGE:
+ issue = (level == 0 && src->level == 1);
+ src->level = level;
+ break;
+ case TRG_PEDGE:
+ issue = (level == 1 && src->level == 0);
+ src->level = level;
+ break;
+ case TRG_BEDGE:
+ issue = ((level ^ src->level) & 1);
+ src->level = level;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if (issue == 0 && src->sense == TRG_LEVEL) {
+ icu->ir[n_IRQ] = 0;
+ if (atomic_read(&icu->req_irq) == n_IRQ) {
+ /* clear request */
+ set_irq(icu, n_IRQ, 0);
+ atomic_set(&icu->req_irq, -1);
+ }
+ return;
+ }
+ if (issue) {
+ icu->ir[n_IRQ] = 1;
+ rxicu_request(icu, n_IRQ);
+ }
+}
+
+static void rxicu_ack_irq(void *opaque, int no, int level)
+{
+ RXICUState *icu = opaque;
+ int i;
+ int n_IRQ;
+ int max_pri;
+
+ n_IRQ = atomic_read(&icu->req_irq);
+ if (n_IRQ < 0) {
+ return;
+ }
+ atomic_set(&icu->req_irq, -1);
+ if (icu->src[n_IRQ].sense != TRG_LEVEL) {
+ icu->ir[n_IRQ] = 0;
+ }
+
+ max_pri = 0;
+ n_IRQ = -1;
+ for (i = 0; i < NR_IRQS; i++) {
+ if (icu->ir[i]) {
+ if (max_pri < icu->ipr[icu->map[i]]) {
+ n_IRQ = i;
+ max_pri = icu->ipr[icu->map[i]];
+ }
+ }
+ }
+
+ if (n_IRQ >= 0) {
+ rxicu_request(icu, n_IRQ);
+ }
+}
+
+static uint64_t icu_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RXICUState *icu = opaque;
+ int reg = addr & 0xff;
+
+ if ((addr != A_FIR && size != 1) ||
+ (addr == A_FIR && size != 2)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid read size 0x%"
+ HWADDR_PRIX "\n",
+ addr);
+ return UINT64_MAX;
+ }
+ switch (addr) {
+ case A_IR ... A_IR + 0xff:
+ return icu->ir[reg] & R_IR_IR_MASK;
+ case A_DTCER ... A_DTCER + 0xff:
+ return icu->dtcer[reg] & R_DTCER_DTCE_MASK;
+ case A_IER ... A_IER + 0x1f:
+ return icu->ier[reg];
+ case A_SWINTR:
+ return 0;
+ case A_FIR:
+ return icu->fir & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK);
+ case A_IPR ... A_IPR + 0x8f:
+ return icu->ipr[reg] & R_IPR_IPR_MASK;
+ case A_DMRSR:
+ case A_DMRSR + 4:
+ case A_DMRSR + 8:
+ case A_DMRSR + 12:
+ return icu->dmasr[reg >> 2];
+ case A_IRQCR ... A_IRQCR + 0x1f:
+ return icu->src[64 + reg].sense << R_IRQCR_IRQMD_SHIFT;
+ case A_NMISR:
+ case A_NMICLR:
+ return 0;
+ case A_NMIER:
+ return icu->nmier;
+ case A_NMICR:
+ return icu->nmicr;
+ default:
+ qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " "
+ "not implemented.\n",
+ addr);
+ break;
+ }
+ return UINT64_MAX;
+}
+
+static void icu_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ RXICUState *icu = opaque;
+ int reg = addr & 0xff;
+
+ if ((addr != A_FIR && size != 1) ||
+ (addr == A_FIR && size != 2)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "rx_icu: Invalid write size at "
+ "0x%" HWADDR_PRIX "\n",
+ addr);
+ return;
+ }
+ switch (addr) {
+ case A_IR ... A_IR + 0xff:
+ if (icu->src[reg].sense != TRG_LEVEL && val == 0) {
+ icu->ir[reg] = 0;
+ }
+ break;
+ case A_DTCER ... A_DTCER + 0xff:
+ icu->dtcer[reg] = val & R_DTCER_DTCE_MASK;
+ qemu_log_mask(LOG_UNIMP, "rx_icu: DTC not implemented\n");
+ break;
+ case A_IER ... A_IER + 0x1f:
+ icu->ier[reg] = val;
+ break;
+ case A_SWINTR:
+ if (val & R_SWINTR_SWINT_MASK) {
+ qemu_irq_pulse(icu->_swi);
+ }
+ break;
+ case A_FIR:
+ icu->fir = val & (R_FIR_FIEN_MASK | R_FIR_FVCT_MASK);
+ break;
+ case A_IPR ... A_IPR + 0x8f:
+ icu->ipr[reg] = val & R_IPR_IPR_MASK;
+ break;
+ case A_DMRSR:
+ case A_DMRSR + 4:
+ case A_DMRSR + 8:
+ case A_DMRSR + 12:
+ icu->dmasr[reg >> 2] = val;
+ qemu_log_mask(LOG_UNIMP, "rx_icu: DMAC not implemented\n");
+ break;
+ case A_IRQCR ... A_IRQCR + 0x1f:
+ icu->src[64 + reg].sense = val >> R_IRQCR_IRQMD_SHIFT;
+ break;
+ case A_NMICLR:
+ break;
+ case A_NMIER:
+ icu->nmier |= val & (R_NMIER_NMIEN_MASK |
+ R_NMIER_LVDEN_MASK |
+ R_NMIER_OSTEN_MASK);
+ break;
+ case A_NMICR:
+ if ((icu->nmier & R_NMIER_NMIEN_MASK) == 0) {
+ icu->nmicr = val & R_NMICR_NMIMD_MASK;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "rx_icu: Register 0x%" HWADDR_PRIX " "
+ "not implemented\n",
+ addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps icu_ops = {
+ .write = icu_write,
+ .read = icu_read,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 2,
+ },
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 2,
+ },
+};
+
+static void rxicu_realize(DeviceState *dev, Error **errp)
+{
+ RXICUState *icu = RX_ICU(dev);
+ int i, j;
+
+ if (icu->init_sense == NULL) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "rx_icu: trigger-level property must be set.");
+ return;
+ }
+ for (i = j = 0; i < NR_IRQS; i++) {
+ if (icu->init_sense[j] == i) {
+ icu->src[i].sense = TRG_LEVEL;
+ if (j < icu->nr_sense) {
+ j++;
+ }
+ } else {
+ icu->src[i].sense = TRG_PEDGE;
+ }
+ }
+ icu->req_irq = -1;
+}
+
+static void rxicu_init(Object *obj)
+{
+ SysBusDevice *d = SYS_BUS_DEVICE(obj);
+ RXICUState *icu = RX_ICU(obj);
+
+ memory_region_init_io(&icu->memory, OBJECT(icu), &icu_ops,
+ icu, "rx-icu", 0x600);
+ sysbus_init_mmio(d, &icu->memory);
+
+ qdev_init_gpio_in(DEVICE(d), rxicu_set_irq, NR_IRQS);
+ qdev_init_gpio_in_named(DEVICE(d), rxicu_ack_irq, "ack", 1);
+ sysbus_init_irq(d, &icu->_irq);
+ sysbus_init_irq(d, &icu->_fir);
+ sysbus_init_irq(d, &icu->_swi);
+}
+
+static void rxicu_fini(Object *obj)
+{
+ RXICUState *icu = RX_ICU(obj);
+ g_free(icu->map);
+ g_free(icu->init_sense);
+}
+
+static const VMStateDescription vmstate_rxicu = {
+ .name = "rx-icu",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(ir, RXICUState, NR_IRQS),
+ VMSTATE_UINT8_ARRAY(dtcer, RXICUState, NR_IRQS),
+ VMSTATE_UINT8_ARRAY(ier, RXICUState, NR_IRQS / 8),
+ VMSTATE_UINT8_ARRAY(ipr, RXICUState, 142),
+ VMSTATE_UINT8_ARRAY(dmasr, RXICUState, 4),
+ VMSTATE_UINT16(fir, RXICUState),
+ VMSTATE_UINT8(nmisr, RXICUState),
+ VMSTATE_UINT8(nmier, RXICUState),
+ VMSTATE_UINT8(nmiclr, RXICUState),
+ VMSTATE_UINT8(nmicr, RXICUState),
+ VMSTATE_INT16(req_irq, RXICUState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property rxicu_properties[] = {
+ DEFINE_PROP_ARRAY("ipr-map", RXICUState, nr_irqs, map,
+ qdev_prop_uint8, uint8_t),
+ DEFINE_PROP_ARRAY("trigger-level", RXICUState, nr_sense, init_sense,
+ qdev_prop_uint8, uint8_t),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rxicu_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = rxicu_realize;
+ dc->vmsd = &vmstate_rxicu;
+ device_class_set_props(dc, rxicu_properties);
+}
+
+static const TypeInfo rxicu_info = {
+ .name = TYPE_RX_ICU,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RXICUState),
+ .instance_init = rxicu_init,
+ .instance_finalize = rxicu_fini,
+ .class_init = rxicu_class_init,
+};
+
+static void rxicu_register_types(void)
+{
+ type_register_static(&rxicu_info);
+}
+
+type_init(rxicu_register_types)
diff --git a/hw/rx/Kconfig b/hw/rx/Kconfig
new file mode 100644
index 0000000..2b297c5
--- /dev/null
+++ b/hw/rx/Kconfig
@@ -0,0 +1,10 @@
+config RX62N_MCU
+ bool
+ select RX_ICU
+ select RENESAS_TMR
+ select RENESAS_CMT
+ select RENESAS_SCI
+
+config RX_GDBSIM
+ bool
+ select RX62N_MCU
diff --git a/hw/rx/Makefile.objs b/hw/rx/Makefile.objs
new file mode 100644
index 0000000..4ef6b9e
--- /dev/null
+++ b/hw/rx/Makefile.objs
@@ -0,0 +1,2 @@
+obj-$(CONFIG_RX62N_MCU) += rx62n.o
+obj-$(CONFIG_RX_GDBSIM) += rx-gdbsim.o
diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c
new file mode 100644
index 0000000..b8a56fa
--- /dev/null
+++ b/hw/rx/rx-gdbsim.c
@@ -0,0 +1,198 @@
+/*
+ * RX QEMU GDB simulator
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include "qemu-common.h"
+#include "cpu.h"
+#include "hw/hw.h"
+#include "hw/sysbus.h"
+#include "hw/loader.h"
+#include "hw/rx/rx62n.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "sysemu/device_tree.h"
+#include "hw/boards.h"
+
+/* Same address of GDB integrated simulator */
+#define SDRAM_BASE EXT_CS_BASE
+
+typedef struct RxGdbSimMachineClass {
+ /*< private >*/
+ MachineClass parent_class;
+ /*< public >*/
+ const char *mcu_name;
+ uint32_t xtal_freq_hz;
+} RxGdbSimMachineClass;
+
+typedef struct RxGdbSimMachineState {
+ /*< private >*/
+ MachineState parent_obj;
+ /*< public >*/
+ RX62NState mcu;
+} RxGdbSimMachineState;
+
+#define TYPE_RX_GDBSIM_MACHINE MACHINE_TYPE_NAME("rx62n-common")
+
+#define RX_GDBSIM_MACHINE(obj) \
+ OBJECT_CHECK(RxGdbSimMachineState, (obj), TYPE_RX_GDBSIM_MACHINE)
+
+#define RX_GDBSIM_MACHINE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(RxGdbSimMachineClass, (klass), TYPE_RX_GDBSIM_MACHINE)
+#define RX_GDBSIM_MACHINE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(RxGdbSimMachineClass, (obj), TYPE_RX_GDBSIM_MACHINE)
+
+static void rx_load_image(RXCPU *cpu, const char *filename,
+ uint32_t start, uint32_t size)
+{
+ static uint32_t extable[32];
+ long kernel_size;
+ int i;
+
+ kernel_size = load_image_targphys(filename, start, size);
+ if (kernel_size < 0) {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n", filename);
+ exit(1);
+ }
+ cpu->env.pc = start;
+
+ /* setup exception trap trampoline */
+ /* linux kernel only works little-endian mode */
+ for (i = 0; i < ARRAY_SIZE(extable); i++) {
+ extable[i] = cpu_to_le32(0x10 + i * 4);
+ }
+ rom_add_blob_fixed("extable", extable, sizeof(extable), VECTOR_TABLE_BASE);
+}
+
+static void rx_gdbsim_init(MachineState *machine)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
+ RxGdbSimMachineState *s = RX_GDBSIM_MACHINE(machine);
+ RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_GET_CLASS(machine);
+ MemoryRegion *sysmem = get_system_memory();
+ const char *kernel_filename = machine->kernel_filename;
+ const char *dtb_filename = machine->dtb;
+
+ if (machine->ram_size < mc->default_ram_size) {
+ char *sz = size_to_str(mc->default_ram_size);
+ error_report("Invalid RAM size, should be more than %s", sz);
+ g_free(sz);
+ }
+
+ /* Allocate memory space */
+ memory_region_add_subregion(sysmem, SDRAM_BASE, machine->ram);
+
+ /* Initialize MCU */
+ object_initialize_child(OBJECT(machine), "mcu", &s->mcu, rxc->mcu_name);
+ object_property_set_link(OBJECT(&s->mcu), OBJECT(sysmem),
+ "main-bus", &error_abort);
+ object_property_set_uint(OBJECT(&s->mcu), rxc->xtal_freq_hz,
+ "xtal-frequency-hz", &error_abort);
+ object_property_set_bool(OBJECT(&s->mcu), kernel_filename != NULL,
+ "load-kernel", &error_abort);
+ qdev_realize(DEVICE(&s->mcu), NULL, &error_abort);
+
+ /* Load kernel and dtb */
+ if (kernel_filename) {
+ ram_addr_t kernel_offset;
+
+ /*
+ * The kernel image is loaded into
+ * the latter half of the SDRAM space.
+ */
+ kernel_offset = machine->ram_size / 2;
+ rx_load_image(RXCPU(first_cpu), kernel_filename,
+ SDRAM_BASE + kernel_offset, kernel_offset);
+ if (dtb_filename) {
+ ram_addr_t dtb_offset;
+ int dtb_size;
+ void *dtb;
+
+ dtb = load_device_tree(dtb_filename, &dtb_size);
+ if (dtb == NULL) {
+ error_report("Couldn't open dtb file %s", dtb_filename);
+ exit(1);
+ }
+ if (machine->kernel_cmdline &&
+ qemu_fdt_setprop_string(dtb, "/chosen", "bootargs",
+ machine->kernel_cmdline) < 0) {
+ error_report("Couldn't set /chosen/bootargs");
+ exit(1);
+ }
+ /* DTB is located at the end of SDRAM space. */
+ dtb_offset = machine->ram_size - dtb_size;
+ rom_add_blob_fixed("dtb", dtb, dtb_size,
+ SDRAM_BASE + dtb_offset);
+ /* Set dtb address to R1 */
+ RXCPU(first_cpu)->env.regs[1] = SDRAM_BASE + dtb_offset;
+ }
+ }
+}
+
+static void rx_gdbsim_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = rx_gdbsim_init;
+ mc->default_cpu_type = TYPE_RX62N_CPU;
+ mc->default_ram_size = 16 * MiB;
+ mc->default_ram_id = "ext-sdram";
+}
+
+static void rx62n7_class_init(ObjectClass *oc, void *data)
+{
+ RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc);
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ rxc->mcu_name = TYPE_R5F562N7_MCU;
+ rxc->xtal_freq_hz = 12 * 1000 * 1000;
+ mc->desc = "gdb simulator (R5F562N7 MCU and external RAM)";
+};
+
+static void rx62n8_class_init(ObjectClass *oc, void *data)
+{
+ RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc);
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ rxc->mcu_name = TYPE_R5F562N8_MCU;
+ rxc->xtal_freq_hz = 12 * 1000 * 1000;
+ mc->desc = "gdb simulator (R5F562N8 MCU and external RAM)";
+};
+
+static const TypeInfo rx_gdbsim_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("gdbsim-r5f562n7"),
+ .parent = TYPE_RX_GDBSIM_MACHINE,
+ .class_init = rx62n7_class_init,
+ }, {
+ .name = MACHINE_TYPE_NAME("gdbsim-r5f562n8"),
+ .parent = TYPE_RX_GDBSIM_MACHINE,
+ .class_init = rx62n8_class_init,
+ }, {
+ .name = TYPE_RX_GDBSIM_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(RxGdbSimMachineState),
+ .class_size = sizeof(RxGdbSimMachineClass),
+ .class_init = rx_gdbsim_class_init,
+ .abstract = true,
+ }
+};
+
+DEFINE_TYPES(rx_gdbsim_types)
diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c
new file mode 100644
index 0000000..b9c217e
--- /dev/null
+++ b/hw/rx/rx62n.c
@@ -0,0 +1,323 @@
+/*
+ * RX62N Microcontroller
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ * Copyright (c) 2020 Philippe Mathieu-Daudé
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/hw.h"
+#include "hw/rx/rx62n.h"
+#include "hw/loader.h"
+#include "hw/sysbus.h"
+#include "hw/qdev-properties.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/qtest.h"
+#include "cpu.h"
+
+/*
+ * RX62N Internal Memory
+ */
+#define RX62N_IRAM_BASE 0x00000000
+#define RX62N_DFLASH_BASE 0x00100000
+#define RX62N_CFLASH_BASE 0xfff80000
+
+/*
+ * RX62N Peripheral Address
+ * See users manual section 5
+ */
+#define RX62N_ICU_BASE 0x00087000
+#define RX62N_TMR_BASE 0x00088200
+#define RX62N_CMT_BASE 0x00088000
+#define RX62N_SCI_BASE 0x00088240
+
+/*
+ * RX62N Peripheral IRQ
+ * See users manual section 11
+ */
+#define RX62N_TMR_IRQ 174
+#define RX62N_CMT_IRQ 28
+#define RX62N_SCI_IRQ 214
+
+#define RX62N_XTAL_MIN_HZ (8 * 1000 * 1000)
+#define RX62N_XTAL_MAX_HZ (14 * 1000 * 1000)
+#define RX62N_PCLK_MAX_HZ (50 * 1000 * 1000)
+
+typedef struct RX62NClass {
+ /*< private >*/
+ DeviceClass parent_class;
+ /*< public >*/
+ const char *name;
+ uint64_t ram_size;
+ uint64_t rom_flash_size;
+ uint64_t data_flash_size;
+} RX62NClass;
+
+#define RX62N_MCU_CLASS(klass) \
+ OBJECT_CLASS_CHECK(RX62NClass, (klass), TYPE_RX62N_MCU)
+#define RX62N_MCU_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(RX62NClass, (obj), TYPE_RX62N_MCU)
+
+/*
+ * IRQ -> IPR mapping table
+ * 0x00 - 0x91: IPR no (IPR00 to IPR91)
+ * 0xff: IPR not assigned
+ * See "11.3.1 Interrupt Vector Table" in hardware manual.
+ */
+static const uint8_t ipr_table[NR_IRQS] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 15 */
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0x02,
+ 0xff, 0xff, 0xff, 0x03, 0x04, 0x05, 0x06, 0x07, /* 31 */
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x14, 0x14, 0x14, /* 47 */
+ 0x15, 0x15, 0x15, 0x15, 0xff, 0xff, 0xff, 0xff,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x1d, 0x1e, 0x1f, /* 63 */
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, /* 79 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x3a, 0x3b, 0x3c, 0xff, 0xff, 0xff, /* 95 */
+ 0x40, 0xff, 0x44, 0x45, 0xff, 0xff, 0x48, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 111 */
+ 0xff, 0xff, 0x51, 0x51, 0x51, 0x51, 0x52, 0x52,
+ 0x52, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x56, /* 127 */
+ 0x56, 0x57, 0x57, 0x57, 0x57, 0x58, 0x59, 0x59,
+ 0x59, 0x59, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, /* 143 */
+ 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5f,
+ 0x5f, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x62, /* 159 */
+ 0x62, 0x63, 0x64, 0x64, 0x64, 0x64, 0x65, 0x66,
+ 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 0x68, 0x68, /* 175 */
+ 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b,
+ 0x6b, 0x6b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 191 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0xff, 0xff, 0xff, 0xff, /* 207 */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x80,
+ 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x82, 0x82, /* 223 */
+ 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0xff, 0xff,
+ 0xff, 0xff, 0x85, 0x85, 0x85, 0x85, 0x86, 0x86, /* 239 */
+ 0x86, 0x86, 0xff, 0xff, 0xff, 0xff, 0x88, 0x89,
+ 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, /* 255 */
+};
+
+/*
+ * Level triggerd IRQ list
+ * Not listed IRQ is Edge trigger.
+ * See "11.3.1 Interrupt Vector Table" in hardware manual.
+ */
+static const uint8_t levelirq[] = {
+ 16, 21, 32, 44, 47, 48, 51, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 78, 79, 90, 91, 170, 171, 172, 173, 214,
+ 217, 218, 221, 222, 225, 226, 229, 234, 237, 238,
+ 241, 246, 249, 250, 253,
+};
+
+static void register_icu(RX62NState *s)
+{
+ int i;
+ SysBusDevice *icu;
+
+ object_initialize_child(OBJECT(s), "icu", &s->icu, TYPE_RX_ICU);
+ icu = SYS_BUS_DEVICE(&s->icu);
+ qdev_prop_set_uint32(DEVICE(icu), "len-ipr-map", NR_IRQS);
+ for (i = 0; i < NR_IRQS; i++) {
+ char propname[32];
+ snprintf(propname, sizeof(propname), "ipr-map[%d]", i);
+ qdev_prop_set_uint32(DEVICE(icu), propname, ipr_table[i]);
+ }
+ qdev_prop_set_uint32(DEVICE(icu), "len-trigger-level",
+ ARRAY_SIZE(levelirq));
+ for (i = 0; i < ARRAY_SIZE(levelirq); i++) {
+ char propname[32];
+ snprintf(propname, sizeof(propname), "trigger-level[%d]", i);
+ qdev_prop_set_uint32(DEVICE(icu), propname, levelirq[i]);
+ }
+
+ for (i = 0; i < NR_IRQS; i++) {
+ s->irq[i] = qdev_get_gpio_in(DEVICE(icu), i);
+ }
+ sysbus_realize(icu, &error_abort);
+ sysbus_connect_irq(icu, 0, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_IRQ));
+ sysbus_connect_irq(icu, 1, qdev_get_gpio_in(DEVICE(&s->cpu), RX_CPU_FIR));
+ sysbus_connect_irq(icu, 2, s->irq[SWI]);
+ sysbus_mmio_map(SYS_BUS_DEVICE(icu), 0, RX62N_ICU_BASE);
+}
+
+static void register_tmr(RX62NState *s, int unit)
+{
+ SysBusDevice *tmr;
+ int i, irqbase;
+
+ object_initialize_child(OBJECT(s), "tmr[*]",
+ &s->tmr[unit], TYPE_RENESAS_TMR);
+ tmr = SYS_BUS_DEVICE(&s->tmr[unit]);
+ qdev_prop_set_uint64(DEVICE(tmr), "input-freq", s->pclk_freq_hz);
+ sysbus_realize(tmr, &error_abort);
+
+ irqbase = RX62N_TMR_IRQ + TMR_NR_IRQ * unit;
+ for (i = 0; i < TMR_NR_IRQ; i++) {
+ sysbus_connect_irq(tmr, i, s->irq[irqbase + i]);
+ }
+ sysbus_mmio_map(tmr, 0, RX62N_TMR_BASE + unit * 0x10);
+}
+
+static void register_cmt(RX62NState *s, int unit)
+{
+ SysBusDevice *cmt;
+ int i, irqbase;
+
+ object_initialize_child(OBJECT(s), "cmt[*]",
+ &s->cmt[unit], TYPE_RENESAS_CMT);
+ cmt = SYS_BUS_DEVICE(&s->cmt[unit]);
+ qdev_prop_set_uint64(DEVICE(cmt), "input-freq", s->pclk_freq_hz);
+ sysbus_realize(cmt, &error_abort);
+
+ irqbase = RX62N_CMT_IRQ + CMT_NR_IRQ * unit;
+ for (i = 0; i < CMT_NR_IRQ; i++) {
+ sysbus_connect_irq(cmt, i, s->irq[irqbase + i]);
+ }
+ sysbus_mmio_map(cmt, 0, RX62N_CMT_BASE + unit * 0x10);
+}
+
+static void register_sci(RX62NState *s, int unit)
+{
+ SysBusDevice *sci;
+ int i, irqbase;
+
+ object_initialize_child(OBJECT(s), "sci[*]",
+ &s->sci[unit], TYPE_RENESAS_SCI);
+ sci = SYS_BUS_DEVICE(&s->sci[unit]);
+ qdev_prop_set_chr(DEVICE(sci), "chardev", serial_hd(unit));
+ qdev_prop_set_uint64(DEVICE(sci), "input-freq", s->pclk_freq_hz);
+ sysbus_realize(sci, &error_abort);
+
+ irqbase = RX62N_SCI_IRQ + SCI_NR_IRQ * unit;
+ for (i = 0; i < SCI_NR_IRQ; i++) {
+ sysbus_connect_irq(sci, i, s->irq[irqbase + i]);
+ }
+ sysbus_mmio_map(sci, 0, RX62N_SCI_BASE + unit * 0x08);
+}
+
+static void rx62n_realize(DeviceState *dev, Error **errp)
+{
+ RX62NState *s = RX62N_MCU(dev);
+ RX62NClass *rxc = RX62N_MCU_GET_CLASS(dev);
+
+ if (s->xtal_freq_hz == 0) {
+ error_setg(errp, "\"xtal-frequency-hz\" property must be provided.");
+ return;
+ }
+ /* XTAL range: 8-14 MHz */
+ if (s->xtal_freq_hz < RX62N_XTAL_MIN_HZ
+ || s->xtal_freq_hz > RX62N_XTAL_MAX_HZ) {
+ error_setg(errp, "\"xtal-frequency-hz\" property in incorrect range.");
+ return;
+ }
+ /* Use a 4x fixed multiplier */
+ s->pclk_freq_hz = 4 * s->xtal_freq_hz;
+ /* PCLK range: 8-50 MHz */
+ assert(s->pclk_freq_hz <= RX62N_PCLK_MAX_HZ);
+
+ memory_region_init_ram(&s->iram, OBJECT(dev), "iram",
+ rxc->ram_size, &error_abort);
+ memory_region_add_subregion(s->sysmem, RX62N_IRAM_BASE, &s->iram);
+ memory_region_init_rom(&s->d_flash, OBJECT(dev), "flash-data",
+ rxc->data_flash_size, &error_abort);
+ memory_region_add_subregion(s->sysmem, RX62N_DFLASH_BASE, &s->d_flash);
+ memory_region_init_rom(&s->c_flash, OBJECT(dev), "flash-code",
+ rxc->rom_flash_size, &error_abort);
+ memory_region_add_subregion(s->sysmem, RX62N_CFLASH_BASE, &s->c_flash);
+
+ if (!s->kernel) {
+ if (bios_name) {
+ rom_add_file_fixed(bios_name, RX62N_CFLASH_BASE, 0);
+ } else if (!qtest_enabled()) {
+ error_report("No bios or kernel specified");
+ exit(1);
+ }
+ }
+
+ /* Initialize CPU */
+ object_initialize_child(OBJECT(s), "cpu", &s->cpu, TYPE_RX62N_CPU);
+ qdev_realize(DEVICE(&s->cpu), NULL, &error_abort);
+
+ register_icu(s);
+ s->cpu.env.ack = qdev_get_gpio_in_named(DEVICE(&s->icu), "ack", 0);
+ register_tmr(s, 0);
+ register_tmr(s, 1);
+ register_cmt(s, 0);
+ register_cmt(s, 1);
+ register_sci(s, 0);
+}
+
+static Property rx62n_properties[] = {
+ DEFINE_PROP_LINK("main-bus", RX62NState, sysmem, TYPE_MEMORY_REGION,
+ MemoryRegion *),
+ DEFINE_PROP_BOOL("load-kernel", RX62NState, kernel, false),
+ DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rx62n_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = rx62n_realize;
+ device_class_set_props(dc, rx62n_properties);
+}
+
+static void r5f562n7_class_init(ObjectClass *oc, void *data)
+{
+ RX62NClass *rxc = RX62N_MCU_CLASS(oc);
+
+ rxc->ram_size = 64 * KiB;
+ rxc->rom_flash_size = 384 * KiB;
+ rxc->data_flash_size = 32 * KiB;
+};
+
+static void r5f562n8_class_init(ObjectClass *oc, void *data)
+{
+ RX62NClass *rxc = RX62N_MCU_CLASS(oc);
+
+ rxc->ram_size = 96 * KiB;
+ rxc->rom_flash_size = 512 * KiB;
+ rxc->data_flash_size = 32 * KiB;
+};
+
+static const TypeInfo rx62n_types[] = {
+ {
+ .name = TYPE_R5F562N7_MCU,
+ .parent = TYPE_RX62N_MCU,
+ .class_init = r5f562n7_class_init,
+ }, {
+ .name = TYPE_R5F562N8_MCU,
+ .parent = TYPE_RX62N_MCU,
+ .class_init = r5f562n8_class_init,
+ }, {
+ .name = TYPE_RX62N_MCU,
+ .parent = TYPE_DEVICE,
+ .instance_size = sizeof(RX62NState),
+ .class_size = sizeof(RX62NClass),
+ .class_init = rx62n_class_init,
+ .abstract = true,
+ }
+};
+
+DEFINE_TYPES(rx62n_types)
diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c
index d660714..f8ac3ec 100644
--- a/hw/sh4/sh7750.c
+++ b/hw/sh4/sh7750.c
@@ -30,6 +30,7 @@
#include "sh7750_regs.h"
#include "sh7750_regnames.h"
#include "hw/sh4/sh_intc.h"
+#include "hw/timer/tmu012.h"
#include "cpu.h"
#include "exec/exec-all.h"
diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig
index 59b3f44..59a667c 100644
--- a/hw/timer/Kconfig
+++ b/hw/timer/Kconfig
@@ -35,3 +35,9 @@
config CMSDK_APB_DUALTIMER
bool
select PTIMER
+
+config RENESAS_TMR
+ bool
+
+config RENESAS_CMT
+ bool
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index dece235..a39f6ec 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -23,6 +23,8 @@
common-obj-$(CONFIG_OMAP) += omap_synctimer.o
common-obj-$(CONFIG_PXA2XX) += pxa2xx_timer.o
common-obj-$(CONFIG_SH4) += sh_timer.o
+common-obj-$(CONFIG_RENESAS_TMR) += renesas_tmr.o
+common-obj-$(CONFIG_RENESAS_CMT) += renesas_cmt.o
common-obj-$(CONFIG_DIGIC) += digic-timer.o
common-obj-$(CONFIG_MIPS_CPS) += mips_gictimer.o
diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c
new file mode 100644
index 0000000..2e0fd21
--- /dev/null
+++ b/hw/timer/renesas_cmt.c
@@ -0,0 +1,283 @@
+/*
+ * Renesas 16bit Compare-match timer
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/timer/renesas_cmt.h"
+#include "migration/vmstate.h"
+
+/*
+ * +0 CMSTR - common control
+ * +2 CMCR - ch0
+ * +4 CMCNT - ch0
+ * +6 CMCOR - ch0
+ * +8 CMCR - ch1
+ * +10 CMCNT - ch1
+ * +12 CMCOR - ch1
+ * If we think that the address of CH 0 has an offset of +2,
+ * we can treat it with the same address as CH 1, so define it like that.
+ */
+REG16(CMSTR, 0)
+ FIELD(CMSTR, STR0, 0, 1)
+ FIELD(CMSTR, STR1, 1, 1)
+ FIELD(CMSTR, STR, 0, 2)
+/* This addeess is channel offset */
+REG16(CMCR, 0)
+ FIELD(CMCR, CKS, 0, 2)
+ FIELD(CMCR, CMIE, 6, 1)
+REG16(CMCNT, 2)
+REG16(CMCOR, 4)
+
+static void update_events(RCMTState *cmt, int ch)
+{
+ int64_t next_time;
+
+ if ((cmt->cmstr & (1 << ch)) == 0) {
+ /* count disable, so not happened next event. */
+ return ;
+ }
+ next_time = cmt->cmcor[ch] - cmt->cmcnt[ch];
+ next_time *= NANOSECONDS_PER_SECOND;
+ next_time /= cmt->input_freq;
+ /*
+ * CKS -> div rate
+ * 0 -> 8 (1 << 3)
+ * 1 -> 32 (1 << 5)
+ * 2 -> 128 (1 << 7)
+ * 3 -> 512 (1 << 9)
+ */
+ next_time *= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
+ next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ timer_mod(&cmt->timer[ch], next_time);
+}
+
+static int64_t read_cmcnt(RCMTState *cmt, int ch)
+{
+ int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+ if (cmt->cmstr & (1 << ch)) {
+ delta = (now - cmt->tick[ch]);
+ delta /= NANOSECONDS_PER_SECOND;
+ delta /= cmt->input_freq;
+ delta /= 1 << (3 + FIELD_EX16(cmt->cmcr[ch], CMCR, CKS) * 2);
+ cmt->tick[ch] = now;
+ return cmt->cmcnt[ch] + delta;
+ } else {
+ return cmt->cmcnt[ch];
+ }
+}
+
+static uint64_t cmt_read(void *opaque, hwaddr offset, unsigned size)
+{
+ RCMTState *cmt = opaque;
+ int ch = offset / 0x08;
+ uint64_t ret;
+
+ if (offset == A_CMSTR) {
+ ret = 0;
+ ret = FIELD_DP16(ret, CMSTR, STR,
+ FIELD_EX16(cmt->cmstr, CMSTR, STR));
+ return ret;
+ } else {
+ offset &= 0x07;
+ if (ch == 0) {
+ offset -= 0x02;
+ }
+ switch (offset) {
+ case A_CMCR:
+ ret = 0;
+ ret = FIELD_DP16(ret, CMCR, CKS,
+ FIELD_EX16(cmt->cmstr, CMCR, CKS));
+ ret = FIELD_DP16(ret, CMCR, CMIE,
+ FIELD_EX16(cmt->cmstr, CMCR, CMIE));
+ return ret;
+ case A_CMCNT:
+ return read_cmcnt(cmt, ch);
+ case A_CMCOR:
+ return cmt->cmcor[ch];
+ }
+ }
+ qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
+ "not implemented\n",
+ offset);
+ return UINT64_MAX;
+}
+
+static void start_stop(RCMTState *cmt, int ch, int st)
+{
+ if (st) {
+ update_events(cmt, ch);
+ } else {
+ timer_del(&cmt->timer[ch]);
+ }
+}
+
+static void cmt_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
+{
+ RCMTState *cmt = opaque;
+ int ch = offset / 0x08;
+
+ if (offset == A_CMSTR) {
+ cmt->cmstr = FIELD_EX16(val, CMSTR, STR);
+ start_stop(cmt, 0, FIELD_EX16(cmt->cmstr, CMSTR, STR0));
+ start_stop(cmt, 1, FIELD_EX16(cmt->cmstr, CMSTR, STR1));
+ } else {
+ offset &= 0x07;
+ if (ch == 0) {
+ offset -= 0x02;
+ }
+ switch (offset) {
+ case A_CMCR:
+ cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CKS,
+ FIELD_EX16(val, CMCR, CKS));
+ cmt->cmcr[ch] = FIELD_DP16(cmt->cmcr[ch], CMCR, CMIE,
+ FIELD_EX16(val, CMCR, CMIE));
+ break;
+ case 2:
+ cmt->cmcnt[ch] = val;
+ break;
+ case 4:
+ cmt->cmcor[ch] = val;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "renesas_cmt: Register 0x%" HWADDR_PRIX " "
+ "not implemented\n",
+ offset);
+ return;
+ }
+ if (FIELD_EX16(cmt->cmstr, CMSTR, STR) & (1 << ch)) {
+ update_events(cmt, ch);
+ }
+ }
+}
+
+static const MemoryRegionOps cmt_ops = {
+ .write = cmt_write,
+ .read = cmt_read,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 2,
+ .max_access_size = 2,
+ },
+ .valid = {
+ .min_access_size = 2,
+ .max_access_size = 2,
+ },
+};
+
+static void timer_events(RCMTState *cmt, int ch)
+{
+ cmt->cmcnt[ch] = 0;
+ cmt->tick[ch] = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ update_events(cmt, ch);
+ if (FIELD_EX16(cmt->cmcr[ch], CMCR, CMIE)) {
+ qemu_irq_pulse(cmt->cmi[ch]);
+ }
+}
+
+static void timer_event0(void *opaque)
+{
+ RCMTState *cmt = opaque;
+
+ timer_events(cmt, 0);
+}
+
+static void timer_event1(void *opaque)
+{
+ RCMTState *cmt = opaque;
+
+ timer_events(cmt, 1);
+}
+
+static void rcmt_reset(DeviceState *dev)
+{
+ RCMTState *cmt = RCMT(dev);
+ cmt->cmstr = 0;
+ cmt->cmcr[0] = cmt->cmcr[1] = 0;
+ cmt->cmcnt[0] = cmt->cmcnt[1] = 0;
+ cmt->cmcor[0] = cmt->cmcor[1] = 0xffff;
+}
+
+static void rcmt_init(Object *obj)
+{
+ SysBusDevice *d = SYS_BUS_DEVICE(obj);
+ RCMTState *cmt = RCMT(obj);
+ int i;
+
+ memory_region_init_io(&cmt->memory, OBJECT(cmt), &cmt_ops,
+ cmt, "renesas-cmt", 0x10);
+ sysbus_init_mmio(d, &cmt->memory);
+
+ for (i = 0; i < ARRAY_SIZE(cmt->cmi); i++) {
+ sysbus_init_irq(d, &cmt->cmi[i]);
+ }
+ timer_init_ns(&cmt->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, cmt);
+ timer_init_ns(&cmt->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, cmt);
+}
+
+static const VMStateDescription vmstate_rcmt = {
+ .name = "rx-cmt",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT16(cmstr, RCMTState),
+ VMSTATE_UINT16_ARRAY(cmcr, RCMTState, CMT_CH),
+ VMSTATE_UINT16_ARRAY(cmcnt, RCMTState, CMT_CH),
+ VMSTATE_UINT16_ARRAY(cmcor, RCMTState, CMT_CH),
+ VMSTATE_INT64_ARRAY(tick, RCMTState, CMT_CH),
+ VMSTATE_TIMER_ARRAY(timer, RCMTState, CMT_CH),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property rcmt_properties[] = {
+ DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rcmt_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_rcmt;
+ dc->reset = rcmt_reset;
+ device_class_set_props(dc, rcmt_properties);
+}
+
+static const TypeInfo rcmt_info = {
+ .name = TYPE_RENESAS_CMT,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RCMTState),
+ .instance_init = rcmt_init,
+ .class_init = rcmt_class_init,
+};
+
+static void rcmt_register_types(void)
+{
+ type_register_static(&rcmt_info);
+}
+
+type_init(rcmt_register_types)
diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c
new file mode 100644
index 0000000..446f2ea
--- /dev/null
+++ b/hw/timer/renesas_tmr.c
@@ -0,0 +1,477 @@
+/*
+ * Renesas 8bit timer
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "hw/timer/renesas_tmr.h"
+#include "migration/vmstate.h"
+
+REG8(TCR, 0)
+ FIELD(TCR, CCLR, 3, 2)
+ FIELD(TCR, OVIE, 5, 1)
+ FIELD(TCR, CMIEA, 6, 1)
+ FIELD(TCR, CMIEB, 7, 1)
+REG8(TCSR, 2)
+ FIELD(TCSR, OSA, 0, 2)
+ FIELD(TCSR, OSB, 2, 2)
+ FIELD(TCSR, ADTE, 4, 2)
+REG8(TCORA, 4)
+REG8(TCORB, 6)
+REG8(TCNT, 8)
+REG8(TCCR, 10)
+ FIELD(TCCR, CKS, 0, 3)
+ FIELD(TCCR, CSS, 3, 2)
+ FIELD(TCCR, TMRIS, 7, 1)
+
+#define INTERNAL 0x01
+#define CASCADING 0x03
+#define CCLR_A 0x01
+#define CCLR_B 0x02
+
+static const int clkdiv[] = {0, 1, 2, 8, 32, 64, 1024, 8192};
+
+static uint8_t concat_reg(uint8_t *reg)
+{
+ return (reg[0] << 8) | reg[1];
+}
+
+static void update_events(RTMRState *tmr, int ch)
+{
+ uint16_t diff[TMR_NR_EVENTS], min;
+ int64_t next_time;
+ int i, event;
+
+ if (tmr->tccr[ch] == 0) {
+ return ;
+ }
+ if (FIELD_EX8(tmr->tccr[ch], TCCR, CSS) == 0) {
+ /* external clock mode */
+ /* event not happened */
+ return ;
+ }
+ if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) == CASCADING) {
+ /* cascading mode */
+ if (ch == 1) {
+ tmr->next[ch] = none;
+ return ;
+ }
+ diff[cmia] = concat_reg(tmr->tcora) - concat_reg(tmr->tcnt);
+ diff[cmib] = concat_reg(tmr->tcorb) - concat_reg(tmr->tcnt);
+ diff[ovi] = 0x10000 - concat_reg(tmr->tcnt);
+ } else {
+ /* separate mode */
+ diff[cmia] = tmr->tcora[ch] - tmr->tcnt[ch];
+ diff[cmib] = tmr->tcorb[ch] - tmr->tcnt[ch];
+ diff[ovi] = 0x100 - tmr->tcnt[ch];
+ }
+ /* Search for the most recently occurring event. */
+ for (event = 0, min = diff[0], i = 1; i < none; i++) {
+ if (min > diff[i]) {
+ event = i;
+ min = diff[i];
+ }
+ }
+ tmr->next[ch] = event;
+ next_time = diff[event];
+ next_time *= clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
+ next_time *= NANOSECONDS_PER_SECOND;
+ next_time /= tmr->input_freq;
+ next_time += qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ timer_mod(&tmr->timer[ch], next_time);
+}
+
+static int elapsed_time(RTMRState *tmr, int ch, int64_t delta)
+{
+ int divrate = clkdiv[FIELD_EX8(tmr->tccr[ch], TCCR, CKS)];
+ int et;
+
+ tmr->div_round[ch] += delta;
+ if (divrate > 0) {
+ et = tmr->div_round[ch] / divrate;
+ tmr->div_round[ch] %= divrate;
+ } else {
+ /* disble clock. so no update */
+ et = 0;
+ }
+ return et;
+}
+
+static uint16_t read_tcnt(RTMRState *tmr, unsigned size, int ch)
+{
+ int64_t delta, now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ int elapsed, ovf = 0;
+ uint16_t tcnt[2];
+ uint32_t ret;
+
+ delta = (now - tmr->tick) * NANOSECONDS_PER_SECOND / tmr->input_freq;
+ if (delta > 0) {
+ tmr->tick = now;
+
+ if (FIELD_EX8(tmr->tccr[1], TCCR, CSS) == INTERNAL) {
+ /* timer1 count update */
+ elapsed = elapsed_time(tmr, 1, delta);
+ if (elapsed >= 0x100) {
+ ovf = elapsed >> 8;
+ }
+ tcnt[1] = tmr->tcnt[1] + (elapsed & 0xff);
+ }
+ switch (FIELD_EX8(tmr->tccr[0], TCCR, CSS)) {
+ case INTERNAL:
+ elapsed = elapsed_time(tmr, 0, delta);
+ tcnt[0] = tmr->tcnt[0] + elapsed;
+ break;
+ case CASCADING:
+ if (ovf > 0) {
+ tcnt[0] = tmr->tcnt[0] + ovf;
+ }
+ break;
+ }
+ } else {
+ tcnt[0] = tmr->tcnt[0];
+ tcnt[1] = tmr->tcnt[1];
+ }
+ if (size == 1) {
+ return tcnt[ch];
+ } else {
+ ret = 0;
+ ret = deposit32(ret, 0, 8, tcnt[1]);
+ ret = deposit32(ret, 8, 8, tcnt[0]);
+ return ret;
+ }
+}
+
+static uint8_t read_tccr(uint8_t r)
+{
+ uint8_t tccr = 0;
+ tccr = FIELD_DP8(tccr, TCCR, TMRIS,
+ FIELD_EX8(r, TCCR, TMRIS));
+ tccr = FIELD_DP8(tccr, TCCR, CSS,
+ FIELD_EX8(r, TCCR, CSS));
+ tccr = FIELD_DP8(tccr, TCCR, CKS,
+ FIELD_EX8(r, TCCR, CKS));
+ return tccr;
+}
+
+static uint64_t tmr_read(void *opaque, hwaddr addr, unsigned size)
+{
+ RTMRState *tmr = opaque;
+ int ch = addr & 1;
+ uint64_t ret;
+
+ if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "renesas_tmr: Invalid read size 0x%"
+ HWADDR_PRIX "\n",
+ addr);
+ return UINT64_MAX;
+ }
+ switch (addr & 0x0e) {
+ case A_TCR:
+ ret = 0;
+ ret = FIELD_DP8(ret, TCR, CCLR,
+ FIELD_EX8(tmr->tcr[ch], TCR, CCLR));
+ ret = FIELD_DP8(ret, TCR, OVIE,
+ FIELD_EX8(tmr->tcr[ch], TCR, OVIE));
+ ret = FIELD_DP8(ret, TCR, CMIEA,
+ FIELD_EX8(tmr->tcr[ch], TCR, CMIEA));
+ ret = FIELD_DP8(ret, TCR, CMIEB,
+ FIELD_EX8(tmr->tcr[ch], TCR, CMIEB));
+ return ret;
+ case A_TCSR:
+ ret = 0;
+ ret = FIELD_DP8(ret, TCSR, OSA,
+ FIELD_EX8(tmr->tcsr[ch], TCSR, OSA));
+ ret = FIELD_DP8(ret, TCSR, OSB,
+ FIELD_EX8(tmr->tcsr[ch], TCSR, OSB));
+ switch (ch) {
+ case 0:
+ ret = FIELD_DP8(ret, TCSR, ADTE,
+ FIELD_EX8(tmr->tcsr[ch], TCSR, ADTE));
+ break;
+ case 1: /* CH1 ADTE unimplement always 1 */
+ ret = FIELD_DP8(ret, TCSR, ADTE, 1);
+ break;
+ }
+ return ret;
+ case A_TCORA:
+ if (size == 1) {
+ return tmr->tcora[ch];
+ } else if (ch == 0) {
+ return concat_reg(tmr->tcora);
+ }
+ case A_TCORB:
+ if (size == 1) {
+ return tmr->tcorb[ch];
+ } else {
+ return concat_reg(tmr->tcorb);
+ }
+ case A_TCNT:
+ return read_tcnt(tmr, size, ch);
+ case A_TCCR:
+ if (size == 1) {
+ return read_tccr(tmr->tccr[ch]);
+ } else {
+ return read_tccr(tmr->tccr[0]) << 8 | read_tccr(tmr->tccr[1]);
+ }
+ default:
+ qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
+ " not implemented\n",
+ addr);
+ break;
+ }
+ return UINT64_MAX;
+}
+
+static void tmr_write_count(RTMRState *tmr, int ch, unsigned size,
+ uint8_t *reg, uint64_t val)
+{
+ if (size == 1) {
+ reg[ch] = val;
+ update_events(tmr, ch);
+ } else {
+ reg[0] = extract32(val, 8, 8);
+ reg[1] = extract32(val, 0, 8);
+ update_events(tmr, 0);
+ update_events(tmr, 1);
+ }
+}
+
+static void tmr_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ RTMRState *tmr = opaque;
+ int ch = addr & 1;
+
+ if (size == 2 && (ch != 0 || addr == A_TCR || addr == A_TCSR)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "renesas_tmr: Invalid write size 0x%" HWADDR_PRIX "\n",
+ addr);
+ return;
+ }
+ switch (addr & 0x0e) {
+ case A_TCR:
+ tmr->tcr[ch] = val;
+ break;
+ case A_TCSR:
+ tmr->tcsr[ch] = val;
+ break;
+ case A_TCORA:
+ tmr_write_count(tmr, ch, size, tmr->tcora, val);
+ break;
+ case A_TCORB:
+ tmr_write_count(tmr, ch, size, tmr->tcorb, val);
+ break;
+ case A_TCNT:
+ tmr_write_count(tmr, ch, size, tmr->tcnt, val);
+ break;
+ case A_TCCR:
+ tmr_write_count(tmr, ch, size, tmr->tccr, val);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "renesas_tmr: Register 0x%" HWADDR_PRIX
+ " not implemented\n",
+ addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps tmr_ops = {
+ .write = tmr_write,
+ .read = tmr_read,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 2,
+ },
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 2,
+ },
+};
+
+static void timer_events(RTMRState *tmr, int ch);
+
+static uint16_t issue_event(RTMRState *tmr, int ch, int sz,
+ uint16_t tcnt, uint16_t tcora, uint16_t tcorb)
+{
+ uint16_t ret = tcnt;
+
+ switch (tmr->next[ch]) {
+ case none:
+ break;
+ case cmia:
+ if (tcnt >= tcora) {
+ if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_A) {
+ ret = tcnt - tcora;
+ }
+ if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEA)) {
+ qemu_irq_pulse(tmr->cmia[ch]);
+ }
+ if (sz == 8 && ch == 0 &&
+ FIELD_EX8(tmr->tccr[1], TCCR, CSS) == CASCADING) {
+ tmr->tcnt[1]++;
+ timer_events(tmr, 1);
+ }
+ }
+ break;
+ case cmib:
+ if (tcnt >= tcorb) {
+ if (FIELD_EX8(tmr->tcr[ch], TCR, CCLR) == CCLR_B) {
+ ret = tcnt - tcorb;
+ }
+ if (FIELD_EX8(tmr->tcr[ch], TCR, CMIEB)) {
+ qemu_irq_pulse(tmr->cmib[ch]);
+ }
+ }
+ break;
+ case ovi:
+ if ((tcnt >= (1 << sz)) && FIELD_EX8(tmr->tcr[ch], TCR, OVIE)) {
+ qemu_irq_pulse(tmr->ovi[ch]);
+ }
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return ret;
+}
+
+static void timer_events(RTMRState *tmr, int ch)
+{
+ uint16_t tcnt;
+
+ tmr->tcnt[ch] = read_tcnt(tmr, 1, ch);
+ if (FIELD_EX8(tmr->tccr[0], TCCR, CSS) != CASCADING) {
+ tmr->tcnt[ch] = issue_event(tmr, ch, 8,
+ tmr->tcnt[ch],
+ tmr->tcora[ch],
+ tmr->tcorb[ch]) & 0xff;
+ } else {
+ if (ch == 1) {
+ return ;
+ }
+ tcnt = issue_event(tmr, ch, 16,
+ concat_reg(tmr->tcnt),
+ concat_reg(tmr->tcora),
+ concat_reg(tmr->tcorb));
+ tmr->tcnt[0] = (tcnt >> 8) & 0xff;
+ tmr->tcnt[1] = tcnt & 0xff;
+ }
+ update_events(tmr, ch);
+}
+
+static void timer_event0(void *opaque)
+{
+ RTMRState *tmr = opaque;
+
+ timer_events(tmr, 0);
+}
+
+static void timer_event1(void *opaque)
+{
+ RTMRState *tmr = opaque;
+
+ timer_events(tmr, 1);
+}
+
+static void rtmr_reset(DeviceState *dev)
+{
+ RTMRState *tmr = RTMR(dev);
+ tmr->tcr[0] = tmr->tcr[1] = 0x00;
+ tmr->tcsr[0] = 0x00;
+ tmr->tcsr[1] = 0x10;
+ tmr->tcnt[0] = tmr->tcnt[1] = 0x00;
+ tmr->tcora[0] = tmr->tcora[1] = 0xff;
+ tmr->tcorb[0] = tmr->tcorb[1] = 0xff;
+ tmr->tccr[0] = tmr->tccr[1] = 0x00;
+ tmr->next[0] = tmr->next[1] = none;
+ tmr->tick = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+}
+
+static void rtmr_init(Object *obj)
+{
+ SysBusDevice *d = SYS_BUS_DEVICE(obj);
+ RTMRState *tmr = RTMR(obj);
+ int i;
+
+ memory_region_init_io(&tmr->memory, OBJECT(tmr), &tmr_ops,
+ tmr, "renesas-tmr", 0x10);
+ sysbus_init_mmio(d, &tmr->memory);
+
+ for (i = 0; i < ARRAY_SIZE(tmr->ovi); i++) {
+ sysbus_init_irq(d, &tmr->cmia[i]);
+ sysbus_init_irq(d, &tmr->cmib[i]);
+ sysbus_init_irq(d, &tmr->ovi[i]);
+ }
+ timer_init_ns(&tmr->timer[0], QEMU_CLOCK_VIRTUAL, timer_event0, tmr);
+ timer_init_ns(&tmr->timer[1], QEMU_CLOCK_VIRTUAL, timer_event1, tmr);
+}
+
+static const VMStateDescription vmstate_rtmr = {
+ .name = "rx-tmr",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(tick, RTMRState),
+ VMSTATE_UINT8_ARRAY(tcnt, RTMRState, TMR_CH),
+ VMSTATE_UINT8_ARRAY(tcora, RTMRState, TMR_CH),
+ VMSTATE_UINT8_ARRAY(tcorb, RTMRState, TMR_CH),
+ VMSTATE_UINT8_ARRAY(tcr, RTMRState, TMR_CH),
+ VMSTATE_UINT8_ARRAY(tccr, RTMRState, TMR_CH),
+ VMSTATE_UINT8_ARRAY(tcor, RTMRState, TMR_CH),
+ VMSTATE_UINT8_ARRAY(tcsr, RTMRState, TMR_CH),
+ VMSTATE_INT64_ARRAY(div_round, RTMRState, TMR_CH),
+ VMSTATE_UINT8_ARRAY(next, RTMRState, TMR_CH),
+ VMSTATE_TIMER_ARRAY(timer, RTMRState, TMR_CH),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property rtmr_properties[] = {
+ DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void rtmr_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &vmstate_rtmr;
+ dc->reset = rtmr_reset;
+ device_class_set_props(dc, rtmr_properties);
+}
+
+static const TypeInfo rtmr_info = {
+ .name = TYPE_RENESAS_TMR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(RTMRState),
+ .instance_init = rtmr_init,
+ .class_init = rtmr_class_init,
+};
+
+static void rtmr_register_types(void)
+{
+ type_register_static(&rtmr_info);
+}
+
+type_init(rtmr_register_types)
diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c
index 13c4051..bb0e1c8 100644
--- a/hw/timer/sh_timer.c
+++ b/hw/timer/sh_timer.c
@@ -9,10 +9,11 @@
*/
#include "qemu/osdep.h"
+#include "exec/memory.h"
#include "hw/hw.h"
#include "hw/irq.h"
#include "hw/sh4/sh.h"
-#include "qemu/timer.h"
+#include "hw/timer/tmu012.h"
#include "hw/ptimer.h"
//#define DEBUG_TIMER
diff --git a/hw/tpm/Kconfig b/hw/tpm/Kconfig
index 4794e7f..29e82f3 100644
--- a/hw/tpm/Kconfig
+++ b/hw/tpm/Kconfig
@@ -1,7 +1,3 @@
-config TPMDEV
- bool
- depends on TPM
-
config TPM_TIS_ISA
bool
depends on TPM && ISA_BUS
@@ -15,26 +11,15 @@
config TPM_TIS
bool
depends on TPM
- select TPMDEV
+ select TPM_BACKEND
config TPM_CRB
bool
depends on TPM && PC
- select TPMDEV
-
-config TPM_PASSTHROUGH
- bool
- default y
- # FIXME: should check for x86 host as well
- depends on TPMDEV && LINUX
-
-config TPM_EMULATOR
- bool
- default y
- depends on TPMDEV
+ select TPM_BACKEND
config TPM_SPAPR
bool
default y
depends on TPM && PSERIES
- select TPMDEV
+ select TPM_BACKEND
diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs
index f1ec4be..6fc05be 100644
--- a/hw/tpm/Makefile.objs
+++ b/hw/tpm/Makefile.objs
@@ -1,9 +1,6 @@
-common-obj-$(CONFIG_TPM) += tpm_util.o
obj-$(call lor,$(CONFIG_TPM_TIS),$(CONFIG_TPM_CRB)) += tpm_ppi.o
common-obj-$(CONFIG_TPM_TIS_ISA) += tpm_tis_isa.o
common-obj-$(CONFIG_TPM_TIS_SYSBUS) += tpm_tis_sysbus.o
common-obj-$(CONFIG_TPM_TIS) += tpm_tis_common.o
common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o
-common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o
-common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o
obj-$(CONFIG_TPM_SPAPR) += tpm_spapr.o
diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c
index cd004e7..6024729 100644
--- a/hw/tpm/tpm_crb.c
+++ b/hw/tpm/tpm_crb.c
@@ -24,9 +24,9 @@
#include "hw/acpi/tpm.h"
#include "migration/vmstate.h"
#include "sysemu/tpm_backend.h"
+#include "sysemu/tpm_util.h"
#include "sysemu/reset.h"
-#include "tpm_int.h"
-#include "tpm_util.h"
+#include "tpm_prop.h"
#include "tpm_ppi.h"
#include "trace.h"
diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c
index 6d9c1a3..72d7a3d 100644
--- a/hw/tpm/tpm_ppi.c
+++ b/hw/tpm/tpm_ppi.c
@@ -17,6 +17,7 @@
#include "cpu.h"
#include "sysemu/memory_mapping.h"
#include "migration/vmstate.h"
+#include "hw/acpi/tpm.h"
#include "tpm_ppi.h"
#include "trace.h"
diff --git a/hw/tpm/tpm_ppi.h b/hw/tpm/tpm_ppi.h
index d33ef27..6f773c2 100644
--- a/hw/tpm/tpm_ppi.h
+++ b/hw/tpm/tpm_ppi.h
@@ -12,7 +12,6 @@
#ifndef TPM_TPM_PPI_H
#define TPM_TPM_PPI_H
-#include "hw/acpi/tpm.h"
#include "exec/address-spaces.h"
typedef struct TPMPPI {
diff --git a/hw/tpm/tpm_prop.h b/hw/tpm/tpm_prop.h
new file mode 100644
index 0000000..85e1ae5
--- /dev/null
+++ b/hw/tpm/tpm_prop.h
@@ -0,0 +1,31 @@
+/*
+ * TPM utility functions
+ *
+ * Copyright (c) 2010 - 2015 IBM Corporation
+ * Authors:
+ * Stefan Berger <stefanb@us.ibm.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 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/>
+ */
+
+#ifndef HW_TPM_PROP_H
+#define HW_TPM_PROP_H
+
+#include "sysemu/tpm_backend.h"
+#include "hw/qdev-properties.h"
+
+#define DEFINE_PROP_TPMBE(_n, _s, _f) \
+ DEFINE_PROP(_n, _s, _f, qdev_prop_tpm, TPMBackend *)
+
+#endif /* HW_TPM_PROP_H */
diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c
index ce65eb2..cb4dfd1 100644
--- a/hw/tpm/tpm_spapr.c
+++ b/hw/tpm/tpm_spapr.c
@@ -20,8 +20,8 @@
#include "migration/vmstate.h"
#include "sysemu/tpm_backend.h"
-#include "tpm_int.h"
-#include "tpm_util.h"
+#include "sysemu/tpm_util.h"
+#include "tpm_prop.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/spapr_vio.h"
diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h
index 5554989..f6b5872 100644
--- a/hw/tpm/tpm_tis.h
+++ b/hw/tpm/tpm_tis.h
@@ -24,7 +24,6 @@
#ifndef TPM_TPM_TIS_H
#define TPM_TPM_TIS_H
-#include "qemu/osdep.h"
#include "sysemu/tpm_backend.h"
#include "tpm_ppi.h"
diff --git a/hw/tpm/tpm_tis_common.c b/hw/tpm/tpm_tis_common.c
index 1af4bce..e700d82 100644
--- a/hw/tpm/tpm_tis_common.c
+++ b/hw/tpm/tpm_tis_common.c
@@ -33,8 +33,7 @@
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "sysemu/tpm_backend.h"
-#include "tpm_int.h"
-#include "tpm_util.h"
+#include "sysemu/tpm_util.h"
#include "tpm_ppi.h"
#include "trace.h"
@@ -79,9 +78,7 @@
*/
static void tpm_tis_tpm_send(TPMState *s, uint8_t locty)
{
- if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
- tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
- }
+ tpm_util_show_buffer(s->buffer, s->be_buffer_size, "To TPM");
/*
* rw_offset serves as length indicator for length of data;
@@ -247,9 +244,7 @@
s->loc[locty].state = TPM_TIS_STATE_COMPLETION;
s->rw_offset = 0;
- if (trace_event_get_state_backends(TRACE_TPM_UTIL_SHOW_BUFFER)) {
- tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
- }
+ tpm_util_show_buffer(s->buffer, s->be_buffer_size, "From TPM");
if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) {
tpm_tis_abort(s);
diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c
index 30ba370..5faf623 100644
--- a/hw/tpm/tpm_tis_isa.c
+++ b/hw/tpm/tpm_tis_isa.c
@@ -26,7 +26,8 @@
#include "hw/isa/isa.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
-#include "tpm_util.h"
+#include "hw/acpi/tpm.h"
+#include "tpm_prop.h"
#include "tpm_tis.h"
typedef struct TPMStateISA {
diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c
index eced1fc..4a3bc70 100644
--- a/hw/tpm/tpm_tis_sysbus.c
+++ b/hw/tpm/tpm_tis_sysbus.c
@@ -25,7 +25,8 @@
#include "qemu/osdep.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
-#include "tpm_util.h"
+#include "hw/acpi/tpm.h"
+#include "tpm_prop.h"
#include "hw/sysbus.h"
#include "tpm_tis.h"
diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events
index 439e514..de9bf1e 100644
--- a/hw/tpm/trace-events
+++ b/hw/tpm/trace-events
@@ -4,38 +4,6 @@
tpm_crb_mmio_read(uint64_t addr, unsigned size, uint32_t val) "CRB read 0x%016" PRIx64 " len:%u val: 0x%" PRIx32
tpm_crb_mmio_write(uint64_t addr, unsigned size, uint32_t val) "CRB write 0x%016" PRIx64 " len:%u val: 0x%" PRIx32
-# tpm_passthrough.c
-tpm_passthrough_handle_request(void *cmd) "processing command %p"
-tpm_passthrough_reset(void) "reset"
-
-# tpm_util.c
-tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu"
-tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu"
-tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu"
-tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu"
-tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu"
-tpm_util_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
-
-# tpm_emulator.c
-tpm_emulator_set_locality(uint8_t locty) "setting locality to %d"
-tpm_emulator_handle_request(void) "processing TPM command"
-tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64
-tpm_emulator_set_buffer_size(uint32_t buffersize, uint32_t minsize, uint32_t maxsize) "buffer size: %u, min: %u, max: %u"
-tpm_emulator_startup_tpm_resume(bool is_resume, size_t buffersize) "is_resume: %d, buffer size: %zu"
-tpm_emulator_get_tpm_established_flag(uint8_t flag) "got established flag: %d"
-tpm_emulator_cancel_cmd_not_supt(void) "Backend does not support CANCEL_TPM_CMD"
-tpm_emulator_handle_device_opts_tpm12(void) "TPM Version 1.2"
-tpm_emulator_handle_device_opts_tpm2(void) "TPM Version 2"
-tpm_emulator_handle_device_opts_unspec(void) "TPM Version Unspecified"
-tpm_emulator_handle_device_opts_startup_error(void) "Startup error"
-tpm_emulator_get_state_blob(uint8_t type, uint32_t size, uint32_t flags) "got state blob type %d, %u bytes, flags 0x%08x"
-tpm_emulator_set_state_blob(uint8_t type, uint32_t size, uint32_t flags) "set state blob type %d, %u bytes, flags 0x%08x"
-tpm_emulator_set_state_blobs(void) "setting state blobs"
-tpm_emulator_set_state_blobs_error(const char *msg) "error while setting state blobs: %s"
-tpm_emulator_set_state_blobs_done(void) "Done setting state blobs"
-tpm_emulator_pre_save(void) ""
-tpm_emulator_inst_init(void) ""
-
# tpm_tis.c
tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x"
tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d"
@@ -56,7 +24,7 @@
# tpm_ppi.c
tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu"
-# hw/tpm/tpm_spapr.c
+# tpm_spapr.c
tpm_spapr_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\n%s"
tpm_spapr_do_crq(uint8_t raw1, uint8_t raw2) "1st 2 bytes in CRQ: 0x%02x 0x%02x"
tpm_spapr_do_crq_crq_result(void) "SPAPR_VTPM_INIT_CRQ_RESULT"
diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
index 1541365..5bbadad 100644
--- a/hw/watchdog/cmsdk-apb-watchdog.c
+++ b/hw/watchdog/cmsdk-apb-watchdog.c
@@ -225,6 +225,7 @@
break;
case A_WDOGLOCK:
s->lock = (value != WDOG_UNLOCK_VALUE);
+ trace_cmsdk_apb_watchdog_lock(s->lock);
break;
case A_WDOGITCR:
if (s->is_luminary) {
diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events
index ab94d7d..3124ca1 100644
--- a/hw/watchdog/trace-events
+++ b/hw/watchdog/trace-events
@@ -4,3 +4,4 @@
cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset"
+cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32
diff --git a/include/hw/char/renesas_sci.h b/include/hw/char/renesas_sci.h
new file mode 100644
index 0000000..efdebc6
--- /dev/null
+++ b/include/hw/char/renesas_sci.h
@@ -0,0 +1,51 @@
+/*
+ * Renesas Serial Communication Interface
+ *
+ * Copyright (c) 2018 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_CHAR_RENESAS_SCI_H
+#define HW_CHAR_RENESAS_SCI_H
+
+#include "chardev/char-fe.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RENESAS_SCI "renesas-sci"
+#define RSCI(obj) OBJECT_CHECK(RSCIState, (obj), TYPE_RENESAS_SCI)
+
+enum {
+ ERI = 0,
+ RXI = 1,
+ TXI = 2,
+ TEI = 3,
+ SCI_NR_IRQ = 4
+};
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion memory;
+ QEMUTimer timer;
+ CharBackend chr;
+ qemu_irq irq[SCI_NR_IRQ];
+
+ uint8_t smr;
+ uint8_t brr;
+ uint8_t scr;
+ uint8_t tdr;
+ uint8_t ssr;
+ uint8_t rdr;
+ uint8_t scmr;
+ uint8_t semr;
+
+ uint8_t read_ssr;
+ int64_t trtime;
+ int64_t rx_next;
+ uint64_t input_freq;
+} RSCIState;
+
+#endif
diff --git a/include/hw/i2c/arm_sbcon_i2c.h b/include/hw/i2c/arm_sbcon_i2c.h
new file mode 100644
index 0000000..5d96507
--- /dev/null
+++ b/include/hw/i2c/arm_sbcon_i2c.h
@@ -0,0 +1,35 @@
+/*
+ * ARM SBCon two-wire serial bus interface (I2C bitbang)
+ * a.k.a.
+ * ARM Versatile I2C controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Copyright (c) 2012 Oskar Andero <oskar.andero@gmail.com>
+ * Copyright (C) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_I2C_ARM_SBCON_H
+#define HW_I2C_ARM_SBCON_H
+
+#include "hw/sysbus.h"
+#include "hw/i2c/bitbang_i2c.h"
+
+#define TYPE_VERSATILE_I2C "versatile_i2c"
+#define TYPE_ARM_SBCON_I2C TYPE_VERSATILE_I2C
+
+#define ARM_SBCON_I2C(obj) \
+ OBJECT_CHECK(ArmSbconI2CState, (obj), TYPE_ARM_SBCON_I2C)
+
+typedef struct ArmSbconI2CState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ bitbang_i2c_interface bitbang;
+ int out;
+ int in;
+} ArmSbconI2CState;
+
+#endif /* HW_I2C_ARM_SBCON_H */
diff --git a/include/hw/intc/rx_icu.h b/include/hw/intc/rx_icu.h
new file mode 100644
index 0000000..7176015
--- /dev/null
+++ b/include/hw/intc/rx_icu.h
@@ -0,0 +1,76 @@
+/*
+ * RX Interrupt Control Unit
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_INTC_RX_ICU_H
+#define HW_INTC_RX_ICU_H
+
+#include "hw/sysbus.h"
+
+enum TRG_MODE {
+ TRG_LEVEL = 0,
+ TRG_NEDGE = 1, /* Falling */
+ TRG_PEDGE = 2, /* Raising */
+ TRG_BEDGE = 3, /* Both */
+};
+
+struct IRQSource {
+ enum TRG_MODE sense;
+ int level;
+};
+
+enum {
+ /* Software interrupt request */
+ SWI = 27,
+ NR_IRQS = 256
+};
+
+struct RXICUState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion memory;
+ struct IRQSource src[NR_IRQS];
+ uint32_t nr_irqs;
+ uint8_t *map;
+ uint32_t nr_sense;
+ uint8_t *init_sense;
+
+ uint8_t ir[NR_IRQS];
+ uint8_t dtcer[NR_IRQS];
+ uint8_t ier[NR_IRQS / 8];
+ uint8_t ipr[142];
+ uint8_t dmasr[4];
+ uint16_t fir;
+ uint8_t nmisr;
+ uint8_t nmier;
+ uint8_t nmiclr;
+ uint8_t nmicr;
+ int16_t req_irq;
+ qemu_irq _irq;
+ qemu_irq _fir;
+ qemu_irq _swi;
+};
+typedef struct RXICUState RXICUState;
+
+#define TYPE_RX_ICU "rx-icu"
+#define RX_ICU(obj) OBJECT_CHECK(RXICUState, (obj), TYPE_RX_ICU)
+
+#endif /* RX_ICU_H */
diff --git a/include/hw/rx/rx62n.h b/include/hw/rx/rx62n.h
new file mode 100644
index 0000000..aa94758
--- /dev/null
+++ b/include/hw/rx/rx62n.h
@@ -0,0 +1,76 @@
+/*
+ * RX62N MCU Object
+ *
+ * Datasheet: RX62N Group, RX621 Group User's Manual: Hardware
+ * (Rev.1.40 R01UH0033EJ0140)
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ */
+
+#ifndef HW_RX_RX62N_MCU_H
+#define HW_RX_RX62N_MCU_H
+
+#include "target/rx/cpu.h"
+#include "hw/intc/rx_icu.h"
+#include "hw/timer/renesas_tmr.h"
+#include "hw/timer/renesas_cmt.h"
+#include "hw/char/renesas_sci.h"
+#include "qemu/units.h"
+
+#define TYPE_RX62N_MCU "rx62n-mcu"
+#define RX62N_MCU(obj) OBJECT_CHECK(RX62NState, (obj), TYPE_RX62N_MCU)
+
+#define TYPE_R5F562N7_MCU "r5f562n7-mcu"
+#define TYPE_R5F562N8_MCU "r5f562n8-mcu"
+
+#define EXT_CS_BASE 0x01000000
+#define VECTOR_TABLE_BASE 0xffffff80
+#define RX62N_CFLASH_BASE 0xfff80000
+
+#define RX62N_NR_TMR 2
+#define RX62N_NR_CMT 2
+#define RX62N_NR_SCI 6
+
+typedef struct RX62NState {
+ /*< private >*/
+ DeviceState parent_obj;
+ /*< public >*/
+
+ RXCPU cpu;
+ RXICUState icu;
+ RTMRState tmr[RX62N_NR_TMR];
+ RCMTState cmt[RX62N_NR_CMT];
+ RSCIState sci[RX62N_NR_SCI];
+
+ MemoryRegion *sysmem;
+ bool kernel;
+
+ MemoryRegion iram;
+ MemoryRegion iomem1;
+ MemoryRegion d_flash;
+ MemoryRegion iomem2;
+ MemoryRegion iomem3;
+ MemoryRegion c_flash;
+ qemu_irq irq[NR_IRQS];
+
+ /* Input Clock (XTAL) frequency */
+ uint32_t xtal_freq_hz;
+ /* Peripheral Module Clock frequency */
+ uint32_t pclk_freq_hz;
+} RX62NState;
+
+#endif
diff --git a/include/hw/sh4/sh.h b/include/hw/sh4/sh.h
index 767a2df..93f464b 100644
--- a/include/hw/sh4/sh.h
+++ b/include/hw/sh4/sh.h
@@ -10,9 +10,8 @@
/* sh7750.c */
struct SH7750State;
-struct MemoryRegion;
-struct SH7750State *sh7750_init(SuperHCPU *cpu, struct MemoryRegion *sysmem);
+struct SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem);
typedef struct {
/* The callback will be triggered if any of the designated lines change */
@@ -28,15 +27,6 @@
int sh7750_register_io_device(struct SH7750State *s,
sh7750_io_device * device);
-/* sh_timer.c */
-#define TMU012_FEAT_TOCR (1 << 0)
-#define TMU012_FEAT_3CHAN (1 << 1)
-#define TMU012_FEAT_EXTCLK (1 << 2)
-void tmu012_init(struct MemoryRegion *sysmem, hwaddr base,
- int feat, uint32_t freq,
- qemu_irq ch0_irq, qemu_irq ch1_irq,
- qemu_irq ch2_irq0, qemu_irq ch2_irq1);
-
/* sh_serial.c */
#define SH_SERIAL_FEAT_SCIF (1 << 0)
diff --git a/include/hw/timer/renesas_cmt.h b/include/hw/timer/renesas_cmt.h
new file mode 100644
index 0000000..e28a15c
--- /dev/null
+++ b/include/hw/timer/renesas_cmt.h
@@ -0,0 +1,40 @@
+/*
+ * Renesas Compare-match timer Object
+ *
+ * Copyright (c) 2019 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_TIMER_RENESAS_CMT_H
+#define HW_TIMER_RENESAS_CMT_H
+
+#include "qemu/timer.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RENESAS_CMT "renesas-cmt"
+#define RCMT(obj) OBJECT_CHECK(RCMTState, (obj), TYPE_RENESAS_CMT)
+
+enum {
+ CMT_CH = 2,
+ CMT_NR_IRQ = 1 * CMT_CH
+};
+
+typedef struct RCMTState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ uint64_t input_freq;
+ MemoryRegion memory;
+
+ uint16_t cmstr;
+ uint16_t cmcr[CMT_CH];
+ uint16_t cmcnt[CMT_CH];
+ uint16_t cmcor[CMT_CH];
+ int64_t tick[CMT_CH];
+ qemu_irq cmi[CMT_CH];
+ QEMUTimer timer[CMT_CH];
+} RCMTState;
+
+#endif
diff --git a/include/hw/timer/renesas_tmr.h b/include/hw/timer/renesas_tmr.h
new file mode 100644
index 0000000..cf3baa7
--- /dev/null
+++ b/include/hw/timer/renesas_tmr.h
@@ -0,0 +1,55 @@
+/*
+ * Renesas 8bit timer Object
+ *
+ * Copyright (c) 2018 Yoshinori Sato
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_TIMER_RENESAS_TMR_H
+#define HW_TIMER_RENESAS_TMR_H
+
+#include "qemu/timer.h"
+#include "hw/sysbus.h"
+
+#define TYPE_RENESAS_TMR "renesas-tmr"
+#define RTMR(obj) OBJECT_CHECK(RTMRState, (obj), TYPE_RENESAS_TMR)
+
+enum timer_event {
+ cmia = 0,
+ cmib = 1,
+ ovi = 2,
+ none = 3,
+ TMR_NR_EVENTS = 4
+};
+
+enum {
+ TMR_CH = 2,
+ TMR_NR_IRQ = 3 * TMR_CH
+};
+
+typedef struct RTMRState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ uint64_t input_freq;
+ MemoryRegion memory;
+
+ int64_t tick;
+ uint8_t tcnt[TMR_CH];
+ uint8_t tcora[TMR_CH];
+ uint8_t tcorb[TMR_CH];
+ uint8_t tcr[TMR_CH];
+ uint8_t tccr[TMR_CH];
+ uint8_t tcor[TMR_CH];
+ uint8_t tcsr[TMR_CH];
+ int64_t div_round[TMR_CH];
+ uint8_t next[TMR_CH];
+ qemu_irq cmia[TMR_CH];
+ qemu_irq cmib[TMR_CH];
+ qemu_irq ovi[TMR_CH];
+ QEMUTimer timer[TMR_CH];
+} RTMRState;
+
+#endif
diff --git a/include/hw/timer/tmu012.h b/include/hw/timer/tmu012.h
new file mode 100644
index 0000000..808ed8d
--- /dev/null
+++ b/include/hw/timer/tmu012.h
@@ -0,0 +1,23 @@
+/*
+ * SuperH Timer
+ *
+ * Copyright (c) 2007 Magnus Damm
+ *
+ * This code is licensed under the GPL.
+ */
+
+#ifndef HW_TIMER_TMU012_H
+#define HW_TIMER_TMU012_H
+
+#include "exec/hwaddr.h"
+
+#define TMU012_FEAT_TOCR (1 << 0)
+#define TMU012_FEAT_3CHAN (1 << 1)
+#define TMU012_FEAT_EXTCLK (1 << 2)
+
+void tmu012_init(MemoryRegion *sysmem, hwaddr base,
+ int feat, uint32_t freq,
+ qemu_irq ch0_irq, qemu_irq ch1_irq,
+ qemu_irq ch2_irq0, qemu_irq ch2_irq1);
+
+#endif
diff --git a/hw/tpm/tpm_util.h b/include/sysemu/tpm_util.h
similarity index 79%
rename from hw/tpm/tpm_util.h
rename to include/sysemu/tpm_util.h
index 7889081..63e872c 100644
--- a/hw/tpm/tpm_util.h
+++ b/include/sysemu/tpm_util.h
@@ -19,8 +19,8 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
-#ifndef TPM_TPM_UTIL_H
-#define TPM_TPM_UTIL_H
+#ifndef SYSEMU_TPM_UTIL_H
+#define SYSEMU_TPM_UTIL_H
#include "sysemu/tpm.h"
#include "qemu/bswap.h"
@@ -66,20 +66,7 @@
stl_be_p(b + 6, error);
}
-int tpm_util_get_buffer_size(int tpm_fd, TPMVersion tpm_version,
- size_t *buffersize);
-
-#define DEFINE_PROP_TPMBE(_n, _s, _f) \
- DEFINE_PROP(_n, _s, _f, qdev_prop_tpm, TPMBackend *)
-
-typedef struct TPMSizedBuffer {
- uint32_t size;
- uint8_t *buffer;
-} TPMSizedBuffer;
-
-void tpm_sized_buffer_reset(TPMSizedBuffer *tsb);
-
void tpm_util_show_buffer(const unsigned char *buffer,
size_t buffer_size, const char *string);
-#endif /* TPM_TPM_UTIL_H */
+#endif /* SYSEMU_TPM_UTIL_H */
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 5b7a36b..e44e180 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -1108,7 +1108,7 @@
ARMCPU *cpu = ARM_CPU(obj);
if (value) {
- if (kvm_enabled() && !kvm_arm_pmu_supported(CPU(cpu))) {
+ if (kvm_enabled() && !kvm_arm_pmu_supported()) {
error_setg(errp, "'pmu' feature not supported by KVM on this host");
return;
}
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index 677584e..cf66b8c 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -2334,7 +2334,7 @@
* migration or KVM state synchronization. (Typically this is for "registers"
* which are actually used as instructions for cache maintenance and so on.)
* IO indicates that this register does I/O and therefore its accesses
- * need to be surrounded by gen_io_start()/gen_io_end(). In particular,
+ * need to be marked with gen_io_start() and also end the TB. In particular,
* registers which implement clocks or timers require this.
* RAISES_EXC is for when the read or write hook might raise an exception;
* the generated code will synchronize the CPU state before calling the hook
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 778cecc..a0c1d88 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -266,7 +266,7 @@
/* Collect the set of vector lengths supported by KVM. */
bitmap_zero(kvm_supported, ARM_MAX_VQ);
- if (kvm_enabled() && kvm_arm_sve_supported(CPU(cpu))) {
+ if (kvm_enabled() && kvm_arm_sve_supported()) {
kvm_arm_sve_get_vls(CPU(cpu), kvm_supported);
} else if (kvm_enabled()) {
assert(!cpu_isar_feature(aa64_sve, cpu));
@@ -473,7 +473,7 @@
return;
}
- if (kvm_enabled() && !kvm_arm_sve_supported(CPU(cpu))) {
+ if (kvm_enabled() && !kvm_arm_sve_supported()) {
error_setg(errp, "cannot set sve-max-vq");
error_append_hint(errp, "SVE not supported by KVM on this host\n");
return;
@@ -519,7 +519,7 @@
return;
}
- if (value && kvm_enabled() && !kvm_arm_sve_supported(CPU(cpu))) {
+ if (value && kvm_enabled() && !kvm_arm_sve_supported()) {
error_setg(errp, "cannot enable %s", name);
error_append_hint(errp, "SVE not supported by KVM on this host\n");
return;
@@ -556,7 +556,7 @@
return;
}
- if (value && kvm_enabled() && !kvm_arm_sve_supported(CPU(cpu))) {
+ if (value && kvm_enabled() && !kvm_arm_sve_supported()) {
error_setg(errp, "'sve' feature not supported by KVM on this host");
return;
}
@@ -751,7 +751,7 @@
* uniform execution state like do_interrupt.
*/
if (value == false) {
- if (!kvm_enabled() || !kvm_arm_aarch32_supported(CPU(cpu))) {
+ if (!kvm_enabled() || !kvm_arm_aarch32_supported()) {
error_setg(errp, "'aarch64' feature cannot be disabled "
"unless KVM is enabled and 32-bit EL1 "
"is supported");
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
index eef3bbd..7c672c7 100644
--- a/target/arm/kvm.c
+++ b/target/arm/kvm.c
@@ -208,9 +208,9 @@
}
}
-bool kvm_arm_pmu_supported(CPUState *cpu)
+bool kvm_arm_pmu_supported(void)
{
- return kvm_check_extension(cpu->kvm_state, KVM_CAP_ARM_PMU_V3);
+ return kvm_check_extension(kvm_state, KVM_CAP_ARM_PMU_V3);
}
int kvm_arm_get_max_vm_ipa_size(MachineState *ms)
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
index f09ed9f..3dc494a 100644
--- a/target/arm/kvm64.c
+++ b/target/arm/kvm64.c
@@ -652,18 +652,14 @@
return true;
}
-bool kvm_arm_aarch32_supported(CPUState *cpu)
+bool kvm_arm_aarch32_supported(void)
{
- KVMState *s = KVM_STATE(current_accel());
-
- return kvm_check_extension(s, KVM_CAP_ARM_EL1_32BIT);
+ return kvm_check_extension(kvm_state, KVM_CAP_ARM_EL1_32BIT);
}
-bool kvm_arm_sve_supported(CPUState *cpu)
+bool kvm_arm_sve_supported(void)
{
- KVMState *s = KVM_STATE(current_accel());
-
- return kvm_check_extension(s, KVM_CAP_ARM_SVE);
+ return kvm_check_extension(kvm_state, KVM_CAP_ARM_SVE);
}
QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1);
@@ -798,7 +794,7 @@
env->features &= ~(1ULL << ARM_FEATURE_PMU);
}
if (cpu_isar_feature(aa64_sve, cpu)) {
- assert(kvm_arm_sve_supported(cs));
+ assert(kvm_arm_sve_supported());
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_SVE;
}
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
index 48bf5e1..a4ce4fd 100644
--- a/target/arm/kvm_arm.h
+++ b/target/arm/kvm_arm.h
@@ -269,29 +269,26 @@
/**
* kvm_arm_aarch32_supported:
- * @cs: CPUState
*
- * Returns: true if the KVM VCPU can enable AArch32 mode
+ * Returns: true if KVM can enable AArch32 mode
* and false otherwise.
*/
-bool kvm_arm_aarch32_supported(CPUState *cs);
+bool kvm_arm_aarch32_supported(void);
/**
* kvm_arm_pmu_supported:
- * @cs: CPUState
*
- * Returns: true if the KVM VCPU can enable its PMU
+ * Returns: true if KVM can enable the PMU
* and false otherwise.
*/
-bool kvm_arm_pmu_supported(CPUState *cs);
+bool kvm_arm_pmu_supported(void);
/**
* kvm_arm_sve_supported:
- * @cs: CPUState
*
- * Returns true if the KVM VCPU can enable SVE and false otherwise.
+ * Returns true if KVM can enable SVE and false otherwise.
*/
-bool kvm_arm_sve_supported(CPUState *cs);
+bool kvm_arm_sve_supported(void);
/**
* kvm_arm_get_max_vm_ipa_size:
@@ -359,17 +356,17 @@
static inline void kvm_arm_add_vcpu_properties(Object *obj) {}
-static inline bool kvm_arm_aarch32_supported(CPUState *cs)
+static inline bool kvm_arm_aarch32_supported(void)
{
return false;
}
-static inline bool kvm_arm_pmu_supported(CPUState *cs)
+static inline bool kvm_arm_pmu_supported(void)
{
return false;
}
-static inline bool kvm_arm_sve_supported(CPUState *cs)
+static inline bool kvm_arm_sve_supported(void)
{
return false;
}
diff --git a/target/arm/neon-dp.decode b/target/arm/neon-dp.decode
index 6d890b2..686f9fb 100644
--- a/target/arm/neon-dp.decode
+++ b/target/arm/neon-dp.decode
@@ -429,6 +429,112 @@
vm=%vm_dp vd=%vd_dp size=1
VDUP_scalar 1111 001 1 1 . 11 index:1 100 .... 11 000 q:1 . 0 .... \
vm=%vm_dp vd=%vd_dp size=2
+
+ ##################################################################
+ # 2-reg-misc grouping:
+ # 1111 001 11 D 11 size:2 opc1:2 Vd:4 0 opc2:4 q:1 M 0 Vm:4
+ ##################################################################
+
+ &2misc vd vm q size
+
+ @2misc .... ... .. . .. size:2 .. .... . .... q:1 . . .... \
+ &2misc vm=%vm_dp vd=%vd_dp
+ @2misc_q0 .... ... .. . .. size:2 .. .... . .... . . . .... \
+ &2misc vm=%vm_dp vd=%vd_dp q=0
+ @2misc_q1 .... ... .. . .. size:2 .. .... . .... . . . .... \
+ &2misc vm=%vm_dp vd=%vd_dp q=1
+
+ VREV64 1111 001 11 . 11 .. 00 .... 0 0000 . . 0 .... @2misc
+ VREV32 1111 001 11 . 11 .. 00 .... 0 0001 . . 0 .... @2misc
+ VREV16 1111 001 11 . 11 .. 00 .... 0 0010 . . 0 .... @2misc
+
+ VPADDL_S 1111 001 11 . 11 .. 00 .... 0 0100 . . 0 .... @2misc
+ VPADDL_U 1111 001 11 . 11 .. 00 .... 0 0101 . . 0 .... @2misc
+
+ AESE 1111 001 11 . 11 .. 00 .... 0 0110 0 . 0 .... @2misc_q1
+ AESD 1111 001 11 . 11 .. 00 .... 0 0110 1 . 0 .... @2misc_q1
+ AESMC 1111 001 11 . 11 .. 00 .... 0 0111 0 . 0 .... @2misc_q1
+ AESIMC 1111 001 11 . 11 .. 00 .... 0 0111 1 . 0 .... @2misc_q1
+
+ VCLS 1111 001 11 . 11 .. 00 .... 0 1000 . . 0 .... @2misc
+ VCLZ 1111 001 11 . 11 .. 00 .... 0 1001 . . 0 .... @2misc
+ VCNT 1111 001 11 . 11 .. 00 .... 0 1010 . . 0 .... @2misc
+
+ VMVN 1111 001 11 . 11 .. 00 .... 0 1011 . . 0 .... @2misc
+
+ VPADAL_S 1111 001 11 . 11 .. 00 .... 0 1100 . . 0 .... @2misc
+ VPADAL_U 1111 001 11 . 11 .. 00 .... 0 1101 . . 0 .... @2misc
+
+ VQABS 1111 001 11 . 11 .. 00 .... 0 1110 . . 0 .... @2misc
+ VQNEG 1111 001 11 . 11 .. 00 .... 0 1111 . . 0 .... @2misc
+
+ VCGT0 1111 001 11 . 11 .. 01 .... 0 0000 . . 0 .... @2misc
+ VCGE0 1111 001 11 . 11 .. 01 .... 0 0001 . . 0 .... @2misc
+ VCEQ0 1111 001 11 . 11 .. 01 .... 0 0010 . . 0 .... @2misc
+ VCLE0 1111 001 11 . 11 .. 01 .... 0 0011 . . 0 .... @2misc
+ VCLT0 1111 001 11 . 11 .. 01 .... 0 0100 . . 0 .... @2misc
+
+ SHA1H 1111 001 11 . 11 .. 01 .... 0 0101 1 . 0 .... @2misc_q1
+
+ VABS 1111 001 11 . 11 .. 01 .... 0 0110 . . 0 .... @2misc
+ VNEG 1111 001 11 . 11 .. 01 .... 0 0111 . . 0 .... @2misc
+
+ VCGT0_F 1111 001 11 . 11 .. 01 .... 0 1000 . . 0 .... @2misc
+ VCGE0_F 1111 001 11 . 11 .. 01 .... 0 1001 . . 0 .... @2misc
+ VCEQ0_F 1111 001 11 . 11 .. 01 .... 0 1010 . . 0 .... @2misc
+ VCLE0_F 1111 001 11 . 11 .. 01 .... 0 1011 . . 0 .... @2misc
+ VCLT0_F 1111 001 11 . 11 .. 01 .... 0 1100 . . 0 .... @2misc
+
+ VABS_F 1111 001 11 . 11 .. 01 .... 0 1110 . . 0 .... @2misc
+ VNEG_F 1111 001 11 . 11 .. 01 .... 0 1111 . . 0 .... @2misc
+
+ VSWP 1111 001 11 . 11 .. 10 .... 0 0000 . . 0 .... @2misc
+ VTRN 1111 001 11 . 11 .. 10 .... 0 0001 . . 0 .... @2misc
+ VUZP 1111 001 11 . 11 .. 10 .... 0 0010 . . 0 .... @2misc
+ VZIP 1111 001 11 . 11 .. 10 .... 0 0011 . . 0 .... @2misc
+
+ VMOVN 1111 001 11 . 11 .. 10 .... 0 0100 0 . 0 .... @2misc_q0
+ # VQMOVUN: unsigned result (source is always signed)
+ VQMOVUN 1111 001 11 . 11 .. 10 .... 0 0100 1 . 0 .... @2misc_q0
+ # VQMOVN: signed result, source may be signed (_S) or unsigned (_U)
+ VQMOVN_S 1111 001 11 . 11 .. 10 .... 0 0101 0 . 0 .... @2misc_q0
+ VQMOVN_U 1111 001 11 . 11 .. 10 .... 0 0101 1 . 0 .... @2misc_q0
+
+ VSHLL 1111 001 11 . 11 .. 10 .... 0 0110 0 . 0 .... @2misc_q0
+
+ SHA1SU1 1111 001 11 . 11 .. 10 .... 0 0111 0 . 0 .... @2misc_q1
+ SHA256SU0 1111 001 11 . 11 .. 10 .... 0 0111 1 . 0 .... @2misc_q1
+
+ VRINTN 1111 001 11 . 11 .. 10 .... 0 1000 . . 0 .... @2misc
+ VRINTX 1111 001 11 . 11 .. 10 .... 0 1001 . . 0 .... @2misc
+ VRINTA 1111 001 11 . 11 .. 10 .... 0 1010 . . 0 .... @2misc
+ VRINTZ 1111 001 11 . 11 .. 10 .... 0 1011 . . 0 .... @2misc
+
+ VCVT_F16_F32 1111 001 11 . 11 .. 10 .... 0 1100 0 . 0 .... @2misc_q0
+
+ VRINTM 1111 001 11 . 11 .. 10 .... 0 1101 . . 0 .... @2misc
+
+ VCVT_F32_F16 1111 001 11 . 11 .. 10 .... 0 1110 0 . 0 .... @2misc_q0
+
+ VRINTP 1111 001 11 . 11 .. 10 .... 0 1111 . . 0 .... @2misc
+
+ VCVTAS 1111 001 11 . 11 .. 11 .... 0 0000 . . 0 .... @2misc
+ VCVTAU 1111 001 11 . 11 .. 11 .... 0 0001 . . 0 .... @2misc
+ VCVTNS 1111 001 11 . 11 .. 11 .... 0 0010 . . 0 .... @2misc
+ VCVTNU 1111 001 11 . 11 .. 11 .... 0 0011 . . 0 .... @2misc
+ VCVTPS 1111 001 11 . 11 .. 11 .... 0 0100 . . 0 .... @2misc
+ VCVTPU 1111 001 11 . 11 .. 11 .... 0 0101 . . 0 .... @2misc
+ VCVTMS 1111 001 11 . 11 .. 11 .... 0 0110 . . 0 .... @2misc
+ VCVTMU 1111 001 11 . 11 .. 11 .... 0 0111 . . 0 .... @2misc
+
+ VRECPE 1111 001 11 . 11 .. 11 .... 0 1000 . . 0 .... @2misc
+ VRSQRTE 1111 001 11 . 11 .. 11 .... 0 1001 . . 0 .... @2misc
+ VRECPE_F 1111 001 11 . 11 .. 11 .... 0 1010 . . 0 .... @2misc
+ VRSQRTE_F 1111 001 11 . 11 .. 11 .... 0 1011 . . 0 .... @2misc
+ VCVT_FS 1111 001 11 . 11 .. 11 .... 0 1100 . . 0 .... @2misc
+ VCVT_FU 1111 001 11 . 11 .. 11 .... 0 1101 . . 0 .... @2misc
+ VCVT_SF 1111 001 11 . 11 .. 11 .... 0 1110 . . 0 .... @2misc
+ VCVT_UF 1111 001 11 . 11 .. 11 .... 0 1111 . . 0 .... @2misc
]
# Subgroup for size != 0b11
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index a0e72ad..4cef862 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -9534,7 +9534,7 @@
TCGv_i64 tcg_op = tcg_temp_new_i64();
TCGv_i64 tcg_zero = tcg_const_i64(0);
TCGv_i64 tcg_res = tcg_temp_new_i64();
- NeonGenTwoDoubleOPFn *genfn;
+ NeonGenTwoDoubleOpFn *genfn;
bool swap = false;
int pass;
@@ -9576,7 +9576,7 @@
TCGv_i32 tcg_op = tcg_temp_new_i32();
TCGv_i32 tcg_zero = tcg_const_i32(0);
TCGv_i32 tcg_res = tcg_temp_new_i32();
- NeonGenTwoSingleOPFn *genfn;
+ NeonGenTwoSingleOpFn *genfn;
bool swap = false;
int pass, maxpasses;
@@ -11370,18 +11370,6 @@
genfn(tcg_res, tcg_op1, tcg_op2);
}
- if (opcode == 0xf) {
- /* SABA, UABA: accumulating ops */
- static NeonGenTwoOpFn * const fns[3] = {
- gen_helper_neon_add_u8,
- gen_helper_neon_add_u16,
- tcg_gen_add_i32,
- };
-
- read_vec_element_i32(s, tcg_op1, rd, pass, MO_32);
- fns[size](tcg_res, tcg_op1, tcg_res);
- }
-
write_vec_element_i32(s, tcg_res, rd, pass, MO_32);
tcg_temp_free_i32(tcg_res);
@@ -11917,8 +11905,8 @@
} else {
for (pass = 0; pass < maxpass; pass++) {
TCGv_i64 tcg_op = tcg_temp_new_i64();
- NeonGenOneOpFn *genfn;
- static NeonGenOneOpFn * const fns[2][2] = {
+ NeonGenOne64OpFn *genfn;
+ static NeonGenOne64OpFn * const fns[2][2] = {
{ gen_helper_neon_addlp_s8, gen_helper_neon_addlp_u8 },
{ gen_helper_neon_addlp_s16, gen_helper_neon_addlp_u16 },
};
diff --git a/target/arm/translate-neon.inc.c b/target/arm/translate-neon.inc.c
index a5aa56b..f6cb921 100644
--- a/target/arm/translate-neon.inc.c
+++ b/target/arm/translate-neon.inc.c
@@ -54,6 +54,107 @@
#include "decode-neon-ls.inc.c"
#include "decode-neon-shared.inc.c"
+/* Return the offset of a 2**SIZE piece of a NEON register, at index ELE,
+ * where 0 is the least significant end of the register.
+ */
+static inline long
+neon_element_offset(int reg, int element, MemOp size)
+{
+ int element_size = 1 << size;
+ int ofs = element * element_size;
+#ifdef HOST_WORDS_BIGENDIAN
+ /* Calculate the offset assuming fully little-endian,
+ * then XOR to account for the order of the 8-byte units.
+ */
+ if (element_size < 8) {
+ ofs ^= 8 - element_size;
+ }
+#endif
+ return neon_reg_offset(reg, 0) + ofs;
+}
+
+static void neon_load_element(TCGv_i32 var, int reg, int ele, MemOp mop)
+{
+ long offset = neon_element_offset(reg, ele, mop & MO_SIZE);
+
+ switch (mop) {
+ case MO_UB:
+ tcg_gen_ld8u_i32(var, cpu_env, offset);
+ break;
+ case MO_UW:
+ tcg_gen_ld16u_i32(var, cpu_env, offset);
+ break;
+ case MO_UL:
+ tcg_gen_ld_i32(var, cpu_env, offset);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void neon_load_element64(TCGv_i64 var, int reg, int ele, MemOp mop)
+{
+ long offset = neon_element_offset(reg, ele, mop & MO_SIZE);
+
+ switch (mop) {
+ case MO_UB:
+ tcg_gen_ld8u_i64(var, cpu_env, offset);
+ break;
+ case MO_UW:
+ tcg_gen_ld16u_i64(var, cpu_env, offset);
+ break;
+ case MO_UL:
+ tcg_gen_ld32u_i64(var, cpu_env, offset);
+ break;
+ case MO_Q:
+ tcg_gen_ld_i64(var, cpu_env, offset);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void neon_store_element(int reg, int ele, MemOp size, TCGv_i32 var)
+{
+ long offset = neon_element_offset(reg, ele, size);
+
+ switch (size) {
+ case MO_8:
+ tcg_gen_st8_i32(var, cpu_env, offset);
+ break;
+ case MO_16:
+ tcg_gen_st16_i32(var, cpu_env, offset);
+ break;
+ case MO_32:
+ tcg_gen_st_i32(var, cpu_env, offset);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
+static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var)
+{
+ long offset = neon_element_offset(reg, ele, size);
+
+ switch (size) {
+ case MO_8:
+ tcg_gen_st8_i64(var, cpu_env, offset);
+ break;
+ case MO_16:
+ tcg_gen_st16_i64(var, cpu_env, offset);
+ break;
+ case MO_32:
+ tcg_gen_st32_i64(var, cpu_env, offset);
+ break;
+ case MO_64:
+ tcg_gen_st_i64(var, cpu_env, offset);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static bool trans_VCMLA(DisasContext *s, arg_VCMLA *a)
{
int opr_sz;
@@ -1664,7 +1765,7 @@
}
static bool do_fp_2sh(DisasContext *s, arg_2reg_shift *a,
- NeonGenTwoSingleOPFn *fn)
+ NeonGenTwoSingleOpFn *fn)
{
/* FP operations in 2-reg-and-shift group */
TCGv_i32 tmp, shiftv;
@@ -2970,3 +3071,1091 @@
a->q ? 16 : 8, a->q ? 16 : 8);
return true;
}
+
+static bool trans_VREV64(DisasContext *s, arg_VREV64 *a)
+{
+ int pass, half;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (a->size == 3) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ for (pass = 0; pass < (a->q ? 2 : 1); pass++) {
+ TCGv_i32 tmp[2];
+
+ for (half = 0; half < 2; half++) {
+ tmp[half] = neon_load_reg(a->vm, pass * 2 + half);
+ switch (a->size) {
+ case 0:
+ tcg_gen_bswap32_i32(tmp[half], tmp[half]);
+ break;
+ case 1:
+ gen_swap_half(tmp[half], tmp[half]);
+ break;
+ case 2:
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ }
+ neon_store_reg(a->vd, pass * 2, tmp[1]);
+ neon_store_reg(a->vd, pass * 2 + 1, tmp[0]);
+ }
+ return true;
+}
+
+static bool do_2misc_pairwise(DisasContext *s, arg_2misc *a,
+ NeonGenWidenFn *widenfn,
+ NeonGenTwo64OpFn *opfn,
+ NeonGenTwo64OpFn *accfn)
+{
+ /*
+ * Pairwise long operations: widen both halves of the pair,
+ * combine the pairs with the opfn, and then possibly accumulate
+ * into the destination with the accfn.
+ */
+ int pass;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!widenfn) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ for (pass = 0; pass < a->q + 1; pass++) {
+ TCGv_i32 tmp;
+ TCGv_i64 rm0_64, rm1_64, rd_64;
+
+ rm0_64 = tcg_temp_new_i64();
+ rm1_64 = tcg_temp_new_i64();
+ rd_64 = tcg_temp_new_i64();
+ tmp = neon_load_reg(a->vm, pass * 2);
+ widenfn(rm0_64, tmp);
+ tcg_temp_free_i32(tmp);
+ tmp = neon_load_reg(a->vm, pass * 2 + 1);
+ widenfn(rm1_64, tmp);
+ tcg_temp_free_i32(tmp);
+ opfn(rd_64, rm0_64, rm1_64);
+ tcg_temp_free_i64(rm0_64);
+ tcg_temp_free_i64(rm1_64);
+
+ if (accfn) {
+ TCGv_i64 tmp64 = tcg_temp_new_i64();
+ neon_load_reg64(tmp64, a->vd + pass);
+ accfn(rd_64, tmp64, rd_64);
+ tcg_temp_free_i64(tmp64);
+ }
+ neon_store_reg64(rd_64, a->vd + pass);
+ tcg_temp_free_i64(rd_64);
+ }
+ return true;
+}
+
+static bool trans_VPADDL_S(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenWidenFn * const widenfn[] = {
+ gen_helper_neon_widen_s8,
+ gen_helper_neon_widen_s16,
+ tcg_gen_ext_i32_i64,
+ NULL,
+ };
+ static NeonGenTwo64OpFn * const opfn[] = {
+ gen_helper_neon_paddl_u16,
+ gen_helper_neon_paddl_u32,
+ tcg_gen_add_i64,
+ NULL,
+ };
+
+ return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL);
+}
+
+static bool trans_VPADDL_U(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenWidenFn * const widenfn[] = {
+ gen_helper_neon_widen_u8,
+ gen_helper_neon_widen_u16,
+ tcg_gen_extu_i32_i64,
+ NULL,
+ };
+ static NeonGenTwo64OpFn * const opfn[] = {
+ gen_helper_neon_paddl_u16,
+ gen_helper_neon_paddl_u32,
+ tcg_gen_add_i64,
+ NULL,
+ };
+
+ return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size], NULL);
+}
+
+static bool trans_VPADAL_S(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenWidenFn * const widenfn[] = {
+ gen_helper_neon_widen_s8,
+ gen_helper_neon_widen_s16,
+ tcg_gen_ext_i32_i64,
+ NULL,
+ };
+ static NeonGenTwo64OpFn * const opfn[] = {
+ gen_helper_neon_paddl_u16,
+ gen_helper_neon_paddl_u32,
+ tcg_gen_add_i64,
+ NULL,
+ };
+ static NeonGenTwo64OpFn * const accfn[] = {
+ gen_helper_neon_addl_u16,
+ gen_helper_neon_addl_u32,
+ tcg_gen_add_i64,
+ NULL,
+ };
+
+ return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size],
+ accfn[a->size]);
+}
+
+static bool trans_VPADAL_U(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenWidenFn * const widenfn[] = {
+ gen_helper_neon_widen_u8,
+ gen_helper_neon_widen_u16,
+ tcg_gen_extu_i32_i64,
+ NULL,
+ };
+ static NeonGenTwo64OpFn * const opfn[] = {
+ gen_helper_neon_paddl_u16,
+ gen_helper_neon_paddl_u32,
+ tcg_gen_add_i64,
+ NULL,
+ };
+ static NeonGenTwo64OpFn * const accfn[] = {
+ gen_helper_neon_addl_u16,
+ gen_helper_neon_addl_u32,
+ tcg_gen_add_i64,
+ NULL,
+ };
+
+ return do_2misc_pairwise(s, a, widenfn[a->size], opfn[a->size],
+ accfn[a->size]);
+}
+
+typedef void ZipFn(TCGv_ptr, TCGv_ptr);
+
+static bool do_zip_uzp(DisasContext *s, arg_2misc *a,
+ ZipFn *fn)
+{
+ TCGv_ptr pd, pm;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!fn) {
+ /* Bad size or size/q combination */
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ pd = vfp_reg_ptr(true, a->vd);
+ pm = vfp_reg_ptr(true, a->vm);
+ fn(pd, pm);
+ tcg_temp_free_ptr(pd);
+ tcg_temp_free_ptr(pm);
+ return true;
+}
+
+static bool trans_VUZP(DisasContext *s, arg_2misc *a)
+{
+ static ZipFn * const fn[2][4] = {
+ {
+ gen_helper_neon_unzip8,
+ gen_helper_neon_unzip16,
+ NULL,
+ NULL,
+ }, {
+ gen_helper_neon_qunzip8,
+ gen_helper_neon_qunzip16,
+ gen_helper_neon_qunzip32,
+ NULL,
+ }
+ };
+ return do_zip_uzp(s, a, fn[a->q][a->size]);
+}
+
+static bool trans_VZIP(DisasContext *s, arg_2misc *a)
+{
+ static ZipFn * const fn[2][4] = {
+ {
+ gen_helper_neon_zip8,
+ gen_helper_neon_zip16,
+ NULL,
+ NULL,
+ }, {
+ gen_helper_neon_qzip8,
+ gen_helper_neon_qzip16,
+ gen_helper_neon_qzip32,
+ NULL,
+ }
+ };
+ return do_zip_uzp(s, a, fn[a->q][a->size]);
+}
+
+static bool do_vmovn(DisasContext *s, arg_2misc *a,
+ NeonGenNarrowEnvFn *narrowfn)
+{
+ TCGv_i64 rm;
+ TCGv_i32 rd0, rd1;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (a->vm & 1) {
+ return false;
+ }
+
+ if (!narrowfn) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ rm = tcg_temp_new_i64();
+ rd0 = tcg_temp_new_i32();
+ rd1 = tcg_temp_new_i32();
+
+ neon_load_reg64(rm, a->vm);
+ narrowfn(rd0, cpu_env, rm);
+ neon_load_reg64(rm, a->vm + 1);
+ narrowfn(rd1, cpu_env, rm);
+ neon_store_reg(a->vd, 0, rd0);
+ neon_store_reg(a->vd, 1, rd1);
+ tcg_temp_free_i64(rm);
+ return true;
+}
+
+#define DO_VMOVN(INSN, FUNC) \
+ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \
+ { \
+ static NeonGenNarrowEnvFn * const narrowfn[] = { \
+ FUNC##8, \
+ FUNC##16, \
+ FUNC##32, \
+ NULL, \
+ }; \
+ return do_vmovn(s, a, narrowfn[a->size]); \
+ }
+
+DO_VMOVN(VMOVN, gen_neon_narrow_u)
+DO_VMOVN(VQMOVUN, gen_helper_neon_unarrow_sat)
+DO_VMOVN(VQMOVN_S, gen_helper_neon_narrow_sat_s)
+DO_VMOVN(VQMOVN_U, gen_helper_neon_narrow_sat_u)
+
+static bool trans_VSHLL(DisasContext *s, arg_2misc *a)
+{
+ TCGv_i32 rm0, rm1;
+ TCGv_i64 rd;
+ static NeonGenWidenFn * const widenfns[] = {
+ gen_helper_neon_widen_u8,
+ gen_helper_neon_widen_u16,
+ tcg_gen_extu_i32_i64,
+ NULL,
+ };
+ NeonGenWidenFn *widenfn = widenfns[a->size];
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (a->vd & 1) {
+ return false;
+ }
+
+ if (!widenfn) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ rd = tcg_temp_new_i64();
+
+ rm0 = neon_load_reg(a->vm, 0);
+ rm1 = neon_load_reg(a->vm, 1);
+
+ widenfn(rd, rm0);
+ tcg_gen_shli_i64(rd, rd, 8 << a->size);
+ neon_store_reg64(rd, a->vd);
+ widenfn(rd, rm1);
+ tcg_gen_shli_i64(rd, rd, 8 << a->size);
+ neon_store_reg64(rd, a->vd + 1);
+
+ tcg_temp_free_i64(rd);
+ tcg_temp_free_i32(rm0);
+ tcg_temp_free_i32(rm1);
+ return true;
+}
+
+static bool trans_VCVT_F16_F32(DisasContext *s, arg_2misc *a)
+{
+ TCGv_ptr fpst;
+ TCGv_i32 ahp, tmp, tmp2, tmp3;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON) ||
+ !dc_isar_feature(aa32_fp16_spconv, s)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if ((a->vm & 1) || (a->size != 1)) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ fpst = get_fpstatus_ptr(true);
+ ahp = get_ahp_flag();
+ tmp = neon_load_reg(a->vm, 0);
+ gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp);
+ tmp2 = neon_load_reg(a->vm, 1);
+ gen_helper_vfp_fcvt_f32_to_f16(tmp2, tmp2, fpst, ahp);
+ tcg_gen_shli_i32(tmp2, tmp2, 16);
+ tcg_gen_or_i32(tmp2, tmp2, tmp);
+ tcg_temp_free_i32(tmp);
+ tmp = neon_load_reg(a->vm, 2);
+ gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp);
+ tmp3 = neon_load_reg(a->vm, 3);
+ neon_store_reg(a->vd, 0, tmp2);
+ gen_helper_vfp_fcvt_f32_to_f16(tmp3, tmp3, fpst, ahp);
+ tcg_gen_shli_i32(tmp3, tmp3, 16);
+ tcg_gen_or_i32(tmp3, tmp3, tmp);
+ neon_store_reg(a->vd, 1, tmp3);
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(ahp);
+ tcg_temp_free_ptr(fpst);
+
+ return true;
+}
+
+static bool trans_VCVT_F32_F16(DisasContext *s, arg_2misc *a)
+{
+ TCGv_ptr fpst;
+ TCGv_i32 ahp, tmp, tmp2, tmp3;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON) ||
+ !dc_isar_feature(aa32_fp16_spconv, s)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if ((a->vd & 1) || (a->size != 1)) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ fpst = get_fpstatus_ptr(true);
+ ahp = get_ahp_flag();
+ tmp3 = tcg_temp_new_i32();
+ tmp = neon_load_reg(a->vm, 0);
+ tmp2 = neon_load_reg(a->vm, 1);
+ tcg_gen_ext16u_i32(tmp3, tmp);
+ gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp);
+ neon_store_reg(a->vd, 0, tmp3);
+ tcg_gen_shri_i32(tmp, tmp, 16);
+ gen_helper_vfp_fcvt_f16_to_f32(tmp, tmp, fpst, ahp);
+ neon_store_reg(a->vd, 1, tmp);
+ tmp3 = tcg_temp_new_i32();
+ tcg_gen_ext16u_i32(tmp3, tmp2);
+ gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp);
+ neon_store_reg(a->vd, 2, tmp3);
+ tcg_gen_shri_i32(tmp2, tmp2, 16);
+ gen_helper_vfp_fcvt_f16_to_f32(tmp2, tmp2, fpst, ahp);
+ neon_store_reg(a->vd, 3, tmp2);
+ tcg_temp_free_i32(ahp);
+ tcg_temp_free_ptr(fpst);
+
+ return true;
+}
+
+static bool do_2misc_vec(DisasContext *s, arg_2misc *a, GVecGen2Fn *fn)
+{
+ int vec_size = a->q ? 16 : 8;
+ int rd_ofs = neon_reg_offset(a->vd, 0);
+ int rm_ofs = neon_reg_offset(a->vm, 0);
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (a->size == 3) {
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ fn(a->size, rd_ofs, rm_ofs, vec_size, vec_size);
+
+ return true;
+}
+
+#define DO_2MISC_VEC(INSN, FN) \
+ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \
+ { \
+ return do_2misc_vec(s, a, FN); \
+ }
+
+DO_2MISC_VEC(VNEG, tcg_gen_gvec_neg)
+DO_2MISC_VEC(VABS, tcg_gen_gvec_abs)
+DO_2MISC_VEC(VCEQ0, gen_gvec_ceq0)
+DO_2MISC_VEC(VCGT0, gen_gvec_cgt0)
+DO_2MISC_VEC(VCLE0, gen_gvec_cle0)
+DO_2MISC_VEC(VCGE0, gen_gvec_cge0)
+DO_2MISC_VEC(VCLT0, gen_gvec_clt0)
+
+static bool trans_VMVN(DisasContext *s, arg_2misc *a)
+{
+ if (a->size != 0) {
+ return false;
+ }
+ return do_2misc_vec(s, a, tcg_gen_gvec_not);
+}
+
+#define WRAP_2M_3_OOL_FN(WRAPNAME, FUNC, DATA) \
+ static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \
+ uint32_t rm_ofs, uint32_t oprsz, \
+ uint32_t maxsz) \
+ { \
+ tcg_gen_gvec_3_ool(rd_ofs, rd_ofs, rm_ofs, oprsz, maxsz, \
+ DATA, FUNC); \
+ }
+
+#define WRAP_2M_2_OOL_FN(WRAPNAME, FUNC, DATA) \
+ static void WRAPNAME(unsigned vece, uint32_t rd_ofs, \
+ uint32_t rm_ofs, uint32_t oprsz, \
+ uint32_t maxsz) \
+ { \
+ tcg_gen_gvec_2_ool(rd_ofs, rm_ofs, oprsz, maxsz, DATA, FUNC); \
+ }
+
+WRAP_2M_3_OOL_FN(gen_AESE, gen_helper_crypto_aese, 0)
+WRAP_2M_3_OOL_FN(gen_AESD, gen_helper_crypto_aese, 1)
+WRAP_2M_2_OOL_FN(gen_AESMC, gen_helper_crypto_aesmc, 0)
+WRAP_2M_2_OOL_FN(gen_AESIMC, gen_helper_crypto_aesmc, 1)
+WRAP_2M_2_OOL_FN(gen_SHA1H, gen_helper_crypto_sha1h, 0)
+WRAP_2M_2_OOL_FN(gen_SHA1SU1, gen_helper_crypto_sha1su1, 0)
+WRAP_2M_2_OOL_FN(gen_SHA256SU0, gen_helper_crypto_sha256su0, 0)
+
+#define DO_2M_CRYPTO(INSN, FEATURE, SIZE) \
+ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \
+ { \
+ if (!dc_isar_feature(FEATURE, s) || a->size != SIZE) { \
+ return false; \
+ } \
+ return do_2misc_vec(s, a, gen_##INSN); \
+ }
+
+DO_2M_CRYPTO(AESE, aa32_aes, 0)
+DO_2M_CRYPTO(AESD, aa32_aes, 0)
+DO_2M_CRYPTO(AESMC, aa32_aes, 0)
+DO_2M_CRYPTO(AESIMC, aa32_aes, 0)
+DO_2M_CRYPTO(SHA1H, aa32_sha1, 2)
+DO_2M_CRYPTO(SHA1SU1, aa32_sha1, 2)
+DO_2M_CRYPTO(SHA256SU0, aa32_sha2, 2)
+
+static bool do_2misc(DisasContext *s, arg_2misc *a, NeonGenOneOpFn *fn)
+{
+ int pass;
+
+ /* Handle a 2-reg-misc operation by iterating 32 bits at a time */
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (!fn) {
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ for (pass = 0; pass < (a->q ? 4 : 2); pass++) {
+ TCGv_i32 tmp = neon_load_reg(a->vm, pass);
+ fn(tmp, tmp);
+ neon_store_reg(a->vd, pass, tmp);
+ }
+
+ return true;
+}
+
+static bool trans_VREV32(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenOneOpFn * const fn[] = {
+ tcg_gen_bswap32_i32,
+ gen_swap_half,
+ NULL,
+ NULL,
+ };
+ return do_2misc(s, a, fn[a->size]);
+}
+
+static bool trans_VREV16(DisasContext *s, arg_2misc *a)
+{
+ if (a->size != 0) {
+ return false;
+ }
+ return do_2misc(s, a, gen_rev16);
+}
+
+static bool trans_VCLS(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenOneOpFn * const fn[] = {
+ gen_helper_neon_cls_s8,
+ gen_helper_neon_cls_s16,
+ gen_helper_neon_cls_s32,
+ NULL,
+ };
+ return do_2misc(s, a, fn[a->size]);
+}
+
+static void do_VCLZ_32(TCGv_i32 rd, TCGv_i32 rm)
+{
+ tcg_gen_clzi_i32(rd, rm, 32);
+}
+
+static bool trans_VCLZ(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenOneOpFn * const fn[] = {
+ gen_helper_neon_clz_u8,
+ gen_helper_neon_clz_u16,
+ do_VCLZ_32,
+ NULL,
+ };
+ return do_2misc(s, a, fn[a->size]);
+}
+
+static bool trans_VCNT(DisasContext *s, arg_2misc *a)
+{
+ if (a->size != 0) {
+ return false;
+ }
+ return do_2misc(s, a, gen_helper_neon_cnt_u8);
+}
+
+static bool trans_VABS_F(DisasContext *s, arg_2misc *a)
+{
+ if (a->size != 2) {
+ return false;
+ }
+ /* TODO: FP16 : size == 1 */
+ return do_2misc(s, a, gen_helper_vfp_abss);
+}
+
+static bool trans_VNEG_F(DisasContext *s, arg_2misc *a)
+{
+ if (a->size != 2) {
+ return false;
+ }
+ /* TODO: FP16 : size == 1 */
+ return do_2misc(s, a, gen_helper_vfp_negs);
+}
+
+static bool trans_VRECPE(DisasContext *s, arg_2misc *a)
+{
+ if (a->size != 2) {
+ return false;
+ }
+ return do_2misc(s, a, gen_helper_recpe_u32);
+}
+
+static bool trans_VRSQRTE(DisasContext *s, arg_2misc *a)
+{
+ if (a->size != 2) {
+ return false;
+ }
+ return do_2misc(s, a, gen_helper_rsqrte_u32);
+}
+
+#define WRAP_1OP_ENV_FN(WRAPNAME, FUNC) \
+ static void WRAPNAME(TCGv_i32 d, TCGv_i32 m) \
+ { \
+ FUNC(d, cpu_env, m); \
+ }
+
+WRAP_1OP_ENV_FN(gen_VQABS_s8, gen_helper_neon_qabs_s8)
+WRAP_1OP_ENV_FN(gen_VQABS_s16, gen_helper_neon_qabs_s16)
+WRAP_1OP_ENV_FN(gen_VQABS_s32, gen_helper_neon_qabs_s32)
+WRAP_1OP_ENV_FN(gen_VQNEG_s8, gen_helper_neon_qneg_s8)
+WRAP_1OP_ENV_FN(gen_VQNEG_s16, gen_helper_neon_qneg_s16)
+WRAP_1OP_ENV_FN(gen_VQNEG_s32, gen_helper_neon_qneg_s32)
+
+static bool trans_VQABS(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenOneOpFn * const fn[] = {
+ gen_VQABS_s8,
+ gen_VQABS_s16,
+ gen_VQABS_s32,
+ NULL,
+ };
+ return do_2misc(s, a, fn[a->size]);
+}
+
+static bool trans_VQNEG(DisasContext *s, arg_2misc *a)
+{
+ static NeonGenOneOpFn * const fn[] = {
+ gen_VQNEG_s8,
+ gen_VQNEG_s16,
+ gen_VQNEG_s32,
+ NULL,
+ };
+ return do_2misc(s, a, fn[a->size]);
+}
+
+static bool do_2misc_fp(DisasContext *s, arg_2misc *a,
+ NeonGenOneSingleOpFn *fn)
+{
+ int pass;
+ TCGv_ptr fpst;
+
+ /* Handle a 2-reg-misc operation by iterating 32 bits at a time */
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (a->size != 2) {
+ /* TODO: FP16 will be the size == 1 case */
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ fpst = get_fpstatus_ptr(1);
+ for (pass = 0; pass < (a->q ? 4 : 2); pass++) {
+ TCGv_i32 tmp = neon_load_reg(a->vm, pass);
+ fn(tmp, tmp, fpst);
+ neon_store_reg(a->vd, pass, tmp);
+ }
+ tcg_temp_free_ptr(fpst);
+
+ return true;
+}
+
+#define DO_2MISC_FP(INSN, FUNC) \
+ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \
+ { \
+ return do_2misc_fp(s, a, FUNC); \
+ }
+
+DO_2MISC_FP(VRECPE_F, gen_helper_recpe_f32)
+DO_2MISC_FP(VRSQRTE_F, gen_helper_rsqrte_f32)
+DO_2MISC_FP(VCVT_FS, gen_helper_vfp_sitos)
+DO_2MISC_FP(VCVT_FU, gen_helper_vfp_uitos)
+DO_2MISC_FP(VCVT_SF, gen_helper_vfp_tosizs)
+DO_2MISC_FP(VCVT_UF, gen_helper_vfp_touizs)
+
+static bool trans_VRINTX(DisasContext *s, arg_2misc *a)
+{
+ if (!arm_dc_feature(s, ARM_FEATURE_V8)) {
+ return false;
+ }
+ return do_2misc_fp(s, a, gen_helper_rints_exact);
+}
+
+#define WRAP_FP_CMP0_FWD(WRAPNAME, FUNC) \
+ static void WRAPNAME(TCGv_i32 d, TCGv_i32 m, TCGv_ptr fpst) \
+ { \
+ TCGv_i32 zero = tcg_const_i32(0); \
+ FUNC(d, m, zero, fpst); \
+ tcg_temp_free_i32(zero); \
+ }
+#define WRAP_FP_CMP0_REV(WRAPNAME, FUNC) \
+ static void WRAPNAME(TCGv_i32 d, TCGv_i32 m, TCGv_ptr fpst) \
+ { \
+ TCGv_i32 zero = tcg_const_i32(0); \
+ FUNC(d, zero, m, fpst); \
+ tcg_temp_free_i32(zero); \
+ }
+
+#define DO_FP_CMP0(INSN, FUNC, REV) \
+ WRAP_FP_CMP0_##REV(gen_##INSN, FUNC) \
+ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \
+ { \
+ return do_2misc_fp(s, a, gen_##INSN); \
+ }
+
+DO_FP_CMP0(VCGT0_F, gen_helper_neon_cgt_f32, FWD)
+DO_FP_CMP0(VCGE0_F, gen_helper_neon_cge_f32, FWD)
+DO_FP_CMP0(VCEQ0_F, gen_helper_neon_ceq_f32, FWD)
+DO_FP_CMP0(VCLE0_F, gen_helper_neon_cge_f32, REV)
+DO_FP_CMP0(VCLT0_F, gen_helper_neon_cgt_f32, REV)
+
+static bool do_vrint(DisasContext *s, arg_2misc *a, int rmode)
+{
+ /*
+ * Handle a VRINT* operation by iterating 32 bits at a time,
+ * with a specified rounding mode in operation.
+ */
+ int pass;
+ TCGv_ptr fpst;
+ TCGv_i32 tcg_rmode;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON) ||
+ !arm_dc_feature(s, ARM_FEATURE_V8)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (a->size != 2) {
+ /* TODO: FP16 will be the size == 1 case */
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ fpst = get_fpstatus_ptr(1);
+ tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ for (pass = 0; pass < (a->q ? 4 : 2); pass++) {
+ TCGv_i32 tmp = neon_load_reg(a->vm, pass);
+ gen_helper_rints(tmp, tmp, fpst);
+ neon_store_reg(a->vd, pass, tmp);
+ }
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+ tcg_temp_free_ptr(fpst);
+
+ return true;
+}
+
+#define DO_VRINT(INSN, RMODE) \
+ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \
+ { \
+ return do_vrint(s, a, RMODE); \
+ }
+
+DO_VRINT(VRINTN, FPROUNDING_TIEEVEN)
+DO_VRINT(VRINTA, FPROUNDING_TIEAWAY)
+DO_VRINT(VRINTZ, FPROUNDING_ZERO)
+DO_VRINT(VRINTM, FPROUNDING_NEGINF)
+DO_VRINT(VRINTP, FPROUNDING_POSINF)
+
+static bool do_vcvt(DisasContext *s, arg_2misc *a, int rmode, bool is_signed)
+{
+ /*
+ * Handle a VCVT* operation by iterating 32 bits at a time,
+ * with a specified rounding mode in operation.
+ */
+ int pass;
+ TCGv_ptr fpst;
+ TCGv_i32 tcg_rmode, tcg_shift;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON) ||
+ !arm_dc_feature(s, ARM_FEATURE_V8)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (a->size != 2) {
+ /* TODO: FP16 will be the size == 1 case */
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ fpst = get_fpstatus_ptr(1);
+ tcg_shift = tcg_const_i32(0);
+ tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ for (pass = 0; pass < (a->q ? 4 : 2); pass++) {
+ TCGv_i32 tmp = neon_load_reg(a->vm, pass);
+ if (is_signed) {
+ gen_helper_vfp_tosls(tmp, tmp, tcg_shift, fpst);
+ } else {
+ gen_helper_vfp_touls(tmp, tmp, tcg_shift, fpst);
+ }
+ neon_store_reg(a->vd, pass, tmp);
+ }
+ gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode, cpu_env);
+ tcg_temp_free_i32(tcg_rmode);
+ tcg_temp_free_i32(tcg_shift);
+ tcg_temp_free_ptr(fpst);
+
+ return true;
+}
+
+#define DO_VCVT(INSN, RMODE, SIGNED) \
+ static bool trans_##INSN(DisasContext *s, arg_2misc *a) \
+ { \
+ return do_vcvt(s, a, RMODE, SIGNED); \
+ }
+
+DO_VCVT(VCVTAU, FPROUNDING_TIEAWAY, false)
+DO_VCVT(VCVTAS, FPROUNDING_TIEAWAY, true)
+DO_VCVT(VCVTNU, FPROUNDING_TIEEVEN, false)
+DO_VCVT(VCVTNS, FPROUNDING_TIEEVEN, true)
+DO_VCVT(VCVTPU, FPROUNDING_POSINF, false)
+DO_VCVT(VCVTPS, FPROUNDING_POSINF, true)
+DO_VCVT(VCVTMU, FPROUNDING_NEGINF, false)
+DO_VCVT(VCVTMS, FPROUNDING_NEGINF, true)
+
+static bool trans_VSWP(DisasContext *s, arg_2misc *a)
+{
+ TCGv_i64 rm, rd;
+ int pass;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if (a->size != 0) {
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ rm = tcg_temp_new_i64();
+ rd = tcg_temp_new_i64();
+ for (pass = 0; pass < (a->q ? 2 : 1); pass++) {
+ neon_load_reg64(rm, a->vm + pass);
+ neon_load_reg64(rd, a->vd + pass);
+ neon_store_reg64(rm, a->vd + pass);
+ neon_store_reg64(rd, a->vm + pass);
+ }
+ tcg_temp_free_i64(rm);
+ tcg_temp_free_i64(rd);
+
+ return true;
+}
+static void gen_neon_trn_u8(TCGv_i32 t0, TCGv_i32 t1)
+{
+ TCGv_i32 rd, tmp;
+
+ rd = tcg_temp_new_i32();
+ tmp = tcg_temp_new_i32();
+
+ tcg_gen_shli_i32(rd, t0, 8);
+ tcg_gen_andi_i32(rd, rd, 0xff00ff00);
+ tcg_gen_andi_i32(tmp, t1, 0x00ff00ff);
+ tcg_gen_or_i32(rd, rd, tmp);
+
+ tcg_gen_shri_i32(t1, t1, 8);
+ tcg_gen_andi_i32(t1, t1, 0x00ff00ff);
+ tcg_gen_andi_i32(tmp, t0, 0xff00ff00);
+ tcg_gen_or_i32(t1, t1, tmp);
+ tcg_gen_mov_i32(t0, rd);
+
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(rd);
+}
+
+static void gen_neon_trn_u16(TCGv_i32 t0, TCGv_i32 t1)
+{
+ TCGv_i32 rd, tmp;
+
+ rd = tcg_temp_new_i32();
+ tmp = tcg_temp_new_i32();
+
+ tcg_gen_shli_i32(rd, t0, 16);
+ tcg_gen_andi_i32(tmp, t1, 0xffff);
+ tcg_gen_or_i32(rd, rd, tmp);
+ tcg_gen_shri_i32(t1, t1, 16);
+ tcg_gen_andi_i32(tmp, t0, 0xffff0000);
+ tcg_gen_or_i32(t1, t1, tmp);
+ tcg_gen_mov_i32(t0, rd);
+
+ tcg_temp_free_i32(tmp);
+ tcg_temp_free_i32(rd);
+}
+
+static bool trans_VTRN(DisasContext *s, arg_2misc *a)
+{
+ TCGv_i32 tmp, tmp2;
+ int pass;
+
+ if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
+ return false;
+ }
+
+ /* UNDEF accesses to D16-D31 if they don't exist. */
+ if (!dc_isar_feature(aa32_simd_r32, s) &&
+ ((a->vd | a->vm) & 0x10)) {
+ return false;
+ }
+
+ if ((a->vd | a->vm) & a->q) {
+ return false;
+ }
+
+ if (a->size == 3) {
+ return false;
+ }
+
+ if (!vfp_access_check(s)) {
+ return true;
+ }
+
+ if (a->size == 2) {
+ for (pass = 0; pass < (a->q ? 4 : 2); pass += 2) {
+ tmp = neon_load_reg(a->vm, pass);
+ tmp2 = neon_load_reg(a->vd, pass + 1);
+ neon_store_reg(a->vm, pass, tmp2);
+ neon_store_reg(a->vd, pass + 1, tmp);
+ }
+ } else {
+ for (pass = 0; pass < (a->q ? 4 : 2); pass++) {
+ tmp = neon_load_reg(a->vm, pass);
+ tmp2 = neon_load_reg(a->vd, pass);
+ if (a->size == 0) {
+ gen_neon_trn_u8(tmp, tmp2);
+ } else {
+ gen_neon_trn_u16(tmp, tmp2);
+ }
+ neon_store_reg(a->vm, pass, tmp2);
+ neon_store_reg(a->vd, pass, tmp);
+ }
+ }
+ return true;
+}
diff --git a/target/arm/translate-vfp.inc.c b/target/arm/translate-vfp.inc.c
index e1a9017..bf31b18 100644
--- a/target/arm/translate-vfp.inc.c
+++ b/target/arm/translate-vfp.inc.c
@@ -119,15 +119,14 @@
if (s->v7m_lspact) {
/*
* Lazy state saving affects external memory and also the NVIC,
- * so we must mark it as an IO operation for icount.
+ * so we must mark it as an IO operation for icount (and cause
+ * this to be the last insn in the TB).
*/
if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
+ s->base.is_jmp = DISAS_UPDATE;
gen_io_start();
}
gen_helper_v7m_preserve_fp_state(cpu_env);
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
- }
/*
* If the preserve_fp_state helper doesn't throw an exception
* then it will clear LSPACT; we don't need to repeat this for
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 6d18892..795964d 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -378,9 +378,9 @@
}
/* Swap low and high halfwords. */
-static void gen_swap_half(TCGv_i32 var)
+static void gen_swap_half(TCGv_i32 dest, TCGv_i32 var)
{
- tcg_gen_rotri_i32(var, var, 16);
+ tcg_gen_rotri_i32(dest, var, 16);
}
/* Dual 16-bit add. Result placed in t0 and t1 is marked as dead.
@@ -1133,25 +1133,6 @@
return vfp_reg_offset(0, sreg);
}
-/* Return the offset of a 2**SIZE piece of a NEON register, at index ELE,
- * where 0 is the least significant end of the register.
- */
-static inline long
-neon_element_offset(int reg, int element, MemOp size)
-{
- int element_size = 1 << size;
- int ofs = element * element_size;
-#ifdef HOST_WORDS_BIGENDIAN
- /* Calculate the offset assuming fully little-endian,
- * then XOR to account for the order of the 8-byte units.
- */
- if (element_size < 8) {
- ofs ^= 8 - element_size;
- }
-#endif
- return neon_reg_offset(reg, 0) + ofs;
-}
-
static TCGv_i32 neon_load_reg(int reg, int pass)
{
TCGv_i32 tmp = tcg_temp_new_i32();
@@ -1159,94 +1140,12 @@
return tmp;
}
-static void neon_load_element(TCGv_i32 var, int reg, int ele, MemOp mop)
-{
- long offset = neon_element_offset(reg, ele, mop & MO_SIZE);
-
- switch (mop) {
- case MO_UB:
- tcg_gen_ld8u_i32(var, cpu_env, offset);
- break;
- case MO_UW:
- tcg_gen_ld16u_i32(var, cpu_env, offset);
- break;
- case MO_UL:
- tcg_gen_ld_i32(var, cpu_env, offset);
- break;
- default:
- g_assert_not_reached();
- }
-}
-
-static void neon_load_element64(TCGv_i64 var, int reg, int ele, MemOp mop)
-{
- long offset = neon_element_offset(reg, ele, mop & MO_SIZE);
-
- switch (mop) {
- case MO_UB:
- tcg_gen_ld8u_i64(var, cpu_env, offset);
- break;
- case MO_UW:
- tcg_gen_ld16u_i64(var, cpu_env, offset);
- break;
- case MO_UL:
- tcg_gen_ld32u_i64(var, cpu_env, offset);
- break;
- case MO_Q:
- tcg_gen_ld_i64(var, cpu_env, offset);
- break;
- default:
- g_assert_not_reached();
- }
-}
-
static void neon_store_reg(int reg, int pass, TCGv_i32 var)
{
tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass));
tcg_temp_free_i32(var);
}
-static void neon_store_element(int reg, int ele, MemOp size, TCGv_i32 var)
-{
- long offset = neon_element_offset(reg, ele, size);
-
- switch (size) {
- case MO_8:
- tcg_gen_st8_i32(var, cpu_env, offset);
- break;
- case MO_16:
- tcg_gen_st16_i32(var, cpu_env, offset);
- break;
- case MO_32:
- tcg_gen_st_i32(var, cpu_env, offset);
- break;
- default:
- g_assert_not_reached();
- }
-}
-
-static void neon_store_element64(int reg, int ele, MemOp size, TCGv_i64 var)
-{
- long offset = neon_element_offset(reg, ele, size);
-
- switch (size) {
- case MO_8:
- tcg_gen_st8_i64(var, cpu_env, offset);
- break;
- case MO_16:
- tcg_gen_st16_i64(var, cpu_env, offset);
- break;
- case MO_32:
- tcg_gen_st32_i64(var, cpu_env, offset);
- break;
- case MO_64:
- tcg_gen_st_i64(var, cpu_env, offset);
- break;
- default:
- g_assert_not_reached();
- }
-}
-
static inline void neon_load_reg64(TCGv_i64 var, int reg)
{
tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(1, reg));
@@ -2934,377 +2833,6 @@
gen_rfe(s, pc, load_cpu_field(spsr));
}
-#define CPU_V001 cpu_V0, cpu_V0, cpu_V1
-
-static int gen_neon_unzip(int rd, int rm, int size, int q)
-{
- TCGv_ptr pd, pm;
-
- if (!q && size == 2) {
- return 1;
- }
- pd = vfp_reg_ptr(true, rd);
- pm = vfp_reg_ptr(true, rm);
- if (q) {
- switch (size) {
- case 0:
- gen_helper_neon_qunzip8(pd, pm);
- break;
- case 1:
- gen_helper_neon_qunzip16(pd, pm);
- break;
- case 2:
- gen_helper_neon_qunzip32(pd, pm);
- break;
- default:
- abort();
- }
- } else {
- switch (size) {
- case 0:
- gen_helper_neon_unzip8(pd, pm);
- break;
- case 1:
- gen_helper_neon_unzip16(pd, pm);
- break;
- default:
- abort();
- }
- }
- tcg_temp_free_ptr(pd);
- tcg_temp_free_ptr(pm);
- return 0;
-}
-
-static int gen_neon_zip(int rd, int rm, int size, int q)
-{
- TCGv_ptr pd, pm;
-
- if (!q && size == 2) {
- return 1;
- }
- pd = vfp_reg_ptr(true, rd);
- pm = vfp_reg_ptr(true, rm);
- if (q) {
- switch (size) {
- case 0:
- gen_helper_neon_qzip8(pd, pm);
- break;
- case 1:
- gen_helper_neon_qzip16(pd, pm);
- break;
- case 2:
- gen_helper_neon_qzip32(pd, pm);
- break;
- default:
- abort();
- }
- } else {
- switch (size) {
- case 0:
- gen_helper_neon_zip8(pd, pm);
- break;
- case 1:
- gen_helper_neon_zip16(pd, pm);
- break;
- default:
- abort();
- }
- }
- tcg_temp_free_ptr(pd);
- tcg_temp_free_ptr(pm);
- return 0;
-}
-
-static void gen_neon_trn_u8(TCGv_i32 t0, TCGv_i32 t1)
-{
- TCGv_i32 rd, tmp;
-
- rd = tcg_temp_new_i32();
- tmp = tcg_temp_new_i32();
-
- tcg_gen_shli_i32(rd, t0, 8);
- tcg_gen_andi_i32(rd, rd, 0xff00ff00);
- tcg_gen_andi_i32(tmp, t1, 0x00ff00ff);
- tcg_gen_or_i32(rd, rd, tmp);
-
- tcg_gen_shri_i32(t1, t1, 8);
- tcg_gen_andi_i32(t1, t1, 0x00ff00ff);
- tcg_gen_andi_i32(tmp, t0, 0xff00ff00);
- tcg_gen_or_i32(t1, t1, tmp);
- tcg_gen_mov_i32(t0, rd);
-
- tcg_temp_free_i32(tmp);
- tcg_temp_free_i32(rd);
-}
-
-static void gen_neon_trn_u16(TCGv_i32 t0, TCGv_i32 t1)
-{
- TCGv_i32 rd, tmp;
-
- rd = tcg_temp_new_i32();
- tmp = tcg_temp_new_i32();
-
- tcg_gen_shli_i32(rd, t0, 16);
- tcg_gen_andi_i32(tmp, t1, 0xffff);
- tcg_gen_or_i32(rd, rd, tmp);
- tcg_gen_shri_i32(t1, t1, 16);
- tcg_gen_andi_i32(tmp, t0, 0xffff0000);
- tcg_gen_or_i32(t1, t1, tmp);
- tcg_gen_mov_i32(t0, rd);
-
- tcg_temp_free_i32(tmp);
- tcg_temp_free_i32(rd);
-}
-
-static inline void gen_neon_narrow(int size, TCGv_i32 dest, TCGv_i64 src)
-{
- switch (size) {
- case 0: gen_helper_neon_narrow_u8(dest, src); break;
- case 1: gen_helper_neon_narrow_u16(dest, src); break;
- case 2: tcg_gen_extrl_i64_i32(dest, src); break;
- default: abort();
- }
-}
-
-static inline void gen_neon_narrow_sats(int size, TCGv_i32 dest, TCGv_i64 src)
-{
- switch (size) {
- case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break;
- case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break;
- case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break;
- default: abort();
- }
-}
-
-static inline void gen_neon_narrow_satu(int size, TCGv_i32 dest, TCGv_i64 src)
-{
- switch (size) {
- case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break;
- case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break;
- case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break;
- default: abort();
- }
-}
-
-static inline void gen_neon_unarrow_sats(int size, TCGv_i32 dest, TCGv_i64 src)
-{
- switch (size) {
- case 0: gen_helper_neon_unarrow_sat8(dest, cpu_env, src); break;
- case 1: gen_helper_neon_unarrow_sat16(dest, cpu_env, src); break;
- case 2: gen_helper_neon_unarrow_sat32(dest, cpu_env, src); break;
- default: abort();
- }
-}
-
-static inline void gen_neon_widen(TCGv_i64 dest, TCGv_i32 src, int size, int u)
-{
- if (u) {
- switch (size) {
- case 0: gen_helper_neon_widen_u8(dest, src); break;
- case 1: gen_helper_neon_widen_u16(dest, src); break;
- case 2: tcg_gen_extu_i32_i64(dest, src); break;
- default: abort();
- }
- } else {
- switch (size) {
- case 0: gen_helper_neon_widen_s8(dest, src); break;
- case 1: gen_helper_neon_widen_s16(dest, src); break;
- case 2: tcg_gen_ext_i32_i64(dest, src); break;
- default: abort();
- }
- }
- tcg_temp_free_i32(src);
-}
-
-static inline void gen_neon_addl(int size)
-{
- switch (size) {
- case 0: gen_helper_neon_addl_u16(CPU_V001); break;
- case 1: gen_helper_neon_addl_u32(CPU_V001); break;
- case 2: tcg_gen_add_i64(CPU_V001); break;
- default: abort();
- }
-}
-
-static void gen_neon_narrow_op(int op, int u, int size,
- TCGv_i32 dest, TCGv_i64 src)
-{
- if (op) {
- if (u) {
- gen_neon_unarrow_sats(size, dest, src);
- } else {
- gen_neon_narrow(size, dest, src);
- }
- } else {
- if (u) {
- gen_neon_narrow_satu(size, dest, src);
- } else {
- gen_neon_narrow_sats(size, dest, src);
- }
- }
-}
-
-/* Symbolic constants for op fields for Neon 2-register miscellaneous.
- * The values correspond to bits [17:16,10:7]; see the ARM ARM DDI0406B
- * table A7-13.
- */
-#define NEON_2RM_VREV64 0
-#define NEON_2RM_VREV32 1
-#define NEON_2RM_VREV16 2
-#define NEON_2RM_VPADDL 4
-#define NEON_2RM_VPADDL_U 5
-#define NEON_2RM_AESE 6 /* Includes AESD */
-#define NEON_2RM_AESMC 7 /* Includes AESIMC */
-#define NEON_2RM_VCLS 8
-#define NEON_2RM_VCLZ 9
-#define NEON_2RM_VCNT 10
-#define NEON_2RM_VMVN 11
-#define NEON_2RM_VPADAL 12
-#define NEON_2RM_VPADAL_U 13
-#define NEON_2RM_VQABS 14
-#define NEON_2RM_VQNEG 15
-#define NEON_2RM_VCGT0 16
-#define NEON_2RM_VCGE0 17
-#define NEON_2RM_VCEQ0 18
-#define NEON_2RM_VCLE0 19
-#define NEON_2RM_VCLT0 20
-#define NEON_2RM_SHA1H 21
-#define NEON_2RM_VABS 22
-#define NEON_2RM_VNEG 23
-#define NEON_2RM_VCGT0_F 24
-#define NEON_2RM_VCGE0_F 25
-#define NEON_2RM_VCEQ0_F 26
-#define NEON_2RM_VCLE0_F 27
-#define NEON_2RM_VCLT0_F 28
-#define NEON_2RM_VABS_F 30
-#define NEON_2RM_VNEG_F 31
-#define NEON_2RM_VSWP 32
-#define NEON_2RM_VTRN 33
-#define NEON_2RM_VUZP 34
-#define NEON_2RM_VZIP 35
-#define NEON_2RM_VMOVN 36 /* Includes VQMOVN, VQMOVUN */
-#define NEON_2RM_VQMOVN 37 /* Includes VQMOVUN */
-#define NEON_2RM_VSHLL 38
-#define NEON_2RM_SHA1SU1 39 /* Includes SHA256SU0 */
-#define NEON_2RM_VRINTN 40
-#define NEON_2RM_VRINTX 41
-#define NEON_2RM_VRINTA 42
-#define NEON_2RM_VRINTZ 43
-#define NEON_2RM_VCVT_F16_F32 44
-#define NEON_2RM_VRINTM 45
-#define NEON_2RM_VCVT_F32_F16 46
-#define NEON_2RM_VRINTP 47
-#define NEON_2RM_VCVTAU 48
-#define NEON_2RM_VCVTAS 49
-#define NEON_2RM_VCVTNU 50
-#define NEON_2RM_VCVTNS 51
-#define NEON_2RM_VCVTPU 52
-#define NEON_2RM_VCVTPS 53
-#define NEON_2RM_VCVTMU 54
-#define NEON_2RM_VCVTMS 55
-#define NEON_2RM_VRECPE 56
-#define NEON_2RM_VRSQRTE 57
-#define NEON_2RM_VRECPE_F 58
-#define NEON_2RM_VRSQRTE_F 59
-#define NEON_2RM_VCVT_FS 60
-#define NEON_2RM_VCVT_FU 61
-#define NEON_2RM_VCVT_SF 62
-#define NEON_2RM_VCVT_UF 63
-
-static bool neon_2rm_is_v8_op(int op)
-{
- /* Return true if this neon 2reg-misc op is ARMv8 and up */
- switch (op) {
- case NEON_2RM_VRINTN:
- case NEON_2RM_VRINTA:
- case NEON_2RM_VRINTM:
- case NEON_2RM_VRINTP:
- case NEON_2RM_VRINTZ:
- case NEON_2RM_VRINTX:
- case NEON_2RM_VCVTAU:
- case NEON_2RM_VCVTAS:
- case NEON_2RM_VCVTNU:
- case NEON_2RM_VCVTNS:
- case NEON_2RM_VCVTPU:
- case NEON_2RM_VCVTPS:
- case NEON_2RM_VCVTMU:
- case NEON_2RM_VCVTMS:
- return true;
- default:
- return false;
- }
-}
-
-/* Each entry in this array has bit n set if the insn allows
- * size value n (otherwise it will UNDEF). Since unallocated
- * op values will have no bits set they always UNDEF.
- */
-static const uint8_t neon_2rm_sizes[] = {
- [NEON_2RM_VREV64] = 0x7,
- [NEON_2RM_VREV32] = 0x3,
- [NEON_2RM_VREV16] = 0x1,
- [NEON_2RM_VPADDL] = 0x7,
- [NEON_2RM_VPADDL_U] = 0x7,
- [NEON_2RM_AESE] = 0x1,
- [NEON_2RM_AESMC] = 0x1,
- [NEON_2RM_VCLS] = 0x7,
- [NEON_2RM_VCLZ] = 0x7,
- [NEON_2RM_VCNT] = 0x1,
- [NEON_2RM_VMVN] = 0x1,
- [NEON_2RM_VPADAL] = 0x7,
- [NEON_2RM_VPADAL_U] = 0x7,
- [NEON_2RM_VQABS] = 0x7,
- [NEON_2RM_VQNEG] = 0x7,
- [NEON_2RM_VCGT0] = 0x7,
- [NEON_2RM_VCGE0] = 0x7,
- [NEON_2RM_VCEQ0] = 0x7,
- [NEON_2RM_VCLE0] = 0x7,
- [NEON_2RM_VCLT0] = 0x7,
- [NEON_2RM_SHA1H] = 0x4,
- [NEON_2RM_VABS] = 0x7,
- [NEON_2RM_VNEG] = 0x7,
- [NEON_2RM_VCGT0_F] = 0x4,
- [NEON_2RM_VCGE0_F] = 0x4,
- [NEON_2RM_VCEQ0_F] = 0x4,
- [NEON_2RM_VCLE0_F] = 0x4,
- [NEON_2RM_VCLT0_F] = 0x4,
- [NEON_2RM_VABS_F] = 0x4,
- [NEON_2RM_VNEG_F] = 0x4,
- [NEON_2RM_VSWP] = 0x1,
- [NEON_2RM_VTRN] = 0x7,
- [NEON_2RM_VUZP] = 0x7,
- [NEON_2RM_VZIP] = 0x7,
- [NEON_2RM_VMOVN] = 0x7,
- [NEON_2RM_VQMOVN] = 0x7,
- [NEON_2RM_VSHLL] = 0x7,
- [NEON_2RM_SHA1SU1] = 0x4,
- [NEON_2RM_VRINTN] = 0x4,
- [NEON_2RM_VRINTX] = 0x4,
- [NEON_2RM_VRINTA] = 0x4,
- [NEON_2RM_VRINTZ] = 0x4,
- [NEON_2RM_VCVT_F16_F32] = 0x2,
- [NEON_2RM_VRINTM] = 0x4,
- [NEON_2RM_VCVT_F32_F16] = 0x2,
- [NEON_2RM_VRINTP] = 0x4,
- [NEON_2RM_VCVTAU] = 0x4,
- [NEON_2RM_VCVTAS] = 0x4,
- [NEON_2RM_VCVTNU] = 0x4,
- [NEON_2RM_VCVTNS] = 0x4,
- [NEON_2RM_VCVTPU] = 0x4,
- [NEON_2RM_VCVTPS] = 0x4,
- [NEON_2RM_VCVTMU] = 0x4,
- [NEON_2RM_VCVTMS] = 0x4,
- [NEON_2RM_VRECPE] = 0x4,
- [NEON_2RM_VRSQRTE] = 0x4,
- [NEON_2RM_VRECPE_F] = 0x4,
- [NEON_2RM_VRSQRTE_F] = 0x4,
- [NEON_2RM_VCVT_FS] = 0x4,
- [NEON_2RM_VCVT_FU] = 0x4,
- [NEON_2RM_VCVT_SF] = 0x4,
- [NEON_2RM_VCVT_UF] = 0x4,
-};
-
static void gen_gvec_fn3_qc(uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs,
uint32_t opr_sz, uint32_t max_sz,
gen_helper_gvec_3_ptr *fn)
@@ -5016,573 +4544,6 @@
tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]);
}
-/* Translate a NEON data processing instruction. Return nonzero if the
- instruction is invalid.
- We process data in a mixture of 32-bit and 64-bit chunks.
- Mostly we use 32-bit chunks so we can use normal scalar instructions. */
-
-static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
-{
- int op;
- int q;
- int rd, rm, rd_ofs, rm_ofs;
- int size;
- int pass;
- int u;
- int vec_size;
- TCGv_i32 tmp, tmp2, tmp3;
-
- if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
- return 1;
- }
-
- /* FIXME: this access check should not take precedence over UNDEF
- * for invalid encodings; we will generate incorrect syndrome information
- * for attempts to execute invalid vfp/neon encodings with FP disabled.
- */
- if (s->fp_excp_el) {
- gen_exception_insn(s, s->pc_curr, EXCP_UDEF,
- syn_simd_access_trap(1, 0xe, false), s->fp_excp_el);
- return 0;
- }
-
- if (!s->vfp_enabled)
- return 1;
- q = (insn & (1 << 6)) != 0;
- u = (insn >> 24) & 1;
- VFP_DREG_D(rd, insn);
- VFP_DREG_M(rm, insn);
- size = (insn >> 20) & 3;
- vec_size = q ? 16 : 8;
- rd_ofs = neon_reg_offset(rd, 0);
- rm_ofs = neon_reg_offset(rm, 0);
-
- if ((insn & (1 << 23)) == 0) {
- /* Three register same length: handled by decodetree */
- return 1;
- } else if (insn & (1 << 4)) {
- /* Two registers and shift or reg and imm: handled by decodetree */
- return 1;
- } else { /* (insn & 0x00800010 == 0x00800000) */
- if (size != 3) {
- /*
- * Three registers of different lengths, or two registers and
- * a scalar: handled by decodetree
- */
- return 1;
- } else { /* size == 3 */
- if (!u) {
- /* Extract: handled by decodetree */
- return 1;
- } else if ((insn & (1 << 11)) == 0) {
- /* Two register misc. */
- op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf);
- size = (insn >> 18) & 3;
- /* UNDEF for unknown op values and bad op-size combinations */
- if ((neon_2rm_sizes[op] & (1 << size)) == 0) {
- return 1;
- }
- if (neon_2rm_is_v8_op(op) &&
- !arm_dc_feature(s, ARM_FEATURE_V8)) {
- return 1;
- }
- if ((op != NEON_2RM_VMOVN && op != NEON_2RM_VQMOVN) &&
- q && ((rm | rd) & 1)) {
- return 1;
- }
- switch (op) {
- case NEON_2RM_VREV64:
- for (pass = 0; pass < (q ? 2 : 1); pass++) {
- tmp = neon_load_reg(rm, pass * 2);
- tmp2 = neon_load_reg(rm, pass * 2 + 1);
- switch (size) {
- case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
- case 1: gen_swap_half(tmp); break;
- case 2: /* no-op */ break;
- default: abort();
- }
- neon_store_reg(rd, pass * 2 + 1, tmp);
- if (size == 2) {
- neon_store_reg(rd, pass * 2, tmp2);
- } else {
- switch (size) {
- case 0: tcg_gen_bswap32_i32(tmp2, tmp2); break;
- case 1: gen_swap_half(tmp2); break;
- default: abort();
- }
- neon_store_reg(rd, pass * 2, tmp2);
- }
- }
- break;
- case NEON_2RM_VPADDL: case NEON_2RM_VPADDL_U:
- case NEON_2RM_VPADAL: case NEON_2RM_VPADAL_U:
- for (pass = 0; pass < q + 1; pass++) {
- tmp = neon_load_reg(rm, pass * 2);
- gen_neon_widen(cpu_V0, tmp, size, op & 1);
- tmp = neon_load_reg(rm, pass * 2 + 1);
- gen_neon_widen(cpu_V1, tmp, size, op & 1);
- switch (size) {
- case 0: gen_helper_neon_paddl_u16(CPU_V001); break;
- case 1: gen_helper_neon_paddl_u32(CPU_V001); break;
- case 2: tcg_gen_add_i64(CPU_V001); break;
- default: abort();
- }
- if (op >= NEON_2RM_VPADAL) {
- /* Accumulate. */
- neon_load_reg64(cpu_V1, rd + pass);
- gen_neon_addl(size);
- }
- neon_store_reg64(cpu_V0, rd + pass);
- }
- break;
- case NEON_2RM_VTRN:
- if (size == 2) {
- int n;
- for (n = 0; n < (q ? 4 : 2); n += 2) {
- tmp = neon_load_reg(rm, n);
- tmp2 = neon_load_reg(rd, n + 1);
- neon_store_reg(rm, n, tmp2);
- neon_store_reg(rd, n + 1, tmp);
- }
- } else {
- goto elementwise;
- }
- break;
- case NEON_2RM_VUZP:
- if (gen_neon_unzip(rd, rm, size, q)) {
- return 1;
- }
- break;
- case NEON_2RM_VZIP:
- if (gen_neon_zip(rd, rm, size, q)) {
- return 1;
- }
- break;
- case NEON_2RM_VMOVN: case NEON_2RM_VQMOVN:
- /* also VQMOVUN; op field and mnemonics don't line up */
- if (rm & 1) {
- return 1;
- }
- tmp2 = NULL;
- for (pass = 0; pass < 2; pass++) {
- neon_load_reg64(cpu_V0, rm + pass);
- tmp = tcg_temp_new_i32();
- gen_neon_narrow_op(op == NEON_2RM_VMOVN, q, size,
- tmp, cpu_V0);
- if (pass == 0) {
- tmp2 = tmp;
- } else {
- neon_store_reg(rd, 0, tmp2);
- neon_store_reg(rd, 1, tmp);
- }
- }
- break;
- case NEON_2RM_VSHLL:
- if (q || (rd & 1)) {
- return 1;
- }
- tmp = neon_load_reg(rm, 0);
- tmp2 = neon_load_reg(rm, 1);
- for (pass = 0; pass < 2; pass++) {
- if (pass == 1)
- tmp = tmp2;
- gen_neon_widen(cpu_V0, tmp, size, 1);
- tcg_gen_shli_i64(cpu_V0, cpu_V0, 8 << size);
- neon_store_reg64(cpu_V0, rd + pass);
- }
- break;
- case NEON_2RM_VCVT_F16_F32:
- {
- TCGv_ptr fpst;
- TCGv_i32 ahp;
-
- if (!dc_isar_feature(aa32_fp16_spconv, s) ||
- q || (rm & 1)) {
- return 1;
- }
- fpst = get_fpstatus_ptr(true);
- ahp = get_ahp_flag();
- tmp = neon_load_reg(rm, 0);
- gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp);
- tmp2 = neon_load_reg(rm, 1);
- gen_helper_vfp_fcvt_f32_to_f16(tmp2, tmp2, fpst, ahp);
- tcg_gen_shli_i32(tmp2, tmp2, 16);
- tcg_gen_or_i32(tmp2, tmp2, tmp);
- tcg_temp_free_i32(tmp);
- tmp = neon_load_reg(rm, 2);
- gen_helper_vfp_fcvt_f32_to_f16(tmp, tmp, fpst, ahp);
- tmp3 = neon_load_reg(rm, 3);
- neon_store_reg(rd, 0, tmp2);
- gen_helper_vfp_fcvt_f32_to_f16(tmp3, tmp3, fpst, ahp);
- tcg_gen_shli_i32(tmp3, tmp3, 16);
- tcg_gen_or_i32(tmp3, tmp3, tmp);
- neon_store_reg(rd, 1, tmp3);
- tcg_temp_free_i32(tmp);
- tcg_temp_free_i32(ahp);
- tcg_temp_free_ptr(fpst);
- break;
- }
- case NEON_2RM_VCVT_F32_F16:
- {
- TCGv_ptr fpst;
- TCGv_i32 ahp;
- if (!dc_isar_feature(aa32_fp16_spconv, s) ||
- q || (rd & 1)) {
- return 1;
- }
- fpst = get_fpstatus_ptr(true);
- ahp = get_ahp_flag();
- tmp3 = tcg_temp_new_i32();
- tmp = neon_load_reg(rm, 0);
- tmp2 = neon_load_reg(rm, 1);
- tcg_gen_ext16u_i32(tmp3, tmp);
- gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp);
- neon_store_reg(rd, 0, tmp3);
- tcg_gen_shri_i32(tmp, tmp, 16);
- gen_helper_vfp_fcvt_f16_to_f32(tmp, tmp, fpst, ahp);
- neon_store_reg(rd, 1, tmp);
- tmp3 = tcg_temp_new_i32();
- tcg_gen_ext16u_i32(tmp3, tmp2);
- gen_helper_vfp_fcvt_f16_to_f32(tmp3, tmp3, fpst, ahp);
- neon_store_reg(rd, 2, tmp3);
- tcg_gen_shri_i32(tmp2, tmp2, 16);
- gen_helper_vfp_fcvt_f16_to_f32(tmp2, tmp2, fpst, ahp);
- neon_store_reg(rd, 3, tmp2);
- tcg_temp_free_i32(ahp);
- tcg_temp_free_ptr(fpst);
- break;
- }
- case NEON_2RM_AESE: case NEON_2RM_AESMC:
- if (!dc_isar_feature(aa32_aes, s) || ((rm | rd) & 1)) {
- return 1;
- }
- /*
- * Bit 6 is the lowest opcode bit; it distinguishes
- * between encryption (AESE/AESMC) and decryption
- * (AESD/AESIMC).
- */
- if (op == NEON_2RM_AESE) {
- tcg_gen_gvec_3_ool(vfp_reg_offset(true, rd),
- vfp_reg_offset(true, rd),
- vfp_reg_offset(true, rm),
- 16, 16, extract32(insn, 6, 1),
- gen_helper_crypto_aese);
- } else {
- tcg_gen_gvec_2_ool(vfp_reg_offset(true, rd),
- vfp_reg_offset(true, rm),
- 16, 16, extract32(insn, 6, 1),
- gen_helper_crypto_aesmc);
- }
- break;
- case NEON_2RM_SHA1H:
- if (!dc_isar_feature(aa32_sha1, s) || ((rm | rd) & 1)) {
- return 1;
- }
- tcg_gen_gvec_2_ool(rd_ofs, rm_ofs, 16, 16, 0,
- gen_helper_crypto_sha1h);
- break;
- case NEON_2RM_SHA1SU1:
- if ((rm | rd) & 1) {
- return 1;
- }
- /* bit 6 (q): set -> SHA256SU0, cleared -> SHA1SU1 */
- if (q) {
- if (!dc_isar_feature(aa32_sha2, s)) {
- return 1;
- }
- } else if (!dc_isar_feature(aa32_sha1, s)) {
- return 1;
- }
- tcg_gen_gvec_2_ool(rd_ofs, rm_ofs, 16, 16, 0,
- q ? gen_helper_crypto_sha256su0
- : gen_helper_crypto_sha1su1);
- break;
- case NEON_2RM_VMVN:
- tcg_gen_gvec_not(0, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
- case NEON_2RM_VNEG:
- tcg_gen_gvec_neg(size, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
- case NEON_2RM_VABS:
- tcg_gen_gvec_abs(size, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
-
- case NEON_2RM_VCEQ0:
- gen_gvec_ceq0(size, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
- case NEON_2RM_VCGT0:
- gen_gvec_cgt0(size, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
- case NEON_2RM_VCLE0:
- gen_gvec_cle0(size, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
- case NEON_2RM_VCGE0:
- gen_gvec_cge0(size, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
- case NEON_2RM_VCLT0:
- gen_gvec_clt0(size, rd_ofs, rm_ofs, vec_size, vec_size);
- break;
-
- default:
- elementwise:
- for (pass = 0; pass < (q ? 4 : 2); pass++) {
- tmp = neon_load_reg(rm, pass);
- switch (op) {
- case NEON_2RM_VREV32:
- switch (size) {
- case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
- case 1: gen_swap_half(tmp); break;
- default: abort();
- }
- break;
- case NEON_2RM_VREV16:
- gen_rev16(tmp, tmp);
- break;
- case NEON_2RM_VCLS:
- switch (size) {
- case 0: gen_helper_neon_cls_s8(tmp, tmp); break;
- case 1: gen_helper_neon_cls_s16(tmp, tmp); break;
- case 2: gen_helper_neon_cls_s32(tmp, tmp); break;
- default: abort();
- }
- break;
- case NEON_2RM_VCLZ:
- switch (size) {
- case 0: gen_helper_neon_clz_u8(tmp, tmp); break;
- case 1: gen_helper_neon_clz_u16(tmp, tmp); break;
- case 2: tcg_gen_clzi_i32(tmp, tmp, 32); break;
- default: abort();
- }
- break;
- case NEON_2RM_VCNT:
- gen_helper_neon_cnt_u8(tmp, tmp);
- break;
- case NEON_2RM_VQABS:
- switch (size) {
- case 0:
- gen_helper_neon_qabs_s8(tmp, cpu_env, tmp);
- break;
- case 1:
- gen_helper_neon_qabs_s16(tmp, cpu_env, tmp);
- break;
- case 2:
- gen_helper_neon_qabs_s32(tmp, cpu_env, tmp);
- break;
- default: abort();
- }
- break;
- case NEON_2RM_VQNEG:
- switch (size) {
- case 0:
- gen_helper_neon_qneg_s8(tmp, cpu_env, tmp);
- break;
- case 1:
- gen_helper_neon_qneg_s16(tmp, cpu_env, tmp);
- break;
- case 2:
- gen_helper_neon_qneg_s32(tmp, cpu_env, tmp);
- break;
- default: abort();
- }
- break;
- case NEON_2RM_VCGT0_F:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- tmp2 = tcg_const_i32(0);
- gen_helper_neon_cgt_f32(tmp, tmp, tmp2, fpstatus);
- tcg_temp_free_i32(tmp2);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCGE0_F:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- tmp2 = tcg_const_i32(0);
- gen_helper_neon_cge_f32(tmp, tmp, tmp2, fpstatus);
- tcg_temp_free_i32(tmp2);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCEQ0_F:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- tmp2 = tcg_const_i32(0);
- gen_helper_neon_ceq_f32(tmp, tmp, tmp2, fpstatus);
- tcg_temp_free_i32(tmp2);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCLE0_F:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- tmp2 = tcg_const_i32(0);
- gen_helper_neon_cge_f32(tmp, tmp2, tmp, fpstatus);
- tcg_temp_free_i32(tmp2);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCLT0_F:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- tmp2 = tcg_const_i32(0);
- gen_helper_neon_cgt_f32(tmp, tmp2, tmp, fpstatus);
- tcg_temp_free_i32(tmp2);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VABS_F:
- gen_helper_vfp_abss(tmp, tmp);
- break;
- case NEON_2RM_VNEG_F:
- gen_helper_vfp_negs(tmp, tmp);
- break;
- case NEON_2RM_VSWP:
- tmp2 = neon_load_reg(rd, pass);
- neon_store_reg(rm, pass, tmp2);
- break;
- case NEON_2RM_VTRN:
- tmp2 = neon_load_reg(rd, pass);
- switch (size) {
- case 0: gen_neon_trn_u8(tmp, tmp2); break;
- case 1: gen_neon_trn_u16(tmp, tmp2); break;
- default: abort();
- }
- neon_store_reg(rm, pass, tmp2);
- break;
- case NEON_2RM_VRINTN:
- case NEON_2RM_VRINTA:
- case NEON_2RM_VRINTM:
- case NEON_2RM_VRINTP:
- case NEON_2RM_VRINTZ:
- {
- TCGv_i32 tcg_rmode;
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- int rmode;
-
- if (op == NEON_2RM_VRINTZ) {
- rmode = FPROUNDING_ZERO;
- } else {
- rmode = fp_decode_rm[((op & 0x6) >> 1) ^ 1];
- }
-
- tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
- gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
- cpu_env);
- gen_helper_rints(tmp, tmp, fpstatus);
- gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
- cpu_env);
- tcg_temp_free_ptr(fpstatus);
- tcg_temp_free_i32(tcg_rmode);
- break;
- }
- case NEON_2RM_VRINTX:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- gen_helper_rints_exact(tmp, tmp, fpstatus);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCVTAU:
- case NEON_2RM_VCVTAS:
- case NEON_2RM_VCVTNU:
- case NEON_2RM_VCVTNS:
- case NEON_2RM_VCVTPU:
- case NEON_2RM_VCVTPS:
- case NEON_2RM_VCVTMU:
- case NEON_2RM_VCVTMS:
- {
- bool is_signed = !extract32(insn, 7, 1);
- TCGv_ptr fpst = get_fpstatus_ptr(1);
- TCGv_i32 tcg_rmode, tcg_shift;
- int rmode = fp_decode_rm[extract32(insn, 8, 2)];
-
- tcg_shift = tcg_const_i32(0);
- tcg_rmode = tcg_const_i32(arm_rmode_to_sf(rmode));
- gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
- cpu_env);
-
- if (is_signed) {
- gen_helper_vfp_tosls(tmp, tmp,
- tcg_shift, fpst);
- } else {
- gen_helper_vfp_touls(tmp, tmp,
- tcg_shift, fpst);
- }
-
- gen_helper_set_neon_rmode(tcg_rmode, tcg_rmode,
- cpu_env);
- tcg_temp_free_i32(tcg_rmode);
- tcg_temp_free_i32(tcg_shift);
- tcg_temp_free_ptr(fpst);
- break;
- }
- case NEON_2RM_VRECPE:
- gen_helper_recpe_u32(tmp, tmp);
- break;
- case NEON_2RM_VRSQRTE:
- gen_helper_rsqrte_u32(tmp, tmp);
- break;
- case NEON_2RM_VRECPE_F:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- gen_helper_recpe_f32(tmp, tmp, fpstatus);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VRSQRTE_F:
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- gen_helper_rsqrte_f32(tmp, tmp, fpstatus);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCVT_FS: /* VCVT.F32.S32 */
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- gen_helper_vfp_sitos(tmp, tmp, fpstatus);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCVT_FU: /* VCVT.F32.U32 */
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- gen_helper_vfp_uitos(tmp, tmp, fpstatus);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCVT_SF: /* VCVT.S32.F32 */
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- gen_helper_vfp_tosizs(tmp, tmp, fpstatus);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- case NEON_2RM_VCVT_UF: /* VCVT.U32.F32 */
- {
- TCGv_ptr fpstatus = get_fpstatus_ptr(1);
- gen_helper_vfp_touizs(tmp, tmp, fpstatus);
- tcg_temp_free_ptr(fpstatus);
- break;
- }
- default:
- /* Reserved op values were caught by the
- * neon_2rm_sizes[] check earlier.
- */
- abort();
- }
- neon_store_reg(rd, pass, tmp);
- }
- break;
- }
- } else {
- /* VTBL, VTBX, VDUP: handled by decodetree */
- return 1;
- }
- }
- }
- return 0;
-}
-
static int disas_coproc_insn(DisasContext *s, uint32_t insn)
{
int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2;
@@ -8417,7 +7378,7 @@
t1 = load_reg(s, a->rn);
t2 = load_reg(s, a->rm);
if (m_swap) {
- gen_swap_half(t2);
+ gen_swap_half(t2, t2);
}
gen_smul_dual(t1, t2);
@@ -8475,7 +7436,7 @@
t1 = load_reg(s, a->rn);
t2 = load_reg(s, a->rm);
if (m_swap) {
- gen_swap_half(t2);
+ gen_swap_half(t2, t2);
}
gen_smul_dual(t1, t2);
@@ -8824,9 +7785,6 @@
gen_io_start();
}
gen_helper_cpsr_write_eret(cpu_env, tmp);
- if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) {
- gen_io_end();
- }
tcg_temp_free_i32(tmp);
/* Must exit loop to check un-masked IRQs */
s->base.is_jmp = DISAS_EXIT;
@@ -9283,13 +8241,6 @@
}
/* fall back to legacy decoder */
- if (((insn >> 25) & 7) == 1) {
- /* NEON Data processing. */
- if (disas_neon_data_insn(s, insn)) {
- goto illegal_op;
- }
- return;
- }
if ((insn & 0x0e000f00) == 0x0c000100) {
if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) {
/* iWMMXt register transfer. */
@@ -9477,11 +8428,8 @@
break;
}
if (((insn >> 24) & 3) == 3) {
- /* Translate into the equivalent ARM encoding. */
- insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28);
- if (disas_neon_data_insn(s, insn)) {
- goto illegal_op;
- }
+ /* Neon DP, but failed disas_neon_dp() */
+ goto illegal_op;
} else if (((insn >> 8) & 0xe) == 10) {
/* VFP, but failed disas_vfp. */
goto illegal_op;
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 62ed5c4..19650a9 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -363,6 +363,7 @@
uint32_t, uint32_t, uint32_t);
/* Function prototype for gen_ functions for calling Neon helpers */
+typedef void NeonGenOneOpFn(TCGv_i32, TCGv_i32);
typedef void NeonGenOneOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32);
typedef void NeonGenTwoOpFn(TCGv_i32, TCGv_i32, TCGv_i32);
typedef void NeonGenTwoOpEnvFn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32);
@@ -372,9 +373,10 @@
typedef void NeonGenNarrowEnvFn(TCGv_i32, TCGv_ptr, TCGv_i64);
typedef void NeonGenWidenFn(TCGv_i64, TCGv_i32);
typedef void NeonGenTwoOpWidenFn(TCGv_i64, TCGv_i32, TCGv_i32);
-typedef void NeonGenTwoSingleOPFn(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr);
-typedef void NeonGenTwoDoubleOPFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr);
-typedef void NeonGenOneOpFn(TCGv_i64, TCGv_i64);
+typedef void NeonGenOneSingleOpFn(TCGv_i32, TCGv_i32, TCGv_ptr);
+typedef void NeonGenTwoSingleOpFn(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_ptr);
+typedef void NeonGenTwoDoubleOpFn(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_ptr);
+typedef void NeonGenOne64OpFn(TCGv_i64, TCGv_i64);
typedef void CryptoTwoOpFn(TCGv_ptr, TCGv_ptr);
typedef void CryptoThreeOpIntFn(TCGv_ptr, TCGv_ptr, TCGv_i32);
typedef void CryptoThreeOpFn(TCGv_ptr, TCGv_ptr, TCGv_ptr);
diff --git a/tests/acceptance/machine_rx_gdbsim.py b/tests/acceptance/machine_rx_gdbsim.py
new file mode 100644
index 0000000..a44f2c8
--- /dev/null
+++ b/tests/acceptance/machine_rx_gdbsim.py
@@ -0,0 +1,68 @@
+# Functional test that boots a Linux kernel and checks the console
+#
+# Copyright (c) 2018 Red Hat, Inc.
+#
+# Author:
+# Cleber Rosa <crosa@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2 or
+# later. See the COPYING file in the top-level directory.
+
+from avocado_qemu import Test
+from avocado_qemu import exec_command_and_wait_for_pattern
+from avocado_qemu import wait_for_console_pattern
+from avocado.utils import archive
+
+
+class RxGdbSimMachine(Test):
+
+ timeout = 30
+ KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 '
+
+ def test_uboot(self):
+ """
+ U-Boot and checks that the console is operational.
+
+ :avocado: tags=arch:rx
+ :avocado: tags=machine:gdbsim-r5f562n8
+ :avocado: tags=endian:little
+ """
+ uboot_url = ('https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz')
+ uboot_hash = '9b78dbd43b40b2526848c0b1ce9de02c24f4dcdb'
+ uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
+ uboot_path = archive.uncompress(uboot_path, self.workdir)
+
+ self.vm.set_console()
+ self.vm.add_args('-bios', uboot_path,
+ '-no-reboot')
+ self.vm.launch()
+ uboot_version = 'U-Boot 2016.05-rc3-23705-ga1ef3c71cb-dirty'
+ wait_for_console_pattern(self, uboot_version)
+ gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)'
+ # FIXME limit baudrate on chardev, else we type too fast
+ #exec_command_and_wait_for_pattern(self, 'version', gcc_version)
+
+ def test_linux_sash(self):
+ """
+ Boots a Linux kernel and checks that the console is operational.
+
+ :avocado: tags=arch:rx
+ :avocado: tags=machine:gdbsim-r5f562n7
+ :avocado: tags=endian:little
+ """
+ dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-qemu.dtb')
+ dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18'
+ dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash)
+ kernel_url = ('http://acc.dl.osdn.jp/users/23/23845/zImage')
+ kernel_hash = '39a81067f8d72faad90866ddfefa19165d68fc99'
+ kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon'
+ self.vm.add_args('-kernel', kernel_path,
+ '-dtb', dtb_path,
+ '-no-reboot')
+ self.vm.launch()
+ wait_for_console_pattern(self, 'Sash command shell (version 1.1.1)',
+ failure_message='Kernel panic - not syncing')
+ exec_command_and_wait_for_pattern(self, 'printenv', 'TERM=linux')
diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c
index 4692173..f7e062c 100644
--- a/tests/qtest/arm-cpu-features.c
+++ b/tests/qtest/arm-cpu-features.c
@@ -159,16 +159,35 @@
qobject_unref(_resp); \
})
-#define assert_feature(qts, cpu_type, feature, expected_value) \
+#define resp_assert_feature(resp, feature, expected_value) \
({ \
- QDict *_resp, *_props; \
+ QDict *_props; \
\
- _resp = do_query_no_props(qts, cpu_type); \
g_assert(_resp); \
g_assert(resp_has_props(_resp)); \
_props = resp_get_props(_resp); \
g_assert(qdict_get(_props, feature)); \
g_assert(qdict_get_bool(_props, feature) == (expected_value)); \
+})
+
+#define assert_feature(qts, cpu_type, feature, expected_value) \
+({ \
+ QDict *_resp; \
+ \
+ _resp = do_query_no_props(qts, cpu_type); \
+ g_assert(_resp); \
+ resp_assert_feature(_resp, feature, expected_value); \
+ qobject_unref(_resp); \
+})
+
+#define assert_set_feature(qts, cpu_type, feature, value) \
+({ \
+ const char *_fmt = (value) ? "{ %s: true }" : "{ %s: false }"; \
+ QDict *_resp; \
+ \
+ _resp = do_query(qts, cpu_type, _fmt, feature); \
+ g_assert(_resp); \
+ resp_assert_feature(_resp, feature, value); \
qobject_unref(_resp); \
})
@@ -424,10 +443,14 @@
assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL);
/* Test expected feature presence/absence for some cpu types */
- assert_has_feature_enabled(qts, "max", "pmu");
assert_has_feature_enabled(qts, "cortex-a15", "pmu");
assert_has_not_feature(qts, "cortex-a15", "aarch64");
+ /* Enabling and disabling pmu should always work. */
+ assert_has_feature_enabled(qts, "max", "pmu");
+ assert_set_feature(qts, "max", "pmu", false);
+ assert_set_feature(qts, "max", "pmu", true);
+
assert_has_not_feature(qts, "max", "kvm-no-adjvtime");
if (g_str_equal(qtest_get_arch(), "aarch64")) {
@@ -464,7 +487,10 @@
return;
}
+ /* Enabling and disabling kvm-no-adjvtime should always work. */
assert_has_feature_disabled(qts, "host", "kvm-no-adjvtime");
+ assert_set_feature(qts, "host", "kvm-no-adjvtime", true);
+ assert_set_feature(qts, "host", "kvm-no-adjvtime", false);
if (g_str_equal(qtest_get_arch(), "aarch64")) {
bool kvm_supports_sve;
@@ -475,7 +501,11 @@
char *error;
assert_has_feature_enabled(qts, "host", "aarch64");
+
+ /* Enabling and disabling pmu should always work. */
assert_has_feature_enabled(qts, "host", "pmu");
+ assert_set_feature(qts, "host", "pmu", false);
+ assert_set_feature(qts, "host", "pmu", true);
assert_error(qts, "cortex-a15",
"We cannot guarantee the CPU type 'cortex-a15' works "
diff --git a/tests/qtest/tpm-emu.c b/tests/qtest/tpm-emu.c
index 298d0ee..2e8eb7b 100644
--- a/tests/qtest/tpm-emu.c
+++ b/tests/qtest/tpm-emu.c
@@ -13,7 +13,7 @@
#include "qemu/osdep.h"
#include <glib/gstdio.h>
-#include "hw/tpm/tpm_ioctl.h"
+#include "backends/tpm/tpm_ioctl.h"
#include "io/channel-socket.h"
#include "qapi/error.h"
#include "tpm-emu.h"
diff --git a/util/oslib-posix.c b/util/oslib-posix.c
index 916f1be..39ddc77 100644
--- a/util/oslib-posix.c
+++ b/util/oslib-posix.c
@@ -57,6 +57,10 @@
#include <lwp.h>
#endif
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+
#include "qemu/mmap-alloc.h"
#ifdef CONFIG_DEBUG_STACK_USAGE
@@ -375,6 +379,17 @@
p = buf;
}
}
+#elif defined(__APPLE__)
+ {
+ char fpath[PATH_MAX];
+ uint32_t len = sizeof(fpath);
+ if (_NSGetExecutablePath(fpath, &len) == 0) {
+ p = realpath(fpath, buf);
+ if (!p) {
+ return;
+ }
+ }
+ }
#endif
/* If we don't have any way of figuring out the actual executable
location then try argv[0]. */