artifacts: true
variables:
IMAGE: alpine
- MAKE_CHECK_ARGS: check
+ MAKE_CHECK_ARGS: check-unit check-qtest
avocado-system-alpine:
extends: .avocado_test_job_template
NAME: freebsd-12
CIRRUS_VM_INSTANCE_TYPE: freebsd_instance
CIRRUS_VM_IMAGE_SELECTOR: image_family
- CIRRUS_VM_IMAGE_NAME: freebsd-12-2
+ CIRRUS_VM_IMAGE_NAME: freebsd-12-3
CIRRUS_VM_CPUS: 8
CIRRUS_VM_RAM: 8G
UPDATE_COMMAND: pkg update
INSTALL_COMMAND: pkg install -y
- # TODO: Enable gnutls again once FreeBSD's libtasn1 got fixed
- # See: https://gitlab.com/gnutls/libtasn1/-/merge_requests/71
- CONFIGURE_ARGS: --disable-gnutls
TEST_TARGETS: check
x64-freebsd-13-build:
#
# $ lcitool variables freebsd-12 qemu
#
-# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
+# https://gitlab.com/libvirt/libvirt-ci
-PACKAGING_COMMAND='pkg'
CCACHE='/usr/local/bin/ccache'
+CPAN_PKGS=''
+CROSS_PKGS=''
MAKE='/usr/local/bin/gmake'
NINJA='/usr/local/bin/ninja'
-PYTHON='/usr/local/bin/python3'
+PACKAGING_COMMAND='pkg'
PIP3='/usr/local/bin/pip-3.8'
-PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
+PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
+PYPI_PKGS=''
+PYTHON='/usr/local/bin/python3'
#
# $ lcitool variables freebsd-13 qemu
#
-# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
+# https://gitlab.com/libvirt/libvirt-ci
-PACKAGING_COMMAND='pkg'
CCACHE='/usr/local/bin/ccache'
+CPAN_PKGS=''
+CROSS_PKGS=''
MAKE='/usr/local/bin/gmake'
NINJA='/usr/local/bin/ninja'
-PYTHON='/usr/local/bin/python3'
+PACKAGING_COMMAND='pkg'
PIP3='/usr/local/bin/pip-3.8'
-PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
+PKGS='alsa-lib bash bzip2 ca_root_nss capstone4 ccache cdrkit-genisoimage ctags curl cyrus-sasl dbus diffutils dtc gettext git glib gmake gnutls gsed gtk3 libepoxy libffi libgcrypt libjpeg-turbo libnfs libspice-server libssh libtasn1 libxml2 llvm lttng-ust lzo2 meson ncurses nettle ninja opencv p5-Test-Harness perl5 pixman pkgconf png py38-numpy py38-pillow py38-pip py38-sphinx py38-sphinx_rtd_theme py38-virtualenv py38-yaml python3 rpm2cpio sdl2 sdl2_image snappy spice-protocol tesseract texinfo usbredir virglrenderer vte3 zstd'
+PYPI_PKGS=''
+PYTHON='/usr/local/bin/python3'
#
# $ lcitool variables macos-11 qemu
#
-# https://gitlab.com/libvirt/libvirt-ci/-/commit/c7e275ab27ac0dcd09da290817b9adeea1fd1eb1
+# https://gitlab.com/libvirt/libvirt-ci
-PACKAGING_COMMAND='brew'
CCACHE='/usr/local/bin/ccache'
+CPAN_PKGS='Test::Harness'
+CROSS_PKGS=''
MAKE='/usr/local/bin/gmake'
NINJA='/usr/local/bin/ninja'
-PYTHON='/usr/local/bin/python3'
+PACKAGING_COMMAND='brew'
PIP3='/usr/local/bin/pip3'
-PKGS='bash bc bzip2 capstone ccache cpanminus ctags curl dbus diffutils gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb libxml2 llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd'
+PKGS='bash bc bzip2 capstone ccache cpanminus ctags curl dbus diffutils dtc gcovr gettext git glib gnu-sed gnutls gtk+3 jemalloc jpeg-turbo libepoxy libffi libgcrypt libiscsi libnfs libpng libslirp libssh libtasn1 libusb libxml2 llvm lzo make meson ncurses nettle ninja perl pixman pkg-config python3 rpm2cpio sdl2 sdl2_image snappy sparse spice-protocol tesseract texinfo usbredir vde vte3 zlib zstd'
PYPI_PKGS='PyYAML numpy pillow sphinx sphinx-rtd-theme virtualenv'
-CPAN_PKGS='Test::Harness'
+PYTHON='/usr/local/bin/python3'
variables:
NAME: ubuntu2004
-amd64-ubuntu-container:
- extends: .container_job_template
- variables:
- NAME: ubuntu
-
amd64-opensuse-leap-container:
extends: .container_job_template
variables:
artifacts:
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
- - pc-bios/opensbi-riscv32-generic-fw_dynamic.elf
- pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
- - pc-bios/opensbi-riscv64-generic-fw_dynamic.elf
- opensbi32-generic-stdout.log
- opensbi32-generic-stderr.log
- opensbi64-generic-stdout.log
[submodule "roms/vbootrom"]
path = roms/vbootrom
url = https://gitlab.com/qemu-project/vbootrom.git
+[submodule "tests/lcitool/libvirt-ci"]
+ path = tests/lcitool/libvirt-ci
+ url = http://gitlab.com/libvirt/libvirt-ci
S: Maintained
F: target/s390x/
F: target/s390x/tcg
-F: target/s390x/cpu_models_*.[ch]
F: hw/s390x/
F: disas/s390.c
F: tests/tcg/s390x/
M: Christian Borntraeger <borntraeger@linux.ibm.com>
S: Supported
F: target/s390x/kvm/
-F: target/s390x/ioinst.[ch]
F: target/s390x/machine.c
F: target/s390x/sigp.c
-F: target/s390x/cpu_features*.[ch]
-F: target/s390x/cpu_models.[ch]
F: hw/s390x/pv.c
F: include/hw/s390x/pv.h
-F: hw/intc/s390_flic.c
-F: hw/intc/s390_flic_kvm.c
-F: include/hw/s390x/s390_flic.h
F: gdb-xml/s390*.xml
T: git https://github.com/borntraeger/qemu.git s390-next
L: qemu-s390x@nongnu.org
L: qemu-arm@nongnu.org
S: Odd Fixes
F: hw/arm/musicpal.c
+F: hw/net/mv88w8618_eth.c
+F: include/hw/net/mv88w8618_eth.h
F: docs/system/arm/musicpal.rst
Nuvoton NPCM7xx
Raspberry Pi
M: Peter Maydell <peter.maydell@linaro.org>
-R: Andrew Baumann <Andrew.Baumann@microsoft.com>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
L: qemu-arm@nongnu.org
S: Odd Fixes
F: include/hw/display/dpcd.h
F: docs/system/arm/xlnx-versal-virt.rst
+Xilinx Versal OSPI
+M: Francisco Iglesias <francisco.iglesias@xilinx.com>
+S: Maintained
+F: hw/ssi/xlnx-versal-ospi.c
+F: include/hw/ssi/xlnx-versal-ospi.h
+
ARM ACPI Subsystem
M: Shannon Zhao <shannon.zhaosl@gmail.com>
L: qemu-arm@nongnu.org
PowerPC Machines
----------------
-405
+405 (ref405ep and taihu)
L: qemu-ppc@nongnu.org
S: Orphan
F: hw/ppc/ppc405_boards.c
M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
L: qemu-ppc@nongnu.org
S: Odd Fixes
+F: docs/system/ppc/powermac.rst
F: hw/ppc/mac_newworld.c
F: hw/pci-host/uninorth.c
F: hw/pci-bridge/dec.[hc]
M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
L: qemu-ppc@nongnu.org
S: Odd Fixes
+F: docs/system/ppc/powermac.rst
F: hw/ppc/mac_oldworld.c
F: hw/pci-host/grackle.c
F: hw/misc/macio/
M: Hervé Poussineau <hpoussin@reactos.org>
L: qemu-ppc@nongnu.org
S: Maintained
+F: docs/system/ppc/prep.rst
F: hw/ppc/prep.c
F: hw/ppc/prep_systemio.c
F: hw/ppc/rs6000_mc.c
F: include/hw/rtc/m48t59.h
F: tests/avocado/ppc_prep_40p.py
-sPAPR
+sPAPR (pseries)
M: Cédric Le Goater <clg@kaod.org>
M: Daniel Henrique Barboza <danielhb413@gmail.com>
R: David Gibson <david@gibson.dropbear.id.au>
F: hw/*/xics*
F: include/hw/*/xics*
F: pc-bios/slof.bin
-F: docs/specs/ppc-spapr-hcalls.txt
-F: docs/specs/ppc-spapr-hotplug.txt
+F: docs/system/ppc/pseries.rst
+F: docs/specs/ppc-spapr-*
F: tests/qtest/spapr*
F: tests/qtest/libqos/*spapr*
F: tests/qtest/rtas*
M: Cédric Le Goater <clg@kaod.org>
L: qemu-ppc@nongnu.org
S: Maintained
+F: docs/system/ppc/powernv.rst
F: hw/ppc/pnv*
F: hw/intc/pnv*
F: hw/intc/xics_pnv.c
S390 Virtio-ccw
M: Halil Pasic <pasic@linux.ibm.com>
M: Christian Borntraeger <borntraeger@linux.ibm.com>
+M: Eric Farman <farman@linux.ibm.com>
S: Supported
-F: hw/char/sclp*.[hc]
-F: hw/char/terminal3270.c
F: hw/s390x/
F: include/hw/s390x/
-F: hw/watchdog/wdt_diag288.c
-F: include/hw/watchdog/wdt_diag288.h
F: configs/devices/s390x-softmmu/default.mak
F: tests/avocado/machine_s390_ccw_virtio.py
T: git https://github.com/borntraeger/qemu.git s390-next
F: include/hw/s390x/s390-pci*
L: qemu-s390x@nongnu.org
+S390 channel subsystem
+M: Halil Pasic <pasic@linux.ibm.com>
+M: Christian Borntraeger <borntraeger@linux.ibm.com>
+M: Eric Farman <farman@linux.ibm.com>
+S: Supported
+F: hw/s390x/ccw-device.[ch]
+F: hw/s390x/css.c
+F: hw/s390x/css-bridge.c
+F: include/hw/s390x/css.h
+F: include/hw/s390x/css-bridge.h
+F: include/hw/s390x/ioinst.h
+F: target/s390x/ioinst.c
+L: qemu-s390x@nongnu.org
+
+S390 CPU models
+M: David Hildenbrand <david@redhat.com>
+S: Maintained
+F: target/s390x/cpu_features*.[ch]
+F: target/s390x/cpu_models.[ch]
+L: qemu-s390x@nongnu.org
+
+S390 SCLP-backed devices
+M: Halil Pasic <pasic@linux.ibm.com>
+M: Christian Borntraeger <borntraeger@linux.ibm.com>
+S: Supported
+F: include/hw/s390x/event-facility.h
+F: include/hw/s390x/sclp.h
+F: hw/char/sclp*.[hc]
+F: hw/s390x/event-facility.c
+F: hw/s390x/sclp*.c
+L: qemu-s390x@nongnu.org
+
X86 Machines
------------
PC
virtio-ccw
M: Cornelia Huck <cohuck@redhat.com>
M: Halil Pasic <pasic@linux.ibm.com>
+M: Eric Farman <farman@linux.ibm.com>
S: Supported
F: hw/s390x/virtio-ccw*.[hc]
F: hw/s390x/vhost-vsock-ccw.c
+F: hw/s390x/vhost-user-fs-ccw.c
T: git https://gitlab.com/cohuck/qemu.git s390-next
T: git https://github.com/borntraeger/qemu.git s390-next
L: qemu-s390x@nongnu.org
F: include/hw/intc/mips_gic.h
F: include/hw/timer/mips_gictimer.h
+S390 3270 device
+M: Halil Pasic <pasic@linux.ibm.com>
+M: Christian Borntraeger <borntraeger@linux.ibm.com>
+S: Odd fixes
+F: include/hw/s390x/3270-ccw.h
+F: hw/char/terminal3270.c
+F: hw/s390x/3270-ccw.c
+L: qemu-s390x@nongnu.org
+
+S390 diag 288 watchdog
+M: Halil Pasic <pasic@linux.ibm.com>
+M: Christian Borntraeger <borntraeger@linux.ibm.com>
+S: Supported
+F: hw/watchdog/wdt_diag288.c
+F: include/hw/watchdog/wdt_diag288.h
+L: qemu-s390x@nongnu.org
+
+S390 storage key device
+M: Halil Pasic <pasic@linux.ibm.com>
+M: Christian Borntraeger <borntraeger@linux.ibm.com>
+S: Supported
+F: hw/s390x/storage-keys.h
+F: hw/390x/s390-skeys*.c
+L: qemu-s390x@nongnu.org
+
+S390 storage attribute device
+M: Halil Pasic <pasic@linux.ibm.com>
+M: Christian Borntraeger <borntraeger@linux.ibm.com>
+S: Supported
+F: hw/s390x/storage-attributes.h
+F: hw/s390/s390-stattrib*.c
+L: qemu-s390x@nongnu.org
+
+S390 floating interrupt controller
+M: Halil Pasic <pasic@linux.ibm.com>
+M: Christian Borntraeger <borntraeger@linux.ibm.com>
+M: David Hildenbrand <david@redhat.com>
+S: Supported
+F: hw/intc/s390_flic*.c
+F: include/hw/s390x/s390_flic.h
+L: qemu-s390x@nongnu.org
+
Subsystems
----------
Overall Audio backends
F: tests/unit/test-qga.c
T: git https://github.com/mdroth/qemu.git qga
+QEMU Guest Agent Win32
+M: Konstantin Kostiuk <kkostiuk@redhat.com>
+S: Maintained
+F: qga/*win32*
+F: qga/vss-win32/
+F: qga/installer/
+T: git https://github.com/kostyanf14/qemu.git qga-win32
+
QOM
M: Paolo Bonzini <pbonzini@redhat.com>
R: Daniel P. Berrange <berrange@redhat.com>
S: Maintained
F: bsd-user/
F: configs/targets/*-bsd-user.mak
+F: tests/vm/*bsd
T: git https://github.com/qemu-bsd-user/qemu-bsd-user bsd-user-rebase-3.1
Linux user
clean: recurse-clean
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean || :
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) clean-ctlist || :
-# avoid old build problems by removing potentially incorrect old files
- rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
find . \( -name '*.so' -o -name '*.dll' -o -name '*.[oda]' \) -type f \
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \
! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \
-exec rm {} +
- rm -f TAGS cscope.* *.pod *~ */*~
- rm -f fsdev/*.pod scsi/*.pod
+ rm -f TAGS cscope.* *~ */*~
VERSION = $(shell cat $(SRC_PATH)/VERSION)
distclean: clean
-$(quiet-@)test -f build.ninja && $(NINJA) $(NINJAFLAGS) -t clean -g || :
- rm -f config-host.mak config-host.h* config-poison.h
+ rm -f config-host.mak
rm -f tests/tcg/config-*.mak
- rm -f config-all-disas.mak config.status
- rm -f roms/seabios/config.mak roms/vgabios/config.mak
+ rm -f config.status
+ rm -f roms/seabios/config.mak
rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
rm -f *-config-target.h *-config-devices.mak *-config-devices.h
rm -rf meson-private meson-logs meson-info compile_commands.json
# Needed by "meson install"
export DESTDIR
+include $(SRC_PATH)/tests/lcitool/Makefile.include
include $(SRC_PATH)/tests/docker/Makefile.include
include $(SRC_PATH)/tests/vm/Makefile.include
@echo 'Test targets:'
$(call print-help,check,Run all tests (check-help for details))
$(call print-help,bench,Run all benchmarks)
+ $(call print-help,lcitool-help,Help about targets for managing build environment manifests)
$(call print-help,docker-help,Help about targets running tests inside containers)
$(call print-help,vm-help,Help about targets running tests inside VM)
@echo ''
return human_readable_text_from_str(buf);
}
+#ifdef CONFIG_PROFILER
+
+int64_t dev_time;
+
+HumanReadableText *qmp_x_query_profile(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+ static int64_t last_cpu_exec_time;
+ int64_t cpu_exec_time;
+ int64_t delta;
+
+ cpu_exec_time = tcg_cpu_exec_time();
+ delta = cpu_exec_time - last_cpu_exec_time;
+
+ g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n",
+ dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
+ g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n",
+ delta, delta / (double)NANOSECONDS_PER_SECOND);
+ last_cpu_exec_time = cpu_exec_time;
+ dev_time = 0;
+
+ return human_readable_text_from_str(buf);
+}
+#else
+HumanReadableText *qmp_x_query_profile(Error **errp)
+{
+ error_setg(errp, "Internal profiler not compiled");
+ return NULL;
+}
+#endif
+
#endif /* !CONFIG_USER_ONLY */
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
HRESULT hr;
- DWORD cpos, rpos, act_size;
+ DWORD rpos, act_size;
size_t req_size;
int err;
void *ret;
- hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
- dscb, &cpos, ds->first_time ? &rpos : NULL);
+ hr = IDirectSoundCaptureBuffer_GetCurrentPosition(dscb, NULL, &rpos);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get capture buffer position\n");
*size = 0;
ds->first_time = false;
}
- req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
+ req_size = audio_ring_dist(rpos, hw->pos_emul, hw->size_emul);
req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
if (req_size == 0) {
ji->c.enabled = enable;
}
+#if !defined(WIN32) && defined(CONFIG_PTHREAD_SETNAME_NP_W_TID)
static int qjack_thread_creator(jack_native_thread_t *thread,
const pthread_attr_t *attr, void *(*function)(void *), void *arg)
{
return ret;
}
+#endif
static void *qjack_init(Audiodev *dev)
{
{
qemu_mutex_init(&qjack_shutdown_lock);
audio_driver_register(&jack_driver);
+#if !defined(WIN32) && defined(CONFIG_PTHREAD_SETNAME_NP_W_TID)
jack_set_thread_creator(qjack_thread_creator);
+#endif
jack_set_error_function(qjack_error);
jack_set_info_function(qjack_info);
}
static void bdrv_reopen_commit(BDRVReopenState *reopen_state);
static void bdrv_reopen_abort(BDRVReopenState *reopen_state);
+static bool bdrv_backing_overridden(BlockDriverState *bs);
+
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
{ BLK_PERM_WRITE, "write" },
{ BLK_PERM_WRITE_UNCHANGED, "write unchanged" },
{ BLK_PERM_RESIZE, "resize" },
- { BLK_PERM_GRAPH_MOD, "change children" },
{ 0, NULL }
};
shared = 0;
}
- shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_GRAPH_MOD |
- BLK_PERM_WRITE_UNCHANGED;
+ shared |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
if (bs->open_flags & BDRV_O_INACTIVE) {
shared |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
[BLOCK_PERMISSION_WRITE] = BLK_PERM_WRITE,
[BLOCK_PERMISSION_WRITE_UNCHANGED] = BLK_PERM_WRITE_UNCHANGED,
[BLOCK_PERMISSION_RESIZE] = BLK_PERM_RESIZE,
- [BLOCK_PERMISSION_GRAPH_MOD] = BLK_PERM_GRAPH_MOD,
};
QEMU_BUILD_BUG_ON(ARRAY_SIZE(permissions) != BLOCK_PERMISSION__MAX);
int ret;
Transaction *tran = tran_new();
+ bdrv_drained_begin(bs);
+
ret = bdrv_set_backing_noperm(bs, backing_hd, tran, errp);
if (ret < 0) {
goto out;
out:
tran_finalize(tran, ret);
+ bdrv_drained_end(bs);
+
return ret;
}
update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top);
/* success - we can delete the intermediate states, and link top->base */
- /* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
- * we've figured out how they should work. */
if (!backing_file_str) {
bdrv_refresh_filename(base);
backing_file_str = base->filename;
/* Note: This function may return false positives; it may return true
* even if opening the backing file specified by bs's image header
* would result in exactly bs->backing. */
-bool bdrv_backing_overridden(BlockDriverState *bs)
+static bool bdrv_backing_overridden(BlockDriverState *bs)
{
if (bs->backing) {
return strcmp(bs->auto_backing_file,
{
BlockBackend *blk = child->opaque;
Error *local_err = NULL;
+ uint64_t saved_shared_perm;
if (!blk->disable_perm) {
return;
blk->disable_perm = false;
+ /*
+ * blk->shared_perm contains the permissions we want to share once
+ * migration is really completely done. For now, we need to share
+ * all; but we also need to retain blk->shared_perm, which is
+ * overwritten by a successful blk_set_perm() call. Save it and
+ * restore it below.
+ */
+ saved_shared_perm = blk->shared_perm;
+
blk_set_perm(blk, blk->perm, BLK_PERM_ALL, &local_err);
if (local_err) {
error_propagate(errp, local_err);
blk->disable_perm = true;
return;
}
+ blk->shared_perm = saved_shared_perm;
if (runstate_check(RUN_STATE_INMIGRATE)) {
/* Activation can happen when migration process is still active, for
void blk_remove_bs(BlockBackend *blk)
{
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
- BlockDriverState *bs;
BdrvChild *root;
notifier_list_notify(&blk->remove_bs_notifiers, blk);
if (tgm->throttle_state) {
- bs = blk_bs(blk);
+ BlockDriverState *bs = blk_bs(blk);
+
+ /*
+ * Take a ref in case blk_bs() changes across bdrv_drained_begin(), for
+ * example, if a temporary filter node is removed by a blockjob.
+ */
+ bdrv_ref(bs);
bdrv_drained_begin(bs);
throttle_group_detach_aio_context(tgm);
throttle_group_attach_aio_context(tgm, qemu_get_aio_context());
bdrv_drained_end(bs);
+ bdrv_unref(bs);
}
blk_update_root_state(blk);
BlockDriverState *bs = blk_bs(blk);
if (bs) {
+ bdrv_ref(bs);
bdrv_drained_begin(bs);
}
if (bs) {
bdrv_drained_end(bs);
+ bdrv_unref(bs);
}
}
int ret;
if (bs) {
+ bdrv_ref(bs);
+
if (update_root_node) {
ret = bdrv_child_try_set_aio_context(bs, new_context, blk->root,
errp);
if (ret < 0) {
+ bdrv_unref(bs);
return ret;
}
}
throttle_group_attach_aio_context(tgm, new_context);
bdrv_drained_end(bs);
}
+
+ bdrv_unref(bs);
}
blk->ctx = new_context;
ThrottleGroupMember *tgm = &blk->public.throttle_group_member;
assert(tgm->throttle_state);
if (bs) {
+ bdrv_ref(bs);
bdrv_drained_begin(bs);
}
throttle_group_unregister_tgm(tgm);
if (bs) {
bdrv_drained_end(bs);
+ bdrv_unref(bs);
}
}
s->base = blk_new(s->common.job.aio_context,
base_perms,
BLK_PERM_CONSISTENT_READ
- | BLK_PERM_GRAPH_MOD
| BLK_PERM_WRITE_UNCHANGED);
ret = blk_insert_bs(s->base, base, errp);
if (ret < 0) {
BDRVCURLState *s = socket->s;
aio_set_fd_handler(s->aio_context, socket->fd, false,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
return true;
}
switch (action) {
case CURL_POLL_IN:
aio_set_fd_handler(s->aio_context, fd, false,
- curl_multi_do, NULL, NULL, socket);
+ curl_multi_do, NULL, NULL, NULL, socket);
break;
case CURL_POLL_OUT:
aio_set_fd_handler(s->aio_context, fd, false,
- NULL, curl_multi_do, NULL, socket);
+ NULL, curl_multi_do, NULL, NULL, socket);
break;
case CURL_POLL_INOUT:
aio_set_fd_handler(s->aio_context, fd, false,
- curl_multi_do, curl_multi_do, NULL, socket);
+ curl_multi_do, curl_multi_do,
+ NULL, NULL, socket);
break;
case CURL_POLL_REMOVE:
aio_set_fd_handler(s->aio_context, fd, false,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
break;
}
aio_set_fd_handler(exp->common.ctx,
fuse_session_fd(exp->fuse_session), true,
- read_from_fuse_export, NULL, NULL, exp);
+ read_from_fuse_export, NULL, NULL, NULL, exp);
exp->fd_handler_set_up = true;
return 0;
if (exp->fd_handler_set_up) {
aio_set_fd_handler(exp->common.ctx,
fuse_session_fd(exp->fuse_session), true,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
exp->fd_handler_set_up = false;
}
}
return;
}
+#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
if (mode & FALLOC_FL_KEEP_SIZE) {
length = MIN(length, blk_len - offset);
}
+#endif /* CONFIG_FALLOCATE_PUNCH_HOLE */
- if (mode & FALLOC_FL_PUNCH_HOLE) {
+ if (!mode) {
+ /* We can only fallocate at the EOF with a truncate */
+ if (offset < blk_len) {
+ fuse_reply_err(req, EOPNOTSUPP);
+ return;
+ }
+
+ if (offset > blk_len) {
+ /* No preallocation needed here */
+ ret = fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF);
+ if (ret < 0) {
+ fuse_reply_err(req, -ret);
+ return;
+ }
+ }
+
+ ret = fuse_do_truncate(exp, offset + length, true,
+ PREALLOC_MODE_FALLOC);
+ }
+#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
+ else if (mode & FALLOC_FL_PUNCH_HOLE) {
if (!(mode & FALLOC_FL_KEEP_SIZE)) {
fuse_reply_err(req, EINVAL);
return;
length -= size;
} while (ret == 0 && length > 0);
}
+#endif /* CONFIG_FALLOCATE_PUNCH_HOLE */
#ifdef CONFIG_FALLOCATE_ZERO_RANGE
else if (mode & FALLOC_FL_ZERO_RANGE) {
if (!(mode & FALLOC_FL_KEEP_SIZE) && offset + length > blk_len) {
} while (ret == 0 && length > 0);
}
#endif /* CONFIG_FALLOCATE_ZERO_RANGE */
- else if (!mode) {
- /* We can only fallocate at the EOF with a truncate */
- if (offset < blk_len) {
- fuse_reply_err(req, EOPNOTSUPP);
- return;
- }
-
- if (offset > blk_len) {
- /* No preallocation needed here */
- ret = fuse_do_truncate(exp, offset, true, PREALLOC_MODE_OFF);
- if (ret < 0) {
- fuse_reply_err(req, -ret);
- return;
- }
- }
-
- ret = fuse_do_truncate(exp, offset + length, true,
- PREALLOC_MODE_FALLOC);
- } else {
+ else {
ret = -EOPNOTSUPP;
}
return VIRTIO_BLK_S_IOERR;
}
+/* Called with server refcount increased, must decrease before returning */
static void coroutine_fn vu_blk_virtio_process_req(void *opaque)
{
VuBlkReq *req = opaque;
}
vu_blk_req_complete(req);
+ vhost_user_server_unref(server);
return;
err:
free(req);
+ vhost_user_server_unref(server);
}
static void vu_blk_process_vq(VuDev *vu_dev, int idx)
Coroutine *co =
qemu_coroutine_create(vu_blk_virtio_process_req, req);
+
+ vhost_user_server_ref(server);
qemu_coroutine_enter(co);
}
}
#include <sys/diskslice.h>
#endif
-#ifdef CONFIG_XFS
-#include <xfs/xfs.h>
-#endif
-
/* OS X does not have O_DSYNC */
#ifndef O_DSYNC
#ifdef O_SYNC
int perm_change_flags;
BDRVReopenState *reopen_state;
-#ifdef CONFIG_XFS
- bool is_xfs:1;
-#endif
bool has_discard:1;
bool has_write_zeroes:1;
bool discard_zeroes:1;
if (probe_logical_blocksize(fd, &bs->bl.request_alignment) < 0) {
bs->bl.request_alignment = 0;
}
-#ifdef CONFIG_XFS
- if (s->is_xfs) {
- struct dioattr da;
- if (xfsctl(NULL, fd, XFS_IOC_DIOINFO, &da) >= 0) {
- bs->bl.request_alignment = da.d_miniosz;
- /* The kernel returns wrong information for d_mem */
- /* s->buf_align = da.d_mem; */
- }
+
+#ifdef __linux__
+ /*
+ * The XFS ioctl definitions are shipped in extra packages that might
+ * not always be available. Since we just need the XFS_IOC_DIOINFO ioctl
+ * here, we simply use our own definition instead:
+ */
+ struct xfs_dioattr {
+ uint32_t d_mem;
+ uint32_t d_miniosz;
+ uint32_t d_maxiosz;
+ } da;
+ if (ioctl(fd, _IOR('X', 30, struct xfs_dioattr), &da) >= 0) {
+ bs->bl.request_alignment = da.d_miniosz;
+ /* The kernel returns wrong information for d_mem */
+ /* s->buf_align = da.d_mem; */
}
#endif
#endif
s->needs_alignment = raw_needs_alignment(bs);
-#ifdef CONFIG_XFS
- if (platform_test_xfs_fd(s->fd)) {
- s->is_xfs = true;
- }
-#endif
-
bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK;
if (S_ISREG(st.st_mode)) {
/* When extending regular files, we get zeros from the OS */
* non-protocol nodes, and then it is never used. However, filling
* the cache requires an RCU update, so double check here to avoid
* such an update if possible.
+ *
+ * Check want_zero, because we only want to update the cache when we
+ * have accurate information about what is zero and what is data.
*/
- if (ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) &&
+ if (want_zero &&
+ ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) &&
QLIST_EMPTY(&bs->children))
{
/*
{
LuringState *s = opaque;
- if (io_uring_cq_ready(&s->ring)) {
- luring_process_completions_and_submit(s);
- return true;
- }
+ return io_uring_cq_ready(&s->ring);
+}
+
+static void qemu_luring_poll_ready(void *opaque)
+{
+ LuringState *s = opaque;
- return false;
+ luring_process_completions_and_submit(s);
}
static void ioq_init(LuringQueue *io_q)
void luring_detach_aio_context(LuringState *s, AioContext *old_context)
{
- aio_set_fd_handler(old_context, s->ring.ring_fd, false, NULL, NULL, NULL,
- s);
+ aio_set_fd_handler(old_context, s->ring.ring_fd, false,
+ NULL, NULL, NULL, NULL, s);
qemu_bh_delete(s->completion_bh);
s->aio_context = NULL;
}
s->aio_context = new_context;
s->completion_bh = aio_bh_new(new_context, qemu_luring_completion_bh, s);
aio_set_fd_handler(s->aio_context, s->ring.ring_fd, false,
- qemu_luring_completion_cb, NULL, qemu_luring_poll_cb, s);
+ qemu_luring_completion_cb, NULL,
+ qemu_luring_poll_cb, qemu_luring_poll_ready, s);
}
LuringState *luring_init(Error **errp)
false,
(ev & POLLIN) ? iscsi_process_read : NULL,
(ev & POLLOUT) ? iscsi_process_write : NULL,
- NULL,
+ NULL, NULL,
iscsilun);
iscsilun->events = ev;
}
IscsiLun *iscsilun = bs->opaque;
aio_set_fd_handler(iscsilun->aio_context, iscsi_get_fd(iscsilun->iscsi),
- false, NULL, NULL, NULL, NULL);
+ false, NULL, NULL, NULL, NULL, NULL);
iscsilun->events = 0;
if (iscsilun->nop_timer) {
LinuxAioState *s = container_of(e, LinuxAioState, e);
struct io_event *events;
- if (!io_getevents_peek(s->ctx, &events)) {
- return false;
- }
+ return io_getevents_peek(s->ctx, &events);
+}
+
+static void qemu_laio_poll_ready(EventNotifier *opaque)
+{
+ EventNotifier *e = opaque;
+ LinuxAioState *s = container_of(e, LinuxAioState, e);
qemu_laio_process_completions_and_submit(s);
- return true;
}
static void ioq_init(LaioQueue *io_q)
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context)
{
- aio_set_event_notifier(old_context, &s->e, false, NULL, NULL);
+ aio_set_event_notifier(old_context, &s->e, false, NULL, NULL, NULL);
qemu_bh_delete(s->completion_bh);
s->aio_context = NULL;
}
s->completion_bh = aio_bh_new(new_context, qemu_laio_completion_bh, s);
aio_set_event_notifier(new_context, &s->e, false,
qemu_laio_completion_cb,
- qemu_laio_poll_cb);
+ qemu_laio_poll_cb,
+ qemu_laio_poll_ready);
}
LinuxAioState *laio_init(Error **errp)
replace_aio_context = bdrv_get_aio_context(s->to_replace);
aio_context_acquire(replace_aio_context);
- /* TODO Translate this into permission system. Current definition of
- * GRAPH_MOD would require to request it for the parents; they might
- * not even be BlockDriverStates, however, so a BdrvChild can't address
- * them. May need redefinition of GRAPH_MOD. */
+ /* TODO Translate this into child freeze system. */
error_setg(&s->replace_blocker,
"block device is in use by block-job-complete");
bdrv_op_block_all(s->to_replace, s->replace_blocker);
s = block_job_create(job_id, driver, NULL, mirror_top_bs,
BLK_PERM_CONSISTENT_READ,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
- BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
+ BLK_PERM_WRITE, speed,
creation_flags, cb, opaque, errp);
if (!s) {
goto fail;
target_perms |= BLK_PERM_RESIZE;
}
- target_shared_perms |= BLK_PERM_CONSISTENT_READ
- | BLK_PERM_WRITE
- | BLK_PERM_GRAPH_MOD;
+ target_shared_perms |= BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE;
} else if (bdrv_chain_contains(bs, bdrv_skip_filters(target))) {
/*
* We may want to allow this in the future, but it would
goto fail;
}
- if (backing_mode != MIRROR_LEAVE_BACKING_CHAIN) {
- target_perms |= BLK_PERM_GRAPH_MOD;
- }
-
s->target = blk_new(s->common.job.aio_context,
target_perms, target_shared_perms);
ret = blk_insert_bs(s->target, target, errp);
return;
}
- opts = drive_def(optstr);
+ opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
if (!opts)
return;
false,
(ev & POLLIN) ? nfs_process_read : NULL,
(ev & POLLOUT) ? nfs_process_write : NULL,
- NULL, client);
+ NULL, NULL, client);
}
client->events = ev;
NFSClient *client = bs->opaque;
aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
- false, NULL, NULL, NULL, NULL);
+ false, NULL, NULL, NULL, NULL, NULL);
client->events = 0;
}
if (client->context) {
qemu_mutex_lock(&client->mutex);
aio_set_fd_handler(client->aio_context, nfs_get_fd(client->context),
- false, NULL, NULL, NULL, NULL);
+ false, NULL, NULL, NULL, NULL, NULL);
qemu_mutex_unlock(&client->mutex);
if (client->fh) {
nfs_close(client->context, client->fh);
return ret;
}
-static bool nvme_poll_queue(NVMeQueuePair *q)
+static void nvme_poll_queue(NVMeQueuePair *q)
{
- bool progress = false;
-
const size_t cqe_offset = q->cq.head * NVME_CQ_ENTRY_BYTES;
NvmeCqe *cqe = (NvmeCqe *)&q->cq.queue[cqe_offset];
* cannot race with itself.
*/
if ((le16_to_cpu(cqe->status) & 0x1) == q->cq_phase) {
- return false;
+ return;
}
qemu_mutex_lock(&q->lock);
while (nvme_process_completion(q)) {
/* Keep polling */
- progress = true;
}
qemu_mutex_unlock(&q->lock);
-
- return progress;
}
-static bool nvme_poll_queues(BDRVNVMeState *s)
+static void nvme_poll_queues(BDRVNVMeState *s)
{
- bool progress = false;
int i;
for (i = 0; i < s->queue_count; i++) {
- if (nvme_poll_queue(s->queues[i])) {
- progress = true;
- }
+ nvme_poll_queue(s->queues[i]);
}
- return progress;
}
static void nvme_handle_event(EventNotifier *n)
EventNotifier *e = opaque;
BDRVNVMeState *s = container_of(e, BDRVNVMeState,
irq_notifier[MSIX_SHARED_IRQ_IDX]);
+ int i;
- return nvme_poll_queues(s);
+ for (i = 0; i < s->queue_count; i++) {
+ NVMeQueuePair *q = s->queues[i];
+ const size_t cqe_offset = q->cq.head * NVME_CQ_ENTRY_BYTES;
+ NvmeCqe *cqe = (NvmeCqe *)&q->cq.queue[cqe_offset];
+
+ /*
+ * q->lock isn't needed because nvme_process_completion() only runs in
+ * the event loop thread and cannot race with itself.
+ */
+ if ((le16_to_cpu(cqe->status) & 0x1) != q->cq_phase) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void nvme_poll_ready(EventNotifier *e)
+{
+ BDRVNVMeState *s = container_of(e, BDRVNVMeState,
+ irq_notifier[MSIX_SHARED_IRQ_IDX]);
+
+ nvme_poll_queues(s);
}
static int nvme_init(BlockDriverState *bs, const char *device, int namespace,
}
aio_set_event_notifier(bdrv_get_aio_context(bs),
&s->irq_notifier[MSIX_SHARED_IRQ_IDX],
- false, nvme_handle_event, nvme_poll_cb);
+ false, nvme_handle_event, nvme_poll_cb,
+ nvme_poll_ready);
if (!nvme_identify(bs, namespace, errp)) {
ret = -EIO;
g_free(s->queues);
aio_set_event_notifier(bdrv_get_aio_context(bs),
&s->irq_notifier[MSIX_SHARED_IRQ_IDX],
- false, NULL, NULL);
+ false, NULL, NULL, NULL);
event_notifier_cleanup(&s->irq_notifier[MSIX_SHARED_IRQ_IDX]);
qemu_vfio_pci_unmap_bar(s->vfio, 0, s->bar0_wo_map,
0, sizeof(NvmeBar) + NVME_DOORBELL_SIZE);
aio_set_event_notifier(bdrv_get_aio_context(bs),
&s->irq_notifier[MSIX_SHARED_IRQ_IDX],
- false, NULL, NULL);
+ false, NULL, NULL, NULL);
}
static void nvme_attach_aio_context(BlockDriverState *bs,
s->aio_context = new_context;
aio_set_event_notifier(new_context, &s->irq_notifier[MSIX_SHARED_IRQ_IDX],
- false, nvme_handle_event, nvme_poll_cb);
+ false, nvme_handle_event, nvme_poll_cb,
+ nvme_poll_ready);
for (unsigned i = 0; i < s->queue_count; i++) {
NVMeQueuePair *q = s->queues[i];
return bs->drv->bdrv_co_preadv_part(bs, offset, qiov->size, qiov, 0, 0);
}
+static int qcow2_has_compressed_clusters(BlockDriverState *bs)
+{
+ int64_t offset = 0;
+ int64_t bytes = bdrv_getlength(bs);
+
+ if (bytes < 0) {
+ return bytes;
+ }
+
+ while (bytes != 0) {
+ int ret;
+ QCow2SubclusterType type;
+ unsigned int cur_bytes = MIN(INT_MAX, bytes);
+ uint64_t host_offset;
+
+ ret = qcow2_get_host_offset(bs, offset, &cur_bytes, &host_offset,
+ &type);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (type == QCOW2_SUBCLUSTER_COMPRESSED) {
+ return 1;
+ }
+
+ offset += cur_bytes;
+ bytes -= cur_bytes;
+ }
+
+ return 0;
+}
+
/*
* Downgrades an image's version. To achieve this, any incompatible features
* have to be removed.
* the first place; if that happens nonetheless, returning -ENOTSUP is the
* best thing to do anyway */
- if (s->incompatible_features) {
+ if (s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION) {
error_setg(errp, "Cannot downgrade an image with incompatible features "
- "%#" PRIx64 " set", s->incompatible_features);
+ "0x%" PRIx64 " set",
+ s->incompatible_features & ~QCOW2_INCOMPAT_COMPRESSION);
return -ENOTSUP;
}
return ret;
}
+ if (s->incompatible_features & QCOW2_INCOMPAT_COMPRESSION) {
+ ret = qcow2_has_compressed_clusters(bs);
+ if (ret < 0) {
+ error_setg(errp, "Failed to check block status");
+ return -EINVAL;
+ }
+ if (ret) {
+ error_setg(errp, "Cannot downgrade an image with zstd compression "
+ "type and existing compressed clusters");
+ return -ENOTSUP;
+ }
+ /*
+ * No compressed clusters for now, so just chose default zlib
+ * compression.
+ */
+ s->incompatible_features &= ~QCOW2_INCOMPAT_COMPRESSION;
+ s->compression_type = QCOW2_COMPRESSION_TYPE_ZLIB;
+ }
+
+ assert(s->incompatible_features == 0);
+
s->qcow_version = target_version;
ret = qcow2_update_header(bs);
if (ret < 0) {
RBDDiffIterateReq *req = opaque;
assert(req->offs + req->bytes <= offs);
- /*
- * we do not diff against a snapshot so we should never receive a callback
- * for a hole.
- */
- assert(exists);
+
+ /* treat a hole like an unallocated area and bail out */
+ if (!exists) {
+ return 0;
+ }
if (!req->exists && offs > req->offs) {
/*
int status, r;
RBDDiffIterateReq req = { .offs = offset };
uint64_t features, flags;
+ uint64_t head = 0;
assert(offset + bytes <= s->image_size);
return status;
}
- r = rbd_diff_iterate2(s->image, NULL, offset, bytes, true, true,
+#if LIBRBD_VERSION_CODE < LIBRBD_VERSION(1, 17, 0)
+ /*
+ * librbd had a bug until early 2022 that affected all versions of ceph that
+ * supported fast-diff. This bug results in reporting of incorrect offsets
+ * if the offset parameter to rbd_diff_iterate2 is not object aligned.
+ * Work around this bug by rounding down the offset to object boundaries.
+ * This is OK because we call rbd_diff_iterate2 with whole_object = true.
+ * However, this workaround only works for non cloned images with default
+ * striping.
+ *
+ * See: https://tracker.ceph.com/issues/53784
+ */
+
+ /* check if RBD image has non-default striping enabled */
+ if (features & RBD_FEATURE_STRIPINGV2) {
+ return status;
+ }
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ /*
+ * check if RBD image is a clone (= has a parent).
+ *
+ * rbd_get_parent_info is deprecated from Nautilus onwards, but the
+ * replacement rbd_get_parent is not present in Luminous and Mimic.
+ */
+ if (rbd_get_parent_info(s->image, NULL, 0, NULL, 0, NULL, 0) != -ENOENT) {
+ return status;
+ }
+#pragma GCC diagnostic pop
+
+ head = req.offs & (s->object_size - 1);
+ req.offs -= head;
+ bytes += head;
+#endif
+
+ r = rbd_diff_iterate2(s->image, NULL, req.offs, bytes, true, true,
qemu_rbd_diff_iterate_cb, &req);
if (r < 0 && r != QEMU_RBD_EXIT_DIFF_ITERATE2) {
return status;
status = BDRV_BLOCK_ZERO | BDRV_BLOCK_OFFSET_VALID;
}
- *pnum = req.bytes;
+ assert(req.bytes > head);
+ *pnum = req.bytes - head;
return status;
}
AioContext *ctx = bdrv_get_aio_context(bs);
trace_ssh_restart_coroutine(restart->co);
- aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL);
+ aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL, NULL);
aio_co_wake(restart->co);
}
trace_ssh_co_yield(s->sock, rd_handler, wr_handler);
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
- false, rd_handler, wr_handler, NULL, &restart);
+ false, rd_handler, wr_handler, NULL, NULL, &restart);
qemu_coroutine_yield();
trace_ssh_co_yield_back(s->sock);
}
return 0;
}
-static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
+static inline int32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
{
return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster;
}
dirname, cyls, heads, secs));
s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
+ bs->total_sectors = cyls * heads * secs;
if (qemu_opt_get_bool(opts, "rw", false)) {
if (!bdrv_is_read_only(bs)) {
}
}
- bs->total_sectors = cyls * heads * secs;
-
if (init_directories(s, dirname, heads, secs, errp)) {
ret = -EIO;
goto fail;
{
BDRVVVFATState *s = bs->opaque;
int i, ret;
+ int first_cluster, last_cluster;
DLOG(checkpoint());
if (sector_num < s->offset_to_fat)
return -1;
- for (i = sector2cluster(s, sector_num);
- i <= sector2cluster(s, sector_num + nb_sectors - 1);) {
- mapping_t* mapping = find_mapping_for_cluster(s, i);
+ /*
+ * Values will be negative for writes to the FAT, which is located before
+ * the root directory.
+ */
+ first_cluster = sector2cluster(s, sector_num);
+ last_cluster = sector2cluster(s, sector_num + nb_sectors - 1);
+
+ for (i = first_cluster; i <= last_cluster;) {
+ mapping_t *mapping = NULL;
+
+ if (i >= 0) {
+ mapping = find_mapping_for_cluster(s, i);
+ }
+
if (mapping) {
if (mapping->read_only) {
fprintf(stderr, "Tried to write to write-protected file %s\n",
}
}
i = mapping->end;
- } else
+ } else {
i++;
+ }
}
/*
return ret;
}
- for (i = sector2cluster(s, sector_num);
- i <= sector2cluster(s, sector_num + nb_sectors - 1); i++)
- if (i >= 0)
+ for (i = first_cluster; i <= last_cluster; i++) {
+ if (i >= 0) {
s->used_clusters[i] |= USED_ALLOCATED;
+ }
+ }
DLOG(checkpoint());
/* TODO: add timeout */
}
opts = qemu_opts_create(bdrv_qcow->create_opts, NULL, 0, &error_abort);
- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, s->sector_count * 512,
- &error_abort);
+ qemu_opt_set_number(opts, BLOCK_OPT_SIZE,
+ bs->total_sectors * BDRV_SECTOR_SIZE, &error_abort);
qemu_opt_set(opts, BLOCK_OPT_BACKING_FILE, "fat:", &error_abort);
ret = bdrv_create(bdrv_qcow, s->qcow_filename, opts, errp);
void win32_aio_detach_aio_context(QEMUWin32AIOState *aio,
AioContext *old_context)
{
- aio_set_event_notifier(old_context, &aio->e, false, NULL, NULL);
+ aio_set_event_notifier(old_context, &aio->e, false, NULL, NULL, NULL);
aio->aio_ctx = NULL;
}
{
aio->aio_ctx = new_context;
aio_set_event_notifier(new_context, &aio->e, false,
- win32_aio_completion_cb, NULL);
+ win32_aio_completion_cb, NULL, NULL);
}
QEMUWin32AIOState *win32_aio_init(void)
}
}
-/**
- * Returns the current mapping of how many units per bus
- * a particular interface can support.
- *
- * A positive integer indicates n units per bus.
- * 0 implies the mapping has not been established.
- * -1 indicates an invalid BlockInterfaceType was given.
- */
-int drive_get_max_devs(BlockInterfaceType type)
-{
- if (type >= IF_IDE && type < IF_COUNT) {
- return if_max_devs[type];
- }
-
- return -1;
-}
-
static int drive_index_to_bus_id(BlockInterfaceType type, int index)
{
int max_devs = if_max_devs[type];
return max_devs ? index % max_devs : index;
}
-QemuOpts *drive_def(const char *optstr)
-{
- return qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
-}
-
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
const char *optstr)
{
QemuOpts *opts;
- opts = drive_def(optstr);
+ opts = qemu_opts_parse_noisily(qemu_find_opts("drive"), optstr, false);
if (!opts) {
return NULL;
}
return 0;
}
+static abi_long get_vfpcontext(CPUARMState *env, abi_ulong frame_addr,
+ struct target_sigframe *frame)
+{
+ /* see sendsig and get_vfpcontext in sys/arm/arm/exec_machdep.c */
+ target_mcontext_vfp_t *vfp = &frame->sf_vfp;
+ target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext;
+
+ /* Assumes that mcp and vfp are locked */
+ for (int i = 0; i < 32; i++) {
+ vfp->mcv_reg[i] = tswap64(*aa32_vfp_dreg(env, i));
+ }
+ vfp->mcv_fpscr = tswap32(vfp_get_fpscr(env));
+ mcp->mc_vfp_size = tswap32(sizeof(*vfp));
+ mcp->mc_vfp_ptr = tswap32(frame_addr + ((uintptr_t)vfp - (uintptr_t)frame));
+ return 0;
+}
+
/*
- * Compare to arm/arm/machdep.c get_mcontext()
+ * Compare to arm/arm/exec_machdep.c get_mcontext()
* Assumes that the memory is locked if mcp points to user memory.
*/
abi_long get_mcontext(CPUARMState *env, target_mcontext_t *mcp, int flags)
{
- int err = 0;
uint32_t *gr = mcp->__gregs;
- if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_size != sizeof(target_mcontext_vfp_t)) {
- return -TARGET_EINVAL;
- }
-
gr[TARGET_REG_CPSR] = tswap32(cpsr_read(env));
if (flags & TARGET_MC_GET_CLEAR_RET) {
gr[TARGET_REG_R0] = 0;
gr[TARGET_REG_LR] = tswap32(env->regs[14]);
gr[TARGET_REG_PC] = tswap32(env->regs[15]);
- if (mcp->mc_vfp_size != 0 && mcp->mc_vfp_ptr != 0) {
- /* see get_vfpcontext in sys/arm/arm/exec_machdep.c */
- target_mcontext_vfp_t *vfp;
- vfp = lock_user(VERIFY_WRITE, mcp->mc_vfp_ptr, sizeof(*vfp), 0);
- for (int i = 0; i < 32; i++) {
- vfp->mcv_reg[i] = tswap64(*aa32_vfp_dreg(env, i));
- }
- vfp->mcv_fpscr = tswap32(vfp_get_fpscr(env));
- unlock_user(vfp, mcp->mc_vfp_ptr, sizeof(*vfp));
- }
- return err;
+ /*
+ * FreeBSD's get_mcontext doesn't save VFP info, but sets the pointer and
+ * size to zero. Applications that need the VFP state use
+ * sysarch(ARM_GET_VFPSTATE) and are expected to adjust mcontext after that.
+ */
+ mcp->mc_vfp_size = 0;
+ mcp->mc_vfp_ptr = 0;
+ memset(&mcp->mc_spare, 0, sizeof(mcp->mc_spare));
+
+ return 0;
+}
+
+/*
+ * Compare to arm/arm/exec_machdep.c sendsig()
+ * Assumes that the memory is locked if frame points to user memory.
+ */
+abi_long setup_sigframe_arch(CPUARMState *env, abi_ulong frame_addr,
+ struct target_sigframe *frame, int flags)
+{
+ target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext;
+
+ get_mcontext(env, mcp, flags);
+ get_vfpcontext(env, frame_addr, frame);
+ return 0;
}
/* Compare to arm/arm/exec_machdep.c set_mcontext() */
#define _TARGET_ARCH_CPU_H_
#include "target_arch.h"
+#include "signal-common.h"
#define TARGET_DEFAULT_CPU_MODEL "any"
static inline void target_cpu_loop(CPUARMState *env)
{
- int trapnr;
- target_siginfo_t info;
+ int trapnr, si_signo, si_code;
unsigned int n;
CPUState *cs = env_cpu(env);
process_queued_cpu_work(cs);
switch (trapnr) {
case EXCP_UDEF:
- {
- /* See arm/arm/undefined.c undefinedinstruction(); */
- info.si_addr = env->regs[15];
-
- /* illegal instruction */
- info.si_signo = TARGET_SIGILL;
- info.si_errno = 0;
- info.si_code = TARGET_ILL_ILLOPC;
- queue_signal(env, info.si_signo, &info);
-
- /* TODO: What about instruction emulation? */
- }
+ case EXCP_NOCP:
+ case EXCP_INVSTATE:
+ /*
+ * See arm/arm/undefined.c undefinedinstruction();
+ *
+ * A number of details aren't emulated (they likely don't matter):
+ * o Misaligned PC generates ILL_ILLADR (these can't come from qemu)
+ * o Thumb-2 instructions generate ILLADR
+ * o Both modes implement coprocessor instructions, which we don't
+ * do here. FreeBSD just implements them for the VFP coprocessor
+ * and special kernel breakpoints, trace points, dtrace, etc.
+ */
+ force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->regs[15]);
break;
case EXCP_SWI:
- case EXCP_BKPT:
{
- /*
- * system call
- * See arm/arm/trap.c cpu_fetch_syscall_args()
- */
- if (trapnr == EXCP_BKPT) {
- if (env->thumb) {
- env->regs[15] += 2;
- } else {
- env->regs[15] += 4;
- }
- }
n = env->regs[7];
if (bsd_type == target_freebsd) {
int ret;
int32_t syscall_nr = n;
int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8;
- /* See arm/arm/trap.c cpu_fetch_syscall_args() */
+ /* See arm/arm/syscall.c cpu_fetch_syscall_args() */
if (syscall_nr == TARGET_FREEBSD_NR_syscall) {
syscall_nr = env->regs[0];
arg1 = env->regs[1];
/* just indicate that signals should be handled asap */
break;
case EXCP_PREFETCH_ABORT:
- /* See arm/arm/trap.c prefetch_abort_handler() */
case EXCP_DATA_ABORT:
- /* See arm/arm/trap.c data_abort_handler() */
- info.si_signo = TARGET_SIGSEGV;
- info.si_errno = 0;
- /* XXX: check env->error_code */
- info.si_code = 0;
- info.si_addr = env->exception.vaddress;
- queue_signal(env, info.si_signo, &info);
- break;
- case EXCP_DEBUG:
- {
+ /*
+ * See arm/arm/trap-v6.c prefetch_abort_handler() and
+ * data_abort_handler()
+ *
+ * However, FreeBSD maps these to a generic value and then uses that
+ * to maybe fault in pages in vm/vm_fault.c:vm_fault_trap(). I
+ * believe that the indirection maps the same as Linux, but haven't
+ * chased down every single possible indirection.
+ */
- info.si_signo = TARGET_SIGTRAP;
- info.si_errno = 0;
- info.si_code = TARGET_TRAP_BRKPT;
- info.si_addr = env->exception.vaddress;
- queue_signal(env, info.si_signo, &info);
+ /* For user-only we don't set TTBCR_EAE, so look at the FSR. */
+ switch (env->exception.fsr & 0x1f) {
+ case 0x1: /* Alignment */
+ si_signo = TARGET_SIGBUS;
+ si_code = TARGET_BUS_ADRALN;
+ break;
+ case 0x3: /* Access flag fault, level 1 */
+ case 0x6: /* Access flag fault, level 2 */
+ case 0x9: /* Domain fault, level 1 */
+ case 0xb: /* Domain fault, level 2 */
+ case 0xd: /* Permission fault, level 1 */
+ case 0xf: /* Permission fault, level 2 */
+ si_signo = TARGET_SIGSEGV;
+ si_code = TARGET_SEGV_ACCERR;
+ break;
+ case 0x5: /* Translation fault, level 1 */
+ case 0x7: /* Translation fault, level 2 */
+ si_signo = TARGET_SIGSEGV;
+ si_code = TARGET_SEGV_MAPERR;
+ break;
+ default:
+ g_assert_not_reached();
}
+ force_sig_fault(si_signo, si_code, env->exception.vaddress);
break;
- case EXCP_ATOMIC:
- cpu_exec_step_atomic(cs);
+ case EXCP_DEBUG:
+ case EXCP_BKPT:
+ force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[15]);
break;
case EXCP_YIELD:
/* nothing to do here for user-mode, just resume guest code */
break;
+ case EXCP_ATOMIC:
+ cpu_exec_step_atomic(cs);
+ break;
default:
fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
trapnr);
env->regs[0] = 0;
}
-static inline void target_cpu_reset(CPUArchState *cpu)
+static inline void target_cpu_reset(CPUArchState *env)
{
}
int32_t _mqd;
} _mesgp;
- /* SIGPOLL */
+ /* SIGPOLL -- Not really genreated in FreeBSD ??? */
struct {
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
} _poll;
+ struct {
+ int _mqd;
+ } _mesgq;
+
+ struct {
+ /*
+ * Syscall number for signals delivered as a result of system calls
+ * denied by Capsicum.
+ */
+ int _syscall;
+ } _capsicum;
+
+ /* Spare for future growth */
struct {
abi_long __spare1__;
int32_t __spare2_[7];
#include "target_os_siginfo.h"
#include "target_arch_signal.h"
+abi_long setup_sigframe_arch(CPUArchState *env, abi_ulong frame_addr,
+ struct target_sigframe *frame, int flags);
+
/* Compare to sys/signal.h */
#define TARGET_SIGHUP 1 /* hangup */
#define TARGET_SIGINT 2 /* interrupt */
struct target_sigframe *frame,
abi_ulong frame_addr,
struct target_sigaction *ka);
-abi_long get_mcontext(CPUArchState *regs, target_mcontext_t *mcp, int flags);
-abi_long set_mcontext(CPUArchState *regs, target_mcontext_t *mcp, int srflag);
-abi_long get_ucontext_sigreturn(CPUArchState *regs, abi_ulong target_sf,
+abi_long get_mcontext(CPUArchState *env, target_mcontext_t *mcp, int flags);
+abi_long set_mcontext(CPUArchState *env, target_mcontext_t *mcp, int srflag);
+abi_long get_ucontext_sigreturn(CPUArchState *env, abi_ulong target_sf,
abi_ulong *target_uc);
#endif /* TARGET_OS_UCONTEXT_H */
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2021 Warner Losh
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef ARM_HOST_SIGNAL_H
+#define ARM_HOST_SIGNAL_H
+
+#include <sys/ucontext.h>
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.__gregs[_REG_PC];
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.__gregs[_REG_PC] = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ /*
+ * In the FSR, bit 11 is WnR. FreeBSD returns this as part of the
+ * si_info.si_trapno.
+ */
+ uint32_t fsr = info->si_trapno;
+
+ return extract32(fsr, 11, 1);
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2021 Warner Losh
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef I386_HOST_SIGNAL_H
+#define I386_HOST_SIGNAL_H
+
+#include <sys/ucontext.h>
+#include <machine/trap.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.mc_eip;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.mc_eip = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ /*
+ * Look in sys/i386/i386/trap.c. NOTE: mc_err == tr_err due to type punning
+ * between a trapframe and mcontext on FreeBSD/i386.
+ */
+ return uc->uc_mcontext.mc_trapno == T_PAGEFLT &&
+ uc->uc_mcontext.mc_err & PGEX_W;
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2021 Warner Losh
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef X86_64_HOST_SIGNAL_H
+#define X86_64_HOST_SIGNAL_H
+
+#include <sys/ucontext.h>
+#include <machine/trap.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.mc_rip;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.mc_rip = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ /*
+ * Look in sys/amd64/amd64/trap.c. NOTE: mc_err == tr_err due to type
+ * punning between a trapframe and mcontext on FreeBSD/amd64.
+ */
+ return uc->uc_mcontext.mc_trapno == T_PAGEFLT &&
+ uc->uc_mcontext.mc_err & PGEX_W;
+}
+
+#endif
return 0;
}
+/*
+ * Compare to i386/i386/exec_machdep.c sendsig()
+ * Assumes that the memory is locked if frame points to user memory.
+ */
+abi_long setup_sigframe_arch(CPUX86State *env, abi_ulong frame_addr,
+ struct target_sigframe *frame, int flags)
+{
+ target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext;
+
+ get_mcontext(env, mcp, flags);
+ return 0;
+}
+
/* Compare to i386/i386/machdep.c get_mcontext() */
abi_long get_mcontext(CPUX86State *regs, target_mcontext_t *mcp, int flags)
{
#define _TARGET_ARCH_CPU_H_
#include "target_arch.h"
+#include "signal-common.h"
#define TARGET_DEFAULT_CPU_MODEL "qemu32"
env->regs[R_EAX] = 0;
}
-static inline void target_cpu_reset(CPUArchState *cpu)
+static inline void target_cpu_reset(CPUArchState *env)
{
- cpu_reset(env_cpu(cpu));
+ cpu_reset(env_cpu(env));
}
#endif /* ! _TARGET_ARCH_CPU_H_ */
--- /dev/null
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * QEMU internal errno values for implementing user-only POSIX.
+ *
+ * Copyright (c) 2021 Linaro, Ltd.
+ */
+
+#ifndef SPECIAL_ERRNO_H
+#define SPECIAL_ERRNO_H
+
+/*
+ * All of these are QEMU internal, not visible to the guest.
+ * They should be chosen so as to not overlap with any host
+ * or guest errno.
+ */
+
+/*
+ * This is returned when a system call should be restarted, to tell the
+ * main loop that it should wind the guest PC backwards so it will
+ * re-execute the syscall after handling any pending signals.
+ */
+#define QEMU_ERESTARTSYS 255
+
+#endif /* SPECIAL_ERRNO_H */
}
/* Assumes contents are already zeroed. */
-void init_task_state(TaskState *ts)
+static void init_task_state(TaskState *ts)
{
- int i;
-
- ts->first_free = ts->sigqueue_table;
- for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) {
- ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1];
- }
- ts->sigqueue_table[i].next = NULL;
+ ts->sigaltstack_used = (struct target_sigaltstack) {
+ .ss_sp = 0,
+ .ss_size = 0,
+ .ss_flags = TARGET_SS_DISABLE,
+ };
}
void gemu_log(const char *fmt, ...)
bsd_user_ss = ss.source_set()
-common_user_inc += include_directories('.')
+common_user_inc += include_directories('include')
bsd_user_ss.add(files(
'bsdload.c',
uint32_t elf_flags;
};
-#define MAX_SIGQUEUE_SIZE 1024
-
-struct qemu_sigqueue {
- struct qemu_sigqueue *next;
- target_siginfo_t info;
-};
-
struct emulated_sigtable {
int pending; /* true if signal is pending */
- struct qemu_sigqueue *first;
- struct qemu_sigqueue info; /* Put first signal info here */
+ target_siginfo_t info;
};
/*
struct bsd_binprm *bprm;
struct image_info *info;
+ struct emulated_sigtable sync_signal;
+ /*
+ * TODO: Since we block all signals while returning to the main CPU
+ * loop, this needn't be an array
+ */
struct emulated_sigtable sigtab[TARGET_NSIG];
- struct qemu_sigqueue sigqueue_table[MAX_SIGQUEUE_SIZE]; /* siginfo queue */
- struct qemu_sigqueue *first_free; /* first free siginfo queue entry */
- int signal_pending; /* non zero if a signal may be pending */
-
- uint8_t stack[];
+ /*
+ * Nonzero if process_pending_signals() needs to do something (either
+ * handle a pending signal or unblock signals).
+ * This flag is written from a signal handler so should be accessed via
+ * the qatomic_read() and qatomic_set() functions. (It is not accessed
+ * from multiple threads.)
+ */
+ int signal_pending;
+ /* True if we're leaving a sigsuspend and sigsuspend_mask is valid. */
+ bool in_sigsuspend;
+ /*
+ * This thread's signal mask, as requested by the guest program.
+ * The actual signal mask of this thread may differ:
+ * + we don't let SIGSEGV and SIGBUS be blocked while running guest code
+ * + sometimes we block all signals to avoid races
+ */
+ sigset_t signal_mask;
+ /*
+ * The signal mask imposed by a guest sigsuspend syscall, if we are
+ * currently in the middle of such a syscall
+ */
+ sigset_t sigsuspend_mask;
+
+ /* This thread's sigaltstack, if it has one */
+ struct target_sigaltstack sigaltstack_used;
} __attribute__((aligned(16))) TaskState;
-void init_task_state(TaskState *ts);
void stop_all_tasks(void);
extern const char *qemu_uname_release;
abi_long arg1, abi_long arg2, abi_long arg3,
abi_long arg4, abi_long arg5, abi_long arg6);
void print_openbsd_syscall_ret(int num, abi_long ret);
+/**
+ * print_taken_signal:
+ * @target_signum: target signal being taken
+ * @tinfo: target_siginfo_t which will be passed to the guest for the signal
+ *
+ * Print strace output indicating that this signal is being taken by the guest,
+ * in a format similar to:
+ * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} ---
+ */
+void print_taken_signal(int target_signum, const target_siginfo_t *tinfo);
extern int do_strace;
-/* signal.c */
-void process_pending_signals(CPUArchState *cpu_env);
-void signal_init(void);
-long do_sigreturn(CPUArchState *env);
-long do_rt_sigreturn(CPUArchState *env);
-void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info);
-abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
-
/* mmap.c */
int target_mprotect(abi_ulong start, abi_ulong len, int prot);
abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
#include <pthread.h>
+#include "user/safe-syscall.h"
+
#endif /* QEMU_H */
--- /dev/null
+/*
+ * Emulation of BSD signals
+ *
+ * Copyright (c) 2013 Stacey Son
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SIGNAL_COMMON_H
+#define SIGNAL_COMMON_H
+
+/**
+ * block_signals: block all signals while handling this guest syscall
+ *
+ * Block all signals, and arrange that the signal mask is returned to
+ * its correct value for the guest before we resume execution of guest code.
+ * If this function returns non-zero, then the caller should immediately
+ * return -TARGET_ERESTARTSYS to the main loop, which will take the pending
+ * signal and restart execution of the syscall.
+ * If block_signals() returns zero, then the caller can continue with
+ * emulation of the system call knowing that no signals can be taken
+ * (and therefore that no race conditions will result).
+ * This should only be called once, because if it is called a second time
+ * it will always return non-zero. (Think of it like a mutex that can't
+ * be recursively locked.)
+ * Signals will be unblocked again by process_pending_signals().
+ *
+ * Return value: non-zero if there was a pending signal, zero if not.
+ */
+int block_signals(void); /* Returns non zero if signal pending */
+
+long do_rt_sigreturn(CPUArchState *env);
+int do_sigaction(int sig, const struct target_sigaction *act,
+ struct target_sigaction *oact);
+abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
+long do_sigreturn(CPUArchState *env, abi_ulong addr);
+void force_sig_fault(int sig, int code, abi_ulong addr);
+int host_to_target_signal(int sig);
+void host_to_target_sigset(target_sigset_t *d, const sigset_t *s);
+void process_pending_signals(CPUArchState *env);
+void queue_signal(CPUArchState *env, int sig, int si_type,
+ target_siginfo_t *info);
+void signal_init(void);
+int target_to_host_signal(int sig);
+void target_to_host_sigset(sigset_t *d, const target_sigset_t *s);
+
+/*
+ * Within QEMU the top 8 bits of si_code indicate which of the parts of the
+ * union in target_siginfo is valid. This only applies between
+ * host_to_target_siginfo_noswap() and tswap_siginfo(); it does not appear
+ * either within host siginfo_t or in target_siginfo structures which we get
+ * from the guest userspace program. Linux kenrels use this internally, but BSD
+ * kernels don't do this, but its a useful abstraction.
+ *
+ * The linux-user version of this uses the top 16 bits, but FreeBSD's SI_USER
+ * and other signal indepenent SI_ codes have bit 16 set, so we only use the top
+ * byte instead.
+ *
+ * For FreeBSD, we have si_pid, si_uid, si_status, and si_addr always. Linux and
+ * {Open,Net}BSD have a different approach (where their reason field is larger,
+ * but whose siginfo has fewer fields always).
+ *
+ * QEMU_SI_CAPSICUM is currently only FreeBSD 14 current only, so only define
+ * it where _capsicum is available.
+ */
+#define QEMU_SI_NOINFO 0 /* nothing other than si_signo valid */
+#define QEMU_SI_FAULT 1 /* _fault is valid in _reason */
+#define QEMU_SI_TIMER 2 /* _timer is valid in _reason */
+#define QEMU_SI_MESGQ 3 /* _mesgq is valid in _reason */
+#define QEMU_SI_POLL 4 /* _poll is valid in _reason */
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 1400026
+#define QEMU_SI_CAPSICUM 5 /* _capsicum is valid in _reason */
+#endif
+
+#endif
* Emulation of BSD signals
*
* Copyright (c) 2003 - 2008 Fabrice Bellard
+ * Copyright (c) 2013 Stacey Son
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "qemu/osdep.h"
#include "qemu.h"
+#include "signal-common.h"
+#include "trace.h"
+#include "hw/core/tcg-cpu-ops.h"
+#include "host-signal.h"
+
+static struct target_sigaction sigact_table[TARGET_NSIG];
+static void host_signal_handler(int host_sig, siginfo_t *info, void *puc);
+static void target_to_host_sigset_internal(sigset_t *d,
+ const target_sigset_t *s);
+
+static inline int on_sig_stack(TaskState *ts, unsigned long sp)
+{
+ return sp - ts->sigaltstack_used.ss_sp < ts->sigaltstack_used.ss_size;
+}
+
+static inline int sas_ss_flags(TaskState *ts, unsigned long sp)
+{
+ return ts->sigaltstack_used.ss_size == 0 ? SS_DISABLE :
+ on_sig_stack(ts, sp) ? SS_ONSTACK : 0;
+}
+
+/*
+ * The BSD ABIs use the same singal numbers across all the CPU architectures, so
+ * (unlike Linux) these functions are just the identity mapping. This might not
+ * be true for XyzBSD running on AbcBSD, which doesn't currently work.
+ */
+int host_to_target_signal(int sig)
+{
+ return sig;
+}
+
+int target_to_host_signal(int sig)
+{
+ return sig;
+}
+
+static inline void target_sigemptyset(target_sigset_t *set)
+{
+ memset(set, 0, sizeof(*set));
+}
+
+static inline void target_sigaddset(target_sigset_t *set, int signum)
+{
+ signum--;
+ uint32_t mask = (uint32_t)1 << (signum % TARGET_NSIG_BPW);
+ set->__bits[signum / TARGET_NSIG_BPW] |= mask;
+}
+
+static inline int target_sigismember(const target_sigset_t *set, int signum)
+{
+ signum--;
+ abi_ulong mask = (abi_ulong)1 << (signum % TARGET_NSIG_BPW);
+ return (set->__bits[signum / TARGET_NSIG_BPW] & mask) != 0;
+}
+
+/* Adjust the signal context to rewind out of safe-syscall if we're in it */
+static inline void rewind_if_in_safe_syscall(void *puc)
+{
+ ucontext_t *uc = (ucontext_t *)puc;
+ uintptr_t pcreg = host_signal_pc(uc);
+
+ if (pcreg > (uintptr_t)safe_syscall_start
+ && pcreg < (uintptr_t)safe_syscall_end) {
+ host_signal_set_pc(uc, (uintptr_t)safe_syscall_start);
+ }
+}
/*
- * Stubbed out routines until we merge signal support from bsd-user
- * fork.
+ * Note: The following take advantage of the BSD signal property that all
+ * signals are available on all architectures.
*/
+static void host_to_target_sigset_internal(target_sigset_t *d,
+ const sigset_t *s)
+{
+ int i;
+
+ target_sigemptyset(d);
+ for (i = 1; i <= NSIG; i++) {
+ if (sigismember(s, i)) {
+ target_sigaddset(d, host_to_target_signal(i));
+ }
+ }
+}
+
+void host_to_target_sigset(target_sigset_t *d, const sigset_t *s)
+{
+ target_sigset_t d1;
+ int i;
+
+ host_to_target_sigset_internal(&d1, s);
+ for (i = 0; i < _SIG_WORDS; i++) {
+ d->__bits[i] = tswap32(d1.__bits[i]);
+ }
+}
+
+static void target_to_host_sigset_internal(sigset_t *d,
+ const target_sigset_t *s)
+{
+ int i;
+
+ sigemptyset(d);
+ for (i = 1; i <= TARGET_NSIG; i++) {
+ if (target_sigismember(s, i)) {
+ sigaddset(d, target_to_host_signal(i));
+ }
+ }
+}
+
+void target_to_host_sigset(sigset_t *d, const target_sigset_t *s)
+{
+ target_sigset_t s1;
+ int i;
+
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+ s1.__bits[i] = tswap32(s->__bits[i]);
+ }
+ target_to_host_sigset_internal(d, &s1);
+}
+
+static bool has_trapno(int tsig)
+{
+ return tsig == TARGET_SIGILL ||
+ tsig == TARGET_SIGFPE ||
+ tsig == TARGET_SIGSEGV ||
+ tsig == TARGET_SIGBUS ||
+ tsig == TARGET_SIGTRAP;
+}
+
+/* Siginfo conversion. */
+
+/*
+ * Populate tinfo w/o swapping based on guessing which fields are valid.
+ */
+static inline void host_to_target_siginfo_noswap(target_siginfo_t *tinfo,
+ const siginfo_t *info)
+{
+ int sig = host_to_target_signal(info->si_signo);
+ int si_code = info->si_code;
+ int si_type;
+
+ /*
+ * Make sure we that the variable portion of the target siginfo is zeroed
+ * out so we don't leak anything into that.
+ */
+ memset(&tinfo->_reason, 0, sizeof(tinfo->_reason));
+
+ /*
+ * This is awkward, because we have to use a combination of the si_code and
+ * si_signo to figure out which of the union's members are valid.o We
+ * therefore make our best guess.
+ *
+ * Once we have made our guess, we record it in the top 16 bits of
+ * the si_code, so that tswap_siginfo() later can use it.
+ * tswap_siginfo() will strip these top bits out before writing
+ * si_code to the guest (sign-extending the lower bits).
+ */
+ tinfo->si_signo = sig;
+ tinfo->si_errno = info->si_errno;
+ tinfo->si_code = info->si_code;
+ tinfo->si_pid = info->si_pid;
+ tinfo->si_uid = info->si_uid;
+ tinfo->si_status = info->si_status;
+ tinfo->si_addr = (abi_ulong)(unsigned long)info->si_addr;
+ /*
+ * si_value is opaque to kernel. On all FreeBSD platforms,
+ * sizeof(sival_ptr) >= sizeof(sival_int) so the following
+ * always will copy the larger element.
+ */
+ tinfo->si_value.sival_ptr =
+ (abi_ulong)(unsigned long)info->si_value.sival_ptr;
+
+ switch (si_code) {
+ /*
+ * All the SI_xxx codes that are defined here are global to
+ * all the signals (they have values that none of the other,
+ * more specific signal info will set).
+ */
+ case SI_USER:
+ case SI_LWP:
+ case SI_KERNEL:
+ case SI_QUEUE:
+ case SI_ASYNCIO:
+ /*
+ * Only the fixed parts are valid (though FreeBSD doesn't always
+ * set all the fields to non-zero values.
+ */
+ si_type = QEMU_SI_NOINFO;
+ break;
+ case SI_TIMER:
+ tinfo->_reason._timer._timerid = info->_reason._timer._timerid;
+ tinfo->_reason._timer._overrun = info->_reason._timer._overrun;
+ si_type = QEMU_SI_TIMER;
+ break;
+ case SI_MESGQ:
+ tinfo->_reason._mesgq._mqd = info->_reason._mesgq._mqd;
+ si_type = QEMU_SI_MESGQ;
+ break;
+ default:
+ /*
+ * We have to go based on the signal number now to figure out
+ * what's valid.
+ */
+ si_type = QEMU_SI_NOINFO;
+ if (has_trapno(sig)) {
+ tinfo->_reason._fault._trapno = info->_reason._fault._trapno;
+ si_type = QEMU_SI_FAULT;
+ }
+#ifdef TARGET_SIGPOLL
+ /*
+ * FreeBSD never had SIGPOLL, but emulates it for Linux so there's
+ * a chance it may popup in the future.
+ */
+ if (sig == TARGET_SIGPOLL) {
+ tinfo->_reason._poll._band = info->_reason._poll._band;
+ si_type = QEMU_SI_POLL;
+ }
+#endif
+ /*
+ * Unsure that this can actually be generated, and our support for
+ * capsicum is somewhere between weak and non-existant, but if we get
+ * one, then we know what to save.
+ */
+#ifdef QEMU_SI_CAPSICUM
+ if (sig == TARGET_SIGTRAP) {
+ tinfo->_reason._capsicum._syscall =
+ info->_reason._capsicum._syscall;
+ si_type = QEMU_SI_CAPSICUM;
+ }
+#endif
+ break;
+ }
+ tinfo->si_code = deposit32(si_code, 24, 8, si_type);
+}
+
+static void tswap_siginfo(target_siginfo_t *tinfo, const target_siginfo_t *info)
+{
+ int si_type = extract32(info->si_code, 24, 8);
+ int si_code = sextract32(info->si_code, 0, 24);
+
+ __put_user(info->si_signo, &tinfo->si_signo);
+ __put_user(info->si_errno, &tinfo->si_errno);
+ __put_user(si_code, &tinfo->si_code); /* Zero out si_type, it's internal */
+ __put_user(info->si_pid, &tinfo->si_pid);
+ __put_user(info->si_uid, &tinfo->si_uid);
+ __put_user(info->si_status, &tinfo->si_status);
+ __put_user(info->si_addr, &tinfo->si_addr);
+ /*
+ * Unswapped, because we passed it through mostly untouched. si_value is
+ * opaque to the kernel, so we didn't bother with potentially wasting cycles
+ * to swap it into host byte order.
+ */
+ tinfo->si_value.sival_ptr = info->si_value.sival_ptr;
+
+ /*
+ * We can use our internal marker of which fields in the structure
+ * are valid, rather than duplicating the guesswork of
+ * host_to_target_siginfo_noswap() here.
+ */
+ switch (si_type) {
+ case QEMU_SI_NOINFO: /* No additional info */
+ break;
+ case QEMU_SI_FAULT:
+ __put_user(info->_reason._fault._trapno,
+ &tinfo->_reason._fault._trapno);
+ break;
+ case QEMU_SI_TIMER:
+ __put_user(info->_reason._timer._timerid,
+ &tinfo->_reason._timer._timerid);
+ __put_user(info->_reason._timer._overrun,
+ &tinfo->_reason._timer._overrun);
+ break;
+ case QEMU_SI_MESGQ:
+ __put_user(info->_reason._mesgq._mqd, &tinfo->_reason._mesgq._mqd);
+ break;
+ case QEMU_SI_POLL:
+ /* Note: Not generated on FreeBSD */
+ __put_user(info->_reason._poll._band, &tinfo->_reason._poll._band);
+ break;
+#ifdef QEMU_SI_CAPSICUM
+ case QEMU_SI_CAPSICUM:
+ __put_user(info->_reason._capsicum._syscall,
+ &tinfo->_reason._capsicum._syscall);
+ break;
+#endif
+ default:
+ g_assert_not_reached();
+ }
+}
+
+int block_signals(void)
+{
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+ sigset_t set;
+
+ /*
+ * It's OK to block everything including SIGSEGV, because we won't run any
+ * further guest code before unblocking signals in
+ * process_pending_signals(). We depend on the FreeBSD behaivor here where
+ * this will only affect this thread's signal mask. We don't use
+ * pthread_sigmask which might seem more correct because that routine also
+ * does odd things with SIGCANCEL to implement pthread_cancel().
+ */
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, 0);
+
+ return qatomic_xchg(&ts->signal_pending, 1);
+}
+
+/* Returns 1 if given signal should dump core if not handled. */
+static int core_dump_signal(int sig)
+{
+ switch (sig) {
+ case TARGET_SIGABRT:
+ case TARGET_SIGFPE:
+ case TARGET_SIGILL:
+ case TARGET_SIGQUIT:
+ case TARGET_SIGSEGV:
+ case TARGET_SIGTRAP:
+ case TARGET_SIGBUS:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* Abort execution with signal. */
+static void QEMU_NORETURN dump_core_and_abort(int target_sig)
+{
+ CPUArchState *env = thread_cpu->env_ptr;
+ CPUState *cpu = env_cpu(env);
+ TaskState *ts = cpu->opaque;
+ int core_dumped = 0;
+ int host_sig;
+ struct sigaction act;
+
+ host_sig = target_to_host_signal(target_sig);
+ gdb_signalled(env, target_sig);
+
+ /* Dump core if supported by target binary format */
+ if (core_dump_signal(target_sig) && (ts->bprm->core_dump != NULL)) {
+ stop_all_tasks();
+ core_dumped =
+ ((*ts->bprm->core_dump)(target_sig, env) == 0);
+ }
+ if (core_dumped) {
+ struct rlimit nodump;
+
+ /*
+ * We already dumped the core of target process, we don't want
+ * a coredump of qemu itself.
+ */
+ getrlimit(RLIMIT_CORE, &nodump);
+ nodump.rlim_cur = 0;
+ setrlimit(RLIMIT_CORE, &nodump);
+ (void) fprintf(stderr, "qemu: uncaught target signal %d (%s) "
+ "- %s\n", target_sig, strsignal(host_sig), "core dumped");
+ }
+
+ /*
+ * The proper exit code for dying from an uncaught signal is
+ * -<signal>. The kernel doesn't allow exit() or _exit() to pass
+ * a negative value. To get the proper exit code we need to
+ * actually die from an uncaught signal. Here the default signal
+ * handler is installed, we send ourself a signal and we wait for
+ * it to arrive.
+ */
+ memset(&act, 0, sizeof(act));
+ sigfillset(&act.sa_mask);
+ act.sa_handler = SIG_DFL;
+ sigaction(host_sig, &act, NULL);
+
+ kill(getpid(), host_sig);
+
+ /*
+ * Make sure the signal isn't masked (just reuse the mask inside
+ * of act).
+ */
+ sigdelset(&act.sa_mask, host_sig);
+ sigsuspend(&act.sa_mask);
+
+ /* unreachable */
+ abort();
+}
/*
* Queue a signal so that it will be send to the virtual CPU as soon as
* possible.
*/
-void queue_signal(CPUArchState *env, int sig, target_siginfo_t *info)
+void queue_signal(CPUArchState *env, int sig, int si_type,
+ target_siginfo_t *info)
{
- qemu_log_mask(LOG_UNIMP, "No signal queueing, dropping signal %d\n", sig);
+ CPUState *cpu = env_cpu(env);
+ TaskState *ts = cpu->opaque;
+
+ trace_user_queue_signal(env, sig);
+
+ info->si_code = deposit32(info->si_code, 24, 8, si_type);
+
+ ts->sync_signal.info = *info;
+ ts->sync_signal.pending = sig;
+ /* Signal that a new signal is pending. */
+ qatomic_set(&ts->signal_pending, 1);
+ return;
+}
+
+static int fatal_signal(int sig)
+{
+
+ switch (sig) {
+ case TARGET_SIGCHLD:
+ case TARGET_SIGURG:
+ case TARGET_SIGWINCH:
+ case TARGET_SIGINFO:
+ /* Ignored by default. */
+ return 0;
+ case TARGET_SIGCONT:
+ case TARGET_SIGSTOP:
+ case TARGET_SIGTSTP:
+ case TARGET_SIGTTIN:
+ case TARGET_SIGTTOU:
+ /* Job control signals. */
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+/*
+ * Force a synchronously taken QEMU_SI_FAULT signal. For QEMU the
+ * 'force' part is handled in process_pending_signals().
+ */
+void force_sig_fault(int sig, int code, abi_ulong addr)
+{
+ CPUState *cpu = thread_cpu;
+ CPUArchState *env = cpu->env_ptr;
+ target_siginfo_t info = {};
+
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = code;
+ info.si_addr = addr;
+ queue_signal(env, sig, QEMU_SI_FAULT, &info);
+}
+
+static void host_signal_handler(int host_sig, siginfo_t *info, void *puc)
+{
+ CPUArchState *env = thread_cpu->env_ptr;
+ CPUState *cpu = env_cpu(env);
+ TaskState *ts = cpu->opaque;
+ target_siginfo_t tinfo;
+ ucontext_t *uc = puc;
+ struct emulated_sigtable *k;
+ int guest_sig;
+ uintptr_t pc = 0;
+ bool sync_sig = false;
+
+ /*
+ * Non-spoofed SIGSEGV and SIGBUS are synchronous, and need special
+ * handling wrt signal blocking and unwinding.
+ */
+ if ((host_sig == SIGSEGV || host_sig == SIGBUS) && info->si_code > 0) {
+ MMUAccessType access_type;
+ uintptr_t host_addr;
+ abi_ptr guest_addr;
+ bool is_write;
+
+ host_addr = (uintptr_t)info->si_addr;
+
+ /*
+ * Convert forcefully to guest address space: addresses outside
+ * reserved_va are still valid to report via SEGV_MAPERR.
+ */
+ guest_addr = h2g_nocheck(host_addr);
+
+ pc = host_signal_pc(uc);
+ is_write = host_signal_write(info, uc);
+ access_type = adjust_signal_pc(&pc, is_write);
+
+ if (host_sig == SIGSEGV) {
+ bool maperr = true;
+
+ if (info->si_code == SEGV_ACCERR && h2g_valid(host_addr)) {
+ /* If this was a write to a TB protected page, restart. */
+ if (is_write &&
+ handle_sigsegv_accerr_write(cpu, &uc->uc_sigmask,
+ pc, guest_addr)) {
+ return;
+ }
+
+ /*
+ * With reserved_va, the whole address space is PROT_NONE,
+ * which means that we may get ACCERR when we want MAPERR.
+ */
+ if (page_get_flags(guest_addr) & PAGE_VALID) {
+ maperr = false;
+ } else {
+ info->si_code = SEGV_MAPERR;
+ }
+ }
+
+ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+ cpu_loop_exit_sigsegv(cpu, guest_addr, access_type, maperr, pc);
+ } else {
+ sigprocmask(SIG_SETMASK, &uc->uc_sigmask, NULL);
+ if (info->si_code == BUS_ADRALN) {
+ cpu_loop_exit_sigbus(cpu, guest_addr, access_type, pc);
+ }
+ }
+
+ sync_sig = true;
+ }
+
+ /* Get the target signal number. */
+ guest_sig = host_to_target_signal(host_sig);
+ if (guest_sig < 1 || guest_sig > TARGET_NSIG) {
+ return;
+ }
+ trace_user_host_signal(cpu, host_sig, guest_sig);
+
+ host_to_target_siginfo_noswap(&tinfo, info);
+
+ k = &ts->sigtab[guest_sig - 1];
+ k->info = tinfo;
+ k->pending = guest_sig;
+ ts->signal_pending = 1;
+
+ /*
+ * For synchronous signals, unwind the cpu state to the faulting
+ * insn and then exit back to the main loop so that the signal
+ * is delivered immediately.
+ */
+ if (sync_sig) {
+ cpu->exception_index = EXCP_INTERRUPT;
+ cpu_loop_exit_restore(cpu, pc);
+ }
+
+ rewind_if_in_safe_syscall(puc);
+
+ /*
+ * Block host signals until target signal handler entered. We
+ * can't block SIGSEGV or SIGBUS while we're executing guest
+ * code in case the guest code provokes one in the window between
+ * now and it getting out to the main loop. Signals will be
+ * unblocked again in process_pending_signals().
+ */
+ sigfillset(&uc->uc_sigmask);
+ sigdelset(&uc->uc_sigmask, SIGSEGV);
+ sigdelset(&uc->uc_sigmask, SIGBUS);
+
+ /* Interrupt the virtual CPU as soon as possible. */
+ cpu_exit(thread_cpu);
+}
+
+/* do_sigaltstack() returns target values and errnos. */
+/* compare to kern/kern_sig.c sys_sigaltstack() and kern_sigaltstack() */
+abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp)
+{
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+ int ret;
+ target_stack_t oss;
+
+ if (uoss_addr) {
+ /* Save current signal stack params */
+ oss.ss_sp = tswapl(ts->sigaltstack_used.ss_sp);
+ oss.ss_size = tswapl(ts->sigaltstack_used.ss_size);
+ oss.ss_flags = tswapl(sas_ss_flags(ts, sp));
+ }
+
+ if (uss_addr) {
+ target_stack_t *uss;
+ target_stack_t ss;
+ size_t minstacksize = TARGET_MINSIGSTKSZ;
+
+ ret = -TARGET_EFAULT;
+ if (!lock_user_struct(VERIFY_READ, uss, uss_addr, 1)) {
+ goto out;
+ }
+ __get_user(ss.ss_sp, &uss->ss_sp);
+ __get_user(ss.ss_size, &uss->ss_size);
+ __get_user(ss.ss_flags, &uss->ss_flags);
+ unlock_user_struct(uss, uss_addr, 0);
+
+ ret = -TARGET_EPERM;
+ if (on_sig_stack(ts, sp)) {
+ goto out;
+ }
+
+ ret = -TARGET_EINVAL;
+ if (ss.ss_flags != TARGET_SS_DISABLE
+ && ss.ss_flags != TARGET_SS_ONSTACK
+ && ss.ss_flags != 0) {
+ goto out;
+ }
+
+ if (ss.ss_flags == TARGET_SS_DISABLE) {
+ ss.ss_size = 0;
+ ss.ss_sp = 0;
+ } else {
+ ret = -TARGET_ENOMEM;
+ if (ss.ss_size < minstacksize) {
+ goto out;
+ }
+ }
+
+ ts->sigaltstack_used.ss_sp = ss.ss_sp;
+ ts->sigaltstack_used.ss_size = ss.ss_size;
+ }
+
+ if (uoss_addr) {
+ ret = -TARGET_EFAULT;
+ if (copy_to_user(uoss_addr, &oss, sizeof(oss))) {
+ goto out;
+ }
+ }
+
+ ret = 0;
+out:
+ return ret;
+}
+
+/* do_sigaction() return host values and errnos */
+int do_sigaction(int sig, const struct target_sigaction *act,
+ struct target_sigaction *oact)
+{
+ struct target_sigaction *k;
+ struct sigaction act1;
+ int host_sig;
+ int ret = 0;
+
+ if (sig < 1 || sig > TARGET_NSIG) {
+ return -TARGET_EINVAL;
+ }
+
+ if ((sig == TARGET_SIGKILL || sig == TARGET_SIGSTOP) &&
+ act != NULL && act->_sa_handler != TARGET_SIG_DFL) {
+ return -TARGET_EINVAL;
+ }
+
+ if (block_signals()) {
+ return -TARGET_ERESTART;
+ }
+
+ k = &sigact_table[sig - 1];
+ if (oact) {
+ oact->_sa_handler = tswapal(k->_sa_handler);
+ oact->sa_flags = tswap32(k->sa_flags);
+ oact->sa_mask = k->sa_mask;
+ }
+ if (act) {
+ k->_sa_handler = tswapal(act->_sa_handler);
+ k->sa_flags = tswap32(act->sa_flags);
+ k->sa_mask = act->sa_mask;
+
+ /* Update the host signal state. */
+ host_sig = target_to_host_signal(sig);
+ if (host_sig != SIGSEGV && host_sig != SIGBUS) {
+ memset(&act1, 0, sizeof(struct sigaction));
+ sigfillset(&act1.sa_mask);
+ act1.sa_flags = SA_SIGINFO;
+ if (k->sa_flags & TARGET_SA_RESTART) {
+ act1.sa_flags |= SA_RESTART;
+ }
+ /*
+ * Note: It is important to update the host kernel signal mask to
+ * avoid getting unexpected interrupted system calls.
+ */
+ if (k->_sa_handler == TARGET_SIG_IGN) {
+ act1.sa_sigaction = (void *)SIG_IGN;
+ } else if (k->_sa_handler == TARGET_SIG_DFL) {
+ if (fatal_signal(sig)) {
+ act1.sa_sigaction = host_signal_handler;
+ } else {
+ act1.sa_sigaction = (void *)SIG_DFL;
+ }
+ } else {
+ act1.sa_sigaction = host_signal_handler;
+ }
+ ret = sigaction(host_sig, &act1, NULL);
+ }
+ }
+ return ret;
+}
+
+static inline abi_ulong get_sigframe(struct target_sigaction *ka,
+ CPUArchState *env, size_t frame_size)
+{
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+ abi_ulong sp;
+
+ /* Use default user stack */
+ sp = get_sp_from_cpustate(env);
+
+ if ((ka->sa_flags & TARGET_SA_ONSTACK) && sas_ss_flags(ts, sp) == 0) {
+ sp = ts->sigaltstack_used.ss_sp + ts->sigaltstack_used.ss_size;
+ }
+
+/* TODO: make this a target_arch function / define */
+#if defined(TARGET_ARM)
+ return (sp - frame_size) & ~7;
+#elif defined(TARGET_AARCH64)
+ return (sp - frame_size) & ~15;
+#else
+ return sp - frame_size;
+#endif
+}
+
+/* compare to $M/$M/exec_machdep.c sendsig and sys/kern/kern_sig.c sigexit */
+
+static void setup_frame(int sig, int code, struct target_sigaction *ka,
+ target_sigset_t *set, target_siginfo_t *tinfo, CPUArchState *env)
+{
+ struct target_sigframe *frame;
+ abi_ulong frame_addr;
+ int i;
+
+ frame_addr = get_sigframe(ka, env, sizeof(*frame));
+ trace_user_setup_frame(env, frame_addr);
+ if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+ unlock_user_struct(frame, frame_addr, 1);
+ dump_core_and_abort(TARGET_SIGILL);
+ return;
+ }
+
+ memset(frame, 0, sizeof(*frame));
+ setup_sigframe_arch(env, frame_addr, frame, 0);
+
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+ __put_user(set->__bits[i], &frame->sf_uc.uc_sigmask.__bits[i]);
+ }
+
+ if (tinfo) {
+ frame->sf_si.si_signo = tinfo->si_signo;
+ frame->sf_si.si_errno = tinfo->si_errno;
+ frame->sf_si.si_code = tinfo->si_code;
+ frame->sf_si.si_pid = tinfo->si_pid;
+ frame->sf_si.si_uid = tinfo->si_uid;
+ frame->sf_si.si_status = tinfo->si_status;
+ frame->sf_si.si_addr = tinfo->si_addr;
+ /* see host_to_target_siginfo_noswap() for more details */
+ frame->sf_si.si_value.sival_ptr = tinfo->si_value.sival_ptr;
+ /*
+ * At this point, whatever is in the _reason union is complete
+ * and in target order, so just copy the whole thing over, even
+ * if it's too large for this specific signal.
+ * host_to_target_siginfo_noswap() and tswap_siginfo() have ensured
+ * that's so.
+ */
+ memcpy(&frame->sf_si._reason, &tinfo->_reason,
+ sizeof(tinfo->_reason));
+ }
+
+ set_sigtramp_args(env, sig, frame, frame_addr, ka);
+
+ unlock_user_struct(frame, frame_addr, 1);
+}
+
+static int reset_signal_mask(target_ucontext_t *ucontext)
+{
+ int i;
+ sigset_t blocked;
+ target_sigset_t target_set;
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+
+ for (i = 0; i < TARGET_NSIG_WORDS; i++) {
+ if (__get_user(target_set.__bits[i],
+ &ucontext->uc_sigmask.__bits[i])) {
+ return -TARGET_EFAULT;
+ }
+ }
+ target_to_host_sigset_internal(&blocked, &target_set);
+ ts->signal_mask = blocked;
+
+ return 0;
+}
+
+/* See sys/$M/$M/exec_machdep.c sigreturn() */
+long do_sigreturn(CPUArchState *env, abi_ulong addr)
+{
+ long ret;
+ abi_ulong target_ucontext;
+ target_ucontext_t *ucontext = NULL;
+
+ /* Get the target ucontext address from the stack frame */
+ ret = get_ucontext_sigreturn(env, addr, &target_ucontext);
+ if (is_error(ret)) {
+ return ret;
+ }
+ trace_user_do_sigreturn(env, addr);
+ if (!lock_user_struct(VERIFY_READ, ucontext, target_ucontext, 0)) {
+ goto badframe;
+ }
+
+ /* Set the register state back to before the signal. */
+ if (set_mcontext(env, &ucontext->uc_mcontext, 1)) {
+ goto badframe;
+ }
+
+ /* And reset the signal mask. */
+ if (reset_signal_mask(ucontext)) {
+ goto badframe;
+ }
+
+ unlock_user_struct(ucontext, target_ucontext, 0);
+ return -TARGET_EJUSTRETURN;
+
+badframe:
+ if (ucontext != NULL) {
+ unlock_user_struct(ucontext, target_ucontext, 0);
+ }
+ return -TARGET_EFAULT;
}
void signal_init(void)
{
+ TaskState *ts = (TaskState *)thread_cpu->opaque;
+ struct sigaction act;
+ struct sigaction oact;
+ int i;
+ int host_sig;
+
+ /* Set the signal mask from the host mask. */
+ sigprocmask(0, 0, &ts->signal_mask);
+
+ sigfillset(&act.sa_mask);
+ act.sa_sigaction = host_signal_handler;
+ act.sa_flags = SA_SIGINFO;
+
+ for (i = 1; i <= TARGET_NSIG; i++) {
+#ifdef CONFIG_GPROF
+ if (i == TARGET_SIGPROF) {
+ continue;
+ }
+#endif
+ host_sig = target_to_host_signal(i);
+ sigaction(host_sig, NULL, &oact);
+ if (oact.sa_sigaction == (void *)SIG_IGN) {
+ sigact_table[i - 1]._sa_handler = TARGET_SIG_IGN;
+ } else if (oact.sa_sigaction == (void *)SIG_DFL) {
+ sigact_table[i - 1]._sa_handler = TARGET_SIG_DFL;
+ }
+ /*
+ * If there's already a handler installed then something has
+ * gone horribly wrong, so don't even try to handle that case.
+ * Install some handlers for our own use. We need at least
+ * SIGSEGV and SIGBUS, to detect exceptions. We can not just
+ * trap all signals because it affects syscall interrupt
+ * behavior. But do trap all default-fatal signals.
+ */
+ if (fatal_signal(i)) {
+ sigaction(host_sig, &act, NULL);
+ }
+ }
}
-void process_pending_signals(CPUArchState *cpu_env)
+static void handle_pending_signal(CPUArchState *env, int sig,
+ struct emulated_sigtable *k)
{
+ CPUState *cpu = env_cpu(env);
+ TaskState *ts = cpu->opaque;
+ struct target_sigaction *sa;
+ int code;
+ sigset_t set;
+ abi_ulong handler;
+ target_siginfo_t tinfo;
+ target_sigset_t target_old_set;
+
+ trace_user_handle_signal(env, sig);
+
+ k->pending = 0;
+
+ sig = gdb_handlesig(cpu, sig);
+ if (!sig) {
+ sa = NULL;
+ handler = TARGET_SIG_IGN;
+ } else {
+ sa = &sigact_table[sig - 1];
+ handler = sa->_sa_handler;
+ }
+
+ if (do_strace) {
+ print_taken_signal(sig, &k->info);
+ }
+
+ if (handler == TARGET_SIG_DFL) {
+ /*
+ * default handler : ignore some signal. The other are job
+ * control or fatal.
+ */
+ if (sig == TARGET_SIGTSTP || sig == TARGET_SIGTTIN ||
+ sig == TARGET_SIGTTOU) {
+ kill(getpid(), SIGSTOP);
+ } else if (sig != TARGET_SIGCHLD && sig != TARGET_SIGURG &&
+ sig != TARGET_SIGINFO && sig != TARGET_SIGWINCH &&
+ sig != TARGET_SIGCONT) {
+ dump_core_and_abort(sig);
+ }
+ } else if (handler == TARGET_SIG_IGN) {
+ /* ignore sig */
+ } else if (handler == TARGET_SIG_ERR) {
+ dump_core_and_abort(sig);
+ } else {
+ /* compute the blocked signals during the handler execution */
+ sigset_t *blocked_set;
+
+ target_to_host_sigset(&set, &sa->sa_mask);
+ /*
+ * SA_NODEFER indicates that the current signal should not be
+ * blocked during the handler.
+ */
+ if (!(sa->sa_flags & TARGET_SA_NODEFER)) {
+ sigaddset(&set, target_to_host_signal(sig));
+ }
+
+ /*
+ * Save the previous blocked signal state to restore it at the
+ * end of the signal execution (see do_sigreturn).
+ */
+ host_to_target_sigset_internal(&target_old_set, &ts->signal_mask);
+
+ blocked_set = ts->in_sigsuspend ?
+ &ts->sigsuspend_mask : &ts->signal_mask;
+ sigorset(&ts->signal_mask, blocked_set, &set);
+ ts->in_sigsuspend = false;
+ sigprocmask(SIG_SETMASK, &ts->signal_mask, NULL);
+
+ /* XXX VM86 on x86 ??? */
+
+ code = k->info.si_code; /* From host, so no si_type */
+ /* prepare the stack frame of the virtual CPU */
+ if (sa->sa_flags & TARGET_SA_SIGINFO) {
+ tswap_siginfo(&tinfo, &k->info);
+ setup_frame(sig, code, sa, &target_old_set, &tinfo, env);
+ } else {
+ setup_frame(sig, code, sa, &target_old_set, NULL, env);
+ }
+ if (sa->sa_flags & TARGET_SA_RESETHAND) {
+ sa->_sa_handler = TARGET_SIG_DFL;
+ }
+ }
+}
+
+void process_pending_signals(CPUArchState *env)
+{
+ CPUState *cpu = env_cpu(env);
+ int sig;
+ sigset_t *blocked_set, set;
+ struct emulated_sigtable *k;
+ TaskState *ts = cpu->opaque;
+
+ while (qatomic_read(&ts->signal_pending)) {
+ sigfillset(&set);
+ sigprocmask(SIG_SETMASK, &set, 0);
+
+ restart_scan:
+ sig = ts->sync_signal.pending;
+ if (sig) {
+ /*
+ * Synchronous signals are forced by the emulated CPU in some way.
+ * If they are set to ignore, restore the default handler (see
+ * sys/kern_sig.c trapsignal() and execsigs() for this behavior)
+ * though maybe this is done only when forcing exit for non SIGCHLD.
+ */
+ if (sigismember(&ts->signal_mask, target_to_host_signal(sig)) ||
+ sigact_table[sig - 1]._sa_handler == TARGET_SIG_IGN) {
+ sigdelset(&ts->signal_mask, target_to_host_signal(sig));
+ sigact_table[sig - 1]._sa_handler = TARGET_SIG_DFL;
+ }
+ handle_pending_signal(env, sig, &ts->sync_signal);
+ }
+
+ k = ts->sigtab;
+ for (sig = 1; sig <= TARGET_NSIG; sig++, k++) {
+ blocked_set = ts->in_sigsuspend ?
+ &ts->sigsuspend_mask : &ts->signal_mask;
+ if (k->pending &&
+ !sigismember(blocked_set, target_to_host_signal(sig))) {
+ handle_pending_signal(env, sig, k);
+ /*
+ * Restart scan from the beginning, as handle_pending_signal
+ * might have resulted in a new synchronous signal (eg SIGSEGV).
+ */
+ goto restart_scan;
+ }
+ }
+
+ /*
+ * Unblock signals and check one more time. Unblocking signals may cause
+ * us to take another host signal, which will set signal_pending again.
+ */
+ qatomic_set(&ts->signal_pending, 0);
+ ts->in_sigsuspend = false;
+ set = ts->signal_mask;
+ sigdelset(&set, SIGSEGV);
+ sigdelset(&set, SIGBUS);
+ sigprocmask(SIG_SETMASK, &set, 0);
+ }
+ ts->in_sigsuspend = false;
}
void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr,
MMUAccessType access_type, bool maperr, uintptr_t ra)
{
- qemu_log_mask(LOG_UNIMP, "No signal support for SIGSEGV\n");
- /* unreachable */
- abort();
+ const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
+
+ if (tcg_ops->record_sigsegv) {
+ tcg_ops->record_sigsegv(cpu, addr, access_type, maperr, ra);
+ }
+
+ force_sig_fault(TARGET_SIGSEGV,
+ maperr ? TARGET_SEGV_MAPERR : TARGET_SEGV_ACCERR,
+ addr);
+ cpu->exception_index = EXCP_INTERRUPT;
+ cpu_loop_exit_restore(cpu, ra);
}
void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr,
MMUAccessType access_type, uintptr_t ra)
{
- qemu_log_mask(LOG_UNIMP, "No signal support for SIGBUS\n");
- /* unreachable */
- abort();
+ const struct TCGCPUOps *tcg_ops = CPU_GET_CLASS(cpu)->tcg_ops;
+
+ if (tcg_ops->record_sigbus) {
+ tcg_ops->record_sigbus(cpu, addr, access_type, ra);
+ }
+
+ force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN, addr);
+ cpu->exception_index = EXCP_INTERRUPT;
+ cpu_loop_exit_restore(cpu, ra);
}
+++ /dev/null
-/* SPDX-License-Identifier: BSD-3-Clause */
-/*
- * QEMU internal errno values for implementing user-only POSIX.
- *
- * Copyright (c) 2021 Linaro, Ltd.
- */
-
-#ifndef SPECIAL_ERRNO_H
-#define SPECIAL_ERRNO_H
-
-/*
- * All of these are QEMU internal, not visible to the guest.
- * They should be chosen so as to not overlap with any host
- * or guest errno.
- */
-
-/*
- * This is returned when a system call should be restarted, to tell the
- * main loop that it should wind the guest PC backwards so it will
- * re-execute the syscall after handling any pending signals.
- */
-#define QEMU_ERESTARTSYS 255
-
-#endif /* SPECIAL_ERRNO_H */
/*
* Utility functions
*/
+static const char *
+get_comma(int last)
+{
+ return (last) ? "" : ",";
+}
+
+/*
+ * Prints out raw parameter using given format. Caller needs
+ * to do byte swapping if needed.
+ */
+static void
+print_raw_param(const char *fmt, abi_long param, int last)
+{
+ char format[64];
+
+ (void)snprintf(format, sizeof(format), "%s%s", fmt, get_comma(last));
+ gemu_log(format, param);
+}
static void print_sysctl(const struct syscallname *name, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5,
print_syscall_ret(num, ret, openbsd_scnames, ARRAY_SIZE(openbsd_scnames));
}
+
+static void
+print_signal(abi_ulong arg, int last)
+{
+ const char *signal_name = NULL;
+ switch (arg) {
+ case TARGET_SIGHUP:
+ signal_name = "SIGHUP";
+ break;
+ case TARGET_SIGINT:
+ signal_name = "SIGINT";
+ break;
+ case TARGET_SIGQUIT:
+ signal_name = "SIGQUIT";
+ break;
+ case TARGET_SIGILL:
+ signal_name = "SIGILL";
+ break;
+ case TARGET_SIGABRT:
+ signal_name = "SIGABRT";
+ break;
+ case TARGET_SIGFPE:
+ signal_name = "SIGFPE";
+ break;
+ case TARGET_SIGKILL:
+ signal_name = "SIGKILL";
+ break;
+ case TARGET_SIGSEGV:
+ signal_name = "SIGSEGV";
+ break;
+ case TARGET_SIGPIPE:
+ signal_name = "SIGPIPE";
+ break;
+ case TARGET_SIGALRM:
+ signal_name = "SIGALRM";
+ break;
+ case TARGET_SIGTERM:
+ signal_name = "SIGTERM";
+ break;
+ case TARGET_SIGUSR1:
+ signal_name = "SIGUSR1";
+ break;
+ case TARGET_SIGUSR2:
+ signal_name = "SIGUSR2";
+ break;
+ case TARGET_SIGCHLD:
+ signal_name = "SIGCHLD";
+ break;
+ case TARGET_SIGCONT:
+ signal_name = "SIGCONT";
+ break;
+ case TARGET_SIGSTOP:
+ signal_name = "SIGSTOP";
+ break;
+ case TARGET_SIGTTIN:
+ signal_name = "SIGTTIN";
+ break;
+ case TARGET_SIGTTOU:
+ signal_name = "SIGTTOU";
+ break;
+ }
+ if (signal_name == NULL) {
+ print_raw_param("%ld", arg, last);
+ return;
+ }
+ gemu_log("%s%s", signal_name, get_comma(last));
+}
+
+void print_taken_signal(int target_signum, const target_siginfo_t *tinfo)
+{
+ /*
+ * Print the strace output for a signal being taken:
+ * --- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} ---
+ */
+ gemu_log("%d ", getpid());
+ gemu_log("--- ");
+ print_signal(target_signum, 1);
+ gemu_log(" ---\n");
+}
#define _SYSCALL_DEFS_H_
#include <sys/syscall.h>
+#include <sys/resource.h>
#include "errno_defs.h"
--- /dev/null
+# See docs/tracing.txt for syntax documentation.
+
+# bsd-user/signal.c
+user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
+user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
+user_do_rt_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
+user_do_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
+user_dump_core_and_abort(void *env, int target_sig, int host_sig) "env=%p signal %d (host %d)"
+user_handle_signal(void *env, int target_sig) "env=%p signal %d"
+user_host_signal(void *env, int host_sig, int target_sig) "env=%p signal %d (target %d("
+user_queue_signal(void *env, int target_sig) "env=%p signal %d"
--- /dev/null
+#include "trace/trace-bsd_user.h"
return 0;
}
+/*
+ * Compare to amd64/amd64/exec_machdep.c sendsig()
+ * Assumes that the memory is locked if frame points to user memory.
+ */
+abi_long setup_sigframe_arch(CPUX86State *env, abi_ulong frame_addr,
+ struct target_sigframe *frame, int flags)
+{
+ target_mcontext_t *mcp = &frame->sf_uc.uc_mcontext;
+
+ get_mcontext(env, mcp, flags);
+ return 0;
+}
+
/* Compare to amd64/amd64/machdep.c get_mcontext() */
abi_long get_mcontext(CPUX86State *regs,
target_mcontext_t *mcp, int flags)
#define _TARGET_ARCH_CPU_H_
#include "target_arch.h"
+#include "signal-common.h"
#define TARGET_DEFAULT_CPU_MODEL "qemu64"
env->regs[R_EAX] = 0;
}
-static inline void target_cpu_reset(CPUArchState *cpu)
+static inline void target_cpu_reset(CPUArchState *env)
{
- cpu_reset(env_cpu(cpu));
+ cpu_reset(env_cpu(env));
}
#endif /* ! _TARGET_ARCH_CPU_H_ */
common_user_inc += include_directories('host/' / host_arch)
-common_user_ss.add(files(
+user_ss.add(files(
'safe-syscall.S',
'safe-syscall-error.c',
))
CONFIG_PCI=y
CONFIG_PCI_DEVICES=y
CONFIG_VGA_ISA=y
-CONFIG_VGA_ISA_MM=y
+CONFIG_VGA_MMIO=y
CONFIG_VGA_CIRRUS=y
CONFIG_VMWARE_VGA=y
CONFIG_SERIAL=y
TMPO="${TMPDIR1}/${TMPB}.o"
TMPCXX="${TMPDIR1}/${TMPB}.cxx"
TMPE="${TMPDIR1}/${TMPB}.exe"
-TMPTXT="${TMPDIR1}/${TMPB}.txt"
rm -f config.log
EXTRA_LDFLAGS=""
xen_ctrl_version="$default_feature"
-xfs="$default_feature"
membarrier="$default_feature"
vhost_kernel="$default_feature"
vhost_net="$default_feature"
sanitizers="no"
tsan="no"
fortify_source="$default_feature"
-strip_opt="yes"
-mingw32="no"
gcov="no"
EXESUF=""
modules="no"
module_upgrades="no"
prefix="/usr/local"
qemu_suffix="qemu"
-bsd="no"
-linux="no"
-solaris="no"
profiler="no"
softmmu="yes"
-linux_user="no"
-bsd_user="no"
+linux_user=""
+bsd_user=""
pkgversion=""
pie=""
qom_cast_debug="yes"
cpuid_h="no"
avx2_opt="$default_feature"
guest_agent="$default_feature"
-guest_agent_with_vss="no"
-guest_agent_ntddscsi="no"
vss_win32_sdk="$default_feature"
win_sdk="no"
want_tools="$default_feature"
;;
--cross-cc-*[!a-zA-Z0-9_-]*=*) error_exit "Passed bad --cross-cc-FOO option"
;;
- --cross-cc-cflags-*) cc_arch=${opt#--cross-cc-flags-}; cc_arch=${cc_arch%%=*}
+ --cross-cc-cflags-*) cc_arch=${opt#--cross-cc-cflags-}; cc_arch=${cc_arch%%=*}
eval "cross_cc_cflags_${cc_arch}=\$optarg"
cross_cc_vars="$cross_cc_vars cross_cc_cflags_${cc_arch}"
;;
# OS specific
+mingw32="no"
+bsd="no"
+linux="no"
+solaris="no"
case $targetos in
windows)
mingw32="yes"
;;
freebsd)
bsd="yes"
- bsd_user="yes"
make="${MAKE-gmake}"
# needed for kinfo_getvmmap(3) in libutil.h
;;
;;
linux)
linux="yes"
- linux_user="yes"
vhost_user=${default_feature:-yes}
;;
esac
debug_tcg="yes"
debug_mutex="yes"
debug="yes"
- strip_opt="no"
fortify_source="no"
;;
--enable-sanitizers) sanitizers="yes"
;;
--disable-tsan) tsan="no"
;;
- --disable-strip) strip_opt="no"
- ;;
--disable-slirp) slirp="disabled"
;;
--enable-slirp) slirp="enabled"
;;
--enable-opengl) opengl="yes"
;;
- --disable-xfsctl) xfs="no"
- ;;
- --enable-xfsctl) xfs="yes"
- ;;
--disable-zlib-test)
;;
--enable-guest-agent) guest_agent="yes"
cross_cc_vars="$cross_cc_vars cross_cc_${cpu}"
fi
-# For user-mode emulation the host arch has to be one we explicitly
-# support, even if we're using TCI.
-if [ "$ARCH" = "unknown" ]; then
- bsd_user="no"
- linux_user="no"
-fi
-
default_target_list=""
deprecated_targets_list=ppc64abi32-linux-user
deprecated_features=""
mak_wilds=""
+if [ "$linux_user" != no ]; then
+ if [ "$targetos" = linux ] && [ -d $source_path/linux-user/include/host/$cpu ]; then
+ linux_user=yes
+ elif [ "$linux_user" = yes ]; then
+ error_exit "linux-user not supported on this architecture"
+ fi
+fi
+if [ "$bsd_user" != no ]; then
+ if [ "$bsd_user" = "" ]; then
+ test $targetos = freebsd && bsd_user=yes
+ fi
+ if [ "$bsd_user" = yes ] && ! [ -d $source_path/bsd-user/$targetos ]; then
+ error_exit "bsd-user not supported on this host OS"
+ fi
+fi
if [ "$softmmu" = "yes" ]; then
mak_wilds="${mak_wilds} $source_path/configs/targets/*-softmmu.mak"
fi
--extra-cxxflags=CXXFLAGS append extra C++ compiler flags CXXFLAGS
--extra-ldflags=LDFLAGS append extra linker flags LDFLAGS
--cross-cc-ARCH=CC use compiler when building ARCH guest test cases
- --cross-cc-flags-ARCH= use compiler flags when building ARCH guest tests
+ --cross-cc-cflags-ARCH= use compiler flags when building ARCH guest tests
--make=MAKE use specified make [$make]
--python=PYTHON use specified python [$python]
--sphinx-build=SPHINX use specified sphinx-build [$sphinx_build]
--enable-debug enable common debug build options
--enable-sanitizers enable default sanitizers
--enable-tsan enable thread sanitizer
- --disable-strip disable stripping binaries
--disable-werror disable compilation abort on warning
--disable-stack-protector disable compiler-provided stack protection
--audio-drv-list=LIST set audio drivers to try if -audiodev is not used
avx512f AVX512F optimization support
replication replication support
opengl opengl support
- xfsctl xfsctl support
qom-cast-debug cast debugging support
tools build qemu-io, qemu-nbd and qemu-img tools
bochs bochs image format support
plugins="no"
fi
fi
+test "$plugins" = "" && plugins=yes
cat > $TMPC << EOF
fi
fi
-##########################################
-# xfsctl() probe, used for file-posix.c
-if test "$xfs" != "no" ; then
- cat > $TMPC << EOF
-#include <stddef.h> /* NULL */
-#include <xfs/xfs.h>
-int main(void)
-{
- xfsctl(NULL, 0, 0, NULL);
- return 0;
-}
-EOF
- if compile_prog "" "" ; then
- xfs="yes"
- else
- if test "$xfs" = "yes" ; then
- feature_not_found "xfs" "Install xfsprogs/xfslibs devel"
- fi
- xfs=no
- fi
-fi
-
-##########################################
-# plugin linker support probe
-
-if test "$plugins" != "no"; then
-
- #########################################
- # See if --dynamic-list is supported by the linker
-
- ld_dynamic_list="no"
- cat > $TMPTXT <<EOF
-{
- foo;
-};
-EOF
-
- cat > $TMPC <<EOF
-#include <stdio.h>
-void foo(void);
-
-void foo(void)
-{
- printf("foo\n");
-}
-
-int main(void)
-{
- foo();
- return 0;
-}
-EOF
-
- if compile_prog "" "-Wl,--dynamic-list=$TMPTXT" ; then
- ld_dynamic_list="yes"
- fi
-
- #########################################
- # See if -exported_symbols_list is supported by the linker
-
- ld_exported_symbols_list="no"
- cat > $TMPTXT <<EOF
- _foo
-EOF
-
- if compile_prog "" "-Wl,-exported_symbols_list,$TMPTXT" ; then
- ld_exported_symbols_list="yes"
- fi
-
- if test "$ld_dynamic_list" = "no" &&
- test "$ld_exported_symbols_list" = "no" ; then
- if test "$plugins" = "yes"; then
- error_exit \
- "Plugin support requires dynamic linking and specifying a set of symbols " \
- "that are exported to plugins. Unfortunately your linker doesn't " \
- "support the flag (--dynamic-list or -exported_symbols_list) used " \
- "for this purpose."
- else
- plugins="no"
- fi
- else
- plugins="yes"
- fi
-fi
-
##########################################
# glib support probe
##########################################
# check if we have VSS SDK headers for win
+guest_agent_with_vss="no"
if test "$mingw32" = "yes" && test "$guest_agent" != "no" && \
test "$vss_win32_sdk" != "no" ; then
case "$vss_win32_sdk" in
echo "ERROR: The headers are extracted in the directory \`inc'."
feature_not_found "VSS support"
fi
- guest_agent_with_vss="no"
fi
fi
##########################################
# check if mingw environment provides a recent ntddscsi.h
+guest_agent_ntddscsi="no"
if test "$mingw32" = "yes" && test "$guest_agent" != "no"; then
cat > $TMPC << EOF
#include <windows.h>
if test "$debug_tcg" = "yes" ; then
echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak
fi
-if test "$strip_opt" = "yes" ; then
- echo "STRIP=${strip}" >> $config_host_mak
-fi
if test "$mingw32" = "yes" ; then
echo "CONFIG_WIN32=y" >> $config_host_mak
if test "$guest_agent_with_vss" = "yes" ; then
if test "$block_drv_whitelist_tools" = "yes" ; then
echo "CONFIG_BDRV_WHITELIST_TOOLS=y" >> $config_host_mak
fi
-if test "$xfs" = "yes" ; then
- echo "CONFIG_XFS=y" >> $config_host_mak
-fi
qemu_version=$(head $source_path/VERSION)
echo "PKGVERSION=$pkgversion" >>$config_host_mak
echo "SRC_PATH=$source_path" >> $config_host_mak
if test "$plugins" = "yes" ; then
echo "CONFIG_PLUGIN=y" >> $config_host_mak
- # Copy the export object list to the build dir
- if test "$ld_dynamic_list" = "yes" ; then
- echo "CONFIG_HAS_LD_DYNAMIC_LIST=yes" >> $config_host_mak
- ld_symbols=qemu-plugins-ld.symbols
- cp "$source_path/plugins/qemu-plugins.symbols" $ld_symbols
- elif test "$ld_exported_symbols_list" = "yes" ; then
- echo "CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST=yes" >> $config_host_mak
- ld64_symbols=qemu-plugins-ld64.symbols
- echo "# Automatically generated by configure - do not modify" > $ld64_symbols
- grep 'qemu_' "$source_path/plugins/qemu-plugins.symbols" | sed 's/;//g' | \
- sed -E 's/^[[:space:]]*(.*)/_\1/' >> $ld64_symbols
- else
- error_exit \
- "If \$plugins=yes, either \$ld_dynamic_list or " \
- "\$ld_exported_symbols_list should have been set to 'yes'."
- fi
fi
if test -n "$gdb_bin"; then
echo "GLIB_VERSION=$(pkg-config --modversion glib-2.0)" >> $config_host_mak
echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak
echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak
+echo "STRIP=$strip" >> $config_host_mak
echo "EXESUF=$EXESUF" >> $config_host_mak
echo "LIBS_QGA=$libs_qga" >> $config_host_mak
# so the build tree will be missing the link back to the new file, and
# tests might fail. Prefer to keep the relevant files in their own
# directory and symlink the directory instead.
-# UNLINK is used to remove symlinks from older development versions
-# that might get into the way when doing "git update" without doing
-# a "make distclean" in between.
LINKS="Makefile"
LINKS="$LINKS tests/tcg/Makefile.target"
LINKS="$LINKS pc-bios/optionrom/Makefile"
LINKS="$LINKS tests/qemu-iotests/check"
LINKS="$LINKS python"
LINKS="$LINKS contrib/plugins/Makefile "
-UNLINK="pc-bios/keymaps"
for bios_file in \
$source_path/pc-bios/*.bin \
$source_path/pc-bios/*.elf \
symlink "$source_path/$f" "$f"
fi
done
-for f in $UNLINK ; do
- if [ -L "$f" ]; then
- rm -f "$f"
- fi
-done
(for i in $cross_cc_vars; do
export $i
$source_path/tests/tcg/configure.sh)
# temporary config to build submodules
-for rom in seabios; do
+if test -f $source_path/roms/seabios/Makefile; then
+ for rom in seabios; do
config_mak=roms/$rom/config.mak
echo "# Automatically generated by configure - do not modify" > $config_mak
echo "SRC_PATH=$source_path/roms/$rom" >> $config_mak
echo "IASL=$iasl" >> $config_mak
echo "LD=$ld" >> $config_mak
echo "RANLIB=$ranlib" >> $config_mak
-done
+ done
+fi
config_mak=pc-bios/optionrom/config.mak
echo "# Automatically generated by configure - do not modify" > $config_mak
-Doptimization=$(if test "$debug" = yes; then echo 0; else echo 2; fi) \
-Ddebug=$(if test "$debug_info" = yes; then echo true; else echo false; fi) \
-Dwerror=$(if test "$werror" = yes; then echo true; else echo false; fi) \
- -Dstrip=$(if test "$strip_opt" = yes; then echo true; else echo false; fi) \
-Db_pie=$(if test "$pie" = yes; then echo true; else echo false; fi) \
-Db_coverage=$(if test "$gcov" = yes; then echo true; else echo false; fi) \
-Db_lto=$lto -Dcfi=$cfi -Dtcg=$tcg -Dxen=$xen \
echo " features: ${deprecated_features}"
fi
-# Create list of config switches that should be poisoned in common code...
-# but filter out CONFIG_TCG and CONFIG_USER_ONLY which are special.
-target_configs_h=$(ls *-config-devices.h *-config-target.h 2>/dev/null)
-if test -n "$target_configs_h" ; then
- sed -n -e '/CONFIG_TCG/d' -e '/CONFIG_USER_ONLY/d' \
- -e '/^#define / { s///; s/ .*//; s/^/#pragma GCC poison /p; }' \
- $target_configs_h | sort -u > config-poison.h
-else
- :> config-poison.h
-fi
-
# Save the configure command line for later reuse.
cat <<EOD >config.status
#!/bin/sh
if curl.found()
- executable('elf2dmp', files('main.c', 'addrspace.c', 'download.c', 'pdb.c', 'qemu_elf.c'),
+ executable('elf2dmp', files('main.c', 'addrspace.c', 'download.c', 'pdb.c', 'qemu_elf.c'), genh,
dependencies: [glib, curl],
install: true)
endif
-executable('ivshmem-client', files('ivshmem-client.c', 'main.c'),
+executable('ivshmem-client', files('ivshmem-client.c', 'main.c'), genh,
dependencies: glib,
build_by_default: targetos == 'linux',
install: false)
-executable('ivshmem-server', files('ivshmem-server.c', 'main.c'),
+executable('ivshmem-server', files('ivshmem-server.c', 'main.c'), genh,
dependencies: [qemuutil, rt],
build_by_default: targetos == 'linux',
install: false)
# if not found, CONFIG_PVRDMA should not be set
# FIXME: broken on big endian architectures
libumad = cc.find_library('ibumad', required: true)
- executable('rdmacm-mux', files('main.c'),
+ executable('rdmacm-mux', files('main.c'), genh,
dependencies: [glib, libumad],
build_by_default: false,
install: false)
all arguments passed to ``device_add`` are consistent with the documented
property types.
+``query-sgx`` return value member ``section-size`` (since 7.0)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Member ``section-size`` in return value elements with meta-type ``uint64`` is
+deprecated. Use ``sections`` instead.
+
+
+``query-sgx-capabilities`` return value member ``section-size`` (since 7.0)
+'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+Member ``section-size`` in return value elements with meta-type ``uint64`` is
+deprecated. Use ``sections`` instead.
+
System accelerators
-------------------
+++ /dev/null
-QEMU CAN bus emulation support
-==============================
-
-The CAN bus emulation provides mechanism to connect multiple
-emulated CAN controller chips together by one or multiple CAN busses
-(the controller device "canbus" parameter). The individual busses
-can be connected to host system CAN API (at this time only Linux
-SocketCAN is supported).
-
-The concept of busses is generic and different CAN controllers
-can be implemented.
-
-The initial submission implemented SJA1000 controller which
-is common and well supported by by drivers for the most operating
-systems.
-
-The PCI addon card hardware has been selected as the first CAN
-interface to implement because such device can be easily connected
-to systems with different CPU architectures (x86, PowerPC, Arm, etc.).
-
-In 2020, CTU CAN FD controller model has been added as part
-of the bachelor thesis of Jan Charvat. This controller is complete
-open-source/design/hardware solution. The core designer
-of the project is Ondrej Ille, the financial support has been
-provided by CTU, and more companies including Volkswagen subsidiaries.
-
-The project has been initially started in frame of RTEMS GSoC 2013
-slot by Jin Yang under our mentoring The initial idea was to provide generic
-CAN subsystem for RTEMS. But lack of common environment for code and RTEMS
-testing lead to goal change to provide environment which provides complete
-emulated environment for testing and RTEMS GSoC slot has been donated
-to work on CAN hardware emulation on QEMU.
-
-Examples how to use CAN emulation for SJA1000 based boards
-==========================================================
-
-When QEMU with CAN PCI support is compiled then one of the next
-CAN boards can be selected
-
- (1) CAN bus Kvaser PCI CAN-S (single SJA1000 channel) boad. QEMU startup options
- -object can-bus,id=canbus0
- -device kvaser_pci,canbus=canbus0
- Add "can-host-socketcan" object to connect device to host system CAN bus
- -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0
-
- (2) CAN bus PCM-3680I PCI (dual SJA1000 channel) emulation
- -object can-bus,id=canbus0
- -device pcm3680_pci,canbus0=canbus0,canbus1=canbus0
-
- another example:
- -object can-bus,id=canbus0
- -object can-bus,id=canbus1
- -device pcm3680_pci,canbus0=canbus0,canbus1=canbus1
-
- (3) CAN bus MIOe-3680 PCI (dual SJA1000 channel) emulation
- -device mioe3680_pci,canbus0=canbus0
-
-
-The ''kvaser_pci'' board/device model is compatible with and has been tested with
-''kvaser_pci'' driver included in mainline Linux kernel.
-The tested setup was Linux 4.9 kernel on the host and guest side.
-Example for qemu-system-x86_64:
-
- qemu-system-x86_64 -accel kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \
- -initrd ramdisk.cpio \
- -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
- -object can-bus,id=canbus0 \
- -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
- -device kvaser_pci,canbus=canbus0 \
- -nographic -append "console=ttyS0"
-
-Example for qemu-system-arm:
-
- qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \
- -kernel kernel-qemu-arm1176-versatilepb \
- -hda rpi-wheezy-overlay \
- -append "console=ttyAMA0 root=/dev/sda2 ro init=/sbin/init-overlay" \
- -nographic \
- -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
- -object can-bus,id=canbus0 \
- -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
- -device kvaser_pci,canbus=canbus0,host=can0 \
-
-The CAN interface of the host system has to be configured for proper
-bitrate and set up. Configuration is not propagated from emulated
-devices through bus to the physical host device. Example configuration
-for 1 Mbit/s
-
- ip link set can0 type can bitrate 1000000
- ip link set can0 up
-
-Virtual (host local only) can interface can be used on the host
-side instead of physical interface
-
- ip link add dev can0 type vcan
-
-The CAN interface on the host side can be used to analyze CAN
-traffic with "candump" command which is included in "can-utils".
-
- candump can0
-
-CTU CAN FD support examples
-===========================
-
-This open-source core provides CAN FD support. CAN FD drames are
-delivered even to the host systems when SocketCAN interface is found
-CAN FD capable.
-
-The PCIe board emulation is provided for now (the device identifier is
-ctucan_pci). The default build defines two CTU CAN FD cores
-on the board.
-
-Example how to connect the canbus0-bus (virtual wire) to the host
-Linux system (SocketCAN used) and to both CTU CAN FD cores emulated
-on the corresponding PCI card expects that host system CAN bus
-is setup according to the previous SJA1000 section.
-
- qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.19.52+ \
- -initrd ramdisk.cpio \
- -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
- -vga cirrus \
- -append "console=ttyS0" \
- -object can-bus,id=canbus0-bus \
- -object can-host-socketcan,if=can0,canbus=canbus0-bus,id=canbus0-socketcan \
- -device ctucan_pci,canbus0=canbus0-bus,canbus1=canbus0-bus \
- -nographic
-
-Setup of CTU CAN FD controller in a guest Linux system
-
- insmod ctucanfd.ko || modprobe ctucanfd
- insmod ctucanfd_pci.ko || modprobe ctucanfd_pci
-
- for ifc in /sys/class/net/can* ; do
- if [ -e $ifc/device/vendor ] ; then
- if ! grep -q 0x1760 $ifc/device/vendor ; then
- continue;
- fi
- else
- continue;
- fi
- if [ -e $ifc/device/device ] ; then
- if ! grep -q 0xff00 $ifc/device/device ; then
- continue;
- fi
- else
- continue;
- fi
- ifc=$(basename $ifc)
- /bin/ip link set $ifc type can bitrate 1000000 dbitrate 10000000 fd on
- /bin/ip link set $ifc up
- done
-
-The test can run for example
-
- candump can1
-
-in the guest system and next commands in the host system for basic CAN
-
- cangen can0
-
-for CAN FD without bitrate switch
-
- cangen can0 -f
-
-and with bitrate switch
-
- cangen can0 -b
-
-The test can be run viceversa, generate messages in the guest system and capture them
-in the host one and much more combinations.
-
-Links to other resources
-========================
-
- (1) CAN related projects at Czech Technical University, Faculty of Electrical Engineering
- http://canbus.pages.fel.cvut.cz/
- (2) Repository with development can-pci branch at Czech Technical University
- https://gitlab.fel.cvut.cz/canbus/qemu-canbus
- (3) RTEMS page describing project
- https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation
- (4) RTLWS 2015 article about the project and its use with CANopen emulation
- http://cmp.felk.cvut.cz/~pisa/can/doc/rtlws-17-pisa-qemu-can.pdf
- (5) GNU/Linux, CAN and CANopen in Real-time Control Applications
- Slides from LinuxDays 2017 (include updated RTLWS 2015 content)
- https://www.linuxdays.cz/2017/video/Pavel_Pisa-CAN_canopen.pdf
- (6) Linux SocketCAN utilities
- https://github.com/linux-can/can-utils/
- (7) CTU CAN FD project including core VHDL design, Linux driver,
- test utilities etc.
- https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core
- (8) CTU CAN FD Core Datasheet Documentation
- http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/Progdokum.pdf
- (9) CTU CAN FD Core System Architecture Documentation
- http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/ctu_can_fd_architecture.pdf
- (10) CTU CAN FD Driver Documentation
- http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/driver_doc/ctucanfd-driver.html
- (11) Integration with PCIe interfacing for Intel/Altera Cyclone IV based board
- https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd
# General information about the project.
project = u'QEMU'
-copyright = u'2021, The QEMU Project Developers'
+copyright = u'2022, The QEMU Project Developers'
author = u'The QEMU Project Developers'
# The version info for the project you're documenting, acts as replacement for
You initialize a pure container with memory_region_init().
-- alias: a subsection of another region. Aliases allow a region to be
- split apart into discontiguous regions. Examples of uses are memory banks
- used when the guest address space is smaller than the amount of RAM
- addressed, or a memory controller that splits main memory to expose a "PCI
- hole". Aliases may point to any type of region, including other aliases,
+- alias: a subsection of another region. Aliases allow a region to be
+ split apart into discontiguous regions. Examples of uses are memory
+ banks used when the guest address space is smaller than the amount
+ of RAM addressed, or a memory controller that splits main memory to
+ expose a "PCI hole". You can also create aliases to avoid trying to
+ add the original region to multiple parents via
+ `memory_region_add_subregion`.
+
+ Aliases may point to any type of region, including other aliases,
but an alias may not point back to itself, directly or indirectly.
You initialize these with memory_region_init_alias().
``$(prefix)qapi-commands.h``
Function prototypes for the QMP commands specified in the schema
+ ``$(prefix)qapi-commands.trace-events``
+ Trace event declarations, see :ref:`tracing`.
+
``$(prefix)qapi-init-commands.h``
Command initialization prototype
void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp);
#endif /* EXAMPLE_QAPI_COMMANDS_H */
+
+ $ cat qapi-generated/example-qapi-commands.trace-events
+ # AUTOMATICALLY GENERATED, DO NOT MODIFY
+
+ qmp_enter_my_command(const char *json) "%s"
+ qmp_exit_my_command(const char *result, bool succeeded) "%s %d"
+
$ cat qapi-generated/example-qapi-commands.c
[Uninteresting stuff omitted...]
goto out;
}
+ if (trace_event_get_state_backends(TRACE_QMP_ENTER_MY_COMMAND)) {
+ g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args));
+
+ trace_qmp_enter_my_command(req_json->str);
+ }
+
retval = qmp_my_command(arg.arg1, &err);
- error_propagate(errp, err);
if (err) {
+ trace_qmp_exit_my_command(error_get_pretty(err), false);
+ error_propagate(errp, err);
goto out;
}
qmp_marshal_output_UserDefOne(retval, ret, errp);
+ if (trace_event_get_state_backends(TRACE_QMP_EXIT_MY_COMMAND)) {
+ g_autoptr(GString) ret_json = qobject_to_json(*ret);
+
+ trace_qmp_exit_my_command(ret_json->str, true);
+ }
+
out:
visit_free(v);
v = qapi_dealloc_visitor_new();
lock held, the function that expects the lock to be already held
usually uses the suffix ``_locked``.
+If a function is a shim designed to deal with compatibility
+workarounds we use the suffix ``_compat``. These are generally not
+called directly and aliased to the plain function name via the
+pre-processor. Another common suffix is ``_impl``; it is used for the
+concrete implementation of a function that will not be called
+directly, but rather through a macro or an inline function.
Block structure
===============
C standard, implementation defined and undefined behaviors
==========================================================
-C code in QEMU should be written to the C99 language specification. A copy
-of the final version of the C99 standard with corrigenda TC1, TC2, and TC3
-included, formatted as a draft, can be downloaded from:
+C code in QEMU should be written to the C11 language specification. A
+copy of the final version of the C11 standard formatted as a draft,
+can be downloaded from:
- `<http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf>`_
+ `<http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf>`_
The C language specification defines regions of undefined behavior and
implementation defined behavior (to give compiler authors enough leeway to
in ``tests/docker/dockerfiles/``, called ``centos8.docker``. ``make docker-help``
command will list all the available images.
-To add a new image, simply create a new ``.docker`` file under the
-``tests/docker/dockerfiles/`` directory.
-
A ``.pre`` script can be added beside the ``.docker`` file, which will be
executed before building the image under the build context directory. This is
mainly used to do necessary host side setup. One such setup is ``binfmt_misc``,
for example, to make qemu-user powered cross build containers work.
+Most of the existing Dockerfiles were written by hand, simply by creating a
+a new ``.docker`` file under the ``tests/docker/dockerfiles/`` directory.
+This has led to an inconsistent set of packages being present across the
+different containers.
+
+Thus going forward, QEMU is aiming to automatically generate the Dockerfiles
+using the ``lcitool`` program provided by the ``libvirt-ci`` project:
+
+ https://gitlab.com/libvirt/libvirt-ci
+
+In that project, there is a ``mappings.yml`` file defining the distro native
+package names for a wide variety of third party projects. This is processed
+in combination with a project defined list of build pre-requisites to determine
+the list of native packages to install on each distribution. This can be used
+to generate dockerfiles, VM package lists and Cirrus CI variables needed to
+setup build environments across OS distributions with a consistent set of
+packages present.
+
+When preparing a patch series that adds a new build pre-requisite to QEMU,
+updates to various lcitool data files may be required.
+
+
+Adding new build pre-requisites
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In the simple case where the pre-requisite is already known to ``libvirt-ci``
+the following steps are needed
+
+ * Edit ``tests/lcitool/projects/qemu.yml`` and add the pre-requisite
+
+ * Run ``make lcitool-refresh`` to re-generate all relevant build environment
+ manifests
+
+In some cases ``libvirt-ci`` will not know about the build pre-requisite and
+thus some extra preparation steps will be required first
+
+ * Fork the ``libvirt-ci`` project on gitlab
+
+ * Edit the ``mappings.yml`` change to add an entry for the new build
+ prerequisite, listing its native package name on as many OS distros
+ as practical.
+
+ * Commit the ``mappings.yml`` change and submit a merge request to
+ the ``libvirt-ci`` project, noting in the description that this
+ is a new build pre-requisite desired for use with QEMU
+
+ * CI pipeline will run to validate that the changes to ``mappings.yml``
+ are correct, by attempting to install the newly listed package on
+ all OS distributions supported by ``libvirt-ci``.
+
+ * Once the merge request is accepted, go back to QEMU and update
+ the ``libvirt-ci`` submodule to point to a commit that contains
+ the ``mappings.yml`` update.
+
+
+Adding new OS distros
+^^^^^^^^^^^^^^^^^^^^^
+
+In some cases ``libvirt-ci`` will not know about the OS distro that is
+desired to be tested. Before adding a new OS distro, discuss the proposed
+addition:
+
+ * Send a mail to qemu-devel, copying people listed in the
+ MAINTAINERS file for ``Build and test automation``.
+
+ There are limited CI compute resources available to QEMU, so the
+ cost/benefit tradeoff of adding new OS distros needs to be considered.
+
+ * File an issue at https://gitlab.com/libvirt/libvirt-ci/-/issues
+ pointing to the qemu-devel mail thread in the archives.
+
+ This alerts other people who might be interested in the work
+ to avoid duplication, as well as to get feedback from libvirt-ci
+ maintainers on any tips to ease the addition
+
+Assuming there is agreement to add a new OS distro then
+
+ * Fork the ``libvirt-ci`` project on gitlab
+
+ * Add metadata under ``guests/lcitool/lcitool/ansible/group_vars/``
+ for the new OS distro. There might be code changes required if
+ the OS distro uses a package format not currently known. The
+ ``libvirt-ci`` maintainers can advise on this when the issue
+ is file.
+
+ * Edit the ``mappings.yml`` change to update all the existing package
+ entries, providing details of the new OS distro
+
+ * Commit the ``mappings.yml`` change and submit a merge request to
+ the ``libvirt-ci`` project, noting in the description that this
+ is a new build pre-requisite desired for use with QEMU
+
+ * CI pipeline will run to validate that the changes to ``mappings.yml``
+ are correct, by attempting to install the newly listed package on
+ all OS distributions supported by ``libvirt-ci``.
+
+ * Once the merge request is accepted, go back to QEMU and update
+ the ``libvirt-ci`` submodule to point to a commit that contains
+ the ``mappings.yml`` update.
+
+
Tests
~~~~~
$(configure) --cross-cc-aarch64=aarch64-cc
-There is also a ``--cross-cc-flags-ARCH`` flag in case additional
+There is also a ``--cross-cc-cflags-ARCH`` flag in case additional
compiler flags are needed to build for a given target.
If you have the ability to run containers as the user the build system
+.. _tracing:
+
=======
Tracing
=======
+++ /dev/null
-POWER (PAPR) Protected Execution Facility (PEF)
-===============================================
-
-Protected Execution Facility (PEF), also known as Secure Guest support
-is a feature found on IBM POWER9 and POWER10 processors.
-
-If a suitable firmware including an Ultravisor is installed, it adds
-an extra memory protection mode to the CPU. The ultravisor manages a
-pool of secure memory which cannot be accessed by the hypervisor.
-
-When this feature is enabled in QEMU, a guest can use ultracalls to
-enter "secure mode". This transfers most of its memory to secure
-memory, where it cannot be eavesdropped by a compromised hypervisor.
-
-Launching
----------
-
-To launch a guest which will be permitted to enter PEF secure mode:
-
-# ${QEMU} \
- -object pef-guest,id=pef0 \
- -machine confidential-guest-support=pef0 \
- ...
-
-Live Migration
-----------------
-
-Live migration is not yet implemented for PEF guests. For
-consistency, we currently prevent migration if the PEF feature is
-enabled, whether or not the guest has actually entered secure mode.
+======================
sPAPR hypervisor calls
-----------------------
+======================
When used with the ``pseries`` machine type, ``qemu-system-ppc64`` implements
-a set of hypervisor calls (a.k.a. hcalls) defined in the `Linux on Power
-Architecture Reference document (LoPAR)
-<https://cdn.openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
-This document is a subset of the Power Architecture Platform Reference (PAPR+)
-specification (IBM internal only), which is what PowerVM, the IBM proprietary
-hypervisor, adheres to.
+a set of hypervisor calls (a.k.a. hcalls) defined in the Linux on Power
+Architecture Reference ([LoPAR]_) document. This document is a subset of the
+Power Architecture Platform Reference (PAPR+) specification (IBM internal only),
+which is what PowerVM, the IBM proprietary hypervisor, adheres to.
The subset in LoPAR is selected based on the requirements of Linux as a guest.
All those hypercalls start at hcall number 0xf000 which correspond
to an implementation specific range in PAPR.
-H_RTAS (0xf000)
-^^^^^^^^^^^^^^^
+``H_RTAS (0xf000)``
+===================
RTAS stands for Run-Time Abstraction Sercies and is a set of runtime services
generally provided by the firmware inside the guest to the operating system. It
``H_PARAMETER``: Unknown token.
-H_LOGICAL_MEMOP (0xf001)
-^^^^^^^^^^^^^^^^^^^^^^^^
+``H_LOGICAL_MEMOP (0xf001)``
+============================
When the guest runs in "real mode" (in powerpc terminology this means with MMU
disabled, i.e. guest effective address equals to guest physical address), it
--- /dev/null
+=============================
+sPAPR Dynamic Reconfiguration
+=============================
+
+sPAPR or pSeries guests make use of a facility called dynamic reconfiguration
+to handle hot plugging of dynamic "physical" resources like PCI cards, or
+"logical"/para-virtual resources like memory, CPUs, and "physical"
+host-bridges, which are generally managed by the host/hypervisor and provided
+to guests as virtualized resources. The specifics of dynamic reconfiguration
+are documented extensively in section 13 of the Linux on Power Architecture
+Reference document ([LoPAR]_). This document provides a summary of that
+information as it applies to the implementation within QEMU.
+
+Dynamic-reconfiguration Connectors
+==================================
+
+To manage hot plug/unplug of these resources, a firmware abstraction known as
+a Dynamic Resource Connector (DRC) is used to assign a particular dynamic
+resource to the guest, and provide an interface for the guest to manage
+configuration/removal of the resource associated with it.
+
+Device tree description of DRCs
+===============================
+
+A set of four Open Firmware device tree array properties are used to describe
+the name/index/power-domain/type of each DRC allocated to a guest at
+boot time. There may be multiple sets of these arrays, rooted at different
+paths in the device tree depending on the type of resource the DRCs manage.
+
+In some cases, the DRCs themselves may be provided by a dynamic resource,
+such as the DRCs managing PCI slots on a hot plugged PHB. In this case the
+arrays would be fetched as part of the device tree retrieval interfaces
+for hot plugged resources described under :ref:`guest-host-interface`.
+
+The array properties are described below. Each entry/element in an array
+describes the DRC identified by the element in the corresponding position
+of ``ibm,drc-indexes``:
+
+``ibm,drc-names``
+-----------------
+
+ First 4-bytes: big-endian (BE) encoded integer denoting the number of entries.
+
+ Each entry: a NULL-terminated ``<name>`` string encoded as a byte array.
+
+ ``<name>`` values for logical/virtual resources are defined in the Linux on
+ Power Architecture Reference ([LoPAR]_) section 13.5.2.4, and basically
+ consist of the type of the resource followed by a space and a numerical
+ value that's unique across resources of that type.
+
+ ``<name>`` values for "physical" resources such as PCI or VIO devices are
+ defined as being "location codes", which are the "location labels" of each
+ encapsulating device, starting from the chassis down to the individual slot
+ for the device, concatenated by a hyphen. This provides a mapping of
+ resources to a physical location in a chassis for debugging purposes. For
+ QEMU, this mapping is less important, so we assign a location code that
+ conforms to naming specifications, but is simply a location label for the
+ slot by itself to simplify the implementation. The naming convention for
+ location labels is documented in detail in the [LoPAR]_ section 12.3.1.5,
+ and in our case amounts to using ``C<n>`` for PCI/VIO device slots, where
+ ``<n>`` is unique across all PCI/VIO device slots.
+
+``ibm,drc-indexes``
+-------------------
+
+ First 4-bytes: BE-encoded integer denoting the number of entries.
+
+ Each 4-byte entry: BE-encoded ``<index>`` integer that is unique across all
+ DRCs in the machine.
+
+ ``<index>`` is arbitrary, but in the case of QEMU we try to maintain the
+ convention used to assign them to pSeries guests on pHyp (the hypervisor
+ portion of PowerVM):
+
+ ``bit[31:28]``: integer encoding of ``<type>``, where ``<type>`` is:
+
+ ``1`` for CPU resource.
+
+ ``2`` for PHB resource.
+
+ ``3`` for VIO resource.
+
+ ``4`` for PCI resource.
+
+ ``8`` for memory resource.
+
+ ``bit[27:0]``: integer encoding of ``<id>``, where ``<id>`` is unique
+ across all resources of specified type.
+
+``ibm,drc-power-domains``
+-------------------------
+
+ First 4-bytes: BE-encoded integer denoting the number of entries.
+
+ Each 4-byte entry: 32-bit, BE-encoded ``<index>`` integer that specifies the
+ power domain the resource will be assigned to. In the case of QEMU we
+ associated all resources with a "live insertion" domain, where the power is
+ assumed to be managed automatically. The integer value for this domain is a
+ special value of ``-1``.
+
+
+``ibm,drc-types``
+-----------------
+
+ First 4-bytes: BE-encoded integer denoting the number of entries.
+
+ Each entry: a NULL-terminated ``<type>`` string encoded as a byte array.
+ ``<type>`` is assigned as follows:
+
+ "CPU" for a CPU.
+
+ "PHB" for a physical host-bridge.
+
+ "SLOT" for a VIO slot.
+
+ "28" for a PCI slot.
+
+ "MEM" for memory resource.
+
+.. _guest-host-interface:
+
+Guest->Host interface to manage dynamic resources
+=================================================
+
+Each DRC is given a globally unique DRC index, and resources associated with a
+particular DRC are configured/managed by the guest via a number of RTAS calls
+which reference individual DRCs based on the DRC index. This can be considered
+the guest->host interface.
+
+``rtas-set-power-level``
+------------------------
+
+Set the power level for a specified power domain.
+
+ ``arg[0]``: integer identifying power domain.
+
+ ``arg[1]``: new power level for the domain, ``0-100``.
+
+ ``output[0]``: status, ``0`` on success.
+
+ ``output[1]``: power level after command.
+
+``rtas-get-power-level``
+------------------------
+
+Get the power level for a specified power domain.
+
+ ``arg[0]``: integer identifying power domain.
+
+ ``output[0]``: status, ``0`` on success.
+
+ ``output[1]``: current power level.
+
+``rtas-set-indicator``
+----------------------
+
+Set the state of an indicator or sensor.
+
+ ``arg[0]``: integer identifying sensor/indicator type.
+
+ ``arg[1]``: index of sensor, for DR-related sensors this is generally the DRC
+ index.
+
+ ``arg[2]``: desired sensor value.
+
+ ``output[0]``: status, ``0`` on success.
+
+For the purpose of this document we focus on the indicator/sensor types
+associated with a DRC. The types are:
+
+* ``9001``: ``isolation-state``, controls/indicates whether a device has been
+ made accessible to a guest. Supported sensor values:
+
+ ``0``: ``isolate``, device is made inaccessible by guest OS.
+
+ ``1``: ``unisolate``, device is made available to guest OS.
+
+* ``9002``: ``dr-indicator``, controls "visual" indicator associated with
+ device. Supported sensor values:
+
+ ``0``: ``inactive``, resource may be safely removed.
+
+ ``1``: ``active``, resource is in use and cannot be safely removed.
+
+ ``2``: ``identify``, used to visually identify slot for interactive hot plug.
+
+ ``3``: ``action``, in most cases, used in the same manner as identify.
+
+* ``9003``: ``allocation-state``, generally only used for "logical" DR resources
+ to request the allocation/deallocation of a resource prior to acquiring it via
+ ``isolation-state->unisolate``, or after releasing it via
+ ``isolation-state->isolate``, respectively. For "physical" DR (like PCI
+ hot plug/unplug) the pre-allocation of the resource is implied and this sensor
+ is unused. Supported sensor values:
+
+ ``0``: ``unusable``, tell firmware/system the resource can be
+ unallocated/reclaimed and added back to the system resource pool.
+
+ ``1``: ``usable``, request the resource be allocated/reserved for use by
+ guest OS.
+
+ ``2``: ``exchange``, used to allocate a spare resource to use for fail-over
+ in certain situations. Unused in QEMU.
+
+ ``3``: ``recover``, used to reclaim a previously allocated resource that's
+ not currently allocated to the guest OS. Unused in QEMU.
+
+``rtas-get-sensor-state:``
+--------------------------
+
+Used to read an indicator or sensor value.
+
+ ``arg[0]``: integer identifying sensor/indicator type.
+
+ ``arg[1]``: index of sensor, for DR-related sensors this is generally the DRC
+ index
+
+ ``output[0]``: status, 0 on success
+
+For DR-related operations, the only noteworthy sensor is ``dr-entity-sense``,
+which has a type value of ``9003``, as ``allocation-state`` does in the case of
+``rtas-set-indicator``. The semantics/encodings of the sensor values are
+distinct however.
+
+Supported sensor values for ``dr-entity-sense`` (``9003``) sensor:
+
+ ``0``: empty.
+
+ For physical resources: DRC/slot is empty.
+
+ For logical resources: unused.
+
+ ``1``: present.
+
+ For physical resources: DRC/slot is populated with a device/resource.
+
+ For logical resources: resource has been allocated to the DRC.
+
+ ``2``: unusable.
+
+ For physical resources: unused.
+
+ For logical resources: DRC has no resource allocated to it.
+
+ ``3``: exchange.
+
+ For physical resources: unused.
+
+ For logical resources: resource available for exchange (see
+ ``allocation-state`` sensor semantics above).
+
+ ``4``: recovery.
+
+ For physical resources: unused.
+
+ For logical resources: resource available for recovery (see
+ ``allocation-state`` sensor semantics above).
+
+``rtas-ibm-configure-connector``
+--------------------------------
+
+Used to fetch an OpenFirmware device tree description of the resource associated
+with a particular DRC.
+
+ ``arg[0]``: guest physical address of 4096-byte work area buffer.
+
+ ``arg[1]``: 0, or address of additional 4096-byte work area buffer; only
+ non-zero if a prior RTAS response indicated a need for additional memory.
+
+ ``output[0]``: status:
+
+ ``0``: completed transmittal of device tree node.
+
+ ``1``: instruct guest to prepare for next device tree sibling node.
+
+ ``2``: instruct guest to prepare for next device tree child node.
+
+ ``3``: instruct guest to prepare for next device tree property.
+
+ ``4``: instruct guest to ascend to parent device tree node.
+
+ ``5``: instruct guest to provide additional work-area buffer via ``arg[1]``.
+
+ ``990x``: instruct guest that operation took too long and to try again
+ later.
+
+The DRC index is encoded in the first 4-bytes of the first work area buffer.
+Work area (``wa``) layout, using 4-byte offsets:
+
+ ``wa[0]``: DRC index of the DRC to fetch device tree nodes from.
+
+ ``wa[1]``: ``0`` (hard-coded).
+
+ ``wa[2]``:
+
+ For next-sibling/next-child response:
+
+ ``wa`` offset of null-terminated string denoting the new node's name.
+
+ For next-property response:
+
+ ``wa`` offset of null-terminated string denoting new property's name.
+
+ ``wa[3]``: for next-property response (unused otherwise):
+
+ Byte-length of new property's value.
+
+ ``wa[4]``: for next-property response (unused otherwise):
+
+ New property's value, encoded as an OFDT-compatible byte array.
+
+Hot plug/unplug events
+======================
+
+For most DR operations, the hypervisor will issue host->guest add/remove events
+using the EPOW/check-exception notification framework, where the host issues a
+check-exception interrupt, then provides an RTAS event log via an
+rtas-check-exception call issued by the guest in response. This framework is
+documented by PAPR+ v2.7, and already use in by QEMU for generating powerdown
+requests via EPOW events.
+
+For DR, this framework has been extended to include hotplug events, which were
+previously unneeded due to direct manipulation of DR-related guest userspace
+tools by host-level management such as an HMC. This level of management is not
+applicable to KVM on Power, hence the reason for extending the notification
+framework to support hotplug events.
+
+The format for these EPOW-signalled events is described below under
+:ref:`hot-plug-unplug-event-structure`. Note that these events are not formally
+part of the PAPR+ specification, and have been superseded by a newer format,
+also described below under :ref:`hot-plug-unplug-event-structure`, and so are
+now deemed a "legacy" format. The formats are similar, but the "modern" format
+contains additional fields/flags, which are denoted for the purposes of this
+documentation with ``#ifdef GUEST_SUPPORTS_MODERN`` guards.
+
+QEMU should assume support only for "legacy" fields/flags unless the guest
+advertises support for the "modern" format via
+``ibm,client-architecture-support`` hcall by setting byte 5, bit 6 of it's
+``ibm,architecture-vec-5`` option vector structure (as described by [LoPAR]_,
+section B.5.2.3). As with "legacy" format events, "modern" format events are
+surfaced to the guest via check-exception RTAS calls, but use a dedicated event
+source to signal the guest. This event source is advertised to the guest by the
+addition of a ``hot-plug-events`` node under ``/event-sources`` node of the
+guest's device tree using the standard format described in [LoPAR]_,
+section B.5.12.2.
+
+.. _hot-plug-unplug-event-structure:
+
+Hot plug/unplug event structure
+===============================
+
+The hot plug specific payload in QEMU is implemented as follows (with all values
+encoded in big-endian format):
+
+.. code-block:: c
+
+ struct rtas_event_log_v6_hp {
+ #define SECTION_ID_HOTPLUG 0x4850 /* HP */
+ struct section_header {
+ uint16_t section_id; /* set to SECTION_ID_HOTPLUG */
+ uint16_t section_length; /* sizeof(rtas_event_log_v6_hp),
+ * plus the length of the DRC name
+ * if a DRC name identifier is
+ * specified for hotplug_identifier
+ */
+ uint8_t section_version; /* version 1 */
+ uint8_t section_subtype; /* unused */
+ uint16_t creator_component_id; /* unused */
+ } hdr;
+ #define RTAS_LOG_V6_HP_TYPE_CPU 1
+ #define RTAS_LOG_V6_HP_TYPE_MEMORY 2
+ #define RTAS_LOG_V6_HP_TYPE_SLOT 3
+ #define RTAS_LOG_V6_HP_TYPE_PHB 4
+ #define RTAS_LOG_V6_HP_TYPE_PCI 5
+ uint8_t hotplug_type; /* type of resource/device */
+ #define RTAS_LOG_V6_HP_ACTION_ADD 1
+ #define RTAS_LOG_V6_HP_ACTION_REMOVE 2
+ uint8_t hotplug_action; /* action (add/remove) */
+ #define RTAS_LOG_V6_HP_ID_DRC_NAME 1
+ #define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
+ #define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
+ #ifdef GUEST_SUPPORTS_MODERN
+ #define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4
+ #endif
+ uint8_t hotplug_identifier; /* type of the resource identifier,
+ * which serves as the discriminator
+ * for the 'drc' union field below
+ */
+ #ifdef GUEST_SUPPORTS_MODERN
+ uint8_t capabilities; /* capability flags, currently unused
+ * by QEMU
+ */
+ #else
+ uint8_t reserved;
+ #endif
+ union {
+ uint32_t index; /* DRC index of resource to take action
+ * on
+ */
+ uint32_t count; /* number of DR resources to take
+ * action on (guest chooses which)
+ */
+ #ifdef GUEST_SUPPORTS_MODERN
+ struct {
+ uint32_t count; /* number of DR resources to take
+ * action on
+ */
+ uint32_t index; /* DRC index of first resource to take
+ * action on. guest will take action
+ * on DRC index <index> through
+ * DRC index <index + count - 1> in
+ * sequential order
+ */
+ } count_indexed;
+ #endif
+ char name[1]; /* string representing the name of the
+ * DRC to take action on
+ */
+ } drc;
+ } QEMU_PACKED;
+
+``ibm,lrdr-capacity``
+=====================
+
+``ibm,lrdr-capacity`` is a property in the /rtas device tree node that
+identifies the dynamic reconfiguration capabilities of the guest. It consists
+of a triple consisting of ``<phys>``, ``<size>`` and ``<maxcpus>``.
+
+ ``<phys>``, encoded in BE format represents the maximum address in bytes and
+ hence the maximum memory that can be allocated to the guest.
+
+ ``<size>``, encoded in BE format represents the size increments in which
+ memory can be hot-plugged to the guest.
+
+ ``<maxcpus>``, a BE-encoded integer, represents the maximum number of
+ processors that the guest can have.
+
+``pseries`` guests use this property to note the maximum allowed CPUs for the
+guest.
+
+``ibm,dynamic-reconfiguration-memory``
+======================================
+
+``ibm,dynamic-reconfiguration-memory`` is a device tree node that represents
+dynamically reconfigurable logical memory blocks (LMB). This node is generated
+only when the guest advertises the support for it via
+``ibm,client-architecture-support`` call. Memory that is not dynamically
+reconfigurable is represented by ``/memory`` nodes. The properties of this node
+that are of interest to the sPAPR memory hotplug implementation in QEMU are
+described here.
+
+``ibm,lmb-size``
+----------------
+
+This 64-bit integer defines the size of each dynamically reconfigurable LMB.
+
+``ibm,associativity-lookup-arrays``
+-----------------------------------
+
+This property defines a lookup array in which the NUMA associativity
+information for each LMB can be found. It is a property encoded array
+that begins with an integer M, the number of associativity lists followed
+by an integer N, the number of entries per associativity list and terminated
+by M associativity lists each of length N integers.
+
+This property provides the same information as given by ``ibm,associativity``
+property in a ``/memory`` node. Each assigned LMB has an index value between
+0 and M-1 which is used as an index into this table to select which
+associativity list to use for the LMB. This index value for each LMB is defined
+in ``ibm,dynamic-memory`` property.
+
+``ibm,dynamic-memory``
+----------------------
+
+This property describes the dynamically reconfigurable memory. It is a
+property encoded array that has an integer N, the number of LMBs followed
+by N LMB list entries.
+
+Each LMB list entry consists of the following elements:
+
+- Logical address of the start of the LMB encoded as a 64-bit integer. This
+ corresponds to ``reg`` property in ``/memory`` node.
+- DRC index of the LMB that corresponds to ``ibm,my-drc-index`` property
+ in a ``/memory`` node.
+- Four bytes reserved for expansion.
+- Associativity list index for the LMB that is used as an index into
+ ``ibm,associativity-lookup-arrays`` property described earlier. This is used
+ to retrieve the right associativity list to be used for this LMB.
+- A 32-bit flags word. The bit at bit position ``0x00000008`` defines whether
+ the LMB is assigned to the partition as of boot time.
+
+``ibm,dynamic-memory-v2``
+-------------------------
+
+This property describes the dynamically reconfigurable memory. This is
+an alternate and newer way to describe dynamically reconfigurable memory.
+It is a property encoded array that has an integer N (the number of
+LMB set entries) followed by N LMB set entries. There is an LMB set entry
+for each sequential group of LMBs that share common attributes.
+
+Each LMB set entry consists of the following elements:
+
+- Number of sequential LMBs in the entry represented by a 32-bit integer.
+- Logical address of the first LMB in the set encoded as a 64-bit integer.
+- DRC index of the first LMB in the set.
+- Associativity list index that is used as an index into
+ ``ibm,associativity-lookup-arrays`` property described earlier. This
+ is used to retrieve the right associativity list to be used for all
+ the LMBs in this set.
+- A 32-bit flags word that applies to all the LMBs in the set.
+++ /dev/null
-= sPAPR Dynamic Reconfiguration =
-
-sPAPR/"pseries" guests make use of a facility called dynamic-reconfiguration
-to handle hotplugging of dynamic "physical" resources like PCI cards, or
-"logical"/paravirtual resources like memory, CPUs, and "physical"
-host-bridges, which are generally managed by the host/hypervisor and provided
-to guests as virtualized resources. The specifics of dynamic-reconfiguration
-are documented extensively in PAPR+ v2.7, Section 13.1. This document
-provides a summary of that information as it applies to the implementation
-within QEMU.
-
-== Dynamic-reconfiguration Connectors ==
-
-To manage hotplug/unplug of these resources, a firmware abstraction known as
-a Dynamic Resource Connector (DRC) is used to assign a particular dynamic
-resource to the guest, and provide an interface for the guest to manage
-configuration/removal of the resource associated with it.
-
-== Device-tree description of DRCs ==
-
-A set of 4 Open Firmware device tree array properties are used to describe
-the name/index/power-domain/type of each DRC allocated to a guest at
-boot-time. There may be multiple sets of these arrays, rooted at different
-paths in the device tree depending on the type of resource the DRCs manage.
-
-In some cases, the DRCs themselves may be provided by a dynamic resource,
-such as the DRCs managing PCI slots on a hotplugged PHB. In this case the
-arrays would be fetched as part of the device tree retrieval interfaces
-for hotplugged resources described under "Guest->Host interface".
-
-The array properties are described below. Each entry/element in an array
-describes the DRC identified by the element in the corresponding position
-of ibm,drc-indexes:
-
-ibm,drc-names:
- first 4-bytes: BE-encoded integer denoting the number of entries
- each entry: a NULL-terminated <name> string encoded as a byte array
-
- <name> values for logical/virtual resources are defined in PAPR+ v2.7,
- Section 13.5.2.4, and basically consist of the type of the resource
- followed by a space and a numerical value that's unique across resources
- of that type.
-
- <name> values for "physical" resources such as PCI or VIO devices are
- defined as being "location codes", which are the "location labels" of
- each encapsulating device, starting from the chassis down to the
- individual slot for the device, concatenated by a hyphen. This provides
- a mapping of resources to a physical location in a chassis for debugging
- purposes. For QEMU, this mapping is less important, so we assign a
- location code that conforms to naming specifications, but is simply a
- location label for the slot by itself to simplify the implementation.
- The naming convention for location labels is documented in detail in
- PAPR+ v2.7, Section 12.3.1.5, and in our case amounts to using "C<n>"
- for PCI/VIO device slots, where <n> is unique across all PCI/VIO
- device slots.
-
-ibm,drc-indexes:
- first 4-bytes: BE-encoded integer denoting the number of entries
- each 4-byte entry: BE-encoded <index> integer that is unique across all DRCs
- in the machine
-
- <index> is arbitrary, but in the case of QEMU we try to maintain the
- convention used to assign them to pSeries guests on pHyp:
-
- bit[31:28]: integer encoding of <type>, where <type> is:
- 1 for CPU resource
- 2 for PHB resource
- 3 for VIO resource
- 4 for PCI resource
- 8 for Memory resource
- bit[27:0]: integer encoding of <id>, where <id> is unique across
- all resources of specified type
-
-ibm,drc-power-domains:
- first 4-bytes: BE-encoded integer denoting the number of entries
- each 4-byte entry: 32-bit, BE-encoded <index> integer that specifies the
- power domain the resource will be assigned to. In the case of QEMU
- we associated all resources with a "live insertion" domain, where the
- power is assumed to be managed automatically. The integer value for
- this domain is a special value of -1.
-
-
-ibm,drc-types:
- first 4-bytes: BE-encoded integer denoting the number of entries
- each entry: a NULL-terminated <type> string encoded as a byte array
-
- <type> is assigned as follows:
- "CPU" for a CPU
- "PHB" for a physical host-bridge
- "SLOT" for a VIO slot
- "28" for a PCI slot
- "MEM" for memory resource
-
-== Guest->Host interface to manage dynamic resources ==
-
-Each DRC is given a globally unique DRC Index, and resources associated with
-a particular DRC are configured/managed by the guest via a number of RTAS
-calls which reference individual DRCs based on the DRC index. This can be
-considered the guest->host interface.
-
-rtas-set-power-level:
- arg[0]: integer identifying power domain
- arg[1]: new power level for the domain, 0-100
- output[0]: status, 0 on success
- output[1]: power level after command
-
- Set the power level for a specified power domain
-
-rtas-get-power-level:
- arg[0]: integer identifying power domain
- output[0]: status, 0 on success
- output[1]: current power level
-
- Get the power level for a specified power domain
-
-rtas-set-indicator:
- arg[0]: integer identifying sensor/indicator type
- arg[1]: index of sensor, for DR-related sensors this is generally the
- DRC index
- arg[2]: desired sensor value
- output[0]: status, 0 on success
-
- Set the state of an indicator or sensor. For the purpose of this document we
- focus on the indicator/sensor types associated with a DRC. The types are:
-
- 9001: isolation-state, controls/indicates whether a device has been made
- accessible to a guest
-
- supported sensor values:
- 0: isolate, device is made unaccessible by guest OS
- 1: unisolate, device is made available to guest OS
-
- 9002: dr-indicator, controls "visual" indicator associated with device
-
- supported sensor values:
- 0: inactive, resource may be safely removed
- 1: active, resource is in use and cannot be safely removed
- 2: identify, used to visually identify slot for interactive hotplug
- 3: action, in most cases, used in the same manner as identify
-
- 9003: allocation-state, generally only used for "logical" DR resources to
- request the allocation/deallocation of a resource prior to acquiring
- it via isolation-state->unisolate, or after releasing it via
- isolation-state->isolate, respectively. for "physical" DR (like PCI
- hotplug/unplug) the pre-allocation of the resource is implied and
- this sensor is unused.
-
- supported sensor values:
- 0: unusable, tell firmware/system the resource can be
- unallocated/reclaimed and added back to the system resource pool
- 1: usable, request the resource be allocated/reserved for use by
- guest OS
- 2: exchange, used to allocate a spare resource to use for fail-over
- in certain situations. unused in QEMU
- 3: recover, used to reclaim a previously allocated resource that's
- not currently allocated to the guest OS. unused in QEMU
-
-rtas-get-sensor-state:
- arg[0]: integer identifying sensor/indicator type
- arg[1]: index of sensor, for DR-related sensors this is generally the
- DRC index
- output[0]: status, 0 on success
-
- Used to read an indicator or sensor value.
-
- For DR-related operations, the only noteworthy sensor is dr-entity-sense,
- which has a type value of 9003, as allocation-state does in the case of
- rtas-set-indicator. The semantics/encodings of the sensor values are distinct
- however:
-
- supported sensor values for dr-entity-sense (9003) sensor:
- 0: empty,
- for physical resources: DRC/slot is empty
- for logical resources: unused
- 1: present,
- for physical resources: DRC/slot is populated with a device/resource
- for logical resources: resource has been allocated to the DRC
- 2: unusable,
- for physical resources: unused
- for logical resources: DRC has no resource allocated to it
- 3: exchange,
- for physical resources: unused
- for logical resources: resource available for exchange (see
- allocation-state sensor semantics above)
- 4: recovery,
- for physical resources: unused
- for logical resources: resource available for recovery (see
- allocation-state sensor semantics above)
-
-rtas-ibm-configure-connector:
- arg[0]: guest physical address of 4096-byte work area buffer
- arg[1]: 0, or address of additional 4096-byte work area buffer. only non-zero
- if a prior RTAS response indicated a need for additional memory
- output[0]: status:
- 0: completed transmittal of device-tree node
- 1: instruct guest to prepare for next DT sibling node
- 2: instruct guest to prepare for next DT child node
- 3: instruct guest to prepare for next DT property
- 4: instruct guest to ascend to parent DT node
- 5: instruct guest to provide additional work-area buffer
- via arg[1]
- 990x: instruct guest that operation took too long and to try
- again later
-
- Used to fetch an OF device-tree description of the resource associated with
- a particular DRC. The DRC index is encoded in the first 4-bytes of the first
- work area buffer.
-
- Work area layout, using 4-byte offsets:
- wa[0]: DRC index of the DRC to fetch device-tree nodes from
- wa[1]: 0 (hard-coded)
- wa[2]: for next-sibling/next-child response:
- wa offset of null-terminated string denoting the new node's name
- for next-property response:
- wa offset of null-terminated string denoting new property's name
- wa[3]: for next-property response (unused otherwise):
- byte-length of new property's value
- wa[4]: for next-property response (unused otherwise):
- new property's value, encoded as an OFDT-compatible byte array
-
-== hotplug/unplug events ==
-
-For most DR operations, the hypervisor will issue host->guest add/remove events
-using the EPOW/check-exception notification framework, where the host issues a
-check-exception interrupt, then provides an RTAS event log via an
-rtas-check-exception call issued by the guest in response. This framework is
-documented by PAPR+ v2.7, and already use in by QEMU for generating powerdown
-requests via EPOW events.
-
-For DR, this framework has been extended to include hotplug events, which were
-previously unneeded due to direct manipulation of DR-related guest userspace
-tools by host-level management such as an HMC. This level of management is not
-applicable to PowerKVM, hence the reason for extending the notification
-framework to support hotplug events.
-
-The format for these EPOW-signalled events is described below under
-"hotplug/unplug event structure". Note that these events are not
-formally part of the PAPR+ specification, and have been superseded by a
-newer format, also described below under "hotplug/unplug event structure",
-and so are now deemed a "legacy" format. The formats are similar, but the
-"modern" format contains additional fields/flags, which are denoted for the
-purposes of this documentation with "#ifdef GUEST_SUPPORTS_MODERN" guards.
-
-QEMU should assume support only for "legacy" fields/flags unless the guest
-advertises support for the "modern" format via ibm,client-architecture-support
-hcall by setting byte 5, bit 6 of it's ibm,architecture-vec-5 option vector
-structure (as described by LoPAPR v11, B.6.2.3). As with "legacy" format events,
-"modern" format events are surfaced to the guest via check-exception RTAS calls,
-but use a dedicated event source to signal the guest. This event source is
-advertised to the guest by the addition of a "hot-plug-events" node under
-"/event-sources" node of the guest's device tree using the standard format
-described in LoPAPR v11, B.6.12.1.
-
-== hotplug/unplug event structure ==
-
-The hotplug-specific payload in QEMU is implemented as follows (with all values
-encoded in big-endian format):
-
-struct rtas_event_log_v6_hp {
-#define SECTION_ID_HOTPLUG 0x4850 /* HP */
- struct section_header {
- uint16_t section_id; /* set to SECTION_ID_HOTPLUG */
- uint16_t section_length; /* sizeof(rtas_event_log_v6_hp),
- * plus the length of the DRC name
- * if a DRC name identifier is
- * specified for hotplug_identifier
- */
- uint8_t section_version; /* version 1 */
- uint8_t section_subtype; /* unused */
- uint16_t creator_component_id; /* unused */
- } hdr;
-#define RTAS_LOG_V6_HP_TYPE_CPU 1
-#define RTAS_LOG_V6_HP_TYPE_MEMORY 2
-#define RTAS_LOG_V6_HP_TYPE_SLOT 3
-#define RTAS_LOG_V6_HP_TYPE_PHB 4
-#define RTAS_LOG_V6_HP_TYPE_PCI 5
- uint8_t hotplug_type; /* type of resource/device */
-#define RTAS_LOG_V6_HP_ACTION_ADD 1
-#define RTAS_LOG_V6_HP_ACTION_REMOVE 2
- uint8_t hotplug_action; /* action (add/remove) */
-#define RTAS_LOG_V6_HP_ID_DRC_NAME 1
-#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2
-#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3
-#ifdef GUEST_SUPPORTS_MODERN
-#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4
-#endif
- uint8_t hotplug_identifier; /* type of the resource identifier,
- * which serves as the discriminator
- * for the 'drc' union field below
- */
-#ifdef GUEST_SUPPORTS_MODERN
- uint8_t capabilities; /* capability flags, currently unused
- * by QEMU
- */
-#else
- uint8_t reserved;
-#endif
- union {
- uint32_t index; /* DRC index of resource to take action
- * on
- */
- uint32_t count; /* number of DR resources to take
- * action on (guest chooses which)
- */
-#ifdef GUEST_SUPPORTS_MODERN
- struct {
- uint32_t count; /* number of DR resources to take
- * action on
- */
- uint32_t index; /* DRC index of first resource to take
- * action on. guest will take action
- * on DRC index <index> through
- * DRC index <index + count - 1> in
- * sequential order
- */
- } count_indexed;
-#endif
- char name[1]; /* string representing the name of the
- * DRC to take action on
- */
- } drc;
-} QEMU_PACKED;
-
-== ibm,lrdr-capacity ==
-
-ibm,lrdr-capacity is a property in the /rtas device tree node that identifies
-the dynamic reconfiguration capabilities of the guest. It consists of a triple
-consisting of <phys>, <size> and <maxcpus>.
-
- <phys>, encoded in BE format represents the maximum address in bytes and
- hence the maximum memory that can be allocated to the guest.
-
- <size>, encoded in BE format represents the size increments in which
- memory can be hot-plugged to the guest.
-
- <maxcpus>, a BE-encoded integer, represents the maximum number of
- processors that the guest can have.
-
-pseries guests use this property to note the maximum allowed CPUs for the
-guest.
-
-== ibm,dynamic-reconfiguration-memory ==
-
-ibm,dynamic-reconfiguration-memory is a device tree node that represents
-dynamically reconfigurable logical memory blocks (LMB). This node
-is generated only when the guest advertises the support for it via
-ibm,client-architecture-support call. Memory that is not dynamically
-reconfigurable is represented by /memory nodes. The properties of this
-node that are of interest to the sPAPR memory hotplug implementation
-in QEMU are described here.
-
-ibm,lmb-size
-
-This 64bit integer defines the size of each dynamically reconfigurable LMB.
-
-ibm,associativity-lookup-arrays
-
-This property defines a lookup array in which the NUMA associativity
-information for each LMB can be found. It is a property encoded array
-that begins with an integer M, the number of associativity lists followed
-by an integer N, the number of entries per associativity list and terminated
-by M associativity lists each of length N integers.
-
-This property provides the same information as given by ibm,associativity
-property in a /memory node. Each assigned LMB has an index value between
-0 and M-1 which is used as an index into this table to select which
-associativity list to use for the LMB. This index value for each LMB
-is defined in ibm,dynamic-memory property.
-
-ibm,dynamic-memory
-
-This property describes the dynamically reconfigurable memory. It is a
-property encoded array that has an integer N, the number of LMBs followed
-by N LMB list entries.
-
-Each LMB list entry consists of the following elements:
-
-- Logical address of the start of the LMB encoded as a 64bit integer. This
- corresponds to reg property in /memory node.
-- DRC index of the LMB that corresponds to ibm,my-drc-index property
- in a /memory node.
-- Four bytes reserved for expansion.
-- Associativity list index for the LMB that is used as an index into
- ibm,associativity-lookup-arrays property described earlier. This
- is used to retrieve the right associativity list to be used for this
- LMB.
-- A 32bit flags word. The bit at bit position 0x00000008 defines whether
- the LMB is assigned to the partition as of boot time.
-
-ibm,dynamic-memory-v2
-
-This property describes the dynamically reconfigurable memory. This is
-an alternate and newer way to describe dynamically reconfigurable memory.
-It is a property encoded array that has an integer N (the number of
-LMB set entries) followed by N LMB set entries. There is an LMB set entry
-for each sequential group of LMBs that share common attributes.
-
-Each LMB set entry consists of the following elements:
-
-- Number of sequential LMBs in the entry represented by a 32bit integer.
-- Logical address of the first LMB in the set encoded as a 64bit integer.
-- DRC index of the first LMB in the set.
-- Associativity list index that is used as an index into
- ibm,associativity-lookup-arrays property described earlier. This
- is used to retrieve the right associativity list to be used for all
- the LMBs in this set.
-- A 32bit flags word that applies to all the LMBs in the set.
-
-[1] http://thread.gmane.org/gmane.linux.ports.ppc.embedded/75350/focus=106867
--- /dev/null
+===================================
+Hypervisor calls and the Ultravisor
+===================================
+
+On PPC64 systems supporting Protected Execution Facility (PEF), system memory
+can be placed in a secured region where only an ultravisor running in firmware
+can provide access to. pSeries guests on such systems can communicate with
+the ultravisor (via ultracalls) to switch to a secure virtual machine (SVM) mode
+where the guest's memory is relocated to this secured region, making its memory
+inaccessible to normal processes/guests running on the host.
+
+The various ultracalls/hypercalls relating to SVM mode are currently only
+documented internally, but are planned for direct inclusion into the Linux on
+Power Architecture Reference document ([LoPAR]_). An internal ACR has been filed
+to reserve a hypercall number range specific to this use case to avoid any
+future conflicts with the IBM internally maintained Power Architecture Platform
+Reference (PAPR+) documentation specification. This document summarizes some of
+these details as they relate to QEMU.
+
+Hypercalls needed by the ultravisor
+===================================
+
+Switching to SVM mode involves a number of hcalls issued by the ultravisor to
+the hypervisor to orchestrate the movement of guest memory to secure memory and
+various other aspects of the SVM mode. Numbers are assigned for these hcalls
+within the reserved range ``0xEF00-0xEF80``. The below documents the hcalls
+relevant to QEMU.
+
+``H_TPM_COMM`` (``0xef10``)
+---------------------------
+
+SVM file systems are encrypted using a symmetric key. This key is then
+wrapped/encrypted using the public key of a trusted system which has the private
+key stored in the system's TPM. An Ultravisor will use this hcall to
+unwrap/unseal the symmetric key using the system's TPM device or a TPM Resource
+Manager associated with the device.
+
+The Ultravisor sets up a separate session key with the TPM in advance during
+host system boot. All sensitive in and out values will be encrypted using the
+session key. Though the hypervisor will see the in and out buffers in raw form,
+any sensitive contents will generally be encrypted using this session key.
+
+Arguments:
+
+ ``r3``: ``H_TPM_COMM`` (``0xef10``)
+
+ ``r4``: ``TPM`` operation, one of:
+
+ ``TPM_COMM_OP_EXECUTE`` (``0x1``): send a request to a TPM and receive a
+ response, opening a new TPM session if one has not already been opened.
+
+ ``TPM_COMM_OP_CLOSE_SESSION`` (``0x2``): close the existing TPM session, if
+ any.
+
+ ``r5``: ``in_buffer``, guest physical address of buffer containing the
+ request. Caller may use the same address for both request and response.
+
+ ``r6``: ``in_size``, size of the in buffer. Must be less than or equal to
+ 4 KB.
+
+ ``r7``: ``out_buffer``, guest physical address of buffer to store the
+ response. Caller may use the same address for both request and response.
+
+ ``r8``: ``out_size``, size of the out buffer. Must be at least 4 KB, as this
+ is the maximum request/response size supported by most TPM implementations,
+ including the TPM Resource Manager in the linux kernel.
+
+Return values:
+
+ ``r3``: one of the following values:
+
+ ``H_Success``: request processed successfully.
+
+ ``H_PARAMETER``: invalid TPM operation.
+
+ ``H_P2``: ``in_buffer`` is invalid.
+
+ ``H_P3``: ``in_size`` is invalid.
+
+ ``H_P4``: ``out_buffer`` is invalid.
+
+ ``H_P5``: ``out_size`` is invalid.
+
+ ``H_RESOURCE``: problem communicating with TPM.
+
+ ``H_FUNCTION``: TPM access is not currently allowed/configured.
+
+ ``r4``: For ``TPM_COMM_OP_EXECUTE``, the size of the response will be stored
+ here upon success.
+++ /dev/null
-On PPC64 systems supporting Protected Execution Facility (PEF), system
-memory can be placed in a secured region where only an "ultravisor"
-running in firmware can provide to access it. pseries guests on such
-systems can communicate with the ultravisor (via ultracalls) to switch to a
-secure VM mode (SVM) where the guest's memory is relocated to this secured
-region, making its memory inaccessible to normal processes/guests running on
-the host.
-
-The various ultracalls/hypercalls relating to SVM mode are currently
-only documented internally, but are planned for direct inclusion into the
-public OpenPOWER version of the PAPR specification (LoPAPR/LoPAR). An internal
-ACR has been filed to reserve a hypercall number range specific to this
-use-case to avoid any future conflicts with the internally-maintained PAPR
-specification. This document summarizes some of these details as they relate
-to QEMU.
-
-== hypercalls needed by the ultravisor ==
-
-Switching to SVM mode involves a number of hcalls issued by the ultravisor
-to the hypervisor to orchestrate the movement of guest memory to secure
-memory and various other aspects SVM mode. Numbers are assigned for these
-hcalls within the reserved range 0xEF00-0xEF80. The below documents the
-hcalls relevant to QEMU.
-
-- H_TPM_COMM (0xef10)
-
- For TPM_COMM_OP_EXECUTE operation:
- Send a request to a TPM and receive a response, opening a new TPM session
- if one has not already been opened.
-
- For TPM_COMM_OP_CLOSE_SESSION operation:
- Close the existing TPM session, if any.
-
- Arguments:
-
- r3 : H_TPM_COMM (0xef10)
- r4 : TPM operation, one of:
- TPM_COMM_OP_EXECUTE (0x1)
- TPM_COMM_OP_CLOSE_SESSION (0x2)
- r5 : in_buffer, guest physical address of buffer containing the request
- - Caller may use the same address for both request and response
- r6 : in_size, size of the in buffer
- - Must be less than or equal to 4KB
- r7 : out_buffer, guest physical address of buffer to store the response
- - Caller may use the same address for both request and response
- r8 : out_size, size of the out buffer
- - Must be at least 4KB, as this is the maximum request/response size
- supported by most TPM implementations, including the TPM Resource
- Manager in the linux kernel.
-
- Return values:
-
- r3 : H_Success request processed successfully
- H_PARAMETER invalid TPM operation
- H_P2 in_buffer is invalid
- H_P3 in_size is invalid
- H_P4 out_buffer is invalid
- H_P5 out_size is invalid
- H_RESOURCE problem communicating with TPM
- H_FUNCTION TPM access is not currently allowed/configured
- r4 : For TPM_COMM_OP_EXECUTE, the size of the response will be stored here
- upon success.
-
- Use-case/notes:
-
- SVM filesystems are encrypted using a symmetric key. This key is then
- wrapped/encrypted using the public key of a trusted system which has the
- private key stored in the system's TPM. An Ultravisor will use this
- hcall to unwrap/unseal the symmetric key using the system's TPM device
- or a TPM Resource Manager associated with the device.
-
- The Ultravisor sets up a separate session key with the TPM in advance
- during host system boot. All sensitive in and out values will be
- encrypted using the session key. Though the hypervisor will see the 'in'
- and 'out' buffers in raw form, any sensitive contents will generally be
- encrypted using this session key.
TCG VCPU features are CPU features that are specific to TCG.
Below is the list of TCG VCPU features and their descriptions.
- pauth Enable or disable ``FEAT_Pauth``, pointer
- authentication. By default, the feature is
- enabled with ``-cpu max``.
-
pauth-impdef When ``FEAT_Pauth`` is enabled, either the
*impdef* (Implementation Defined) algorithm
is enabled or the *architected* QARMA algorithm
Set ``on``/``off`` to enable/disable reporting host memory errors to a guest
using ACPI and guest external abort exceptions. The default is off.
+dtb-kaslr-seed
+ Set ``on``/``off`` to pass a random seed via the guest dtb
+ kaslr-seed node (in both "/chosen" and /secure-chosen) to use
+ for features like address space randomisation. The default is
+ ``on``. You will want to disable it if your trusted boot chain will
+ verify the DTB it is passed. It would be the responsibility of the
+ firmware to come up with a seed and pass it on if it wants to.
+
Linux guest kernel configuration
""""""""""""""""""""""""""""""""
.. toctree::
:maxdepth: 1
+ devices/can.rst
devices/ivshmem.rst
devices/net.rst
devices/nvme.rst
--- /dev/null
+CAN Bus Emulation Support
+=========================
+The CAN bus emulation provides mechanism to connect multiple
+emulated CAN controller chips together by one or multiple CAN busses
+(the controller device "canbus" parameter). The individual busses
+can be connected to host system CAN API (at this time only Linux
+SocketCAN is supported).
+
+The concept of busses is generic and different CAN controllers
+can be implemented.
+
+The initial submission implemented SJA1000 controller which
+is common and well supported by by drivers for the most operating
+systems.
+
+The PCI addon card hardware has been selected as the first CAN
+interface to implement because such device can be easily connected
+to systems with different CPU architectures (x86, PowerPC, Arm, etc.).
+
+In 2020, CTU CAN FD controller model has been added as part
+of the bachelor thesis of Jan Charvat. This controller is complete
+open-source/design/hardware solution. The core designer
+of the project is Ondrej Ille, the financial support has been
+provided by CTU, and more companies including Volkswagen subsidiaries.
+
+The project has been initially started in frame of RTEMS GSoC 2013
+slot by Jin Yang under our mentoring The initial idea was to provide generic
+CAN subsystem for RTEMS. But lack of common environment for code and RTEMS
+testing lead to goal change to provide environment which provides complete
+emulated environment for testing and RTEMS GSoC slot has been donated
+to work on CAN hardware emulation on QEMU.
+
+Examples how to use CAN emulation for SJA1000 based boards
+----------------------------------------------------------
+When QEMU with CAN PCI support is compiled then one of the next
+CAN boards can be selected
+
+(1) CAN bus Kvaser PCI CAN-S (single SJA1000 channel) board. QEMU startup options::
+
+ -object can-bus,id=canbus0
+ -device kvaser_pci,canbus=canbus0
+
+Add "can-host-socketcan" object to connect device to host system CAN bus::
+
+ -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0
+
+(2) CAN bus PCM-3680I PCI (dual SJA1000 channel) emulation::
+
+ -object can-bus,id=canbus0
+ -device pcm3680_pci,canbus0=canbus0,canbus1=canbus0
+
+Another example::
+
+ -object can-bus,id=canbus0
+ -object can-bus,id=canbus1
+ -device pcm3680_pci,canbus0=canbus0,canbus1=canbus1
+
+(3) CAN bus MIOe-3680 PCI (dual SJA1000 channel) emulation::
+
+ -device mioe3680_pci,canbus0=canbus0
+
+The ''kvaser_pci'' board/device model is compatible with and has been tested with
+the ''kvaser_pci'' driver included in mainline Linux kernel.
+The tested setup was Linux 4.9 kernel on the host and guest side.
+
+Example for qemu-system-x86_64::
+
+ qemu-system-x86_64 -accel kvm -kernel /boot/vmlinuz-4.9.0-4-amd64 \
+ -initrd ramdisk.cpio \
+ -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
+ -object can-bus,id=canbus0 \
+ -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
+ -device kvaser_pci,canbus=canbus0 \
+ -nographic -append "console=ttyS0"
+
+Example for qemu-system-arm::
+
+ qemu-system-arm -cpu arm1176 -m 256 -M versatilepb \
+ -kernel kernel-qemu-arm1176-versatilepb \
+ -hda rpi-wheezy-overlay \
+ -append "console=ttyAMA0 root=/dev/sda2 ro init=/sbin/init-overlay" \
+ -nographic \
+ -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
+ -object can-bus,id=canbus0 \
+ -object can-host-socketcan,id=canhost0,if=can0,canbus=canbus0 \
+ -device kvaser_pci,canbus=canbus0,host=can0 \
+
+The CAN interface of the host system has to be configured for proper
+bitrate and set up. Configuration is not propagated from emulated
+devices through bus to the physical host device. Example configuration
+for 1 Mbit/s::
+
+ ip link set can0 type can bitrate 1000000
+ ip link set can0 up
+
+Virtual (host local only) can interface can be used on the host
+side instead of physical interface::
+
+ ip link add dev can0 type vcan
+
+The CAN interface on the host side can be used to analyze CAN
+traffic with "candump" command which is included in "can-utils"::
+
+ candump can0
+
+CTU CAN FD support examples
+---------------------------
+This open-source core provides CAN FD support. CAN FD drames are
+delivered even to the host systems when SocketCAN interface is found
+CAN FD capable.
+
+The PCIe board emulation is provided for now (the device identifier is
+ctucan_pci). The default build defines two CTU CAN FD cores
+on the board.
+
+Example how to connect the canbus0-bus (virtual wire) to the host
+Linux system (SocketCAN used) and to both CTU CAN FD cores emulated
+on the corresponding PCI card expects that host system CAN bus
+is setup according to the previous SJA1000 section::
+
+ qemu-system-x86_64 -enable-kvm -kernel /boot/vmlinuz-4.19.52+ \
+ -initrd ramdisk.cpio \
+ -virtfs local,path=shareddir,security_model=none,mount_tag=shareddir \
+ -vga cirrus \
+ -append "console=ttyS0" \
+ -object can-bus,id=canbus0-bus \
+ -object can-host-socketcan,if=can0,canbus=canbus0-bus,id=canbus0-socketcan \
+ -device ctucan_pci,canbus0=canbus0-bus,canbus1=canbus0-bus \
+ -nographic
+
+Setup of CTU CAN FD controller in a guest Linux system::
+
+ insmod ctucanfd.ko || modprobe ctucanfd
+ insmod ctucanfd_pci.ko || modprobe ctucanfd_pci
+
+ for ifc in /sys/class/net/can* ; do
+ if [ -e $ifc/device/vendor ] ; then
+ if ! grep -q 0x1760 $ifc/device/vendor ; then
+ continue;
+ fi
+ else
+ continue;
+ fi
+ if [ -e $ifc/device/device ] ; then
+ if ! grep -q 0xff00 $ifc/device/device ; then
+ continue;
+ fi
+ else
+ continue;
+ fi
+ ifc=$(basename $ifc)
+ /bin/ip link set $ifc type can bitrate 1000000 dbitrate 10000000 fd on
+ /bin/ip link set $ifc up
+ done
+
+The test can run for example::
+
+ candump can1
+
+in the guest system and next commands in the host system for basic CAN::
+
+ cangen can0
+
+for CAN FD without bitrate switch::
+
+ cangen can0 -f
+
+and with bitrate switch::
+
+ cangen can0 -b
+
+The test can be run viceversa, generate messages in the guest system and capture them
+in the host one and much more combinations.
+
+Links to other resources
+------------------------
+
+ (1) `CAN related projects at Czech Technical University, Faculty of Electrical Engineering <http://canbus.pages.fel.cvut.cz>`_
+ (2) `Repository with development can-pci branch at Czech Technical University <https://gitlab.fel.cvut.cz/canbus/qemu-canbus>`_
+ (3) `RTEMS page describing project <https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation>`_
+ (4) `RTLWS 2015 article about the project and its use with CANopen emulation <http://cmp.felk.cvut.cz/~pisa/can/doc/rtlws-17-pisa-qemu-can.pdf>`_
+ (5) `GNU/Linux, CAN and CANopen in Real-time Control Applications Slides from LinuxDays 2017 (include updated RTLWS 2015 content) <https://www.linuxdays.cz/2017/video/Pavel_Pisa-CAN_canopen.pdf>`_
+ (6) `Linux SocketCAN utilities <https://github.com/linux-can/can-utils>`_
+ (7) `CTU CAN FD project including core VHDL design, Linux driver, test utilities etc. <https://gitlab.fel.cvut.cz/canbus/ctucanfd_ip_core>`_
+ (8) `CTU CAN FD Core Datasheet Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/Progdokum.pdf>`_
+ (9) `CTU CAN FD Core System Architecture Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/ctu_can_fd_architecture.pdf>`_
+ (10) `CTU CAN FD Driver Documentation <http://canbus.pages.fel.cvut.cz/ctucanfd_ip_core/driver_doc/ctucanfd-driver.html>`_
+ (11) `Integration with PCIe interfacing for Intel/Altera Cyclone IV based board <https://gitlab.fel.cvut.cz/canbus/pcie-ctu_can_fd>`_
+===================================
pSeries family boards (``pseries``)
===================================
-The Power machine para-virtualized environment described by the `Linux on Power
-Architecture Reference document (LoPAR)
-<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_
-is called pSeries. This environment is also known as sPAPR, System p guests, or
-simply Power Linux guests (although it is capable of running other operating
-systems, such as AIX).
+The Power machine para-virtualized environment described by the Linux on Power
+Architecture Reference ([LoPAR]_) document is called pSeries. This environment
+is also known as sPAPR, System p guests, or simply Power Linux guests (although
+it is capable of running other operating systems, such as AIX).
Even though pSeries is designed to behave as a guest environment, it is also
capable of acting as a hypervisor OS, providing, on that role, nested
virtualization capabilities.
Supported devices
------------------
+=================
* Multi processor support for many Power processors generations: POWER7,
POWER7+, POWER8, POWER8NVL, POWER9, and Power10. Support for POWER5+ exists,
* PCIe device pass through.
Missing devices
----------------
+===============
* SPICE support.
Firmware
---------
+========
`SLOF <https://github.com/aik/SLOF>`_ (Slimline Open Firmware) is an
implementation of the `IEEE 1275-1994, Standard for Boot (Initialization
version is required.
Build directions
-----------------
+================
.. code-block:: bash
./configure --target-list=ppc64-softmmu && make
Running instructions
---------------------
+====================
Someone can select the pSeries machine type by running QEMU with the following
options:
qemu-system-ppc64 -M pseries <other QEMU arguments>
sPAPR devices
--------------
+=============
The sPAPR specification defines a set of para-virtualized devices, which are
also supported by the pSeries machine in QEMU and can be instantiated with the
NVRAM device with ``-global spapr-nvram.drive=pfid``.
sPAPR specification
-^^^^^^^^^^^^^^^^^^^
+-------------------
-The main source of documentation on the sPAPR standard is the `Linux on Power
-Architecture Reference document (LoPAR)
-<https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
+The main source of documentation on the sPAPR standard is the [LoPAR]_ document.
However, documentation specific to QEMU's implementation of the specification
can also be found in QEMU documentation:
.. toctree::
:maxdepth: 1
+ ../../specs/ppc-spapr-hotplug.rst
../../specs/ppc-spapr-hcalls.rst
../../specs/ppc-spapr-numa.rst
+ ../../specs/ppc-spapr-uv-hcalls.rst
../../specs/ppc-spapr-xive.rst
-Other documentation available in QEMU docs directory:
-
-* Hot plug (``/docs/specs/ppc-spapr-hotplug.txt``).
-* Hypervisor calls needed by the Ultravisor
- (``/docs/specs/ppc-spapr-uv-hcalls.txt``).
-
Switching between the KVM-PR and KVM-HV kernel module
------------------------------------------------------
+=====================================================
Currently, there are two implementations of KVM on Power, ``kvm_hv.ko`` and
``kvm_pr.ko``.
instead.
KVM-PR
-^^^^^^
+------
KVM-PR uses the so-called **PR**\ oblem state of the PPC CPUs to run the guests,
i.e. the virtual machine is run in user mode and all privileged instructions
QEMU with ``kernel_irqchip=off`` command line option.
KVM-HV
-^^^^^^
+------
KVM-HV uses the hypervisor mode of more recent Power processors, that allow
access to the bare metal hardware directly. Although POWER7 had this capability,
``-cpu POWER8,compat=power7`` as parameter to QEMU.
Modules support
----------------
+===============
As noticed in the sections above, each module can run in a different
environment. The following table shows with which environment each module can
.. [3] Introduced on Power10 machines.
+
+POWER (PAPR) Protected Execution Facility (PEF)
+-----------------------------------------------
+
+Protected Execution Facility (PEF), also known as Secure Guest support
+is a feature found on IBM POWER9 and POWER10 processors.
+
+If a suitable firmware including an Ultravisor is installed, it adds
+an extra memory protection mode to the CPU. The ultravisor manages a
+pool of secure memory which cannot be accessed by the hypervisor.
+
+When this feature is enabled in QEMU, a guest can use ultracalls to
+enter "secure mode". This transfers most of its memory to secure
+memory, where it cannot be eavesdropped by a compromised hypervisor.
+
+Launching
+^^^^^^^^^
+
+To launch a guest which will be permitted to enter PEF secure mode::
+
+ $ qemu-system-ppc64 \
+ -object pef-guest,id=pef0 \
+ -machine confidential-guest-support=pef0 \
+ ...
+
+Live Migration
+^^^^^^^^^^^^^^
+
+Live migration is not yet implemented for PEF guests. For
+consistency, QEMU currently prevents migration if the PEF feature is
+enabled, whether or not the guest has actually entered secure mode.
+
+
Maintainer contact information
-------------------------------
+==============================
Cédric Le Goater <clg@kaod.org>
Daniel Henrique Barboza <danielhb413@gmail.com>
+
+.. [LoPAR] `Linux on Power Architecture Reference document (LoPAR) revision
+ 2.9 <https://openpowerfoundation.org/wp-content/uploads/2020/07/LoPAR-20200812.pdf>`_.
``--skip-broken-bitmaps`` is also specified to copy only the
consistent bitmaps.
-.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE]
+.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE [-F BACKING_FMT]] [-u] [-o OPTIONS] FILENAME [SIZE]
Create the new disk image *FILENAME* of size *SIZE* and format
*FMT*. Depending on the file format, you can add one or more *OPTIONS*
.. option:: --export [type=]nbd,id=<id>,node-name=<node-name>[,name=<export-name>][,writable=on|off][,bitmap=<name>]
--export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,addr.type=unix,addr.path=<socket-path>[,writable=on|off][,logical-block-size=<block-size>][,num-queues=<num-queues>]
--export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,addr.type=fd,addr.str=<fd>[,writable=on|off][,logical-block-size=<block-size>][,num-queues=<num-queues>]
- --export [type=]fuse,id=<id>,node-name=<node-name>,mountpoint=<file>[,growable=on|off][,writable=on|off]
+ --export [type=]fuse,id=<id>,node-name=<node-name>,mountpoint=<file>[,growable=on|off][,writable=on|off][,allow-other=on|off|auto]
is a block export definition. ``node-name`` is the block node that should be
exported. ``writable`` determines whether or not the export allows write
mounted). Consequently, applications that have opened the given file before
the export became active will continue to see its original content. If
``growable`` is set, writes after the end of the exported file will grow the
- block node to fit.
+ block node to fit. The ``allow-other`` option controls whether users other
+ than the user running the process will be allowed to access the export. Note
+ that enabling this option as a non-root user requires enabling the
+ user_allow_other option in the global fuse.conf configuration file. Setting
+ ``allow-other`` to auto (the default) will try enabling this option, and on
+ error fall back to disabling it.
.. option:: --monitor MONITORDEF
--nbd-server addr.type=unix,addr.path=nbd.sock \
--export type=nbd,id=export,node-name=disk,writable=on
-Export a qcow2 image file ``disk.qcow2`` as a vhosts-user-blk device over UNIX
+Export a qcow2 image file ``disk.qcow2`` as a vhost-user-blk device over UNIX
domain socket ``vhost-user-blk.sock``::
$ qemu-storage-daemon \
Show host USB devices.
ERST
+#if defined(CONFIG_TCG)
{
.name = "profile",
.args_type = "",
.help = "show profiling information",
.cmd_info_hrt = qmp_x_query_profile,
},
+#endif
SRST
``info profile``
void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms,
const char *oem_id, const char *oem_table_id)
{
- int pptt_start = table_data->len;
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ GQueue *list = g_queue_new();
+ guint pptt_start = table_data->len;
+ guint parent_offset;
+ guint length, i;
int uid = 0;
int socket;
AcpiTable table = { .sig = "PPTT", .rev = 2,
acpi_table_begin(&table, table_data);
for (socket = 0; socket < ms->smp.sockets; socket++) {
- uint32_t socket_offset = table_data->len - pptt_start;
- int core;
-
+ g_queue_push_tail(list,
+ GUINT_TO_POINTER(table_data->len - pptt_start));
build_processor_hierarchy_node(
table_data,
/*
*/
(1 << 0),
0, socket, NULL, 0);
+ }
- for (core = 0; core < ms->smp.cores; core++) {
- uint32_t core_offset = table_data->len - pptt_start;
- int thread;
+ if (mc->smp_props.clusters_supported) {
+ length = g_queue_get_length(list);
+ for (i = 0; i < length; i++) {
+ int cluster;
+
+ parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list));
+ for (cluster = 0; cluster < ms->smp.clusters; cluster++) {
+ g_queue_push_tail(list,
+ GUINT_TO_POINTER(table_data->len - pptt_start));
+ build_processor_hierarchy_node(
+ table_data,
+ (0 << 0), /* not a physical package */
+ parent_offset, cluster, NULL, 0);
+ }
+ }
+ }
+ length = g_queue_get_length(list);
+ for (i = 0; i < length; i++) {
+ int core;
+
+ parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list));
+ for (core = 0; core < ms->smp.cores; core++) {
if (ms->smp.threads > 1) {
+ g_queue_push_tail(list,
+ GUINT_TO_POINTER(table_data->len - pptt_start));
build_processor_hierarchy_node(
table_data,
(0 << 0), /* not a physical package */
- socket_offset, core, NULL, 0);
-
- for (thread = 0; thread < ms->smp.threads; thread++) {
- build_processor_hierarchy_node(
- table_data,
- (1 << 1) | /* ACPI Processor ID valid */
- (1 << 2) | /* Processor is a Thread */
- (1 << 3), /* Node is a Leaf */
- core_offset, uid++, NULL, 0);
- }
+ parent_offset, core, NULL, 0);
} else {
build_processor_hierarchy_node(
table_data,
(1 << 1) | /* ACPI Processor ID valid */
(1 << 3), /* Node is a Leaf */
- socket_offset, uid++, NULL, 0);
+ parent_offset, uid++, NULL, 0);
}
}
}
+ length = g_queue_get_length(list);
+ for (i = 0; i < length; i++) {
+ int thread;
+
+ parent_offset = GPOINTER_TO_UINT(g_queue_pop_head(list));
+ for (thread = 0; thread < ms->smp.threads; thread++) {
+ build_processor_hierarchy_node(
+ table_data,
+ (1 << 1) | /* ACPI Processor ID valid */
+ (1 << 2) | /* Processor is a Thread */
+ (1 << 3), /* Node is a Leaf */
+ parent_offset, uid++, NULL, 0);
+ }
+ }
+
+ g_queue_free(list);
acpi_table_end(linker, &table);
}
.name = "CPU hotplug device state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_BOOL(is_inserting, AcpiCpuStatus),
VMSTATE_BOOL(is_removing, AcpiCpuStatus),
.name = "CPU hotplug state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(selector, CPUHotplugState),
VMSTATE_UINT8(command, CPUHotplugState),
.name = "ich9_pm/memhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_memhp,
.fields = (VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, ICH9LPCPMRegs),
.name = "ich9_pm/tco",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_tco,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(tco_regs, ICH9LPCPMRegs, 1, vmstate_tco_io_sts,
.name = "ich9_pm/cpuhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_cpuhp,
.pre_load = vmstate_cpuhp_pre_load,
.fields = (VMStateField[]) {
.name = "memory hotplug device state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_BOOL(is_enabled, MemStatus),
VMSTATE_BOOL(is_inserting, MemStatus),
.name = "memory hotplug state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(selector, MemHotplugState),
VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, MemHotplugState, dev_count,
.name = "piix4_pm/memhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_memhp,
.fields = (VMStateField[]) {
VMSTATE_MEMORY_HOTPLUG(acpi_memory_hotplug, PIIX4PMState),
.name = "piix4_pm/cpuhp",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = vmstate_test_use_cpuhp,
.pre_load = vmstate_cpuhp_pre_load,
.fields = (VMStateField[]) {
.name = "tco io device status",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT16(tco.rld, TCOIORegs),
VMSTATE_UINT8(tco.din, TCOIORegs),
select ACPI_HW_REDUCED
select ACPI_APEI
select ACPI_VIOT
+ select VIRTIO_MEM_SUPPORTED
config CHEETAH
bool
select SPLIT_IRQ
select UNIMP
+config MARVELL_88W8618
+ bool
+
config MUSICPAL
bool
select OR_IRQ
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
- VMSTATE_CLOCK(refclk, SysTickState),
- VMSTATE_CLOCK(cpuclk, SysTickState),
+ VMSTATE_CLOCK(refclk, ARMv7MState),
+ VMSTATE_CLOCK(cpuclk, ARMv7MState),
VMSTATE_END_OF_LIST()
}
};
[ASPEED_DEV_PWM] = 0x1E610000,
[ASPEED_DEV_FMC] = 0x1E620000,
[ASPEED_DEV_SPI1] = 0x1E630000,
- [ASPEED_DEV_SPI2] = 0x1E641000,
+ [ASPEED_DEV_SPI2] = 0x1E631000,
[ASPEED_DEV_EHCI1] = 0x1E6A1000,
[ASPEED_DEV_EHCI2] = 0x1E6A3000,
[ASPEED_DEV_MII1] = 0x1E650000,
[ASPEED_DEV_UART1] = 0x1E783000,
[ASPEED_DEV_UART5] = 0x1E784000,
[ASPEED_DEV_VUART] = 0x1E787000,
+ [ASPEED_DEV_I3C] = 0x1E7A0000,
[ASPEED_DEV_SDRAM] = 0x80000000,
};
[ASPEED_DEV_ETH4] = 33,
[ASPEED_DEV_KCS] = 138, /* 138 -> 142 */
[ASPEED_DEV_DP] = 62,
+ [ASPEED_DEV_I3C] = 102, /* 102 -> 107 */
};
static qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int ctrl)
snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname);
object_initialize_child(obj, "hace", &s->hace, typename);
+
+ object_initialize_child(obj, "i3c", &s->i3c, TYPE_ASPEED_I3C);
}
/*
sysbus_mmio_map(SYS_BUS_DEVICE(&s->hace), 0, sc->memmap[ASPEED_DEV_HACE]);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0,
aspeed_soc_get_irq(s, ASPEED_DEV_HACE));
+
+ /* I3C */
+ if (!sysbus_realize(SYS_BUS_DEVICE(&s->i3c), errp)) {
+ return;
+ }
+ sysbus_mmio_map(SYS_BUS_DEVICE(&s->i3c), 0, sc->memmap[ASPEED_DEV_I3C]);
+ for (i = 0; i < ASPEED_I3C_NR_DEVICES; i++) {
+ qemu_irq irq = qdev_get_gpio_in(DEVICE(&s->a7mpcore),
+ sc->irqmap[ASPEED_DEV_I3C] + i);
+ /* The AST2600 I3C controller has one IRQ per bus. */
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i3c.devices[i]), 0, irq);
+ }
}
static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
#include "ui/pixel_ops.h"
#include "qemu/cutils.h"
#include "qom/object.h"
+#include "hw/net/mv88w8618_eth.h"
#define MP_MISC_BASE 0x80002000
#define MP_MISC_SIZE 0x00001000
#define MP_ETH_BASE 0x80008000
-#define MP_ETH_SIZE 0x00001000
#define MP_WLAN_BASE 0x8000C000
#define MP_WLAN_SIZE 0x00000800
/* Wolfson 8750 I2C address */
#define MP_WM_ADDR 0x1A
-/* Ethernet register offsets */
-#define MP_ETH_SMIR 0x010
-#define MP_ETH_PCXR 0x408
-#define MP_ETH_SDCMR 0x448
-#define MP_ETH_ICR 0x450
-#define MP_ETH_IMR 0x458
-#define MP_ETH_FRDP0 0x480
-#define MP_ETH_FRDP1 0x484
-#define MP_ETH_FRDP2 0x488
-#define MP_ETH_FRDP3 0x48C
-#define MP_ETH_CRDP0 0x4A0
-#define MP_ETH_CRDP1 0x4A4
-#define MP_ETH_CRDP2 0x4A8
-#define MP_ETH_CRDP3 0x4AC
-#define MP_ETH_CTDP0 0x4E0
-#define MP_ETH_CTDP1 0x4E4
-
-/* MII PHY access */
-#define MP_ETH_SMIR_DATA 0x0000FFFF
-#define MP_ETH_SMIR_ADDR 0x03FF0000
-#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
-#define MP_ETH_SMIR_RDVALID (1 << 27)
-
-/* PHY registers */
-#define MP_ETH_PHY1_BMSR 0x00210000
-#define MP_ETH_PHY1_PHYSID1 0x00410000
-#define MP_ETH_PHY1_PHYSID2 0x00610000
-
-#define MP_PHY_BMSR_LINK 0x0004
-#define MP_PHY_BMSR_AUTONEG 0x0008
-
-#define MP_PHY_88E3015 0x01410E20
-
-/* TX descriptor status */
-#define MP_ETH_TX_OWN (1U << 31)
-
-/* RX descriptor status */
-#define MP_ETH_RX_OWN (1U << 31)
-
-/* Interrupt cause/mask bits */
-#define MP_ETH_IRQ_RX_BIT 0
-#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
-#define MP_ETH_IRQ_TXHI_BIT 2
-#define MP_ETH_IRQ_TXLO_BIT 3
-
-/* Port config bits */
-#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
-
-/* SDMA command bits */
-#define MP_ETH_CMD_TXHI (1 << 23)
-#define MP_ETH_CMD_TXLO (1 << 22)
-
-typedef struct mv88w8618_tx_desc {
- uint32_t cmdstat;
- uint16_t res;
- uint16_t bytes;
- uint32_t buffer;
- uint32_t next;
-} mv88w8618_tx_desc;
-
-typedef struct mv88w8618_rx_desc {
- uint32_t cmdstat;
- uint16_t bytes;
- uint16_t buffer_size;
- uint32_t buffer;
- uint32_t next;
-} mv88w8618_rx_desc;
-
-#define TYPE_MV88W8618_ETH "mv88w8618_eth"
-OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
-
-struct mv88w8618_eth_state {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- MemoryRegion iomem;
- qemu_irq irq;
- MemoryRegion *dma_mr;
- AddressSpace dma_as;
- uint32_t smir;
- uint32_t icr;
- uint32_t imr;
- int mmio_index;
- uint32_t vlan_header;
- uint32_t tx_queue[2];
- uint32_t rx_queue[4];
- uint32_t frx_queue[4];
- uint32_t cur_rx[4];
- NICState *nic;
- NICConf conf;
-};
-
-static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
- mv88w8618_rx_desc *desc)
-{
- cpu_to_le32s(&desc->cmdstat);
- cpu_to_le16s(&desc->bytes);
- cpu_to_le16s(&desc->buffer_size);
- cpu_to_le32s(&desc->buffer);
- cpu_to_le32s(&desc->next);
- dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
-}
-
-static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
- mv88w8618_rx_desc *desc)
-{
- dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
- le32_to_cpus(&desc->cmdstat);
- le16_to_cpus(&desc->bytes);
- le16_to_cpus(&desc->buffer_size);
- le32_to_cpus(&desc->buffer);
- le32_to_cpus(&desc->next);
-}
-
-static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
-{
- mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
- uint32_t desc_addr;
- mv88w8618_rx_desc desc;
- int i;
-
- for (i = 0; i < 4; i++) {
- desc_addr = s->cur_rx[i];
- if (!desc_addr) {
- continue;
- }
- do {
- eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
- if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
- dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
- buf, size, MEMTXATTRS_UNSPECIFIED);
- desc.bytes = size + s->vlan_header;
- desc.cmdstat &= ~MP_ETH_RX_OWN;
- s->cur_rx[i] = desc.next;
-
- s->icr |= MP_ETH_IRQ_RX;
- if (s->icr & s->imr) {
- qemu_irq_raise(s->irq);
- }
- eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
- return size;
- }
- desc_addr = desc.next;
- } while (desc_addr != s->rx_queue[i]);
- }
- return size;
-}
-
-static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
- mv88w8618_tx_desc *desc)
-{
- cpu_to_le32s(&desc->cmdstat);
- cpu_to_le16s(&desc->res);
- cpu_to_le16s(&desc->bytes);
- cpu_to_le32s(&desc->buffer);
- cpu_to_le32s(&desc->next);
- dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
-}
-
-static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
- mv88w8618_tx_desc *desc)
-{
- dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
- le32_to_cpus(&desc->cmdstat);
- le16_to_cpus(&desc->res);
- le16_to_cpus(&desc->bytes);
- le32_to_cpus(&desc->buffer);
- le32_to_cpus(&desc->next);
-}
-
-static void eth_send(mv88w8618_eth_state *s, int queue_index)
-{
- uint32_t desc_addr = s->tx_queue[queue_index];
- mv88w8618_tx_desc desc;
- uint32_t next_desc;
- uint8_t buf[2048];
- int len;
-
- do {
- eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
- next_desc = desc.next;
- if (desc.cmdstat & MP_ETH_TX_OWN) {
- len = desc.bytes;
- if (len < 2048) {
- dma_memory_read(&s->dma_as, desc.buffer, buf, len,
- MEMTXATTRS_UNSPECIFIED);
- qemu_send_packet(qemu_get_queue(s->nic), buf, len);
- }
- desc.cmdstat &= ~MP_ETH_TX_OWN;
- s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
- eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
- }
- desc_addr = next_desc;
- } while (desc_addr != s->tx_queue[queue_index]);
-}
-
-static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
- unsigned size)
-{
- mv88w8618_eth_state *s = opaque;
-
- switch (offset) {
- case MP_ETH_SMIR:
- if (s->smir & MP_ETH_SMIR_OPCODE) {
- switch (s->smir & MP_ETH_SMIR_ADDR) {
- case MP_ETH_PHY1_BMSR:
- return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
- MP_ETH_SMIR_RDVALID;
- case MP_ETH_PHY1_PHYSID1:
- return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
- case MP_ETH_PHY1_PHYSID2:
- return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
- default:
- return MP_ETH_SMIR_RDVALID;
- }
- }
- return 0;
-
- case MP_ETH_ICR:
- return s->icr;
-
- case MP_ETH_IMR:
- return s->imr;
-
- case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
- return s->frx_queue[(offset - MP_ETH_FRDP0)/4];
-
- case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
- return s->rx_queue[(offset - MP_ETH_CRDP0)/4];
-
- case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
- return s->tx_queue[(offset - MP_ETH_CTDP0)/4];
-
- default:
- return 0;
- }
-}
-
-static void mv88w8618_eth_write(void *opaque, hwaddr offset,
- uint64_t value, unsigned size)
-{
- mv88w8618_eth_state *s = opaque;
-
- switch (offset) {
- case MP_ETH_SMIR:
- s->smir = value;
- break;
-
- case MP_ETH_PCXR:
- s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
- break;
-
- case MP_ETH_SDCMR:
- if (value & MP_ETH_CMD_TXHI) {
- eth_send(s, 1);
- }
- if (value & MP_ETH_CMD_TXLO) {
- eth_send(s, 0);
- }
- if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
- qemu_irq_raise(s->irq);
- }
- break;
-
- case MP_ETH_ICR:
- s->icr &= value;
- break;
-
- case MP_ETH_IMR:
- s->imr = value;
- if (s->icr & s->imr) {
- qemu_irq_raise(s->irq);
- }
- break;
-
- case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
- s->frx_queue[(offset - MP_ETH_FRDP0)/4] = value;
- break;
-
- case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
- s->rx_queue[(offset - MP_ETH_CRDP0)/4] =
- s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value;
- break;
-
- case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
- s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value;
- break;
- }
-}
-
-static const MemoryRegionOps mv88w8618_eth_ops = {
- .read = mv88w8618_eth_read,
- .write = mv88w8618_eth_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void eth_cleanup(NetClientState *nc)
-{
- mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
-
- s->nic = NULL;
-}
-
-static NetClientInfo net_mv88w8618_info = {
- .type = NET_CLIENT_DRIVER_NIC,
- .size = sizeof(NICState),
- .receive = eth_receive,
- .cleanup = eth_cleanup,
-};
-
-static void mv88w8618_eth_init(Object *obj)
-{
- SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- DeviceState *dev = DEVICE(sbd);
- mv88w8618_eth_state *s = MV88W8618_ETH(dev);
-
- sysbus_init_irq(sbd, &s->irq);
- memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
- "mv88w8618-eth", MP_ETH_SIZE);
- sysbus_init_mmio(sbd, &s->iomem);
-}
-
-static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
-{
- mv88w8618_eth_state *s = MV88W8618_ETH(dev);
-
- if (!s->dma_mr) {
- error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
- return;
- }
-
- address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
- s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
- object_get_typename(OBJECT(dev)), dev->id, s);
-}
-
-static const VMStateDescription mv88w8618_eth_vmsd = {
- .name = "mv88w8618_eth",
- .version_id = 1,
- .minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32(smir, mv88w8618_eth_state),
- VMSTATE_UINT32(icr, mv88w8618_eth_state),
- VMSTATE_UINT32(imr, mv88w8618_eth_state),
- VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
- VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
- VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
- VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
- VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
- VMSTATE_END_OF_LIST()
- }
-};
-
-static Property mv88w8618_eth_properties[] = {
- DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
- DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
- TYPE_MEMORY_REGION, MemoryRegion *),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- dc->vmsd = &mv88w8618_eth_vmsd;
- device_class_set_props(dc, mv88w8618_eth_properties);
- dc->realize = mv88w8618_eth_realize;
-}
-
-static const TypeInfo mv88w8618_eth_info = {
- .name = TYPE_MV88W8618_ETH,
- .parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(mv88w8618_eth_state),
- .instance_init = mv88w8618_eth_init,
- .class_init = mv88w8618_eth_class_init,
-};
-
/* LCD register offsets */
#define MP_LCD_IRQCTRL 0x180
#define MP_LCD_IRQSTAT 0x184
type_register_static(&mv88w8618_pic_info);
type_register_static(&mv88w8618_pit_info);
type_register_static(&mv88w8618_flashcfg_info);
- type_register_static(&mv88w8618_eth_info);
type_register_static(&mv88w8618_wlan_info);
type_register_static(&musicpal_lcd_info);
type_register_static(&musicpal_gpio_info);
{
I2CSlave *i2c_mux;
- i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1), TYPE_PCA9548, 0x75);
+ i2c_mux = i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1),
+ TYPE_PCA9548, 0x75);
+
+ /* tmp105 is compatible with the lm75 */
+ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 4), "tmp105", 0x5c);
+ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 5), "tmp105", 0x5c);
+ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6), "tmp105", 0x5c);
+ i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 7), "tmp105", 0x5c);
+
i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1), TYPE_PCA9548, 0x77);
i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 4), TYPE_PCA9548, 0x77);
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qapi/error.h"
-#include "qemu-common.h"
#include "cpu.h"
#include "exec/address-spaces.h"
#include "hw/hw.h"
#include "sysemu/qtest.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
+#include "sysemu/rtc.h"
#include "qemu/range.h"
#include "hw/sysbus.h"
#include "qemu/cutils.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "chardev/char-fe.h"
#include "sysemu/blockdev.h"
#include "sysemu/qtest.h"
+#include "sysemu/rtc.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
#include "qom/object.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "cpu.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "chardev/char-fe.h"
#include "chardev/char-serial.h"
#include "sysemu/sysemu.h"
+#include "sysemu/rtc.h"
#include "hw/ssi/ssi.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
}
static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap,
- uint32_t irq, bool use_highmem, bool highmem_ecam,
- VirtMachineState *vms)
+ uint32_t irq, VirtMachineState *vms)
{
- int ecam_id = VIRT_ECAM_ID(highmem_ecam);
+ int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam);
struct GPEXConfig cfg = {
.mmio32 = memmap[VIRT_PCIE_MMIO],
.pio = memmap[VIRT_PCIE_PIO],
.bus = vms->bus,
};
- if (use_highmem) {
+ if (vms->highmem_mmio) {
cfg.mmio64 = memmap[VIRT_HIGH_PCIE_MMIO];
}
acpi_dsdt_add_fw_cfg(scope, &memmap[VIRT_FW_CFG]);
acpi_dsdt_add_virtio(scope, &memmap[VIRT_MMIO],
(irqmap[VIRT_MMIO] + ARM_SPI_BASE), NUM_VIRTIO_TRANSPORTS);
- acpi_dsdt_add_pci(scope, memmap, (irqmap[VIRT_PCIE] + ARM_SPI_BASE),
- vms->highmem, vms->highmem_ecam, vms);
+ acpi_dsdt_add_pci(scope, memmap, irqmap[VIRT_PCIE] + ARM_SPI_BASE, vms);
if (vms->acpi_dev) {
build_ged_aml(scope, "\\_SB."GED_DEVICE,
HOTPLUG_HANDLER(vms->acpi_dev),
#include "hw/arm/smmuv3.h"
#include "hw/acpi/acpi.h"
#include "target/arm/internals.h"
+#include "hw/mem/memory-device.h"
#include "hw/mem/pc-dimm.h"
#include "hw/mem/nvdimm.h"
#include "hw/acpi/generic_event_device.h"
+#include "hw/virtio/virtio-mem-pci.h"
#include "hw/virtio/virtio-iommu.h"
#include "hw/char/pl011.h"
#include "qemu/guest-random.h"
/* /chosen must exist for load_dtb to fill in necessary properties later */
qemu_fdt_add_subnode(fdt, "/chosen");
- create_kaslr_seed(ms, "/chosen");
+ if (vms->dtb_kaslr_seed) {
+ create_kaslr_seed(ms, "/chosen");
+ }
if (vms->secure) {
qemu_fdt_add_subnode(fdt, "/secure-chosen");
- create_kaslr_seed(ms, "/secure-chosen");
+ if (vms->dtb_kaslr_seed) {
+ create_kaslr_seed(ms, "/secure-chosen");
+ }
}
/* Clock node, for the benefit of the UART. The kernel device tree
* can contain several layers of clustering within a single physical
* package and cluster nodes can be contained in parent cluster nodes.
*
- * Given that cluster is not yet supported in the vCPU topology,
- * we currently generate one cluster node within each socket node
- * by default.
+ * Note: currently we only support one layer of clustering within
+ * each physical package.
*/
qemu_fdt_add_subnode(ms->fdt, "/cpus/cpu-map");
if (ms->smp.threads > 1) {
map_path = g_strdup_printf(
- "/cpus/cpu-map/socket%d/cluster0/core%d/thread%d",
- cpu / (ms->smp.cores * ms->smp.threads),
+ "/cpus/cpu-map/socket%d/cluster%d/core%d/thread%d",
+ cpu / (ms->smp.clusters * ms->smp.cores * ms->smp.threads),
+ (cpu / (ms->smp.cores * ms->smp.threads)) % ms->smp.clusters,
(cpu / ms->smp.threads) % ms->smp.cores,
cpu % ms->smp.threads);
} else {
map_path = g_strdup_printf(
- "/cpus/cpu-map/socket%d/cluster0/core%d",
- cpu / ms->smp.cores,
+ "/cpus/cpu-map/socket%d/cluster%d/core%d",
+ cpu / (ms->smp.clusters * ms->smp.cores),
+ (cpu / ms->smp.cores) % ms->smp.clusters,
cpu % ms->smp.cores);
}
qemu_fdt_add_path(ms->fdt, map_path);
mmio_reg, base_mmio, size_mmio);
memory_region_add_subregion(get_system_memory(), base_mmio, mmio_alias);
- if (vms->highmem) {
+ if (vms->highmem_mmio) {
/* Map high MMIO space */
MemoryRegion *high_mmio_alias = g_new0(MemoryRegion, 1);
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "reg",
2, base_ecam, 2, size_ecam);
- if (vms->highmem) {
+ if (vms->highmem_mmio) {
qemu_fdt_setprop_sized_cells(ms->fdt, nodename, "ranges",
1, FDT_PCI_RANGE_IOPORT, 2, 0,
2, base_pio, 2, size_pio,
return arm_cpu_mp_affinity(idx, clustersz);
}
-static void virt_set_memmap(VirtMachineState *vms)
+static void virt_set_memmap(VirtMachineState *vms, int pa_bits)
{
MachineState *ms = MACHINE(vms);
- hwaddr base, device_memory_base, device_memory_size;
+ hwaddr base, device_memory_base, device_memory_size, memtop;
int i;
vms->memmap = extended_memmap;
exit(EXIT_FAILURE);
}
+ /*
+ * !highmem is exactly the same as limiting the PA space to 32bit,
+ * irrespective of the underlying capabilities of the HW.
+ */
+ if (!vms->highmem) {
+ pa_bits = 32;
+ }
+
/*
* We compute the base of the high IO region depending on the
* amount of initial and device memory. The device memory start/size
device_memory_size = ms->maxram_size - ms->ram_size + ms->ram_slots * GiB;
/* Base address of the high IO region */
- base = device_memory_base + ROUND_UP(device_memory_size, GiB);
+ memtop = base = device_memory_base + ROUND_UP(device_memory_size, GiB);
+ if (memtop > BIT_ULL(pa_bits)) {
+ error_report("Addressing limited to %d bits, but memory exceeds it by %llu bytes\n",
+ pa_bits, memtop - BIT_ULL(pa_bits));
+ exit(EXIT_FAILURE);
+ }
if (base < device_memory_base) {
error_report("maxmem/slots too huge");
exit(EXIT_FAILURE);
base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES;
}
+ /* We know for sure that at least the memory fits in the PA space */
+ vms->highest_gpa = memtop - 1;
+
for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) {
hwaddr size = extended_memmap[i].size;
+ bool fits;
base = ROUND_UP(base, size);
vms->memmap[i].base = base;
vms->memmap[i].size = size;
+
+ /*
+ * Check each device to see if they fit in the PA space,
+ * moving highest_gpa as we go.
+ *
+ * For each device that doesn't fit, disable it.
+ */
+ fits = (base + size) <= BIT_ULL(pa_bits);
+ if (fits) {
+ vms->highest_gpa = base + size - 1;
+ }
+
+ switch (i) {
+ case VIRT_HIGH_GIC_REDIST2:
+ vms->highmem_redists &= fits;
+ break;
+ case VIRT_HIGH_PCIE_ECAM:
+ vms->highmem_ecam &= fits;
+ break;
+ case VIRT_HIGH_PCIE_MMIO:
+ vms->highmem_mmio &= fits;
+ break;
+ }
+
base += size;
}
- vms->highest_gpa = base - 1;
+
if (device_memory_size > 0) {
ms->device_memory = g_malloc0(sizeof(*ms->device_memory));
ms->device_memory->base = device_memory_base;
unsigned int smp_cpus = machine->smp.cpus;
unsigned int max_cpus = machine->smp.max_cpus;
+ if (!cpu_type_valid(machine->cpu_type)) {
+ error_report("mach-virt: CPU type %s not supported", machine->cpu_type);
+ exit(1);
+ }
+
+ possible_cpus = mc->possible_cpu_arch_ids(machine);
+
/*
* In accelerated mode, the memory map is computed earlier in kvm_type()
* to create a VM with the right number of IPA bits.
*/
if (!vms->memmap) {
- virt_set_memmap(vms);
+ Object *cpuobj;
+ ARMCPU *armcpu;
+ int pa_bits;
+
+ /*
+ * Instanciate a temporary CPU object to find out about what
+ * we are about to deal with. Once this is done, get rid of
+ * the object.
+ */
+ cpuobj = object_new(possible_cpus->cpus[0].type);
+ armcpu = ARM_CPU(cpuobj);
+
+ if (object_property_get_bool(cpuobj, "aarch64", NULL)) {
+ pa_bits = arm_pamax(armcpu);
+ } else if (arm_feature(&armcpu->env, ARM_FEATURE_LPAE)) {
+ /* v7 with LPAE */
+ pa_bits = 40;
+ } else {
+ /* Anything else */
+ pa_bits = 32;
+ }
+
+ object_unref(cpuobj);
+
+ virt_set_memmap(vms, pa_bits);
}
/* We can probe only here because during property set
*/
finalize_gic_version(vms);
- if (!cpu_type_valid(machine->cpu_type)) {
- error_report("mach-virt: CPU type %s not supported", machine->cpu_type);
- exit(1);
- }
-
if (vms->secure) {
/*
* The Secure view of the world is the same as the NonSecure,
create_fdt(vms);
- possible_cpus = mc->possible_cpu_arch_ids(machine);
assert(possible_cpus->len == max_cpus);
for (n = 0; n < possible_cpus->len; n++) {
Object *cpuobj;
machine->ram_size, "mach-virt.tag");
}
- vms->highmem_ecam &= vms->highmem && (!firmware_loaded || aarch64);
+ vms->highmem_ecam &= (!firmware_loaded || aarch64);
create_rtc(vms);
vms->its = value;
}
+static bool virt_get_dtb_kaslr_seed(Object *obj, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ return vms->dtb_kaslr_seed;
+}
+
+static void virt_set_dtb_kaslr_seed(Object *obj, bool value, Error **errp)
+{
+ VirtMachineState *vms = VIRT_MACHINE(obj);
+
+ vms->dtb_kaslr_seed = value;
+}
+
static char *virt_get_oem_id(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
dev, &error_abort);
}
+static void virt_virtio_md_pci_pre_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev);
+ Error *local_err = NULL;
+
+ if (!hotplug_dev2 && dev->hotplugged) {
+ /*
+ * Without a bus hotplug handler, we cannot control the plug/unplug
+ * order. We should never reach this point when hotplugging on ARM.
+ * However, it's nice to add a safety net, similar to what we have
+ * on x86.
+ */
+ error_setg(errp, "hotplug of virtio based memory devices not supported"
+ " on this bus.");
+ return;
+ }
+ /*
+ * First, see if we can plug this memory device at all. If that
+ * succeeds, branch of to the actual hotplug handler.
+ */
+ memory_device_pre_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev), NULL,
+ &local_err);
+ if (!local_err && hotplug_dev2) {
+ hotplug_handler_pre_plug(hotplug_dev2, dev, &local_err);
+ }
+ error_propagate(errp, local_err);
+}
+
+static void virt_virtio_md_pci_plug(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ HotplugHandler *hotplug_dev2 = qdev_get_bus_hotplug_handler(dev);
+ Error *local_err = NULL;
+
+ /*
+ * Plug the memory device first and then branch off to the actual
+ * hotplug handler. If that one fails, we can easily undo the memory
+ * device bits.
+ */
+ memory_device_plug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
+ if (hotplug_dev2) {
+ hotplug_handler_plug(hotplug_dev2, dev, &local_err);
+ if (local_err) {
+ memory_device_unplug(MEMORY_DEVICE(dev), MACHINE(hotplug_dev));
+ }
+ }
+ error_propagate(errp, local_err);
+}
+
+static void virt_virtio_md_pci_unplug_request(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ /* We don't support hot unplug of virtio based memory devices */
+ error_setg(errp, "virtio based memory devices cannot be unplugged.");
+}
+
+
static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,
DeviceState *dev, Error **errp)
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_memory_pre_plug(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
+ virt_virtio_md_pci_pre_plug(hotplug_dev, dev, errp);
} else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
hwaddr db_start = 0, db_end = 0;
char *resv_prop_str;
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_memory_plug(hotplug_dev, dev, errp);
}
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
+ virt_virtio_md_pci_plug(hotplug_dev, dev, errp);
+ }
+
if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
PCIDevice *pdev = PCI_DEVICE(dev);
{
if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) {
virt_dimm_unplug_request(hotplug_dev, dev, errp);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI)) {
+ virt_virtio_md_pci_unplug_request(hotplug_dev, dev, errp);
} else {
error_setg(errp, "device unplug request for unsupported device"
" type: %s", object_get_typename(OBJECT(dev)));
if (device_is_dynamic_sysbus(mc, dev) ||
object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) ||
+ object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MEM_PCI) ||
object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) {
return HOTPLUG_HANDLER(machine);
}
max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms, &fixed_ipa);
/* we freeze the memory map to compute the highest gpa */
- virt_set_memmap(vms);
+ virt_set_memmap(vms, max_vm_pa_size);
requested_pa_size = 64 - clz64(vms->highest_gpa);
hc->unplug_request = virt_machine_device_unplug_request_cb;
hc->unplug = virt_machine_device_unplug_cb;
mc->nvdimm_supported = true;
+ mc->smp_props.clusters_supported = true;
mc->auto_enable_numa_with_memhp = true;
mc->auto_enable_numa_with_memdev = true;
mc->default_ram_id = "mach-virt.ram";
"Set on/off to enable/disable "
"ITS instantiation");
+ object_class_property_add_bool(oc, "dtb-kaslr-seed",
+ virt_get_dtb_kaslr_seed,
+ virt_set_dtb_kaslr_seed);
+ object_class_property_set_description(oc, "dtb-kaslr-seed",
+ "Set off to disable passing of kaslr-seed "
+ "dtb node to guest");
+
object_class_property_add_str(oc, "x-oem-id",
virt_get_oem_id,
virt_set_oem_id);
vms->gic_version = VIRT_GIC_VERSION_NOSEL;
vms->highmem_ecam = !vmc->no_highmem_ecam;
+ vms->highmem_mmio = true;
+ vms->highmem_redists = true;
if (vmc->no_its) {
vms->its = false;
/* MTE is disabled by default. */
vms->mte = false;
+ /* Supply a kaslr-seed by default */
+ vms->dtb_kaslr_seed = true;
+
vms->irqmap = a15irqmap;
virt_flash_create(vms);
#define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt")
OBJECT_DECLARE_SIMPLE_TYPE(VersalVirt, XLNX_VERSAL_VIRT_MACHINE)
+#define XLNX_VERSAL_NUM_OSPI_FLASH 4
+
struct VersalVirt {
MachineState parent_obj;
qemu_fdt_add_subnode(s->fdt, name);
qemu_fdt_setprop_cells(s->fdt, name, "interrupts",
- GIC_FDT_IRQ_TYPE_SPI, VERSAL_BBRAM_APB_IRQ_0,
+ GIC_FDT_IRQ_TYPE_SPI, VERSAL_PMC_APB_IRQ,
GIC_FDT_IRQ_FLAGS_LEVEL_HI);
qemu_fdt_setprop(s->fdt, name, "interrupt-names",
interrupt_names, sizeof(interrupt_names));
exit(EXIT_FAILURE);
}
}
+
+ for (i = 0; i < XLNX_VERSAL_NUM_OSPI_FLASH; i++) {
+ BusState *spi_bus;
+ DeviceState *flash_dev;
+ qemu_irq cs_line;
+ DriveInfo *dinfo = drive_get(IF_MTD, 0, i);
+
+ spi_bus = qdev_get_child_bus(DEVICE(&s->soc.pmc.iou.ospi), "spi0");
+
+ flash_dev = qdev_new("mt35xu01g");
+ if (dinfo) {
+ qdev_prop_set_drive_err(flash_dev, "drive",
+ blk_by_legacy_dinfo(dinfo), &error_fatal);
+ }
+ qdev_realize_and_unref(flash_dev, spi_bus, &error_fatal);
+
+ cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.pmc.iou.ospi),
+ i + 1, cs_line);
+ }
}
static void versal_virt_machine_instance_init(Object *obj)
#include "kvm_arm.h"
#include "hw/misc/unimp.h"
#include "hw/arm/xlnx-versal.h"
+#include "qemu/log.h"
+#include "hw/sysbus.h"
#define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72")
#define GEM_REVISION 0x40070106
+#define VERSAL_NUM_PMC_APB_IRQS 3
+#define NUM_OSPI_IRQ_LINES 3
+
static void versal_create_apu_cpus(Versal *s)
{
int i;
}
}
+static void versal_create_pmc_apb_irq_orgate(Versal *s, qemu_irq *pic)
+{
+ DeviceState *orgate;
+
+ /*
+ * The VERSAL_PMC_APB_IRQ is an 'or' of the interrupts from the following
+ * models:
+ * - RTC
+ * - BBRAM
+ * - PMC SLCR
+ */
+ object_initialize_child(OBJECT(s), "pmc-apb-irq-orgate",
+ &s->pmc.apb_irq_orgate, TYPE_OR_IRQ);
+ orgate = DEVICE(&s->pmc.apb_irq_orgate);
+ object_property_set_int(OBJECT(orgate),
+ "num-lines", VERSAL_NUM_PMC_APB_IRQS, &error_fatal);
+ qdev_realize(orgate, NULL, &error_fatal);
+ qdev_connect_gpio_out(orgate, 0, pic[VERSAL_PMC_APB_IRQ]);
+}
+
static void versal_create_rtc(Versal *s, qemu_irq *pic)
{
SysBusDevice *sbd;
* TODO: Connect the ALARM and SECONDS interrupts once our RTC model
* supports them.
*/
- sysbus_connect_irq(sbd, 1, pic[VERSAL_RTC_APB_ERR_IRQ]);
+ sysbus_connect_irq(sbd, 1,
+ qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0));
}
static void versal_create_xrams(Versal *s, qemu_irq *pic)
sysbus_realize(sbd, &error_fatal);
memory_region_add_subregion(&s->mr_ps, MM_PMC_BBRAM_CTRL,
sysbus_mmio_get_region(sbd, 0));
- sysbus_connect_irq(sbd, 0, pic[VERSAL_BBRAM_APB_IRQ_0]);
+ sysbus_connect_irq(sbd, 0,
+ qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 1));
}
static void versal_realize_efuse_part(Versal *s, Object *dev, hwaddr base)
sysbus_connect_irq(SYS_BUS_DEVICE(ctrl), 0, pic[VERSAL_EFUSE_IRQ]);
}
+static void versal_create_pmc_iou_slcr(Versal *s, qemu_irq *pic)
+{
+ SysBusDevice *sbd;
+
+ object_initialize_child(OBJECT(s), "versal-pmc-iou-slcr", &s->pmc.iou.slcr,
+ TYPE_XILINX_VERSAL_PMC_IOU_SLCR);
+
+ sbd = SYS_BUS_DEVICE(&s->pmc.iou.slcr);
+ sysbus_realize(sbd, &error_fatal);
+
+ memory_region_add_subregion(&s->mr_ps, MM_PMC_PMC_IOU_SLCR,
+ sysbus_mmio_get_region(sbd, 0));
+
+ sysbus_connect_irq(sbd, 0,
+ qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 2));
+}
+
+static void versal_create_ospi(Versal *s, qemu_irq *pic)
+{
+ SysBusDevice *sbd;
+ MemoryRegion *mr_dac;
+ qemu_irq ospi_mux_sel;
+ DeviceState *orgate;
+
+ memory_region_init(&s->pmc.iou.ospi.linear_mr, OBJECT(s),
+ "versal-ospi-linear-mr" , MM_PMC_OSPI_DAC_SIZE);
+
+ object_initialize_child(OBJECT(s), "versal-ospi", &s->pmc.iou.ospi.ospi,
+ TYPE_XILINX_VERSAL_OSPI);
+
+ mr_dac = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 1);
+ memory_region_add_subregion(&s->pmc.iou.ospi.linear_mr, 0x0, mr_dac);
+
+ /* Create the OSPI destination DMA */
+ object_initialize_child(OBJECT(s), "versal-ospi-dma-dst",
+ &s->pmc.iou.ospi.dma_dst,
+ TYPE_XLNX_CSU_DMA);
+
+ object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_dst),
+ "dma", OBJECT(get_system_memory()),
+ &error_abort);
+
+ sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst);
+ sysbus_realize(sbd, &error_fatal);
+
+ memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_DST,
+ sysbus_mmio_get_region(sbd, 0));
+
+ /* Create the OSPI source DMA */
+ object_initialize_child(OBJECT(s), "versal-ospi-dma-src",
+ &s->pmc.iou.ospi.dma_src,
+ TYPE_XLNX_CSU_DMA);
+
+ object_property_set_bool(OBJECT(&s->pmc.iou.ospi.dma_src), "is-dst",
+ false, &error_abort);
+
+ object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src),
+ "dma", OBJECT(mr_dac), &error_abort);
+
+ object_property_set_link(OBJECT(&s->pmc.iou.ospi.dma_src),
+ "stream-connected-dma",
+ OBJECT(&s->pmc.iou.ospi.dma_dst),
+ &error_abort);
+
+ sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src);
+ sysbus_realize(sbd, &error_fatal);
+
+ memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DMA_SRC,
+ sysbus_mmio_get_region(sbd, 0));
+
+ /* Realize the OSPI */
+ object_property_set_link(OBJECT(&s->pmc.iou.ospi.ospi), "dma-src",
+ OBJECT(&s->pmc.iou.ospi.dma_src), &error_abort);
+
+ sbd = SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi);
+ sysbus_realize(sbd, &error_fatal);
+
+ memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI,
+ sysbus_mmio_get_region(sbd, 0));
+
+ memory_region_add_subregion(&s->mr_ps, MM_PMC_OSPI_DAC,
+ &s->pmc.iou.ospi.linear_mr);
+
+ /* ospi_mux_sel */
+ ospi_mux_sel = qdev_get_gpio_in_named(DEVICE(&s->pmc.iou.ospi.ospi),
+ "ospi-mux-sel", 0);
+ qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "ospi-mux-sel", 0,
+ ospi_mux_sel);
+
+ /* OSPI irq */
+ object_initialize_child(OBJECT(s), "ospi-irq-orgate",
+ &s->pmc.iou.ospi.irq_orgate, TYPE_OR_IRQ);
+ object_property_set_int(OBJECT(&s->pmc.iou.ospi.irq_orgate),
+ "num-lines", NUM_OSPI_IRQ_LINES, &error_fatal);
+
+ orgate = DEVICE(&s->pmc.iou.ospi.irq_orgate);
+ qdev_realize(orgate, NULL, &error_fatal);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.ospi), 0,
+ qdev_get_gpio_in(orgate, 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_src), 0,
+ qdev_get_gpio_in(orgate, 1));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->pmc.iou.ospi.dma_dst), 0,
+ qdev_get_gpio_in(orgate, 2));
+
+ qdev_connect_gpio_out(orgate, 0, pic[VERSAL_OSPI_IRQ]);
+}
+
/* This takes the board allocated linear DDR memory and creates aliases
* for each split DDR range/aperture on the Versal address map.
*/
memory_region_add_subregion(mr, base, mr_dev);
}
+static void versal_unimp_sd_emmc_sel(void *opaque, int n, int level)
+{
+ qemu_log_mask(LOG_UNIMP,
+ "Selecting between enabling SD mode or eMMC mode on "
+ "controller %d is not yet implemented\n", n);
+}
+
+static void versal_unimp_qspi_ospi_mux_sel(void *opaque, int n, int level)
+{
+ qemu_log_mask(LOG_UNIMP,
+ "Selecting between enabling the QSPI or OSPI linear address "
+ "region is not yet implemented\n");
+}
+
+static void versal_unimp_irq_parity_imr(void *opaque, int n, int level)
+{
+ qemu_log_mask(LOG_UNIMP,
+ "PMC SLCR parity interrupt behaviour "
+ "is not yet implemented\n");
+}
+
static void versal_unimp(Versal *s)
{
+ qemu_irq gpio_in;
+
versal_unimp_area(s, "psm", &s->mr_ps,
MM_PSM_START, MM_PSM_END - MM_PSM_START);
versal_unimp_area(s, "crl", &s->mr_ps,
MM_IOU_SCNTR, MM_IOU_SCNTR_SIZE);
versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps,
MM_IOU_SCNTRS, MM_IOU_SCNTRS_SIZE);
+
+ qdev_init_gpio_in_named(DEVICE(s), versal_unimp_sd_emmc_sel,
+ "sd-emmc-sel-dummy", 2);
+ qdev_init_gpio_in_named(DEVICE(s), versal_unimp_qspi_ospi_mux_sel,
+ "qspi-ospi-mux-sel-dummy", 1);
+ qdev_init_gpio_in_named(DEVICE(s), versal_unimp_irq_parity_imr,
+ "irq-parity-imr-dummy", 1);
+
+ gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 0);
+ qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 0,
+ gpio_in);
+
+ gpio_in = qdev_get_gpio_in_named(DEVICE(s), "sd-emmc-sel-dummy", 1);
+ qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr), "sd-emmc-sel", 1,
+ gpio_in);
+
+ gpio_in = qdev_get_gpio_in_named(DEVICE(s), "qspi-ospi-mux-sel-dummy", 0);
+ qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr),
+ "qspi-ospi-mux-sel", 0,
+ gpio_in);
+
+ gpio_in = qdev_get_gpio_in_named(DEVICE(s), "irq-parity-imr-dummy", 0);
+ qdev_connect_gpio_out_named(DEVICE(&s->pmc.iou.slcr),
+ SYSBUS_DEVICE_GPIO_IRQ, 0,
+ gpio_in);
}
static void versal_realize(DeviceState *dev, Error **errp)
versal_create_gems(s, pic);
versal_create_admas(s, pic);
versal_create_sds(s, pic);
+ versal_create_pmc_apb_irq_orgate(s, pic);
versal_create_rtc(s, pic);
versal_create_xrams(s, pic);
versal_create_bbram(s, pic);
versal_create_efuse(s, pic);
+ versal_create_pmc_iou_slcr(s, pic);
+ versal_create_ospi(s, pic);
versal_map_ddr(s);
versal_unimp(s);
config CS4231
bool
-
-config MARVELL_88W8618
- bool
if (st->ctl & 0x01) {
/* reset */
dprint(d, 1, "st #%d: reset\n", reg->stream);
- st->ctl = SD_STS_FIFO_READY << 24;
+ st->ctl = SD_STS_FIFO_READY << 24 | SD_CTL_STREAM_RESET;
}
if ((st->ctl & 0x02) != (old & 0x02)) {
uint32_t stnr = (st->ctl >> 20) & 0x0f;
.name = "pcspk",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = migrate_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT8(data_on, PCSpkState),
perm |= BLK_PERM_WRITE;
}
- shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
- BLK_PERM_GRAPH_MOD;
+ shared_perm = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED;
if (resizable) {
shared_perm |= BLK_PERM_RESIZE;
}
g_free(s);
}
-static bool virtio_blk_data_plane_handle_output(VirtIODevice *vdev,
- VirtQueue *vq)
-{
- VirtIOBlock *s = (VirtIOBlock *)vdev;
-
- assert(s->dataplane);
- assert(s->dataplane_started);
-
- return virtio_blk_handle_vq(s, vq);
-}
-
/* Context: QEMU global mutex held */
int virtio_blk_data_plane_start(VirtIODevice *vdev)
{
for (i = 0; i < nvqs; i++) {
VirtQueue *vq = virtio_get_queue(s->vdev, i);
- virtio_queue_aio_set_host_notifier_handler(vq, s->ctx,
- virtio_blk_data_plane_handle_output);
+ virtio_queue_aio_attach_host_notifier(vq, s->ctx);
}
aio_context_release(s->ctx);
return 0;
for (i = 0; i < s->conf->num_queues; i++) {
VirtQueue *vq = virtio_get_queue(s->vdev, i);
- virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL);
+ virtio_queue_aio_detach_host_notifier(vq, s->ctx);
}
}
{ INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
{ INFO("n25q512ax3", 0x20ba20, 0x1000, 64 << 10, 1024, ER_4K) },
{ INFO("mt25ql512ab", 0x20ba20, 0x1044, 64 << 10, 1024, ER_4K | ER_32K) },
+ { INFO_STACKED("mt35xu01g", 0x2c5b1b, 0x104100, 128 << 10, 1024,
+ ER_4K | ER_32K, 2) },
{ INFO_STACKED("n25q00", 0x20ba21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
{ INFO_STACKED("n25q00a", 0x20bb21, 0x1000, 64 << 10, 2048, ER_4K, 4) },
{ INFO_STACKED("mt25ql01g", 0x20ba21, 0x1040, 64 << 10, 2048, ER_4K, 2) },
return 0;
}
-bool virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
+void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
{
VirtIOBlockReq *req;
MultiReqBuffer mrb = {};
bool suppress_notifications = virtio_queue_get_notification(vq);
- bool progress = false;
aio_context_acquire(blk_get_aio_context(s->blk));
blk_io_plug(s->blk);
}
while ((req = virtio_blk_get_request(s, vq))) {
- progress = true;
if (virtio_blk_handle_request(req, &mrb)) {
virtqueue_detach_element(req->vq, &req->elem, 0);
virtio_blk_free_request(req);
blk_io_unplug(s->blk);
aio_context_release(blk_get_aio_context(s->blk));
- return progress;
-}
-
-static void virtio_blk_handle_output_do(VirtIOBlock *s, VirtQueue *vq)
-{
- virtio_blk_handle_vq(s, vq);
}
static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq)
{
VirtIOBlock *s = (VirtIOBlock *)vdev;
- if (s->dataplane) {
+ if (s->dataplane && !s->dataplane_started) {
/* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start
* dataplane here instead of waiting for .set_status().
*/
return;
}
}
- virtio_blk_handle_output_do(s, vq);
+ virtio_blk_handle_vq(s, vq);
}
void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh)
.name = "exynos4210.uart.fifo",
.version_id = 1,
.minimum_version_id = 1,
- .post_load = exynos4210_uart_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(sp, Exynos4210UartFIFO),
VMSTATE_UINT32(rp, Exynos4210UartFIFO),
.name = "exynos4210.uart",
.version_id = 1,
.minimum_version_id = 1,
+ .post_load = exynos4210_uart_post_load,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(rx, Exynos4210UartState, 1,
vmstate_exynos4210_uart_fifo, Exynos4210UartFIFO),
.write = htif_mm_write,
};
+bool htif_uses_elf_symbols(void)
+{
+ return (address_symbol_set == 3) ? true : false;
+}
+
HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
- CPURISCVState *env, Chardev *chr)
+ CPURISCVState *env, Chardev *chr, uint64_t nonelf_base)
{
- uint64_t base = MIN(tohost_addr, fromhost_addr);
- uint64_t size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
- uint64_t tohost_offset = tohost_addr - base;
- uint64_t fromhost_offset = fromhost_addr - base;
+ uint64_t base, size, tohost_offset, fromhost_offset;
+
+ if (!htif_uses_elf_symbols()) {
+ fromhost_addr = nonelf_base;
+ tohost_addr = nonelf_base + 8;
+ }
+
+ base = MIN(tohost_addr, fromhost_addr);
+ size = MAX(tohost_addr + 8, fromhost_addr + 8) - base;
+ tohost_offset = tohost_addr - base;
+ fromhost_offset = fromhost_addr - base;
HTIFState *s = g_malloc0(sizeof(HTIFState));
s->address_space = address_space;
qemu_chr_fe_init(&s->chr, chr, &error_abort);
qemu_chr_fe_set_handlers(&s->chr, htif_can_recv, htif_recv, htif_event,
htif_be_change, s, NULL, true);
- if (address_symbol_set == 3) {
- memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
- TYPE_HTIF_UART, size);
- memory_region_add_subregion_overlap(address_space, base,
- &s->mmio, 1);
- }
+
+ memory_region_init_io(&s->mmio, NULL, &htif_mm_ops, s,
+ TYPE_HTIF_UART, size);
+ memory_region_add_subregion_overlap(address_space, base,
+ &s->mmio, 1);
return s;
}
if (rom->mr) {
void *host = memory_region_get_ram_ptr(rom->mr);
memcpy(host, rom->data, rom->datasize);
+ memset(host + rom->datasize, 0, rom->romsize - rom->datasize);
} else {
address_space_write_rom(rom->as, rom->addr, MEMTXATTRS_UNSPECIFIED,
rom->data, rom->datasize);
+ address_space_set(rom->as, rom->addr + rom->datasize, 0,
+ rom->romsize - rom->datasize,
+ MEMTXATTRS_UNSPECIFIED);
}
if (rom->isrom) {
/* rom needs to be written only once */
{
MemoryRegion *ret = host_memory_backend_get_memory(backend);
- if (memory_region_is_mapped(ret)) {
+ if (host_memory_backend_is_mapped(backend)) {
error_report("memory backend %s can't be used multiple times.",
object_get_canonical_path_component(OBJECT(backend)));
exit(EXIT_FAILURE);
depends on ISA_BUS
select VGA
-config VGA_ISA_MM
+config VGA_MMIO
bool
select VGA
{ .xres = 2048, .yres = 1152 },
{ .xres = 1920, .yres = 1080, .dta = 31 },
+ /* dea/dta extension timings (all @ 60 Hz) */
+ { .xres = 3840, .yres = 2160, .dta = 97 },
+
/* additional standard timings 3 (all @ 60Hz) */
{ .xres = 1920, .yres = 1200, .xtra3 = 10, .bit = 0 },
{ .xres = 1600, .yres = 1200, .xtra3 = 9, .bit = 2 },
info->name = "QEMU Monitor";
}
if (!info->prefx) {
- info->prefx = 1024;
+ info->prefx = 1280;
}
if (!info->prefy) {
- info->prefy = 768;
+ info->prefy = 800;
}
if (info->prefx >= 4096 || info->prefy >= 4096) {
large_screen = 1;
.name = "macfb",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.post_load = macfb_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY(color_palette, MacfbState, 256 * 3),
softmmu_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('vga-pci.c'))
softmmu_ss.add(when: 'CONFIG_VGA_ISA', if_true: files('vga-isa.c'))
-softmmu_ss.add(when: 'CONFIG_VGA_ISA_MM', if_true: files('vga-isa-mm.c'))
+softmmu_ss.add(when: 'CONFIG_VGA_MMIO', if_true: files('vga-mmio.c'))
softmmu_ss.add(when: 'CONFIG_VMWARE_VGA', if_true: files('vmware_vga.c'))
softmmu_ss.add(when: 'CONFIG_BOCHS_DISPLAY', if_true: files('bochs-display.c'))
/* spice display interface callbacks */
-static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+static void interface_attached_worker(QXLInstance *sin)
{
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
trace_qxl_interface_attach_worker(qxl->id);
}
+#if !(SPICE_HAS_ATTACHED_WORKER)
+static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
+{
+ interface_attached_worker(sin);
+}
+#endif
+
static void interface_set_compression_level(QXLInstance *sin, int level)
{
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
.base.major_version = SPICE_INTERFACE_QXL_MAJOR,
.base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+#if SPICE_HAS_ATTACHED_WORKER
+ .attached_worker = interface_attached_worker,
+#else
.attache_worker = interface_attach_worker,
+#endif
+
.set_compression_level = interface_set_compression_level,
#if SPICE_NEEDS_SET_MM_TIME
.set_mm_time = interface_set_mm_time,
+++ /dev/null
-/*
- * QEMU ISA MM VGA Emulator.
- *
- * Copyright (c) 2003 Fabrice Bellard
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-#include "qemu/osdep.h"
-#include "qemu/bitops.h"
-#include "qemu/units.h"
-#include "migration/vmstate.h"
-#include "hw/display/vga.h"
-#include "vga_int.h"
-#include "ui/pixel_ops.h"
-
-#define VGA_RAM_SIZE (8 * MiB)
-
-typedef struct ISAVGAMMState {
- VGACommonState vga;
- int it_shift;
-} ISAVGAMMState;
-
-/* Memory mapped interface */
-static uint64_t vga_mm_read(void *opaque, hwaddr addr, unsigned size)
-{
- ISAVGAMMState *s = opaque;
-
- return vga_ioport_read(&s->vga, addr >> s->it_shift) &
- MAKE_64BIT_MASK(0, size * 8);
-}
-
-static void vga_mm_write(void *opaque, hwaddr addr, uint64_t value,
- unsigned size)
-{
- ISAVGAMMState *s = opaque;
-
- vga_ioport_write(&s->vga, addr >> s->it_shift,
- value & MAKE_64BIT_MASK(0, size * 8));
-}
-
-static const MemoryRegionOps vga_mm_ctrl_ops = {
- .read = vga_mm_read,
- .write = vga_mm_write,
- .valid.min_access_size = 1,
- .valid.max_access_size = 4,
- .impl.min_access_size = 1,
- .impl.max_access_size = 4,
- .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void vga_mm_init(ISAVGAMMState *s, hwaddr vram_base,
- hwaddr ctrl_base, int it_shift,
- MemoryRegion *address_space)
-{
- MemoryRegion *s_ioport_ctrl, *vga_io_memory;
-
- s->it_shift = it_shift;
- s_ioport_ctrl = g_malloc(sizeof(*s_ioport_ctrl));
- memory_region_init_io(s_ioport_ctrl, NULL, &vga_mm_ctrl_ops, s,
- "vga-mm-ctrl", 0x100000);
- memory_region_set_flush_coalesced(s_ioport_ctrl);
-
- vga_io_memory = g_malloc(sizeof(*vga_io_memory));
- /* XXX: endianness? */
- memory_region_init_io(vga_io_memory, NULL, &vga_mem_ops, &s->vga,
- "vga-mem", 0x20000);
-
- vmstate_register(NULL, 0, &vmstate_vga_common, s);
-
- memory_region_add_subregion(address_space, ctrl_base, s_ioport_ctrl);
- s->vga.bank_offset = 0;
- memory_region_add_subregion(address_space,
- vram_base + 0x000a0000, vga_io_memory);
- memory_region_set_coalescing(vga_io_memory);
-}
-
-int isa_vga_mm_init(hwaddr vram_base,
- hwaddr ctrl_base, int it_shift,
- MemoryRegion *address_space)
-{
- ISAVGAMMState *s;
-
- s = g_malloc0(sizeof(*s));
-
- s->vga.vram_size_mb = VGA_RAM_SIZE / MiB;
- s->vga.global_vmstate = true;
- vga_common_init(&s->vga, NULL);
- vga_mm_init(s, vram_base, ctrl_base, it_shift, address_space);
-
- s->vga.con = graphic_console_init(NULL, 0, s->vga.hw_ops, s);
-
- memory_region_add_subregion(address_space,
- VBE_DISPI_LFB_PHYSICAL_ADDRESS,
- &s->vga.vram);
-
- return 0;
-}
--- /dev/null
+/*
+ * QEMU MMIO VGA Emulator.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/sysbus.h"
+#include "hw/display/vga.h"
+#include "hw/qdev-properties.h"
+#include "vga_int.h"
+
+/*
+ * QEMU interface:
+ * + sysbus MMIO region 0: VGA I/O registers
+ * + sysbus MMIO region 1: VGA MMIO registers
+ * + sysbus MMIO region 2: VGA memory
+ */
+
+OBJECT_DECLARE_SIMPLE_TYPE(VGAMmioState, VGA_MMIO)
+
+struct VGAMmioState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ VGACommonState vga;
+ MemoryRegion iomem;
+ MemoryRegion lowmem;
+
+ uint8_t it_shift;
+};
+
+static uint64_t vga_mm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ VGAMmioState *s = opaque;
+
+ return vga_ioport_read(&s->vga, addr >> s->it_shift) &
+ MAKE_64BIT_MASK(0, size * 8);
+}
+
+static void vga_mm_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned size)
+{
+ VGAMmioState *s = opaque;
+
+ vga_ioport_write(&s->vga, addr >> s->it_shift,
+ value & MAKE_64BIT_MASK(0, size * 8));
+}
+
+static const MemoryRegionOps vga_mm_ctrl_ops = {
+ .read = vga_mm_read,
+ .write = vga_mm_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void vga_mmio_reset(DeviceState *dev)
+{
+ VGAMmioState *s = VGA_MMIO(dev);
+
+ vga_common_reset(&s->vga);
+}
+
+static void vga_mmio_realizefn(DeviceState *dev, Error **errp)
+{
+ VGAMmioState *s = VGA_MMIO(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init_io(&s->iomem, OBJECT(dev), &vga_mm_ctrl_ops, s,
+ "vga-mmio", 0x100000);
+ memory_region_set_flush_coalesced(&s->iomem);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ /* XXX: endianness? */
+ memory_region_init_io(&s->lowmem, OBJECT(dev), &vga_mem_ops, &s->vga,
+ "vga-lowmem", 0x20000);
+ memory_region_set_coalescing(&s->lowmem);
+ sysbus_init_mmio(sbd, &s->lowmem);
+
+ s->vga.bank_offset = 0;
+ s->vga.global_vmstate = true;
+ vga_common_init(&s->vga, OBJECT(dev));
+ sysbus_init_mmio(sbd, &s->vga.vram);
+ s->vga.con = graphic_console_init(dev, 0, s->vga.hw_ops, &s->vga);
+}
+
+static Property vga_mmio_properties[] = {
+ DEFINE_PROP_UINT8("it_shift", VGAMmioState, it_shift, 0),
+ DEFINE_PROP_UINT32("vgamem_mb", VGAMmioState, vga.vram_size_mb, 8),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void vga_mmio_class_initfn(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = vga_mmio_realizefn;
+ dc->reset = vga_mmio_reset;
+ dc->vmsd = &vmstate_vga_common;
+ device_class_set_props(dc, vga_mmio_properties);
+ set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories);
+}
+
+static const TypeInfo vga_mmio_info = {
+ .name = TYPE_VGA_MMIO,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(VGAMmioState),
+ .class_init = vga_mmio_class_initfn,
+};
+
+static void vga_mmio_register_types(void)
+{
+ type_register_static(&vga_mmio_info);
+}
+
+type_init(vga_mmio_register_types)
.name = TYPE_XLNX_ZDMA,
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX),
VMSTATE_UINT32(state, XlnxZDMA),
return val & R_ADDR_MSB_ADDR_MSB_MASK;
}
+static MemTxResult xlnx_csu_dma_class_read(XlnxCSUDMA *s, hwaddr addr,
+ uint32_t len)
+{
+ RegisterInfo *reg = &s->regs_info[R_SIZE];
+ uint64_t we = MAKE_64BIT_MASK(0, 4 * 8);
+
+ s->regs[R_ADDR] = addr;
+ s->regs[R_ADDR_MSB] = (uint64_t)addr >> 32;
+
+ register_write(reg, len, we, object_get_typename(OBJECT(s)), false);
+
+ return (s->regs[R_SIZE] == 0) ? MEMTX_OK : MEMTX_ERROR;
+}
+
static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = {
#define DMACH_REGINFO(NAME, snd) \
(const RegisterAccessInfo []) { \
.name = TYPE_XLNX_CSU_DMA,
.version_id = 0,
.minimum_version_id = 0,
- .minimum_version_id_old = 0,
.fields = (VMStateField[]) {
VMSTATE_PTIMER(src_timer, XlnxCSUDMA),
VMSTATE_UINT16(width, XlnxCSUDMA),
{
DeviceClass *dc = DEVICE_CLASS(klass);
StreamSinkClass *ssc = STREAM_SINK_CLASS(klass);
+ XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_CLASS(klass);
dc->reset = xlnx_csu_dma_reset;
dc->realize = xlnx_csu_dma_realize;
ssc->push = xlnx_csu_dma_stream_push;
ssc->can_push = xlnx_csu_dma_stream_can_push;
+
+ xcdc->read = xlnx_csu_dma_class_read;
}
static void xlnx_csu_dma_init(Object *obj)
.name = TYPE_IMX_GPIO,
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(dr, IMXGPIOState),
VMSTATE_UINT32(gdir, IMXGPIOState),
((high & MAKE_64BIT_MASK(0, 20)) << 32);
}
-static SGXEPCSectionList *sgx_calc_host_epc_sections(void)
+static SGXEPCSectionList *sgx_calc_host_epc_sections(uint64_t *size)
{
SGXEPCSectionList *head = NULL, **tail = &head;
SGXEPCSection *section;
section = g_new0(SGXEPCSection, 1);
section->node = j++;
section->size = sgx_calc_section_metric(ecx, edx);
+ *size += section->size;
QAPI_LIST_APPEND(tail, section);
}
{
SGXInfo *info = NULL;
uint32_t eax, ebx, ecx, edx;
+ uint64_t size = 0;
int fd = qemu_open_old("/dev/sgx_vepc", O_RDWR);
if (fd < 0) {
info->sgx1 = eax & (1U << 0) ? true : false;
info->sgx2 = eax & (1U << 1) ? true : false;
- info->sections = sgx_calc_host_epc_sections();
+ info->sections = sgx_calc_host_epc_sections(&size);
+ info->section_size = size;
close(fd);
return NULL;
}
+ SGXEPCState *sgx_epc = &pcms->sgx_epc;
info = g_new0(SGXInfo, 1);
info->sgx = true;
info->sgx1 = true;
info->sgx2 = true;
info->flc = true;
+ info->section_size = sgx_epc->size;
info->sections = sgx_get_epc_sections_list();
return info;
info->sgx2 ? "enabled" : "disabled");
monitor_printf(mon, "FLC support: %s\n",
info->flc ? "enabled" : "disabled");
+ monitor_printf(mon, "size: %" PRIu64 "\n",
+ info->section_size);
section_list = info->sections;
for (section = section_list; section; section = section->next) {
}
}
-static int handle_buffered_iopage(XenIOState *state)
+static bool handle_buffered_iopage(XenIOState *state)
{
buffered_iopage_t *buf_page = state->buffered_io_page;
buf_ioreq_t *buf_req = NULL;
+ bool handled_ioreq = false;
ioreq_t req;
int qw;
assert(!req.data_is_ptr);
qatomic_add(&buf_page->read_pointer, qw + 1);
+ handled_ioreq = true;
}
- return req.count;
+ return handled_ioreq;
}
static void handle_buffered_io(void *opaque)
hwaddr paddr_index;
uint8_t *vaddr_base;
unsigned long *valid_mapping;
- uint8_t lock;
+ uint32_t lock;
#define XEN_MAPCACHE_ENTRY_DUMMY (1 << 0)
uint8_t flags;
hwaddr size;
if (lock) {
MapCacheRev *reventry = g_malloc0(sizeof(MapCacheRev));
entry->lock++;
+ if (entry->lock == 0) {
+ fprintf(stderr,
+ "mapcache entry lock overflow: "TARGET_FMT_plx" -> %p\n",
+ entry->paddr_index, entry->vaddr_base);
+ abort();
+ }
reventry->dma = dma;
reventry->vaddr_req = mapcache->last_entry->vaddr_base + address_offset;
reventry->paddr_index = mapcache->last_entry->paddr_index;
ahci_populate_sglist(ad, &ncq_tfs->sglist, ncq_tfs->cmdh, size, 0);
if (ncq_tfs->sglist.size < size) {
- error_report("ahci: PRDT length for NCQ command (0x%zx) "
+ error_report("ahci: PRDT length for NCQ command (0x" DMA_ADDR_FMT ") "
"is smaller than the requested size (0x%zx)",
ncq_tfs->sglist.size, size);
ncq_err(ncq_tfs);
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
if (is_write) {
- dma_buf_write(s->data_ptr, size, &s->sg, attrs);
+ dma_buf_write(s->data_ptr, size, NULL, &s->sg, attrs);
} else {
- dma_buf_read(s->data_ptr, size, &s->sg, attrs);
+ dma_buf_read(s->data_ptr, size, NULL, &s->sg, attrs);
}
}
}
if (is_write) {
- dma_buf_read(p, l, &s->sg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
} else {
- dma_buf_write(p, l, &s->sg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_write(p, l, NULL, &s->sg, MEMTXATTRS_UNSPECIFIED);
}
/* free sglist, update byte count */
int mouse_dx; /* current values, needed for 'poll' mode */
int mouse_dy;
int mouse_dz;
+ int mouse_dw;
uint8_t mouse_buttons;
} PS2MouseState;
/* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
const int needed = s->mouse_type ? 4 : 3;
unsigned int b;
- int dx1, dy1, dz1;
+ int dx1, dy1, dz1, dw1;
if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
return 0;
dx1 = s->mouse_dx;
dy1 = s->mouse_dy;
dz1 = s->mouse_dz;
+ dw1 = s->mouse_dw;
/* XXX: increase range to 8 bits ? */
if (dx1 > 127)
dx1 = 127;
/* extra byte for IMPS/2 or IMEX */
switch(s->mouse_type) {
default:
+ /* Just ignore the wheels if not supported */
+ s->mouse_dz = 0;
+ s->mouse_dw = 0;
break;
case 3:
if (dz1 > 127)
else if (dz1 < -127)
dz1 = -127;
ps2_queue_noirq(&s->common, dz1 & 0xff);
+ s->mouse_dz -= dz1;
+ s->mouse_dw = 0;
break;
case 4:
- if (dz1 > 7)
- dz1 = 7;
- else if (dz1 < -7)
- dz1 = -7;
- b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+ /*
+ * This matches what the Linux kernel expects for exps/2 in
+ * drivers/input/mouse/psmouse-base.c. Note, if you happen to
+ * press/release the 4th or 5th buttons at the same moment as a
+ * horizontal wheel scroll, those button presses will get lost. I'm not
+ * sure what to do about that, since by this point we don't know
+ * whether those buttons actually changed state.
+ */
+ if (dw1 != 0) {
+ if (dw1 > 31) {
+ dw1 = 31;
+ } else if (dw1 < -31) {
+ dw1 = -31;
+ }
+
+ /*
+ * linux kernel expects first 6 bits to represent the value
+ * for horizontal scroll
+ */
+ b = (dw1 & 0x3f) | 0x40;
+ s->mouse_dw -= dw1;
+ } else {
+ if (dz1 > 7) {
+ dz1 = 7;
+ } else if (dz1 < -7) {
+ dz1 = -7;
+ }
+
+ b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
+ s->mouse_dz -= dz1;
+ }
ps2_queue_noirq(&s->common, b);
break;
}
/* update deltas */
s->mouse_dx -= dx1;
s->mouse_dy -= dy1;
- s->mouse_dz -= dz1;
return 1;
}
} else if (btn->button == INPUT_BUTTON_WHEEL_DOWN) {
s->mouse_dz++;
}
+
+ if (btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
+ s->mouse_dw--;
+ } else if (btn->button == INPUT_BUTTON_WHEEL_LEFT) {
+ s->mouse_dw++;
+ }
} else {
s->mouse_buttons &= ~bmap[btn->button];
}
/* if not remote, send event. Multiple events are sent if
too big deltas */
while (ps2_mouse_send_packet(s)) {
- if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
+ if (s->mouse_dx == 0 && s->mouse_dy == 0
+ && s->mouse_dz == 0 && s->mouse_dw == 0) {
break;
+ }
}
}
}
s->mouse_dx = 0;
s->mouse_dy = 0;
s->mouse_dz = 0;
+ s->mouse_dw = 0;
s->mouse_buttons = 0;
}
}
break;
}
+ case 0xfc:
+ if (s->revision == REV_11MPCORE) {
+ /* Reserved on 11MPCore */
+ *data = 0;
+ } else {
+ /* GICv1 or v2; Arm implementation */
+ *data = (s->revision << 16) | 0x43b;
+ }
+ break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"gic_cpu_read: Bad offset %x\n", (int)offset);
} else {
s->apr[regno][cpu] = value;
}
+ s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
break;
}
case 0xe0: case 0xe4: case 0xe8: case 0xec:
return MEMTX_OK;
}
s->nsapr[regno][cpu] = value;
+ s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu);
break;
}
case 0x1000:
}
if ((cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) && cs->gic->lpi_enable &&
+ (cs->gic->gicd_ctlr & GICD_CTLR_EN_GRP1NS) &&
(cs->hpplpi.prio != 0xff)) {
if (irqbetter(cs, cs->hpplpi.irq, cs->hpplpi.prio)) {
cs->hppi.irq = cs->hpplpi.irq;
return;
}
+ if (s->lpi_enable) {
+ address_space_init(&s->dma_as, s->dma,
+ "gicv3-its-sysmem");
+ }
+
s->cpu = g_new0(GICv3CPUState, s->num_cpu);
for (i = 0; i < s->num_cpu; i++) {
cs->level = 0;
cs->gicr_ctlr = 0;
+ if (s->lpi_enable) {
+ /* Our implementation supports clearing GICR_CTLR.EnableLPIs */
+ cs->gicr_ctlr |= GICR_CTLR_CES;
+ }
cs->gicr_statusr[GICV3_S] = 0;
cs->gicr_statusr[GICV3_NS] = 0;
cs->gicr_waker = GICR_WAKER_ProcessorSleep | GICR_WAKER_ChildrenAsleep;
#include "qemu/osdep.h"
#include "qemu/log.h"
+#include "trace.h"
#include "hw/qdev-properties.h"
#include "hw/intc/arm_gicv3_its_common.h"
#include "gicv3_internal.h"
uint64_t itel;
} IteEntry;
+/*
+ * The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options
+ * if a command parameter is not correct. These include both "stall
+ * processing of the command queue" and "ignore this command, and
+ * keep processing the queue". In our implementation we choose that
+ * memory transaction errors reading the command packet provoke a
+ * stall, but errors in parameters cause us to ignore the command
+ * and continue processing.
+ * The process_* functions which handle individual ITS commands all
+ * return an ItsCmdResult which tells process_cmdq() whether it should
+ * stall or keep going.
+ */
+typedef enum ItsCmdResult {
+ CMD_STALL = 0,
+ CMD_CONTINUE = 1,
+} ItsCmdResult;
+
static uint64_t baser_base_addr(uint64_t value, uint32_t page_sz)
{
uint64_t result = 0;
return result;
}
-static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte,
- MemTxResult *res)
+static uint64_t table_entry_addr(GICv3ITSState *s, TableDesc *td,
+ uint32_t idx, MemTxResult *res)
{
+ /*
+ * Given a TableDesc describing one of the ITS in-guest-memory
+ * tables and an index into it, return the guest address
+ * corresponding to that table entry.
+ * If there was a memory error reading the L1 table of an
+ * indirect table, *res is set accordingly, and we return -1.
+ * If the L1 table entry is marked not valid, we return -1 with
+ * *res set to MEMTX_OK.
+ *
+ * The specification defines the format of level 1 entries of a
+ * 2-level table, but the format of level 2 entries and the format
+ * of flat-mapped tables is IMPDEF.
+ */
AddressSpace *as = &s->gicv3->dma_as;
- uint64_t l2t_addr;
- uint64_t value;
- bool valid_l2t;
- uint32_t l2t_id;
+ uint32_t l2idx;
+ uint64_t l2;
uint32_t num_l2_entries;
- if (s->ct.indirect) {
- l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
+ *res = MEMTX_OK;
+
+ if (!td->indirect) {
+ /* Single level table */
+ return td->base_addr + idx * td->entry_sz;
+ }
- value = address_space_ldq_le(as,
- s->ct.base_addr +
- (l2t_id * L1TABLE_ENTRY_SIZE),
- MEMTXATTRS_UNSPECIFIED, res);
+ /* Two level table */
+ l2idx = idx / (td->page_sz / L1TABLE_ENTRY_SIZE);
- if (*res == MEMTX_OK) {
- valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
+ l2 = address_space_ldq_le(as,
+ td->base_addr + (l2idx * L1TABLE_ENTRY_SIZE),
+ MEMTXATTRS_UNSPECIFIED, res);
+ if (*res != MEMTX_OK) {
+ return -1;
+ }
+ if (!(l2 & L2_TABLE_VALID_MASK)) {
+ return -1;
+ }
- if (valid_l2t) {
- num_l2_entries = s->ct.page_sz / s->ct.entry_sz;
+ num_l2_entries = td->page_sz / td->entry_sz;
+ return (l2 & ((1ULL << 51) - 1)) + (idx % num_l2_entries) * td->entry_sz;
+}
- l2t_addr = value & ((1ULL << 51) - 1);
+static bool get_cte(GICv3ITSState *s, uint16_t icid, uint64_t *cte,
+ MemTxResult *res)
+{
+ AddressSpace *as = &s->gicv3->dma_as;
+ uint64_t entry_addr = table_entry_addr(s, &s->ct, icid, res);
- *cte = address_space_ldq_le(as, l2t_addr +
- ((icid % num_l2_entries) * GITS_CTE_SIZE),
- MEMTXATTRS_UNSPECIFIED, res);
- }
- }
- } else {
- /* Flat level table */
- *cte = address_space_ldq_le(as, s->ct.base_addr +
- (icid * GITS_CTE_SIZE),
- MEMTXATTRS_UNSPECIFIED, res);
+ if (entry_addr == -1) {
+ return false; /* not valid */
}
+ *cte = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res);
return FIELD_EX64(*cte, CTE, VALID);
}
static uint64_t get_dte(GICv3ITSState *s, uint32_t devid, MemTxResult *res)
{
AddressSpace *as = &s->gicv3->dma_as;
- uint64_t l2t_addr;
- uint64_t value;
- bool valid_l2t;
- uint32_t l2t_id;
- uint32_t num_l2_entries;
+ uint64_t entry_addr = table_entry_addr(s, &s->dt, devid, res);
- if (s->dt.indirect) {
- l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
-
- value = address_space_ldq_le(as,
- s->dt.base_addr +
- (l2t_id * L1TABLE_ENTRY_SIZE),
- MEMTXATTRS_UNSPECIFIED, res);
-
- if (*res == MEMTX_OK) {
- valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
-
- if (valid_l2t) {
- num_l2_entries = s->dt.page_sz / s->dt.entry_sz;
-
- l2t_addr = value & ((1ULL << 51) - 1);
-
- value = address_space_ldq_le(as, l2t_addr +
- ((devid % num_l2_entries) * GITS_DTE_SIZE),
- MEMTXATTRS_UNSPECIFIED, res);
- }
- }
- } else {
- /* Flat level table */
- value = address_space_ldq_le(as, s->dt.base_addr +
- (devid * GITS_DTE_SIZE),
- MEMTXATTRS_UNSPECIFIED, res);
+ if (entry_addr == -1) {
+ return 0; /* a DTE entry with the Valid bit clear */
}
-
- return value;
+ return address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, res);
}
/*
* 3. handling of ITS CLEAR command
* 4. handling of ITS DISCARD command
*/
-static bool process_its_cmd(GICv3ITSState *s, uint64_t value, uint32_t offset,
- ItsCmdType cmd)
+static ItsCmdResult process_its_cmd(GICv3ITSState *s, uint64_t value,
+ uint32_t offset, ItsCmdType cmd)
{
AddressSpace *as = &s->gicv3->dma_as;
uint32_t devid, eventid;
MemTxResult res = MEMTX_OK;
bool dte_valid;
uint64_t dte = 0;
- uint32_t max_eventid;
+ uint64_t num_eventids;
uint16_t icid = 0;
uint32_t pIntid = 0;
bool ite_valid = false;
uint64_t cte = 0;
bool cte_valid = false;
- bool result = false;
uint64_t rdbase;
if (cmd == NONE) {
}
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
eventid = (value & EVENTID_MASK);
+ if (devid >= s->dt.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: devid %d>=%d",
+ __func__, devid, s->dt.num_entries);
+ return CMD_CONTINUE;
+ }
+
dte = get_dte(s, devid, &res);
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
dte_valid = FIELD_EX64(dte, DTE, VALID);
- if (dte_valid) {
- max_eventid = 1UL << (FIELD_EX64(dte, DTE, SIZE) + 1);
+ if (!dte_valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: "
+ "invalid dte: %"PRIx64" for %d\n",
+ __func__, dte, devid);
+ return CMD_CONTINUE;
+ }
- ite_valid = get_ite(s, eventid, dte, &icid, &pIntid, &res);
+ num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
- if (res != MEMTX_OK) {
- return result;
- }
+ if (eventid >= num_eventids) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: eventid %d >= %"
+ PRId64 "\n",
+ __func__, eventid, num_eventids);
+ return CMD_CONTINUE;
+ }
- if (ite_valid) {
- cte_valid = get_cte(s, icid, &cte, &res);
- }
+ ite_valid = get_ite(s, eventid, dte, &icid, &pIntid, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
- if (res != MEMTX_OK) {
- return result;
- }
- } else {
+ if (!ite_valid) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: "
- "invalid dte: %"PRIx64" for %d (MEM_TX: %d)\n",
- __func__, dte, devid, res);
- return result;
+ "%s: invalid command attributes: invalid ITE\n",
+ __func__);
+ return CMD_CONTINUE;
}
-
- /*
- * In this implementation, in case of guest errors we ignore the
- * command and move onto the next command in the queue.
- */
- if (devid >= s->dt.num_ids) {
+ if (icid >= s->ct.num_entries) {
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: devid %d>=%d",
- __func__, devid, s->dt.num_ids);
+ "%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
+ __func__, icid);
+ return CMD_CONTINUE;
+ }
- } else if (!dte_valid || !ite_valid || !cte_valid) {
+ cte_valid = get_cte(s, icid, &cte, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+ if (!cte_valid) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes: "
- "dte: %s, ite: %s, cte: %s\n",
- __func__,
- dte_valid ? "valid" : "invalid",
- ite_valid ? "valid" : "invalid",
- cte_valid ? "valid" : "invalid");
- } else if (eventid > max_eventid) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "%s: invalid command attributes: eventid %d > %d\n",
- __func__, eventid, max_eventid);
- } else {
- /*
- * Current implementation only supports rdbase == procnum
- * Hence rdbase physical address is ignored
- */
- rdbase = FIELD_EX64(cte, CTE, RDBASE);
+ "invalid cte: %"PRIx64"\n",
+ __func__, cte);
+ return CMD_CONTINUE;
+ }
- if (rdbase >= s->gicv3->num_cpu) {
- return result;
- }
+ /*
+ * Current implementation only supports rdbase == procnum
+ * Hence rdbase physical address is ignored
+ */
+ rdbase = FIELD_EX64(cte, CTE, RDBASE);
- if ((cmd == CLEAR) || (cmd == DISCARD)) {
- gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0);
- } else {
- gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1);
- }
+ if (rdbase >= s->gicv3->num_cpu) {
+ return CMD_CONTINUE;
+ }
- if (cmd == DISCARD) {
- IteEntry ite = {};
- /* remove mapping from interrupt translation table */
- result = update_ite(s, eventid, dte, ite);
- }
+ if ((cmd == CLEAR) || (cmd == DISCARD)) {
+ gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 0);
+ } else {
+ gicv3_redist_process_lpi(&s->gicv3->cpu[rdbase], pIntid, 1);
}
- return result;
+ if (cmd == DISCARD) {
+ IteEntry ite = {};
+ /* remove mapping from interrupt translation table */
+ return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL;
+ }
+ return CMD_CONTINUE;
}
-static bool process_mapti(GICv3ITSState *s, uint64_t value, uint32_t offset,
- bool ignore_pInt)
+static ItsCmdResult process_mapti(GICv3ITSState *s, uint64_t value,
+ uint32_t offset, bool ignore_pInt)
{
AddressSpace *as = &s->gicv3->dma_as;
uint32_t devid, eventid;
uint32_t pIntid = 0;
- uint32_t max_eventid, max_Intid;
+ uint64_t num_eventids;
+ uint32_t num_intids;
bool dte_valid;
MemTxResult res = MEMTX_OK;
uint16_t icid = 0;
uint64_t dte = 0;
- bool result = false;
+ IteEntry ite = {};
devid = ((value & DEVID_MASK) >> DEVID_SHIFT);
offset += NUM_BYTES_IN_DW;
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
eventid = (value & EVENTID_MASK);
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
icid = value & ICID_MASK;
+ if (devid >= s->dt.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: devid %d>=%d",
+ __func__, devid, s->dt.num_entries);
+ return CMD_CONTINUE;
+ }
+
dte = get_dte(s, devid, &res);
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
dte_valid = FIELD_EX64(dte, DTE, VALID);
- max_eventid = 1UL << (FIELD_EX64(dte, DTE, SIZE) + 1);
- max_Intid = (1ULL << (GICD_TYPER_IDBITS + 1)) - 1;
+ num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
+ num_intids = 1ULL << (GICD_TYPER_IDBITS + 1);
- if ((devid >= s->dt.num_ids) || (icid >= s->ct.num_ids)
- || !dte_valid || (eventid > max_eventid) ||
- (((pIntid < GICV3_LPI_INTID_START) || (pIntid > max_Intid)) &&
+ if ((icid >= s->ct.num_entries)
+ || !dte_valid || (eventid >= num_eventids) ||
+ (((pIntid < GICV3_LPI_INTID_START) || (pIntid >= num_intids)) &&
(pIntid != INTID_SPURIOUS))) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid command attributes "
- "devid %d or icid %d or eventid %d or pIntid %d or"
- "unmapped dte %d\n", __func__, devid, icid, eventid,
+ "icid %d or eventid %d or pIntid %d or"
+ "unmapped dte %d\n", __func__, icid, eventid,
pIntid, dte_valid);
/*
* in this implementation, in case of error
* we ignore this command and move onto the next
* command in the queue
*/
- } else {
- /* add ite entry to interrupt translation table */
- IteEntry ite = {};
- ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, dte_valid);
- ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL);
- ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, pIntid);
- ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS);
- ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, icid);
-
- result = update_ite(s, eventid, dte, ite);
+ return CMD_CONTINUE;
}
- return result;
+ /* add ite entry to interrupt translation table */
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, dte_valid);
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL);
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, pIntid);
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS);
+ ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, icid);
+
+ return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL;
}
static bool update_cte(GICv3ITSState *s, uint16_t icid, bool valid,
uint64_t rdbase)
{
AddressSpace *as = &s->gicv3->dma_as;
- uint64_t value;
- uint64_t l2t_addr;
- bool valid_l2t;
- uint32_t l2t_id;
- uint32_t num_l2_entries;
+ uint64_t entry_addr;
uint64_t cte = 0;
MemTxResult res = MEMTX_OK;
cte = FIELD_DP64(cte, CTE, RDBASE, rdbase);
}
- /*
- * The specification defines the format of level 1 entries of a
- * 2-level table, but the format of level 2 entries and the format
- * of flat-mapped tables is IMPDEF.
- */
- if (s->ct.indirect) {
- l2t_id = icid / (s->ct.page_sz / L1TABLE_ENTRY_SIZE);
-
- value = address_space_ldq_le(as,
- s->ct.base_addr +
- (l2t_id * L1TABLE_ENTRY_SIZE),
- MEMTXATTRS_UNSPECIFIED, &res);
-
- if (res != MEMTX_OK) {
- return false;
- }
-
- valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
-
- if (valid_l2t) {
- num_l2_entries = s->ct.page_sz / s->ct.entry_sz;
-
- l2t_addr = value & ((1ULL << 51) - 1);
-
- address_space_stq_le(as, l2t_addr +
- ((icid % num_l2_entries) * GITS_CTE_SIZE),
- cte, MEMTXATTRS_UNSPECIFIED, &res);
- }
- } else {
- /* Flat level table */
- address_space_stq_le(as, s->ct.base_addr + (icid * GITS_CTE_SIZE),
- cte, MEMTXATTRS_UNSPECIFIED, &res);
- }
+ entry_addr = table_entry_addr(s, &s->ct, icid, &res);
if (res != MEMTX_OK) {
+ /* memory access error: stall */
return false;
- } else {
+ }
+ if (entry_addr == -1) {
+ /* No L2 table for this index: discard write and continue */
return true;
}
+
+ address_space_stq_le(as, entry_addr, cte, MEMTXATTRS_UNSPECIFIED, &res);
+ return res == MEMTX_OK;
}
-static bool process_mapc(GICv3ITSState *s, uint32_t offset)
+static ItsCmdResult process_mapc(GICv3ITSState *s, uint32_t offset)
{
AddressSpace *as = &s->gicv3->dma_as;
uint16_t icid;
uint64_t rdbase;
bool valid;
MemTxResult res = MEMTX_OK;
- bool result = false;
uint64_t value;
offset += NUM_BYTES_IN_DW;
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
icid = value & ICID_MASK;
valid = (value & CMD_FIELD_VALID_MASK);
- if ((icid >= s->ct.num_ids) || (rdbase >= s->gicv3->num_cpu)) {
+ if ((icid >= s->ct.num_entries) || (rdbase >= s->gicv3->num_cpu)) {
qemu_log_mask(LOG_GUEST_ERROR,
"ITS MAPC: invalid collection table attributes "
"icid %d rdbase %" PRIu64 "\n", icid, rdbase);
* we ignore this command and move onto the next
* command in the queue
*/
- } else {
- result = update_cte(s, icid, valid, rdbase);
+ return CMD_CONTINUE;
}
- return result;
+ return update_cte(s, icid, valid, rdbase) ? CMD_CONTINUE : CMD_STALL;
}
static bool update_dte(GICv3ITSState *s, uint32_t devid, bool valid,
uint8_t size, uint64_t itt_addr)
{
AddressSpace *as = &s->gicv3->dma_as;
- uint64_t value;
- uint64_t l2t_addr;
- bool valid_l2t;
- uint32_t l2t_id;
- uint32_t num_l2_entries;
+ uint64_t entry_addr;
uint64_t dte = 0;
MemTxResult res = MEMTX_OK;
return true;
}
- /*
- * The specification defines the format of level 1 entries of a
- * 2-level table, but the format of level 2 entries and the format
- * of flat-mapped tables is IMPDEF.
- */
- if (s->dt.indirect) {
- l2t_id = devid / (s->dt.page_sz / L1TABLE_ENTRY_SIZE);
-
- value = address_space_ldq_le(as,
- s->dt.base_addr +
- (l2t_id * L1TABLE_ENTRY_SIZE),
- MEMTXATTRS_UNSPECIFIED, &res);
-
- if (res != MEMTX_OK) {
- return false;
- }
-
- valid_l2t = (value & L2_TABLE_VALID_MASK) != 0;
-
- if (valid_l2t) {
- num_l2_entries = s->dt.page_sz / s->dt.entry_sz;
-
- l2t_addr = value & ((1ULL << 51) - 1);
-
- address_space_stq_le(as, l2t_addr +
- ((devid % num_l2_entries) * GITS_DTE_SIZE),
- dte, MEMTXATTRS_UNSPECIFIED, &res);
- }
- } else {
- /* Flat level table */
- address_space_stq_le(as, s->dt.base_addr + (devid * GITS_DTE_SIZE),
- dte, MEMTXATTRS_UNSPECIFIED, &res);
- }
+ entry_addr = table_entry_addr(s, &s->dt, devid, &res);
if (res != MEMTX_OK) {
+ /* memory access error: stall */
return false;
- } else {
+ }
+ if (entry_addr == -1) {
+ /* No L2 table for this index: discard write and continue */
return true;
}
+ address_space_stq_le(as, entry_addr, dte, MEMTXATTRS_UNSPECIFIED, &res);
+ return res == MEMTX_OK;
}
-static bool process_mapd(GICv3ITSState *s, uint64_t value, uint32_t offset)
+static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value,
+ uint32_t offset)
{
AddressSpace *as = &s->gicv3->dma_as;
uint32_t devid;
uint64_t itt_addr;
bool valid;
MemTxResult res = MEMTX_OK;
- bool result = false;
devid = ((value & DEVID_MASK) >> DEVID_SHIFT);
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
size = (value & SIZE_MASK);
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
- return result;
+ return CMD_STALL;
}
itt_addr = (value & ITTADDR_MASK) >> ITTADDR_SHIFT;
valid = (value & CMD_FIELD_VALID_MASK);
- if ((devid >= s->dt.num_ids) ||
+ if ((devid >= s->dt.num_entries) ||
(size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
qemu_log_mask(LOG_GUEST_ERROR,
"ITS MAPD: invalid device table attributes "
* we ignore this command and move onto the next
* command in the queue
*/
- } else {
- result = update_dte(s, devid, valid, size, itt_addr);
+ return CMD_CONTINUE;
}
- return result;
+ return update_dte(s, devid, valid, size, itt_addr) ? CMD_CONTINUE : CMD_STALL;
+}
+
+static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value,
+ uint32_t offset)
+{
+ AddressSpace *as = &s->gicv3->dma_as;
+ MemTxResult res = MEMTX_OK;
+ uint64_t rd1, rd2;
+
+ /* No fields in dwords 0 or 1 */
+ offset += NUM_BYTES_IN_DW;
+ offset += NUM_BYTES_IN_DW;
+ value = address_space_ldq_le(as, s->cq.base_addr + offset,
+ MEMTXATTRS_UNSPECIFIED, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+
+ rd1 = FIELD_EX64(value, MOVALL_2, RDBASE1);
+ if (rd1 >= s->gicv3->num_cpu) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: RDBASE1 %" PRId64
+ " out of range (must be less than %d)\n",
+ __func__, rd1, s->gicv3->num_cpu);
+ return CMD_CONTINUE;
+ }
+
+ offset += NUM_BYTES_IN_DW;
+ value = address_space_ldq_le(as, s->cq.base_addr + offset,
+ MEMTXATTRS_UNSPECIFIED, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+
+ rd2 = FIELD_EX64(value, MOVALL_3, RDBASE2);
+ if (rd2 >= s->gicv3->num_cpu) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: RDBASE2 %" PRId64
+ " out of range (must be less than %d)\n",
+ __func__, rd2, s->gicv3->num_cpu);
+ return CMD_CONTINUE;
+ }
+
+ if (rd1 == rd2) {
+ /* Move to same target must succeed as a no-op */
+ return CMD_CONTINUE;
+ }
+
+ /* Move all pending LPIs from redistributor 1 to redistributor 2 */
+ gicv3_redist_movall_lpis(&s->gicv3->cpu[rd1], &s->gicv3->cpu[rd2]);
+
+ return CMD_CONTINUE;
+}
+
+static ItsCmdResult process_movi(GICv3ITSState *s, uint64_t value,
+ uint32_t offset)
+{
+ AddressSpace *as = &s->gicv3->dma_as;
+ MemTxResult res = MEMTX_OK;
+ uint32_t devid, eventid, intid;
+ uint16_t old_icid, new_icid;
+ uint64_t old_cte, new_cte;
+ uint64_t old_rdbase, new_rdbase;
+ uint64_t dte;
+ bool dte_valid, ite_valid, cte_valid;
+ uint64_t num_eventids;
+ IteEntry ite = {};
+
+ devid = FIELD_EX64(value, MOVI_0, DEVICEID);
+
+ offset += NUM_BYTES_IN_DW;
+ value = address_space_ldq_le(as, s->cq.base_addr + offset,
+ MEMTXATTRS_UNSPECIFIED, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+ eventid = FIELD_EX64(value, MOVI_1, EVENTID);
+
+ offset += NUM_BYTES_IN_DW;
+ value = address_space_ldq_le(as, s->cq.base_addr + offset,
+ MEMTXATTRS_UNSPECIFIED, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+ new_icid = FIELD_EX64(value, MOVI_2, ICID);
+
+ if (devid >= s->dt.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: devid %d>=%d",
+ __func__, devid, s->dt.num_entries);
+ return CMD_CONTINUE;
+ }
+ dte = get_dte(s, devid, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+
+ dte_valid = FIELD_EX64(dte, DTE, VALID);
+ if (!dte_valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: "
+ "invalid dte: %"PRIx64" for %d\n",
+ __func__, dte, devid);
+ return CMD_CONTINUE;
+ }
+
+ num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
+ if (eventid >= num_eventids) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: eventid %d >= %"
+ PRId64 "\n",
+ __func__, eventid, num_eventids);
+ return CMD_CONTINUE;
+ }
+
+ ite_valid = get_ite(s, eventid, dte, &old_icid, &intid, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+
+ if (!ite_valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: invalid ITE\n",
+ __func__);
+ return CMD_CONTINUE;
+ }
+
+ if (old_icid >= s->ct.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
+ __func__, old_icid);
+ return CMD_CONTINUE;
+ }
+
+ if (new_icid >= s->ct.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: ICID 0x%x\n",
+ __func__, new_icid);
+ return CMD_CONTINUE;
+ }
+
+ cte_valid = get_cte(s, old_icid, &old_cte, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+ if (!cte_valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: "
+ "invalid cte: %"PRIx64"\n",
+ __func__, old_cte);
+ return CMD_CONTINUE;
+ }
+
+ cte_valid = get_cte(s, new_icid, &new_cte, &res);
+ if (res != MEMTX_OK) {
+ return CMD_STALL;
+ }
+ if (!cte_valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid command attributes: "
+ "invalid cte: %"PRIx64"\n",
+ __func__, new_cte);
+ return CMD_CONTINUE;
+ }
+
+ old_rdbase = FIELD_EX64(old_cte, CTE, RDBASE);
+ if (old_rdbase >= s->gicv3->num_cpu) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: CTE has invalid rdbase 0x%"PRIx64"\n",
+ __func__, old_rdbase);
+ return CMD_CONTINUE;
+ }
+
+ new_rdbase = FIELD_EX64(new_cte, CTE, RDBASE);
+ if (new_rdbase >= s->gicv3->num_cpu) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: CTE has invalid rdbase 0x%"PRIx64"\n",
+ __func__, new_rdbase);
+ return CMD_CONTINUE;
+ }
+
+ if (old_rdbase != new_rdbase) {
+ /* Move the LPI from the old redistributor to the new one */
+ gicv3_redist_mov_lpi(&s->gicv3->cpu[old_rdbase],
+ &s->gicv3->cpu[new_rdbase],
+ intid);
+ }
+
+ /* Update the ICID field in the interrupt translation table entry */
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, 1);
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL);
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, intid);
+ ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS);
+ ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, new_icid);
+ return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL;
}
/*
uint64_t data;
AddressSpace *as = &s->gicv3->dma_as;
MemTxResult res = MEMTX_OK;
- bool result = true;
uint8_t cmd;
int i;
}
while (wr_offset != rd_offset) {
+ ItsCmdResult result = CMD_CONTINUE;
+
cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
data = address_space_ldq_le(as, s->cq.base_addr + cq_offset,
MEMTXATTRS_UNSPECIFIED, &res);
if (res != MEMTX_OK) {
- result = false;
+ s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: could not read command at 0x%" PRIx64 "\n",
+ __func__, s->cq.base_addr + cq_offset);
+ break;
}
+
cmd = (data & CMD_MASK);
+ trace_gicv3_its_process_command(rd_offset, cmd);
+
switch (cmd) {
case GITS_CMD_INT:
- res = process_its_cmd(s, data, cq_offset, INTERRUPT);
+ result = process_its_cmd(s, data, cq_offset, INTERRUPT);
break;
case GITS_CMD_CLEAR:
- res = process_its_cmd(s, data, cq_offset, CLEAR);
+ result = process_its_cmd(s, data, cq_offset, CLEAR);
break;
case GITS_CMD_SYNC:
/*
gicv3_redist_update_lpi(&s->gicv3->cpu[i]);
}
break;
+ case GITS_CMD_MOVI:
+ result = process_movi(s, data, cq_offset);
+ break;
+ case GITS_CMD_MOVALL:
+ result = process_movall(s, data, cq_offset);
+ break;
default:
break;
}
- if (result) {
+ if (result == CMD_CONTINUE) {
rd_offset++;
rd_offset %= s->cq.num_entries;
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET, rd_offset);
} else {
- /*
- * in this implementation, in case of dma read/write error
- * we stall the command processing
- */
+ /* CMD_STALL */
s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, STALLED, 1);
qemu_log_mask(LOG_GUEST_ERROR,
- "%s: %x cmd processing failed\n", __func__, cmd);
+ "%s: 0x%x cmd processing failed, stalling\n",
+ __func__, cmd);
break;
}
}
L1TABLE_ENTRY_SIZE) *
(page_sz / td->entry_sz));
}
- td->num_ids = 1ULL << idbits;
+ td->num_entries = MIN(td->num_entries, 1ULL << idbits);
}
}
}
}
+static MemTxResult gicv3_its_translation_read(void *opaque, hwaddr offset,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ /*
+ * GITS_TRANSLATER is write-only, and all other addresses
+ * in the interrupt translation space frame are RES0.
+ */
+ *data = 0;
+ return MEMTX_OK;
+}
+
static MemTxResult gicv3_its_translation_write(void *opaque, hwaddr offset,
uint64_t data, unsigned size,
MemTxAttrs attrs)
bool result = true;
uint32_t devid = 0;
+ trace_gicv3_its_translation_write(offset, data, size, attrs.requester_id);
+
switch (offset) {
case GITS_TRANSLATER:
if (s->ctlr & R_GITS_CTLR_ENABLED_MASK) {
s->ctlr |= R_GITS_CTLR_ENABLED_MASK;
extract_table_params(s);
extract_cmdq_params(s);
- s->creadr = 0;
process_cmdq(s);
} else {
s->ctlr &= ~R_GITS_CTLR_ENABLED_MASK;
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
s->cbaser = deposit64(s->cbaser, 0, 32, value);
s->creadr = 0;
- s->cwriter = s->creadr;
}
break;
case GITS_CBASER + 4:
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
s->cbaser = deposit64(s->cbaser, 32, 32, value);
s->creadr = 0;
- s->cwriter = s->creadr;
}
break;
case GITS_CWRITER:
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
index = (offset - GITS_BASER) / 8;
+ if (s->baser[index] == 0) {
+ /* Unimplemented GITS_BASERn: RAZ/WI */
+ break;
+ }
if (offset & 7) {
value <<= 32;
value &= ~GITS_BASER_RO_MASK;
*/
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
index = (offset - GITS_BASER) / 8;
+ if (s->baser[index] == 0) {
+ /* Unimplemented GITS_BASERn: RAZ/WI */
+ break;
+ }
s->baser[index] &= GITS_BASER_RO_MASK;
s->baser[index] |= (value & ~GITS_BASER_RO_MASK);
}
if (!(s->ctlr & R_GITS_CTLR_ENABLED_MASK)) {
s->cbaser = value;
s->creadr = 0;
- s->cwriter = s->creadr;
}
break;
case GITS_CWRITER:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid guest read at offset " TARGET_FMT_plx
"size %u\n", __func__, offset, size);
+ trace_gicv3_its_badread(offset, size);
/*
* The spec requires that reserved registers are RAZ/WI;
* so use false returns from leaf functions as a way to
* the caller, or we'll cause a spurious guest data abort.
*/
*data = 0;
+ } else {
+ trace_gicv3_its_read(offset, *data, size);
}
return MEMTX_OK;
}
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid guest write at offset " TARGET_FMT_plx
"size %u\n", __func__, offset, size);
+ trace_gicv3_its_badwrite(offset, data, size);
/*
* The spec requires that reserved registers are RAZ/WI;
* so use false returns from leaf functions as a way to
* trigger the guest-error logging but don't return it to
* the caller, or we'll cause a spurious guest data abort.
*/
+ } else {
+ trace_gicv3_its_write(offset, data, size);
}
return MEMTX_OK;
}
};
static const MemoryRegionOps gicv3_its_translation_ops = {
+ .read_with_attrs = gicv3_its_translation_read,
.write_with_attrs = gicv3_its_translation_write,
.valid.min_access_size = 2,
.valid.max_access_size = 4,
gicv3_its_init_mmio(s, &gicv3_its_control_ops, &gicv3_its_translation_ops);
- address_space_init(&s->gicv3->dma_as, s->gicv3->dma,
- "gicv3-its-sysmem");
-
/* set the ITS default features supported */
s->typer = FIELD_DP64(s->typer, GITS_TYPER, PHYSICAL, 1);
s->typer = FIELD_DP64(s->typer, GITS_TYPER, ITT_ENTRY_SIZE,
break;
}
- if (r == MEMTX_ERROR) {
+ if (r != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid guest read at offset " TARGET_FMT_plx
" size %u\n", __func__, offset, size);
break;
}
- if (r == MEMTX_ERROR) {
+ if (r != MEMTX_OK) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: invalid guest write at offset " TARGET_FMT_plx
" size %u\n", __func__, offset, size);
idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
GICD_TYPER_IDBITS);
- if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
- !cs->gicr_pendbaser) {
+ if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
return;
}
idbits = MIN(FIELD_EX64(cs->gicr_propbaser, GICR_PROPBASER, IDBITS),
GICD_TYPER_IDBITS);
- if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) || !cs->gicr_propbaser ||
- !cs->gicr_pendbaser || (irq > (1ULL << (idbits + 1)) - 1) ||
- irq < GICV3_LPI_INTID_START) {
+ if (!(cs->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
+ (irq > (1ULL << (idbits + 1)) - 1) || irq < GICV3_LPI_INTID_START) {
return;
}
gicv3_redist_lpi_pending(cs, irq, level);
}
+void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
+{
+ /*
+ * Move the specified LPI's pending state from the source redistributor
+ * to the destination.
+ *
+ * If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE:
+ * we choose to NOP. If LPIs are disabled on source there's nothing
+ * to be transferred anyway.
+ */
+ AddressSpace *as = &src->gic->dma_as;
+ uint64_t idbits;
+ uint32_t pendt_size;
+ uint64_t src_baddr;
+ uint8_t src_pend;
+
+ if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
+ !(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
+ return;
+ }
+
+ idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS),
+ GICD_TYPER_IDBITS);
+ idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS),
+ idbits);
+
+ pendt_size = 1ULL << (idbits + 1);
+ if ((irq / 8) >= pendt_size) {
+ return;
+ }
+
+ src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
+
+ address_space_read(as, src_baddr + (irq / 8),
+ MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
+ if (!extract32(src_pend, irq % 8, 1)) {
+ /* Not pending on source, nothing to do */
+ return;
+ }
+ src_pend &= ~(1 << (irq % 8));
+ address_space_write(as, src_baddr + (irq / 8),
+ MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
+ if (irq == src->hpplpi.irq) {
+ /*
+ * We just made this LPI not-pending so only need to update
+ * if it was previously the highest priority pending LPI
+ */
+ gicv3_redist_update_lpi(src);
+ }
+ /* Mark it pending on the destination */
+ gicv3_redist_lpi_pending(dest, irq, 1);
+}
+
+void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
+{
+ /*
+ * We must move all pending LPIs from the source redistributor
+ * to the destination. That is, for every pending LPI X on
+ * src, we must set it not-pending on src and pending on dest.
+ * LPIs that are already pending on dest are not cleared.
+ *
+ * If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE:
+ * we choose to NOP. If LPIs are disabled on source there's nothing
+ * to be transferred anyway.
+ */
+ AddressSpace *as = &src->gic->dma_as;
+ uint64_t idbits;
+ uint32_t pendt_size;
+ uint64_t src_baddr, dest_baddr;
+ int i;
+
+ if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
+ !(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
+ return;
+ }
+
+ idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS),
+ GICD_TYPER_IDBITS);
+ idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS),
+ idbits);
+
+ pendt_size = 1ULL << (idbits + 1);
+ src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
+ dest_baddr = dest->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
+
+ for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
+ uint8_t src_pend, dest_pend;
+
+ address_space_read(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED,
+ &src_pend, sizeof(src_pend));
+ if (!src_pend) {
+ continue;
+ }
+ address_space_read(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED,
+ &dest_pend, sizeof(dest_pend));
+ dest_pend |= src_pend;
+ src_pend = 0;
+ address_space_write(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED,
+ &src_pend, sizeof(src_pend));
+ address_space_write(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED,
+ &dest_pend, sizeof(dest_pend));
+ }
+
+ gicv3_redist_update_lpi(src);
+ gicv3_redist_update_lpi(dest);
+}
+
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
{
/* Update redistributor state for a change in an external PPI input line */
#define GICR_NSACR (GICR_SGI_OFFSET + 0x0E00)
#define GICR_CTLR_ENABLE_LPIS (1U << 0)
+#define GICR_CTLR_CES (1U << 1)
#define GICR_CTLR_RWP (1U << 3)
#define GICR_CTLR_DPG0 (1U << 24)
#define GICR_CTLR_DPG1NS (1U << 25)
#define CMD_MASK 0xff
/* ITS Commands */
-#define GITS_CMD_CLEAR 0x04
-#define GITS_CMD_DISCARD 0x0F
+#define GITS_CMD_MOVI 0x01
#define GITS_CMD_INT 0x03
-#define GITS_CMD_MAPC 0x09
+#define GITS_CMD_CLEAR 0x04
+#define GITS_CMD_SYNC 0x05
#define GITS_CMD_MAPD 0x08
-#define GITS_CMD_MAPI 0x0B
+#define GITS_CMD_MAPC 0x09
#define GITS_CMD_MAPTI 0x0A
+#define GITS_CMD_MAPI 0x0B
#define GITS_CMD_INV 0x0C
#define GITS_CMD_INVALL 0x0D
-#define GITS_CMD_SYNC 0x05
+#define GITS_CMD_MOVALL 0x0E
+#define GITS_CMD_DISCARD 0x0F
/* MAPC command fields */
#define ICID_LENGTH 16
#define L2_TABLE_VALID_MASK CMD_FIELD_VALID_MASK
#define TABLE_ENTRY_VALID_MASK (1ULL << 0)
+/* MOVALL command fields */
+FIELD(MOVALL_2, RDBASE1, 16, 36)
+FIELD(MOVALL_3, RDBASE2, 16, 36)
+
+/* MOVI command fields */
+FIELD(MOVI_0, DEVICEID, 32, 32)
+FIELD(MOVI_1, EVENTID, 0, 32)
+FIELD(MOVI_2, ICID, 0, 16)
+
/*
* 12 bytes Interrupt translation Table Entry size
* as per Table 5.3 in GICv3 spec
* an incoming migration has loaded new state.
*/
void gicv3_redist_update_lpi_only(GICv3CPUState *cs);
+/**
+ * gicv3_redist_mov_lpi:
+ * @src: source redistributor
+ * @dest: destination redistributor
+ * @irq: LPI to update
+ *
+ * Move the pending state of the specified LPI from @src to @dest,
+ * as required by the ITS MOVI command.
+ */
+void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq);
+/**
+ * gicv3_redist_movall_lpis:
+ * @src: source redistributor
+ * @dest: destination redistributor
+ *
+ * Scan the LPI pending table for @src, and for each pending LPI there
+ * mark it as not-pending for @src and pending for @dest, as required
+ * by the ITS MOVALL command.
+ */
+void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest);
+
void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
void gicv3_init_cpuif(GICv3State *s);
}
}
-bool pic_get_statistics(InterruptStatsProvider *obj,
- uint64_t **irq_counts, unsigned int *nb_irqs)
+static bool pic_get_statistics(InterruptStatsProvider *obj,
+ uint64_t **irq_counts, unsigned int *nb_irqs)
{
PICCommonState *s = PIC_COMMON(obj);
return true;
}
-void pic_print_info(InterruptStatsProvider *obj, Monitor *mon)
+static void pic_print_info(InterruptStatsProvider *obj, Monitor *mon)
{
PICCommonState *s = PIC_COMMON(obj);
monitor_printf(mon, "\n");
}
-void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
+static void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s)
{
static const char *delm_str[] = {
"fixed", "lowest", "SMI", "...", "NMI", "INIT", "...", "extINT"};
/* Get the page size of the indirect table. */
vsd_addr = vsd & VSD_ADDRESS_MASK;
- ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED);
+ if (ldq_be_dma(&address_space_memory, vsd_addr, &vsd,
+ MEMTXATTRS_UNSPECIFIED)) {
+ xive_error(xive, "VST: failed to access %s entry %x @0x%" PRIx64,
+ info->name, idx, vsd_addr);
+ return 0;
+ }
if (!(vsd & VSD_ADDRESS_MASK)) {
#ifdef XIVE_DEBUG
/* Load the VSD we are looking for, if not already done */
if (vsd_idx) {
vsd_addr = vsd_addr + vsd_idx * XIVE_VSD_SIZE;
- ldq_be_dma(&address_space_memory, vsd_addr, &vsd,
- MEMTXATTRS_UNSPECIFIED);
+ if (ldq_be_dma(&address_space_memory, vsd_addr, &vsd,
+ MEMTXATTRS_UNSPECIFIED)) {
+ xive_error(xive, "VST: failed to access %s entry %x @0x%"
+ PRIx64, info->name, vsd_idx, vsd_addr);
+ return 0;
+ }
if (!(vsd & VSD_ADDRESS_MASK)) {
#ifdef XIVE_DEBUG
/* Get the page size of the indirect table. */
vsd_addr = vsd & VSD_ADDRESS_MASK;
- ldq_be_dma(&address_space_memory, vsd_addr, &vsd, MEMTXATTRS_UNSPECIFIED);
+ if (ldq_be_dma(&address_space_memory, vsd_addr, &vsd,
+ MEMTXATTRS_UNSPECIFIED)) {
+ xive_error(xive, "VST: failed to access %s entry @0x%" PRIx64,
+ info->name, vsd_addr);
+ return 0;
+ }
if (!(vsd & VSD_ADDRESS_MASK)) {
#ifdef XIVE_DEBUG
#include "target/riscv/cpu.h"
#include "migration/vmstate.h"
#include "hw/irq.h"
+#include "sysemu/kvm.h"
static bool addr_between(uint32_t addr, uint32_t base, uint32_t num)
{
uint32_t context_stride, uint32_t aperture_size)
{
DeviceState *dev = qdev_new(TYPE_SIFIVE_PLIC);
- int i;
+ int i, j = 0;
+ SiFivePLICState *plic;
assert(enable_stride == (enable_stride & -enable_stride));
assert(context_stride == (context_stride & -context_stride));
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, addr);
+ plic = SIFIVE_PLIC(dev);
for (i = 0; i < num_harts; i++) {
CPUState *cpu = qemu_get_cpu(hartid_base + i);
- qdev_connect_gpio_out(dev, i,
- qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
- qdev_connect_gpio_out(dev, num_harts + i,
- qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
+ if (plic->addr_config[j].mode == PLICMode_M) {
+ j++;
+ qdev_connect_gpio_out(dev, num_harts + i,
+ qdev_get_gpio_in(DEVICE(cpu), IRQ_M_EXT));
+ }
+
+ if (plic->addr_config[j].mode == PLICMode_S) {
+ j++;
+ qdev_connect_gpio_out(dev, i,
+ qdev_get_gpio_in(DEVICE(cpu), IRQ_S_EXT));
+ }
}
return dev;
gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor 0x%x interrupt %d level changed to %d"
gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor 0x%x pending SGI %d"
+# arm_gicv3_its.c
+gicv3_its_read(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+gicv3_its_badread(uint64_t offset, unsigned size) "GICv3 ITS read: offset 0x%" PRIx64 " size %u: error"
+gicv3_its_write(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+gicv3_its_badwrite(uint64_t offset, uint64_t data, unsigned size) "GICv3 ITS write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u: error"
+gicv3_its_translation_write(uint64_t offset, uint64_t data, unsigned size, uint32_t requester_id) "GICv3 ITS TRANSLATER write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u requester_id 0x%x"
+gicv3_its_process_command(uint32_t rd_offset, uint8_t cmd) "GICv3 ITS: processing command at offset 0x%x: 0x%x"
+
# armv7m_nvic.c
nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d"
nvic_recompute_state_secure(int vectpending, bool vectpending_is_s_banked, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d is_s_banked %d vectpending_prio %d exception_prio %d"
#define VIRT_VIRTIO_MMIO_BASE 0xff010000 /* MMIO: 0xff010000 - 0xff01ffff */
#define VIRT_VIRTIO_IRQ_BASE PIC_IRQ(2, 1) /* PIC: 2, 3, 4, 5, IRQ: ALL */
+typedef struct {
+ M68kCPU *cpu;
+ hwaddr initial_pc;
+ hwaddr initial_stack;
+} ResetInfo;
+
static void main_cpu_reset(void *opaque)
{
- M68kCPU *cpu = opaque;
+ ResetInfo *reset_info = opaque;
+ M68kCPU *cpu = reset_info->cpu;
CPUState *cs = CPU(cpu);
cpu_reset(cs);
- cpu->env.aregs[7] = ldl_phys(cs->as, 0);
- cpu->env.pc = ldl_phys(cs->as, 4);
+ cpu->env.aregs[7] = reset_info->initial_stack;
+ cpu->env.pc = reset_info->initial_pc;
}
static void virt_init(MachineState *machine)
SysBusDevice *sysbus;
hwaddr io_base;
int i;
+ ResetInfo *reset_info;
if (ram_size > 3399672 * KiB) {
/*
exit(1);
}
+ reset_info = g_malloc0(sizeof(ResetInfo));
+
/* init CPUs */
cpu = M68K_CPU(cpu_create(machine->cpu_type));
- qemu_register_reset(main_cpu_reset, cpu);
+
+ reset_info->cpu = cpu;
+ qemu_register_reset(main_cpu_reset, reset_info);
/* RAM */
memory_region_add_subregion(get_system_memory(), 0, machine->ram);
error_report("could not load kernel '%s'", kernel_filename);
exit(1);
}
- stl_phys(cs->as, 4, elf_entry); /* reset initial PC */
+ reset_info->initial_pc = elf_entry;
parameters_base = (high + 1) & ~1;
BOOTINFO1(cs->as, parameters_base, BI_MACHTYPE, MACH_VIRT);
select I8254
select I8257
select PCSPK
- select VGA_ISA_MM
+ select VGA_MMIO
select G364FB
select DP8393X
select ESP
#include "hw/rtc/mc146818rtc.h"
#include "hw/timer/i8254.h"
#include "hw/display/vga.h"
+#include "hw/display/bochs-vbe.h"
#include "hw/audio/pcspk.h"
#include "hw/input/i8042.h"
#include "hw/sysbus.h"
}
break;
case JAZZ_PICA61:
- isa_vga_mm_init(0x40000000, 0x60000000, 0, get_system_memory());
+ dev = qdev_new(TYPE_VGA_MMIO);
+ qdev_prop_set_uint8(dev, "it_shift", 0);
+ sysbus = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sysbus, &error_fatal);
+ sysbus_mmio_map(sysbus, 0, 0x60000000);
+ sysbus_mmio_map(sysbus, 1, 0x400a0000);
+ sysbus_mmio_map(sysbus, 2, VBE_DISPI_LFB_PHYSICAL_ADDRESS);
break;
default:
break;
--- /dev/null
+/*
+ * ASPEED I3C Controller
+ *
+ * Copyright (C) 2021 ASPEED Technology Inc.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/error-report.h"
+#include "hw/misc/aspeed_i3c.h"
+#include "hw/registerfields.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "migration/vmstate.h"
+#include "trace.h"
+
+/* I3C Controller Registers */
+REG32(I3C1_REG0, 0x10)
+REG32(I3C1_REG1, 0x14)
+ FIELD(I3C1_REG1, I2C_MODE, 0, 1)
+ FIELD(I3C1_REG1, SA_EN, 15, 1)
+REG32(I3C2_REG0, 0x20)
+REG32(I3C2_REG1, 0x24)
+ FIELD(I3C2_REG1, I2C_MODE, 0, 1)
+ FIELD(I3C2_REG1, SA_EN, 15, 1)
+REG32(I3C3_REG0, 0x30)
+REG32(I3C3_REG1, 0x34)
+ FIELD(I3C3_REG1, I2C_MODE, 0, 1)
+ FIELD(I3C3_REG1, SA_EN, 15, 1)
+REG32(I3C4_REG0, 0x40)
+REG32(I3C4_REG1, 0x44)
+ FIELD(I3C4_REG1, I2C_MODE, 0, 1)
+ FIELD(I3C4_REG1, SA_EN, 15, 1)
+REG32(I3C5_REG0, 0x50)
+REG32(I3C5_REG1, 0x54)
+ FIELD(I3C5_REG1, I2C_MODE, 0, 1)
+ FIELD(I3C5_REG1, SA_EN, 15, 1)
+REG32(I3C6_REG0, 0x60)
+REG32(I3C6_REG1, 0x64)
+ FIELD(I3C6_REG1, I2C_MODE, 0, 1)
+ FIELD(I3C6_REG1, SA_EN, 15, 1)
+
+/* I3C Device Registers */
+REG32(DEVICE_CTRL, 0x00)
+REG32(DEVICE_ADDR, 0x04)
+REG32(HW_CAPABILITY, 0x08)
+REG32(COMMAND_QUEUE_PORT, 0x0c)
+REG32(RESPONSE_QUEUE_PORT, 0x10)
+REG32(RX_TX_DATA_PORT, 0x14)
+REG32(IBI_QUEUE_STATUS, 0x18)
+REG32(IBI_QUEUE_DATA, 0x18)
+REG32(QUEUE_THLD_CTRL, 0x1c)
+REG32(DATA_BUFFER_THLD_CTRL, 0x20)
+REG32(IBI_QUEUE_CTRL, 0x24)
+REG32(IBI_MR_REQ_REJECT, 0x2c)
+REG32(IBI_SIR_REQ_REJECT, 0x30)
+REG32(RESET_CTRL, 0x34)
+REG32(SLV_EVENT_CTRL, 0x38)
+REG32(INTR_STATUS, 0x3c)
+REG32(INTR_STATUS_EN, 0x40)
+REG32(INTR_SIGNAL_EN, 0x44)
+REG32(INTR_FORCE, 0x48)
+REG32(QUEUE_STATUS_LEVEL, 0x4c)
+REG32(DATA_BUFFER_STATUS_LEVEL, 0x50)
+REG32(PRESENT_STATE, 0x54)
+REG32(CCC_DEVICE_STATUS, 0x58)
+REG32(DEVICE_ADDR_TABLE_POINTER, 0x5c)
+ FIELD(DEVICE_ADDR_TABLE_POINTER, DEPTH, 16, 16)
+ FIELD(DEVICE_ADDR_TABLE_POINTER, ADDR, 0, 16)
+REG32(DEV_CHAR_TABLE_POINTER, 0x60)
+REG32(VENDOR_SPECIFIC_REG_POINTER, 0x6c)
+REG32(SLV_MIPI_PID_VALUE, 0x70)
+REG32(SLV_PID_VALUE, 0x74)
+REG32(SLV_CHAR_CTRL, 0x78)
+REG32(SLV_MAX_LEN, 0x7c)
+REG32(MAX_READ_TURNAROUND, 0x80)
+REG32(MAX_DATA_SPEED, 0x84)
+REG32(SLV_DEBUG_STATUS, 0x88)
+REG32(SLV_INTR_REQ, 0x8c)
+REG32(DEVICE_CTRL_EXTENDED, 0xb0)
+REG32(SCL_I3C_OD_TIMING, 0xb4)
+REG32(SCL_I3C_PP_TIMING, 0xb8)
+REG32(SCL_I2C_FM_TIMING, 0xbc)
+REG32(SCL_I2C_FMP_TIMING, 0xc0)
+REG32(SCL_EXT_LCNT_TIMING, 0xc8)
+REG32(SCL_EXT_TERMN_LCNT_TIMING, 0xcc)
+REG32(BUS_FREE_TIMING, 0xd4)
+REG32(BUS_IDLE_TIMING, 0xd8)
+REG32(I3C_VER_ID, 0xe0)
+REG32(I3C_VER_TYPE, 0xe4)
+REG32(EXTENDED_CAPABILITY, 0xe8)
+REG32(SLAVE_CONFIG, 0xec)
+
+static const uint32_t ast2600_i3c_device_resets[ASPEED_I3C_DEVICE_NR_REGS] = {
+ [R_HW_CAPABILITY] = 0x000e00bf,
+ [R_QUEUE_THLD_CTRL] = 0x01000101,
+ [R_I3C_VER_ID] = 0x3130302a,
+ [R_I3C_VER_TYPE] = 0x6c633033,
+ [R_DEVICE_ADDR_TABLE_POINTER] = 0x00080280,
+ [R_DEV_CHAR_TABLE_POINTER] = 0x00020200,
+ [A_VENDOR_SPECIFIC_REG_POINTER] = 0x000000b0,
+ [R_SLV_MAX_LEN] = 0x00ff00ff,
+};
+
+static uint64_t aspeed_i3c_device_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
+ uint32_t addr = offset >> 2;
+ uint64_t value;
+
+ switch (addr) {
+ case R_COMMAND_QUEUE_PORT:
+ value = 0;
+ break;
+ default:
+ value = s->regs[addr];
+ break;
+ }
+
+ trace_aspeed_i3c_device_read(s->id, offset, value);
+
+ return value;
+}
+
+static void aspeed_i3c_device_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ AspeedI3CDevice *s = ASPEED_I3C_DEVICE(opaque);
+ uint32_t addr = offset >> 2;
+
+ trace_aspeed_i3c_device_write(s->id, offset, value);
+
+ switch (addr) {
+ case R_HW_CAPABILITY:
+ case R_RESPONSE_QUEUE_PORT:
+ case R_IBI_QUEUE_DATA:
+ case R_QUEUE_STATUS_LEVEL:
+ case R_PRESENT_STATE:
+ case R_CCC_DEVICE_STATUS:
+ case R_DEVICE_ADDR_TABLE_POINTER:
+ case R_VENDOR_SPECIFIC_REG_POINTER:
+ case R_SLV_CHAR_CTRL:
+ case R_SLV_MAX_LEN:
+ case R_MAX_READ_TURNAROUND:
+ case R_I3C_VER_ID:
+ case R_I3C_VER_TYPE:
+ case R_EXTENDED_CAPABILITY:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: write to readonly register[0x%02" HWADDR_PRIx
+ "] = 0x%08" PRIx64 "\n",
+ __func__, offset, value);
+ break;
+ case R_RX_TX_DATA_PORT:
+ break;
+ case R_RESET_CTRL:
+ break;
+ default:
+ s->regs[addr] = value;
+ break;
+ }
+}
+
+static const VMStateDescription aspeed_i3c_device_vmstate = {
+ .name = TYPE_ASPEED_I3C,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]){
+ VMSTATE_UINT32_ARRAY(regs, AspeedI3CDevice, ASPEED_I3C_DEVICE_NR_REGS),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static const MemoryRegionOps aspeed_i3c_device_ops = {
+ .read = aspeed_i3c_device_read,
+ .write = aspeed_i3c_device_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void aspeed_i3c_device_reset(DeviceState *dev)
+{
+ AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
+
+ memcpy(s->regs, ast2600_i3c_device_resets, sizeof(s->regs));
+}
+
+static void aspeed_i3c_device_realize(DeviceState *dev, Error **errp)
+{
+ AspeedI3CDevice *s = ASPEED_I3C_DEVICE(dev);
+ g_autofree char *name = g_strdup_printf(TYPE_ASPEED_I3C_DEVICE ".%d",
+ s->id);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
+
+ memory_region_init_io(&s->mr, OBJECT(s), &aspeed_i3c_device_ops,
+ s, name, ASPEED_I3C_DEVICE_NR_REGS << 2);
+}
+
+static uint64_t aspeed_i3c_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ AspeedI3CState *s = ASPEED_I3C(opaque);
+ uint64_t val = 0;
+
+ val = s->regs[addr >> 2];
+
+ trace_aspeed_i3c_read(addr, val);
+
+ return val;
+}
+
+static void aspeed_i3c_write(void *opaque,
+ hwaddr addr,
+ uint64_t data,
+ unsigned int size)
+{
+ AspeedI3CState *s = ASPEED_I3C(opaque);
+
+ trace_aspeed_i3c_write(addr, data);
+
+ addr >>= 2;
+
+ /* I3C controller register */
+ switch (addr) {
+ case R_I3C1_REG1:
+ case R_I3C2_REG1:
+ case R_I3C3_REG1:
+ case R_I3C4_REG1:
+ case R_I3C5_REG1:
+ case R_I3C6_REG1:
+ if (data & R_I3C1_REG1_I2C_MODE_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Unsupported I2C mode [0x%08" HWADDR_PRIx
+ "]=%08" PRIx64 "\n",
+ __func__, addr << 2, data);
+ break;
+ }
+ if (data & R_I3C1_REG1_SA_EN_MASK) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Unsupported slave mode [%08" HWADDR_PRIx
+ "]=0x%08" PRIx64 "\n",
+ __func__, addr << 2, data);
+ break;
+ }
+ s->regs[addr] = data;
+ break;
+ default:
+ s->regs[addr] = data;
+ break;
+ }
+}
+
+static const MemoryRegionOps aspeed_i3c_ops = {
+ .read = aspeed_i3c_read,
+ .write = aspeed_i3c_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 1,
+ .max_access_size = 4,
+ }
+};
+
+static void aspeed_i3c_reset(DeviceState *dev)
+{
+ AspeedI3CState *s = ASPEED_I3C(dev);
+ memset(s->regs, 0, sizeof(s->regs));
+}
+
+static void aspeed_i3c_instance_init(Object *obj)
+{
+ AspeedI3CState *s = ASPEED_I3C(obj);
+ int i;
+
+ for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
+ object_initialize_child(obj, "device[*]", &s->devices[i],
+ TYPE_ASPEED_I3C_DEVICE);
+ }
+}
+
+static void aspeed_i3c_realize(DeviceState *dev, Error **errp)
+{
+ int i;
+ AspeedI3CState *s = ASPEED_I3C(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ memory_region_init(&s->iomem_container, OBJECT(s),
+ TYPE_ASPEED_I3C ".container", 0x8000);
+
+ sysbus_init_mmio(sbd, &s->iomem_container);
+
+ memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_i3c_ops, s,
+ TYPE_ASPEED_I3C ".regs", ASPEED_I3C_NR_REGS << 2);
+
+ memory_region_add_subregion(&s->iomem_container, 0x0, &s->iomem);
+
+ for (i = 0; i < ASPEED_I3C_NR_DEVICES; ++i) {
+ Object *dev = OBJECT(&s->devices[i]);
+
+ if (!object_property_set_uint(dev, "device-id", i, errp)) {
+ return;
+ }
+
+ if (!sysbus_realize(SYS_BUS_DEVICE(dev), errp)) {
+ return;
+ }
+
+ /*
+ * Register Address of I3CX Device =
+ * (Base Address of Global Register) + (Offset of I3CX) + Offset
+ * X = 0, 1, 2, 3, 4, 5
+ * Offset of I3C0 = 0x2000
+ * Offset of I3C1 = 0x3000
+ * Offset of I3C2 = 0x4000
+ * Offset of I3C3 = 0x5000
+ * Offset of I3C4 = 0x6000
+ * Offset of I3C5 = 0x7000
+ */
+ memory_region_add_subregion(&s->iomem_container,
+ 0x2000 + i * 0x1000, &s->devices[i].mr);
+ }
+
+}
+
+static Property aspeed_i3c_device_properties[] = {
+ DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "Aspeed I3C Device";
+ dc->realize = aspeed_i3c_device_realize;
+ dc->reset = aspeed_i3c_device_reset;
+ device_class_set_props(dc, aspeed_i3c_device_properties);
+}
+
+static const TypeInfo aspeed_i3c_device_info = {
+ .name = TYPE_ASPEED_I3C_DEVICE,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(AspeedI3CDevice),
+ .class_init = aspeed_i3c_device_class_init,
+};
+
+static const VMStateDescription vmstate_aspeed_i3c = {
+ .name = TYPE_ASPEED_I3C,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, AspeedI3CState, ASPEED_I3C_NR_REGS),
+ VMSTATE_STRUCT_ARRAY(devices, AspeedI3CState, ASPEED_I3C_NR_DEVICES, 1,
+ aspeed_i3c_device_vmstate, AspeedI3CDevice),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void aspeed_i3c_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = aspeed_i3c_realize;
+ dc->reset = aspeed_i3c_reset;
+ dc->desc = "Aspeed I3C Controller";
+ dc->vmsd = &vmstate_aspeed_i3c;
+}
+
+static const TypeInfo aspeed_i3c_info = {
+ .name = TYPE_ASPEED_I3C,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_init = aspeed_i3c_instance_init,
+ .instance_size = sizeof(AspeedI3CState),
+ .class_init = aspeed_i3c_class_init,
+};
+
+static void aspeed_i3c_register_types(void)
+{
+ type_register_static(&aspeed_i3c_device_info);
+ type_register_static(&aspeed_i3c_info);
+}
+
+type_init(aspeed_i3c_register_types);
.name = TYPE_BCM2835_MBOX,
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_BOOL_ARRAY(available, BCM2835MboxState, MBOX_CHAN_COUNT),
VMSTATE_STRUCT_ARRAY(mbox, BCM2835MboxState, 2, 1,
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "migration/vmstate.h"
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "sysemu/block-backend.h"
+#include "sysemu/rtc.h"
#include "trace.h"
#include "qemu/log.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/ppc/mac.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/timer.h"
#include "sysemu/runstate.h"
+#include "sysemu/rtc.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/ppc/mac.h"
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/timer.h"
#include "sysemu/runstate.h"
+#include "sysemu/rtc.h"
#include "qapi/error.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
))
softmmu_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
softmmu_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c'))
-softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-xramc.c'))
+softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
+ 'xlnx-versal-xramc.c',
+ 'xlnx-versal-pmc-iou-slcr.c',
+))
softmmu_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c'))
softmmu_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c'))
softmmu_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c'))
softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c'))
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files(
'aspeed_hace.c',
+ 'aspeed_i3c.c',
'aspeed_lpc.c',
'aspeed_scu.c',
'aspeed_sdmc.c',
# aspeed_xdma.c
aspeed_xdma_write(uint64_t offset, uint64_t data) "XDMA write: offset 0x%" PRIx64 " data 0x%" PRIx64
+# aspeed_i3c.c
+aspeed_i3c_read(uint64_t offset, uint64_t data) "I3C read: offset 0x%" PRIx64 " data 0x%" PRIx64
+aspeed_i3c_write(uint64_t offset, uint64_t data) "I3C write: offset 0x%" PRIx64 " data 0x%" PRIx64
+aspeed_i3c_device_read(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] read: offset 0x%" PRIx64 " data 0x%" PRIx64
+aspeed_i3c_device_write(uint32_t deviceid, uint64_t offset, uint64_t data) "I3C Dev[%u] write: offset 0x%" PRIx64 " data 0x%" PRIx64
+
# bcm2835_property.c
bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu"
--- /dev/null
+/*
+ * QEMU model of Versal's PMC IOU SLCR (system level control registers)
+ *
+ * Copyright (c) 2021 Xilinx Inc.
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "hw/register.h"
+#include "hw/irq.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/xlnx-versal-pmc-iou-slcr.h"
+
+#ifndef XILINX_VERSAL_PMC_IOU_SLCR_ERR_DEBUG
+#define XILINX_VERSAL_PMC_IOU_SLCR_ERR_DEBUG 0
+#endif
+
+REG32(MIO_PIN_0, 0x0)
+ FIELD(MIO_PIN_0, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_0, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_0, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_0, L0_SEL, 1, 2)
+REG32(MIO_PIN_1, 0x4)
+ FIELD(MIO_PIN_1, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_1, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_1, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_1, L0_SEL, 1, 2)
+REG32(MIO_PIN_2, 0x8)
+ FIELD(MIO_PIN_2, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_2, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_2, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_2, L0_SEL, 1, 2)
+REG32(MIO_PIN_3, 0xc)
+ FIELD(MIO_PIN_3, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_3, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_3, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_3, L0_SEL, 1, 2)
+REG32(MIO_PIN_4, 0x10)
+ FIELD(MIO_PIN_4, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_4, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_4, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_4, L0_SEL, 1, 2)
+REG32(MIO_PIN_5, 0x14)
+ FIELD(MIO_PIN_5, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_5, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_5, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_5, L0_SEL, 1, 2)
+REG32(MIO_PIN_6, 0x18)
+ FIELD(MIO_PIN_6, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_6, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_6, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_6, L0_SEL, 1, 2)
+REG32(MIO_PIN_7, 0x1c)
+ FIELD(MIO_PIN_7, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_7, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_7, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_7, L0_SEL, 1, 2)
+REG32(MIO_PIN_8, 0x20)
+ FIELD(MIO_PIN_8, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_8, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_8, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_8, L0_SEL, 1, 2)
+REG32(MIO_PIN_9, 0x24)
+ FIELD(MIO_PIN_9, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_9, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_9, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_9, L0_SEL, 1, 2)
+REG32(MIO_PIN_10, 0x28)
+ FIELD(MIO_PIN_10, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_10, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_10, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_10, L0_SEL, 1, 2)
+REG32(MIO_PIN_11, 0x2c)
+ FIELD(MIO_PIN_11, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_11, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_11, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_11, L0_SEL, 1, 2)
+REG32(MIO_PIN_12, 0x30)
+ FIELD(MIO_PIN_12, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_12, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_12, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_12, L0_SEL, 1, 2)
+REG32(MIO_PIN_13, 0x34)
+ FIELD(MIO_PIN_13, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_13, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_13, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_13, L0_SEL, 1, 2)
+REG32(MIO_PIN_14, 0x38)
+ FIELD(MIO_PIN_14, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_14, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_14, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_14, L0_SEL, 1, 2)
+REG32(MIO_PIN_15, 0x3c)
+ FIELD(MIO_PIN_15, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_15, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_15, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_15, L0_SEL, 1, 2)
+REG32(MIO_PIN_16, 0x40)
+ FIELD(MIO_PIN_16, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_16, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_16, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_16, L0_SEL, 1, 2)
+REG32(MIO_PIN_17, 0x44)
+ FIELD(MIO_PIN_17, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_17, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_17, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_17, L0_SEL, 1, 2)
+REG32(MIO_PIN_18, 0x48)
+ FIELD(MIO_PIN_18, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_18, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_18, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_18, L0_SEL, 1, 2)
+REG32(MIO_PIN_19, 0x4c)
+ FIELD(MIO_PIN_19, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_19, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_19, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_19, L0_SEL, 1, 2)
+REG32(MIO_PIN_20, 0x50)
+ FIELD(MIO_PIN_20, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_20, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_20, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_20, L0_SEL, 1, 2)
+REG32(MIO_PIN_21, 0x54)
+ FIELD(MIO_PIN_21, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_21, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_21, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_21, L0_SEL, 1, 2)
+REG32(MIO_PIN_22, 0x58)
+ FIELD(MIO_PIN_22, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_22, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_22, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_22, L0_SEL, 1, 2)
+REG32(MIO_PIN_23, 0x5c)
+ FIELD(MIO_PIN_23, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_23, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_23, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_23, L0_SEL, 1, 2)
+REG32(MIO_PIN_24, 0x60)
+ FIELD(MIO_PIN_24, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_24, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_24, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_24, L0_SEL, 1, 2)
+REG32(MIO_PIN_25, 0x64)
+ FIELD(MIO_PIN_25, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_25, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_25, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_25, L0_SEL, 1, 2)
+REG32(MIO_PIN_26, 0x68)
+ FIELD(MIO_PIN_26, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_26, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_26, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_26, L0_SEL, 1, 2)
+REG32(MIO_PIN_27, 0x6c)
+ FIELD(MIO_PIN_27, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_27, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_27, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_27, L0_SEL, 1, 2)
+REG32(MIO_PIN_28, 0x70)
+ FIELD(MIO_PIN_28, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_28, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_28, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_28, L0_SEL, 1, 2)
+REG32(MIO_PIN_29, 0x74)
+ FIELD(MIO_PIN_29, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_29, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_29, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_29, L0_SEL, 1, 2)
+REG32(MIO_PIN_30, 0x78)
+ FIELD(MIO_PIN_30, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_30, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_30, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_30, L0_SEL, 1, 2)
+REG32(MIO_PIN_31, 0x7c)
+ FIELD(MIO_PIN_31, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_31, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_31, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_31, L0_SEL, 1, 2)
+REG32(MIO_PIN_32, 0x80)
+ FIELD(MIO_PIN_32, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_32, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_32, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_32, L0_SEL, 1, 2)
+REG32(MIO_PIN_33, 0x84)
+ FIELD(MIO_PIN_33, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_33, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_33, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_33, L0_SEL, 1, 2)
+REG32(MIO_PIN_34, 0x88)
+ FIELD(MIO_PIN_34, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_34, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_34, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_34, L0_SEL, 1, 2)
+REG32(MIO_PIN_35, 0x8c)
+ FIELD(MIO_PIN_35, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_35, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_35, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_35, L0_SEL, 1, 2)
+REG32(MIO_PIN_36, 0x90)
+ FIELD(MIO_PIN_36, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_36, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_36, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_36, L0_SEL, 1, 2)
+REG32(MIO_PIN_37, 0x94)
+ FIELD(MIO_PIN_37, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_37, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_37, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_37, L0_SEL, 1, 2)
+REG32(MIO_PIN_38, 0x98)
+ FIELD(MIO_PIN_38, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_38, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_38, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_38, L0_SEL, 1, 2)
+REG32(MIO_PIN_39, 0x9c)
+ FIELD(MIO_PIN_39, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_39, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_39, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_39, L0_SEL, 1, 2)
+REG32(MIO_PIN_40, 0xa0)
+ FIELD(MIO_PIN_40, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_40, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_40, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_40, L0_SEL, 1, 2)
+REG32(MIO_PIN_41, 0xa4)
+ FIELD(MIO_PIN_41, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_41, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_41, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_41, L0_SEL, 1, 2)
+REG32(MIO_PIN_42, 0xa8)
+ FIELD(MIO_PIN_42, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_42, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_42, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_42, L0_SEL, 1, 2)
+REG32(MIO_PIN_43, 0xac)
+ FIELD(MIO_PIN_43, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_43, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_43, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_43, L0_SEL, 1, 2)
+REG32(MIO_PIN_44, 0xb0)
+ FIELD(MIO_PIN_44, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_44, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_44, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_44, L0_SEL, 1, 2)
+REG32(MIO_PIN_45, 0xb4)
+ FIELD(MIO_PIN_45, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_45, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_45, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_45, L0_SEL, 1, 2)
+REG32(MIO_PIN_46, 0xb8)
+ FIELD(MIO_PIN_46, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_46, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_46, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_46, L0_SEL, 1, 2)
+REG32(MIO_PIN_47, 0xbc)
+ FIELD(MIO_PIN_47, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_47, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_47, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_47, L0_SEL, 1, 2)
+REG32(MIO_PIN_48, 0xc0)
+ FIELD(MIO_PIN_48, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_48, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_48, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_48, L0_SEL, 1, 2)
+REG32(MIO_PIN_49, 0xc4)
+ FIELD(MIO_PIN_49, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_49, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_49, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_49, L0_SEL, 1, 2)
+REG32(MIO_PIN_50, 0xc8)
+ FIELD(MIO_PIN_50, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_50, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_50, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_50, L0_SEL, 1, 2)
+REG32(MIO_PIN_51, 0xcc)
+ FIELD(MIO_PIN_51, L3_SEL, 7, 3)
+ FIELD(MIO_PIN_51, L2_SEL, 5, 2)
+ FIELD(MIO_PIN_51, L1_SEL, 3, 2)
+ FIELD(MIO_PIN_51, L0_SEL, 1, 2)
+REG32(BNK0_EN_RX, 0x100)
+ FIELD(BNK0_EN_RX, BNK0_EN_RX, 0, 26)
+REG32(BNK0_SEL_RX0, 0x104)
+REG32(BNK0_SEL_RX1, 0x108)
+ FIELD(BNK0_SEL_RX1, BNK0_SEL_RX, 0, 20)
+REG32(BNK0_EN_RX_SCHMITT_HYST, 0x10c)
+ FIELD(BNK0_EN_RX_SCHMITT_HYST, BNK0_EN_RX_SCHMITT_HYST, 0, 26)
+REG32(BNK0_EN_WK_PD, 0x110)
+ FIELD(BNK0_EN_WK_PD, BNK0_EN_WK_PD, 0, 26)
+REG32(BNK0_EN_WK_PU, 0x114)
+ FIELD(BNK0_EN_WK_PU, BNK0_EN_WK_PU, 0, 26)
+REG32(BNK0_SEL_DRV0, 0x118)
+REG32(BNK0_SEL_DRV1, 0x11c)
+ FIELD(BNK0_SEL_DRV1, BNK0_SEL_DRV, 0, 20)
+REG32(BNK0_SEL_SLEW, 0x120)
+ FIELD(BNK0_SEL_SLEW, BNK0_SEL_SLEW, 0, 26)
+REG32(BNK0_EN_DFT_OPT_INV, 0x124)
+ FIELD(BNK0_EN_DFT_OPT_INV, BNK0_EN_DFT_OPT_INV, 0, 26)
+REG32(BNK0_EN_PAD2PAD_LOOPBACK, 0x128)
+ FIELD(BNK0_EN_PAD2PAD_LOOPBACK, BNK0_EN_PAD2PAD_LOOPBACK, 0, 13)
+REG32(BNK0_RX_SPARE0, 0x12c)
+REG32(BNK0_RX_SPARE1, 0x130)
+ FIELD(BNK0_RX_SPARE1, BNK0_RX_SPARE, 0, 20)
+REG32(BNK0_TX_SPARE0, 0x134)
+REG32(BNK0_TX_SPARE1, 0x138)
+ FIELD(BNK0_TX_SPARE1, BNK0_TX_SPARE, 0, 20)
+REG32(BNK0_SEL_EN1P8, 0x13c)
+ FIELD(BNK0_SEL_EN1P8, BNK0_SEL_EN1P8, 0, 1)
+REG32(BNK0_EN_B_POR_DETECT, 0x140)
+ FIELD(BNK0_EN_B_POR_DETECT, BNK0_EN_B_POR_DETECT, 0, 1)
+REG32(BNK0_LPF_BYP_POR_DETECT, 0x144)
+ FIELD(BNK0_LPF_BYP_POR_DETECT, BNK0_LPF_BYP_POR_DETECT, 0, 1)
+REG32(BNK0_EN_LATCH, 0x148)
+ FIELD(BNK0_EN_LATCH, BNK0_EN_LATCH, 0, 1)
+REG32(BNK0_VBG_LPF_BYP_B, 0x14c)
+ FIELD(BNK0_VBG_LPF_BYP_B, BNK0_VBG_LPF_BYP_B, 0, 1)
+REG32(BNK0_EN_AMP_B, 0x150)
+ FIELD(BNK0_EN_AMP_B, BNK0_EN_AMP_B, 0, 2)
+REG32(BNK0_SPARE_BIAS, 0x154)
+ FIELD(BNK0_SPARE_BIAS, BNK0_SPARE_BIAS, 0, 4)
+REG32(BNK0_DRIVER_BIAS, 0x158)
+ FIELD(BNK0_DRIVER_BIAS, BNK0_DRIVER_BIAS, 0, 15)
+REG32(BNK0_VMODE, 0x15c)
+ FIELD(BNK0_VMODE, BNK0_VMODE, 0, 1)
+REG32(BNK0_SEL_AUX_IO_RX, 0x160)
+ FIELD(BNK0_SEL_AUX_IO_RX, BNK0_SEL_AUX_IO_RX, 0, 26)
+REG32(BNK0_EN_TX_HS_MODE, 0x164)
+ FIELD(BNK0_EN_TX_HS_MODE, BNK0_EN_TX_HS_MODE, 0, 26)
+REG32(MIO_MST_TRI0, 0x200)
+ FIELD(MIO_MST_TRI0, PIN_25_TRI, 25, 1)
+ FIELD(MIO_MST_TRI0, PIN_24_TRI, 24, 1)
+ FIELD(MIO_MST_TRI0, PIN_23_TRI, 23, 1)
+ FIELD(MIO_MST_TRI0, PIN_22_TRI, 22, 1)
+ FIELD(MIO_MST_TRI0, PIN_21_TRI, 21, 1)
+ FIELD(MIO_MST_TRI0, PIN_20_TRI, 20, 1)
+ FIELD(MIO_MST_TRI0, PIN_19_TRI, 19, 1)
+ FIELD(MIO_MST_TRI0, PIN_18_TRI, 18, 1)
+ FIELD(MIO_MST_TRI0, PIN_17_TRI, 17, 1)
+ FIELD(MIO_MST_TRI0, PIN_16_TRI, 16, 1)
+ FIELD(MIO_MST_TRI0, PIN_15_TRI, 15, 1)
+ FIELD(MIO_MST_TRI0, PIN_14_TRI, 14, 1)
+ FIELD(MIO_MST_TRI0, PIN_13_TRI, 13, 1)
+ FIELD(MIO_MST_TRI0, PIN_12_TRI, 12, 1)
+ FIELD(MIO_MST_TRI0, PIN_11_TRI, 11, 1)
+ FIELD(MIO_MST_TRI0, PIN_10_TRI, 10, 1)
+ FIELD(MIO_MST_TRI0, PIN_09_TRI, 9, 1)
+ FIELD(MIO_MST_TRI0, PIN_08_TRI, 8, 1)
+ FIELD(MIO_MST_TRI0, PIN_07_TRI, 7, 1)
+ FIELD(MIO_MST_TRI0, PIN_06_TRI, 6, 1)
+ FIELD(MIO_MST_TRI0, PIN_05_TRI, 5, 1)
+ FIELD(MIO_MST_TRI0, PIN_04_TRI, 4, 1)
+ FIELD(MIO_MST_TRI0, PIN_03_TRI, 3, 1)
+ FIELD(MIO_MST_TRI0, PIN_02_TRI, 2, 1)
+ FIELD(MIO_MST_TRI0, PIN_01_TRI, 1, 1)
+ FIELD(MIO_MST_TRI0, PIN_00_TRI, 0, 1)
+REG32(MIO_MST_TRI1, 0x204)
+ FIELD(MIO_MST_TRI1, PIN_51_TRI, 25, 1)
+ FIELD(MIO_MST_TRI1, PIN_50_TRI, 24, 1)
+ FIELD(MIO_MST_TRI1, PIN_49_TRI, 23, 1)
+ FIELD(MIO_MST_TRI1, PIN_48_TRI, 22, 1)
+ FIELD(MIO_MST_TRI1, PIN_47_TRI, 21, 1)
+ FIELD(MIO_MST_TRI1, PIN_46_TRI, 20, 1)
+ FIELD(MIO_MST_TRI1, PIN_45_TRI, 19, 1)
+ FIELD(MIO_MST_TRI1, PIN_44_TRI, 18, 1)
+ FIELD(MIO_MST_TRI1, PIN_43_TRI, 17, 1)
+ FIELD(MIO_MST_TRI1, PIN_42_TRI, 16, 1)
+ FIELD(MIO_MST_TRI1, PIN_41_TRI, 15, 1)
+ FIELD(MIO_MST_TRI1, PIN_40_TRI, 14, 1)
+ FIELD(MIO_MST_TRI1, PIN_39_TRI, 13, 1)
+ FIELD(MIO_MST_TRI1, PIN_38_TRI, 12, 1)
+ FIELD(MIO_MST_TRI1, PIN_37_TRI, 11, 1)
+ FIELD(MIO_MST_TRI1, PIN_36_TRI, 10, 1)
+ FIELD(MIO_MST_TRI1, PIN_35_TRI, 9, 1)
+ FIELD(MIO_MST_TRI1, PIN_34_TRI, 8, 1)
+ FIELD(MIO_MST_TRI1, PIN_33_TRI, 7, 1)
+ FIELD(MIO_MST_TRI1, PIN_32_TRI, 6, 1)
+ FIELD(MIO_MST_TRI1, PIN_31_TRI, 5, 1)
+ FIELD(MIO_MST_TRI1, PIN_30_TRI, 4, 1)
+ FIELD(MIO_MST_TRI1, PIN_29_TRI, 3, 1)
+ FIELD(MIO_MST_TRI1, PIN_28_TRI, 2, 1)
+ FIELD(MIO_MST_TRI1, PIN_27_TRI, 1, 1)
+ FIELD(MIO_MST_TRI1, PIN_26_TRI, 0, 1)
+REG32(BNK1_EN_RX, 0x300)
+ FIELD(BNK1_EN_RX, BNK1_EN_RX, 0, 26)
+REG32(BNK1_SEL_RX0, 0x304)
+REG32(BNK1_SEL_RX1, 0x308)
+ FIELD(BNK1_SEL_RX1, BNK1_SEL_RX, 0, 20)
+REG32(BNK1_EN_RX_SCHMITT_HYST, 0x30c)
+ FIELD(BNK1_EN_RX_SCHMITT_HYST, BNK1_EN_RX_SCHMITT_HYST, 0, 26)
+REG32(BNK1_EN_WK_PD, 0x310)
+ FIELD(BNK1_EN_WK_PD, BNK1_EN_WK_PD, 0, 26)
+REG32(BNK1_EN_WK_PU, 0x314)
+ FIELD(BNK1_EN_WK_PU, BNK1_EN_WK_PU, 0, 26)
+REG32(BNK1_SEL_DRV0, 0x318)
+REG32(BNK1_SEL_DRV1, 0x31c)
+ FIELD(BNK1_SEL_DRV1, BNK1_SEL_DRV, 0, 20)
+REG32(BNK1_SEL_SLEW, 0x320)
+ FIELD(BNK1_SEL_SLEW, BNK1_SEL_SLEW, 0, 26)
+REG32(BNK1_EN_DFT_OPT_INV, 0x324)
+ FIELD(BNK1_EN_DFT_OPT_INV, BNK1_EN_DFT_OPT_INV, 0, 26)
+REG32(BNK1_EN_PAD2PAD_LOOPBACK, 0x328)
+ FIELD(BNK1_EN_PAD2PAD_LOOPBACK, BNK1_EN_PAD2PAD_LOOPBACK, 0, 13)
+REG32(BNK1_RX_SPARE0, 0x32c)
+REG32(BNK1_RX_SPARE1, 0x330)
+ FIELD(BNK1_RX_SPARE1, BNK1_RX_SPARE, 0, 20)
+REG32(BNK1_TX_SPARE0, 0x334)
+REG32(BNK1_TX_SPARE1, 0x338)
+ FIELD(BNK1_TX_SPARE1, BNK1_TX_SPARE, 0, 20)
+REG32(BNK1_SEL_EN1P8, 0x33c)
+ FIELD(BNK1_SEL_EN1P8, BNK1_SEL_EN1P8, 0, 1)
+REG32(BNK1_EN_B_POR_DETECT, 0x340)
+ FIELD(BNK1_EN_B_POR_DETECT, BNK1_EN_B_POR_DETECT, 0, 1)
+REG32(BNK1_LPF_BYP_POR_DETECT, 0x344)
+ FIELD(BNK1_LPF_BYP_POR_DETECT, BNK1_LPF_BYP_POR_DETECT, 0, 1)
+REG32(BNK1_EN_LATCH, 0x348)
+ FIELD(BNK1_EN_LATCH, BNK1_EN_LATCH, 0, 1)
+REG32(BNK1_VBG_LPF_BYP_B, 0x34c)
+ FIELD(BNK1_VBG_LPF_BYP_B, BNK1_VBG_LPF_BYP_B, 0, 1)
+REG32(BNK1_EN_AMP_B, 0x350)
+ FIELD(BNK1_EN_AMP_B, BNK1_EN_AMP_B, 0, 2)
+REG32(BNK1_SPARE_BIAS, 0x354)
+ FIELD(BNK1_SPARE_BIAS, BNK1_SPARE_BIAS, 0, 4)
+REG32(BNK1_DRIVER_BIAS, 0x358)
+ FIELD(BNK1_DRIVER_BIAS, BNK1_DRIVER_BIAS, 0, 15)
+REG32(BNK1_VMODE, 0x35c)
+ FIELD(BNK1_VMODE, BNK1_VMODE, 0, 1)
+REG32(BNK1_SEL_AUX_IO_RX, 0x360)
+ FIELD(BNK1_SEL_AUX_IO_RX, BNK1_SEL_AUX_IO_RX, 0, 26)
+REG32(BNK1_EN_TX_HS_MODE, 0x364)
+ FIELD(BNK1_EN_TX_HS_MODE, BNK1_EN_TX_HS_MODE, 0, 26)
+REG32(SD0_CLK_CTRL, 0x400)
+ FIELD(SD0_CLK_CTRL, SDIO0_FBCLK_SEL, 2, 1)
+ FIELD(SD0_CLK_CTRL, SDIO0_RX_SRC_SEL, 0, 2)
+REG32(SD0_CTRL_REG, 0x404)
+ FIELD(SD0_CTRL_REG, SD0_EMMC_SEL, 0, 1)
+REG32(SD0_CONFIG_REG1, 0x410)
+ FIELD(SD0_CONFIG_REG1, SD0_BASECLK, 7, 8)
+ FIELD(SD0_CONFIG_REG1, SD0_TUNIGCOUNT, 1, 6)
+ FIELD(SD0_CONFIG_REG1, SD0_ASYNCWKPENA, 0, 1)
+REG32(SD0_CONFIG_REG2, 0x414)
+ FIELD(SD0_CONFIG_REG2, SD0_SLOTTYPE, 12, 2)
+ FIELD(SD0_CONFIG_REG2, SD0_ASYCINTR, 11, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_64BIT, 10, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_1P8V, 9, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_3P0V, 8, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_3P3V, 7, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_SUSPRES, 6, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_SDMA, 5, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_HIGHSPEED, 4, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_ADMA2, 3, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_8BIT, 2, 1)
+ FIELD(SD0_CONFIG_REG2, SD0_MAXBLK, 0, 2)
+REG32(SD0_CONFIG_REG3, 0x418)
+ FIELD(SD0_CONFIG_REG3, SD0_TUNINGSDR50, 10, 1)
+ FIELD(SD0_CONFIG_REG3, SD0_RETUNETMR, 6, 4)
+ FIELD(SD0_CONFIG_REG3, SD0_DDRIVER, 5, 1)
+ FIELD(SD0_CONFIG_REG3, SD0_CDRIVER, 4, 1)
+ FIELD(SD0_CONFIG_REG3, SD0_ADRIVER, 3, 1)
+ FIELD(SD0_CONFIG_REG3, SD0_DDR50, 2, 1)
+ FIELD(SD0_CONFIG_REG3, SD0_SDR104, 1, 1)
+ FIELD(SD0_CONFIG_REG3, SD0_SDR50, 0, 1)
+REG32(SD0_INITPRESET, 0x41c)
+ FIELD(SD0_INITPRESET, SD0_INITPRESET, 0, 13)
+REG32(SD0_DSPPRESET, 0x420)
+ FIELD(SD0_DSPPRESET, SD0_DSPPRESET, 0, 13)
+REG32(SD0_HSPDPRESET, 0x424)
+ FIELD(SD0_HSPDPRESET, SD0_HSPDPRESET, 0, 13)
+REG32(SD0_SDR12PRESET, 0x428)
+ FIELD(SD0_SDR12PRESET, SD0_SDR12PRESET, 0, 13)
+REG32(SD0_SDR25PRESET, 0x42c)
+ FIELD(SD0_SDR25PRESET, SD0_SDR25PRESET, 0, 13)
+REG32(SD0_SDR50PRSET, 0x430)
+ FIELD(SD0_SDR50PRSET, SD0_SDR50PRESET, 0, 13)
+REG32(SD0_SDR104PRST, 0x434)
+ FIELD(SD0_SDR104PRST, SD0_SDR104PRESET, 0, 13)
+REG32(SD0_DDR50PRESET, 0x438)
+ FIELD(SD0_DDR50PRESET, SD0_DDR50PRESET, 0, 13)
+REG32(SD0_MAXCUR1P8, 0x43c)
+ FIELD(SD0_MAXCUR1P8, SD0_MAXCUR1P8, 0, 8)
+REG32(SD0_MAXCUR3P0, 0x440)
+ FIELD(SD0_MAXCUR3P0, SD0_MAXCUR3P0, 0, 8)
+REG32(SD0_MAXCUR3P3, 0x444)
+ FIELD(SD0_MAXCUR3P3, SD0_MAXCUR3P3, 0, 8)
+REG32(SD0_DLL_CTRL, 0x448)
+ FIELD(SD0_DLL_CTRL, SD0_CLKSTABLE_CFG, 9, 1)
+ FIELD(SD0_DLL_CTRL, SD0_DLL_CFG, 5, 4)
+ FIELD(SD0_DLL_CTRL, SD0_DLL_PSDONE, 4, 1)
+ FIELD(SD0_DLL_CTRL, SD0_DLL_OVF, 3, 1)
+ FIELD(SD0_DLL_CTRL, SD0_DLL_RST, 2, 1)
+ FIELD(SD0_DLL_CTRL, SD0_DLL_TESTMODE, 1, 1)
+ FIELD(SD0_DLL_CTRL, SD0_DLL_LOCK, 0, 1)
+REG32(SD0_CDN_CTRL, 0x44c)
+ FIELD(SD0_CDN_CTRL, SD0_CDN_CTRL, 0, 1)
+REG32(SD0_DLL_TEST, 0x450)
+ FIELD(SD0_DLL_TEST, DLL_DIV, 16, 8)
+ FIELD(SD0_DLL_TEST, DLL_TX_SEL, 9, 7)
+ FIELD(SD0_DLL_TEST, DLL_RX_SEL, 0, 9)
+REG32(SD0_RX_TUNING_SEL, 0x454)
+ FIELD(SD0_RX_TUNING_SEL, SD0_RX_SEL, 0, 9)
+REG32(SD0_DLL_DIV_MAP0, 0x458)
+ FIELD(SD0_DLL_DIV_MAP0, DIV_3, 24, 8)
+ FIELD(SD0_DLL_DIV_MAP0, DIV_2, 16, 8)
+ FIELD(SD0_DLL_DIV_MAP0, DIV_1, 8, 8)
+ FIELD(SD0_DLL_DIV_MAP0, DIV_0, 0, 8)
+REG32(SD0_DLL_DIV_MAP1, 0x45c)
+ FIELD(SD0_DLL_DIV_MAP1, DIV_7, 24, 8)
+ FIELD(SD0_DLL_DIV_MAP1, DIV_6, 16, 8)
+ FIELD(SD0_DLL_DIV_MAP1, DIV_5, 8, 8)
+ FIELD(SD0_DLL_DIV_MAP1, DIV_4, 0, 8)
+REG32(SD0_IOU_COHERENT_CTRL, 0x460)
+ FIELD(SD0_IOU_COHERENT_CTRL, SD0_AXI_COH, 0, 4)
+REG32(SD0_IOU_INTERCONNECT_ROUTE, 0x464)
+ FIELD(SD0_IOU_INTERCONNECT_ROUTE, SD0, 0, 1)
+REG32(SD0_IOU_RAM, 0x468)
+ FIELD(SD0_IOU_RAM, EMASA0, 6, 1)
+ FIELD(SD0_IOU_RAM, EMAB0, 3, 3)
+ FIELD(SD0_IOU_RAM, EMAA0, 0, 3)
+REG32(SD0_IOU_INTERCONNECT_QOS, 0x46c)
+ FIELD(SD0_IOU_INTERCONNECT_QOS, SD0_QOS, 0, 4)
+REG32(SD1_CLK_CTRL, 0x480)
+ FIELD(SD1_CLK_CTRL, SDIO1_FBCLK_SEL, 1, 1)
+ FIELD(SD1_CLK_CTRL, SDIO1_RX_SRC_SEL, 0, 1)
+REG32(SD1_CTRL_REG, 0x484)
+ FIELD(SD1_CTRL_REG, SD1_EMMC_SEL, 0, 1)
+REG32(SD1_CONFIG_REG1, 0x490)
+ FIELD(SD1_CONFIG_REG1, SD1_BASECLK, 7, 8)
+ FIELD(SD1_CONFIG_REG1, SD1_TUNIGCOUNT, 1, 6)
+ FIELD(SD1_CONFIG_REG1, SD1_ASYNCWKPENA, 0, 1)
+REG32(SD1_CONFIG_REG2, 0x494)
+ FIELD(SD1_CONFIG_REG2, SD1_SLOTTYPE, 12, 2)
+ FIELD(SD1_CONFIG_REG2, SD1_ASYCINTR, 11, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_64BIT, 10, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_1P8V, 9, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_3P0V, 8, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_3P3V, 7, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_SUSPRES, 6, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_SDMA, 5, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_HIGHSPEED, 4, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_ADMA2, 3, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_8BIT, 2, 1)
+ FIELD(SD1_CONFIG_REG2, SD1_MAXBLK, 0, 2)
+REG32(SD1_CONFIG_REG3, 0x498)
+ FIELD(SD1_CONFIG_REG3, SD1_TUNINGSDR50, 10, 1)
+ FIELD(SD1_CONFIG_REG3, SD1_RETUNETMR, 6, 4)
+ FIELD(SD1_CONFIG_REG3, SD1_DDRIVER, 5, 1)
+ FIELD(SD1_CONFIG_REG3, SD1_CDRIVER, 4, 1)
+ FIELD(SD1_CONFIG_REG3, SD1_ADRIVER, 3, 1)
+ FIELD(SD1_CONFIG_REG3, SD1_DDR50, 2, 1)
+ FIELD(SD1_CONFIG_REG3, SD1_SDR104, 1, 1)
+ FIELD(SD1_CONFIG_REG3, SD1_SDR50, 0, 1)
+REG32(SD1_INITPRESET, 0x49c)
+ FIELD(SD1_INITPRESET, SD1_INITPRESET, 0, 13)
+REG32(SD1_DSPPRESET, 0x4a0)
+ FIELD(SD1_DSPPRESET, SD1_DSPPRESET, 0, 13)
+REG32(SD1_HSPDPRESET, 0x4a4)
+ FIELD(SD1_HSPDPRESET, SD1_HSPDPRESET, 0, 13)
+REG32(SD1_SDR12PRESET, 0x4a8)
+ FIELD(SD1_SDR12PRESET, SD1_SDR12PRESET, 0, 13)
+REG32(SD1_SDR25PRESET, 0x4ac)
+ FIELD(SD1_SDR25PRESET, SD1_SDR25PRESET, 0, 13)
+REG32(SD1_SDR50PRSET, 0x4b0)
+ FIELD(SD1_SDR50PRSET, SD1_SDR50PRESET, 0, 13)
+REG32(SD1_SDR104PRST, 0x4b4)
+ FIELD(SD1_SDR104PRST, SD1_SDR104PRESET, 0, 13)
+REG32(SD1_DDR50PRESET, 0x4b8)
+ FIELD(SD1_DDR50PRESET, SD1_DDR50PRESET, 0, 13)
+REG32(SD1_MAXCUR1P8, 0x4bc)
+ FIELD(SD1_MAXCUR1P8, SD1_MAXCUR1P8, 0, 8)
+REG32(SD1_MAXCUR3P0, 0x4c0)
+ FIELD(SD1_MAXCUR3P0, SD1_MAXCUR3P0, 0, 8)
+REG32(SD1_MAXCUR3P3, 0x4c4)
+ FIELD(SD1_MAXCUR3P3, SD1_MAXCUR3P3, 0, 8)
+REG32(SD1_DLL_CTRL, 0x4c8)
+ FIELD(SD1_DLL_CTRL, SD1_CLKSTABLE_CFG, 9, 1)
+ FIELD(SD1_DLL_CTRL, SD1_DLL_CFG, 5, 4)
+ FIELD(SD1_DLL_CTRL, SD1_DLL_PSDONE, 4, 1)
+ FIELD(SD1_DLL_CTRL, SD1_DLL_OVF, 3, 1)
+ FIELD(SD1_DLL_CTRL, SD1_DLL_RST, 2, 1)
+ FIELD(SD1_DLL_CTRL, SD1_DLL_TESTMODE, 1, 1)
+ FIELD(SD1_DLL_CTRL, SD1_DLL_LOCK, 0, 1)
+REG32(SD1_CDN_CTRL, 0x4cc)
+ FIELD(SD1_CDN_CTRL, SD1_CDN_CTRL, 0, 1)
+REG32(SD1_DLL_TEST, 0x4d0)
+ FIELD(SD1_DLL_TEST, DLL_DIV, 16, 8)
+ FIELD(SD1_DLL_TEST, DLL_TX_SEL, 9, 7)
+ FIELD(SD1_DLL_TEST, DLL_RX_SEL, 0, 9)
+REG32(SD1_RX_TUNING_SEL, 0x4d4)
+ FIELD(SD1_RX_TUNING_SEL, SD1_RX_SEL, 0, 9)
+REG32(SD1_DLL_DIV_MAP0, 0x4d8)
+ FIELD(SD1_DLL_DIV_MAP0, DIV_3, 24, 8)
+ FIELD(SD1_DLL_DIV_MAP0, DIV_2, 16, 8)
+ FIELD(SD1_DLL_DIV_MAP0, DIV_1, 8, 8)
+ FIELD(SD1_DLL_DIV_MAP0, DIV_0, 0, 8)
+REG32(SD1_DLL_DIV_MAP1, 0x4dc)
+ FIELD(SD1_DLL_DIV_MAP1, DIV_7, 24, 8)
+ FIELD(SD1_DLL_DIV_MAP1, DIV_6, 16, 8)
+ FIELD(SD1_DLL_DIV_MAP1, DIV_5, 8, 8)
+ FIELD(SD1_DLL_DIV_MAP1, DIV_4, 0, 8)
+REG32(SD1_IOU_COHERENT_CTRL, 0x4e0)
+ FIELD(SD1_IOU_COHERENT_CTRL, SD1_AXI_COH, 0, 4)
+REG32(SD1_IOU_INTERCONNECT_ROUTE, 0x4e4)
+ FIELD(SD1_IOU_INTERCONNECT_ROUTE, SD1, 0, 1)
+REG32(SD1_IOU_RAM, 0x4e8)
+ FIELD(SD1_IOU_RAM, EMASA0, 6, 1)
+ FIELD(SD1_IOU_RAM, EMAB0, 3, 3)
+ FIELD(SD1_IOU_RAM, EMAA0, 0, 3)
+REG32(SD1_IOU_INTERCONNECT_QOS, 0x4ec)
+ FIELD(SD1_IOU_INTERCONNECT_QOS, SD1_QOS, 0, 4)
+REG32(OSPI_QSPI_IOU_AXI_MUX_SEL, 0x504)
+ FIELD(OSPI_QSPI_IOU_AXI_MUX_SEL, OSPI_MUX_SEL, 1, 1)
+ FIELD(OSPI_QSPI_IOU_AXI_MUX_SEL, QSPI_OSPI_MUX_SEL, 0, 1)
+REG32(QSPI_IOU_COHERENT_CTRL, 0x508)
+ FIELD(QSPI_IOU_COHERENT_CTRL, QSPI_AXI_COH, 0, 4)
+REG32(QSPI_IOU_INTERCONNECT_ROUTE, 0x50c)
+ FIELD(QSPI_IOU_INTERCONNECT_ROUTE, QSPI, 0, 1)
+REG32(QSPI_IOU_RAM, 0x510)
+ FIELD(QSPI_IOU_RAM, EMASA1, 13, 1)
+ FIELD(QSPI_IOU_RAM, EMAB1, 10, 3)
+ FIELD(QSPI_IOU_RAM, EMAA1, 7, 3)
+ FIELD(QSPI_IOU_RAM, EMASA0, 6, 1)
+ FIELD(QSPI_IOU_RAM, EMAB0, 3, 3)
+ FIELD(QSPI_IOU_RAM, EMAA0, 0, 3)
+REG32(QSPI_IOU_INTERCONNECT_QOS, 0x514)
+ FIELD(QSPI_IOU_INTERCONNECT_QOS, QSPI_QOS, 0, 4)
+REG32(OSPI_IOU_COHERENT_CTRL, 0x530)
+ FIELD(OSPI_IOU_COHERENT_CTRL, OSPI_AXI_COH, 0, 4)
+REG32(OSPI_IOU_INTERCONNECT_ROUTE, 0x534)
+ FIELD(OSPI_IOU_INTERCONNECT_ROUTE, OSPI, 0, 1)
+REG32(OSPI_IOU_RAM, 0x538)
+ FIELD(OSPI_IOU_RAM, EMAS0, 5, 1)
+ FIELD(OSPI_IOU_RAM, EMAW0, 3, 2)
+ FIELD(OSPI_IOU_RAM, EMA0, 0, 3)
+REG32(OSPI_IOU_INTERCONNECT_QOS, 0x53c)
+ FIELD(OSPI_IOU_INTERCONNECT_QOS, OSPI_QOS, 0, 4)
+REG32(OSPI_REFCLK_DLY_CTRL, 0x540)
+ FIELD(OSPI_REFCLK_DLY_CTRL, DLY1, 3, 2)
+ FIELD(OSPI_REFCLK_DLY_CTRL, DLY0, 0, 3)
+REG32(CUR_PWR_ST, 0x600)
+ FIELD(CUR_PWR_ST, U2PMU, 0, 2)
+REG32(CONNECT_ST, 0x604)
+ FIELD(CONNECT_ST, U2PMU, 0, 1)
+REG32(PW_STATE_REQ, 0x608)
+ FIELD(PW_STATE_REQ, BIT_1_0, 0, 2)
+REG32(HOST_U2_PORT_DISABLE, 0x60c)
+ FIELD(HOST_U2_PORT_DISABLE, BIT_0, 0, 1)
+REG32(DBG_U2PMU, 0x610)
+REG32(DBG_U2PMU_EXT1, 0x614)
+REG32(DBG_U2PMU_EXT2, 0x618)
+ FIELD(DBG_U2PMU_EXT2, BIT_67_64, 0, 4)
+REG32(PME_GEN_U2PMU, 0x61c)
+ FIELD(PME_GEN_U2PMU, BIT_0, 0, 1)
+REG32(PWR_CONFIG_USB2, 0x620)
+ FIELD(PWR_CONFIG_USB2, STRAP, 0, 30)
+REG32(PHY_HUB, 0x624)
+ FIELD(PHY_HUB, VBUS_CTRL, 1, 1)
+ FIELD(PHY_HUB, OVER_CURRENT, 0, 1)
+REG32(CTRL, 0x700)
+ FIELD(CTRL, SLVERR_ENABLE, 0, 1)
+REG32(ISR, 0x800)
+ FIELD(ISR, ADDR_DECODE_ERR, 0, 1)
+REG32(IMR, 0x804)
+ FIELD(IMR, ADDR_DECODE_ERR, 0, 1)
+REG32(IER, 0x808)
+ FIELD(IER, ADDR_DECODE_ERR, 0, 1)
+REG32(IDR, 0x80c)
+ FIELD(IDR, ADDR_DECODE_ERR, 0, 1)
+REG32(ITR, 0x810)
+ FIELD(ITR, ADDR_DECODE_ERR, 0, 1)
+REG32(PARITY_ISR, 0x814)
+ FIELD(PARITY_ISR, PERR_AXI_SD1_IOU, 12, 1)
+ FIELD(PARITY_ISR, PERR_AXI_SD0_IOU, 11, 1)
+ FIELD(PARITY_ISR, PERR_AXI_QSPI_IOU, 10, 1)
+ FIELD(PARITY_ISR, PERR_AXI_OSPI_IOU, 9, 1)
+ FIELD(PARITY_ISR, PERR_IOU_SD1, 8, 1)
+ FIELD(PARITY_ISR, PERR_IOU_SD0, 7, 1)
+ FIELD(PARITY_ISR, PERR_IOU_QSPI1, 6, 1)
+ FIELD(PARITY_ISR, PERR_IOUSLCR_SECURE_APB, 5, 1)
+ FIELD(PARITY_ISR, PERR_IOUSLCR_APB, 4, 1)
+ FIELD(PARITY_ISR, PERR_QSPI0_APB, 3, 1)
+ FIELD(PARITY_ISR, PERR_OSPI_APB, 2, 1)
+ FIELD(PARITY_ISR, PERR_I2C_APB, 1, 1)
+ FIELD(PARITY_ISR, PERR_GPIO_APB, 0, 1)
+REG32(PARITY_IMR, 0x818)
+ FIELD(PARITY_IMR, PERR_AXI_SD1_IOU, 12, 1)
+ FIELD(PARITY_IMR, PERR_AXI_SD0_IOU, 11, 1)
+ FIELD(PARITY_IMR, PERR_AXI_QSPI_IOU, 10, 1)
+ FIELD(PARITY_IMR, PERR_AXI_OSPI_IOU, 9, 1)
+ FIELD(PARITY_IMR, PERR_IOU_SD1, 8, 1)
+ FIELD(PARITY_IMR, PERR_IOU_SD0, 7, 1)
+ FIELD(PARITY_IMR, PERR_IOU_QSPI1, 6, 1)
+ FIELD(PARITY_IMR, PERR_IOUSLCR_SECURE_APB, 5, 1)
+ FIELD(PARITY_IMR, PERR_IOUSLCR_APB, 4, 1)
+ FIELD(PARITY_IMR, PERR_QSPI0_APB, 3, 1)
+ FIELD(PARITY_IMR, PERR_OSPI_APB, 2, 1)
+ FIELD(PARITY_IMR, PERR_I2C_APB, 1, 1)
+ FIELD(PARITY_IMR, PERR_GPIO_APB, 0, 1)
+REG32(PARITY_IER, 0x81c)
+ FIELD(PARITY_IER, PERR_AXI_SD1_IOU, 12, 1)
+ FIELD(PARITY_IER, PERR_AXI_SD0_IOU, 11, 1)
+ FIELD(PARITY_IER, PERR_AXI_QSPI_IOU, 10, 1)
+ FIELD(PARITY_IER, PERR_AXI_OSPI_IOU, 9, 1)
+ FIELD(PARITY_IER, PERR_IOU_SD1, 8, 1)
+ FIELD(PARITY_IER, PERR_IOU_SD0, 7, 1)
+ FIELD(PARITY_IER, PERR_IOU_QSPI1, 6, 1)
+ FIELD(PARITY_IER, PERR_IOUSLCR_SECURE_APB, 5, 1)
+ FIELD(PARITY_IER, PERR_IOUSLCR_APB, 4, 1)
+ FIELD(PARITY_IER, PERR_QSPI0_APB, 3, 1)
+ FIELD(PARITY_IER, PERR_OSPI_APB, 2, 1)
+ FIELD(PARITY_IER, PERR_I2C_APB, 1, 1)
+ FIELD(PARITY_IER, PERR_GPIO_APB, 0, 1)
+REG32(PARITY_IDR, 0x820)
+ FIELD(PARITY_IDR, PERR_AXI_SD1_IOU, 12, 1)
+ FIELD(PARITY_IDR, PERR_AXI_SD0_IOU, 11, 1)
+ FIELD(PARITY_IDR, PERR_AXI_QSPI_IOU, 10, 1)
+ FIELD(PARITY_IDR, PERR_AXI_OSPI_IOU, 9, 1)
+ FIELD(PARITY_IDR, PERR_IOU_SD1, 8, 1)
+ FIELD(PARITY_IDR, PERR_IOU_SD0, 7, 1)
+ FIELD(PARITY_IDR, PERR_IOU_QSPI1, 6, 1)
+ FIELD(PARITY_IDR, PERR_IOUSLCR_SECURE_APB, 5, 1)
+ FIELD(PARITY_IDR, PERR_IOUSLCR_APB, 4, 1)
+ FIELD(PARITY_IDR, PERR_QSPI0_APB, 3, 1)
+ FIELD(PARITY_IDR, PERR_OSPI_APB, 2, 1)
+ FIELD(PARITY_IDR, PERR_I2C_APB, 1, 1)
+ FIELD(PARITY_IDR, PERR_GPIO_APB, 0, 1)
+REG32(PARITY_ITR, 0x824)
+ FIELD(PARITY_ITR, PERR_AXI_SD1_IOU, 12, 1)
+ FIELD(PARITY_ITR, PERR_AXI_SD0_IOU, 11, 1)
+ FIELD(PARITY_ITR, PERR_AXI_QSPI_IOU, 10, 1)
+ FIELD(PARITY_ITR, PERR_AXI_OSPI_IOU, 9, 1)
+ FIELD(PARITY_ITR, PERR_IOU_SD1, 8, 1)
+ FIELD(PARITY_ITR, PERR_IOU_SD0, 7, 1)
+ FIELD(PARITY_ITR, PERR_IOU_QSPI1, 6, 1)
+ FIELD(PARITY_ITR, PERR_IOUSLCR_SECURE_APB, 5, 1)
+ FIELD(PARITY_ITR, PERR_IOUSLCR_APB, 4, 1)
+ FIELD(PARITY_ITR, PERR_QSPI0_APB, 3, 1)
+ FIELD(PARITY_ITR, PERR_OSPI_APB, 2, 1)
+ FIELD(PARITY_ITR, PERR_I2C_APB, 1, 1)
+ FIELD(PARITY_ITR, PERR_GPIO_APB, 0, 1)
+REG32(WPROT0, 0x828)
+ FIELD(WPROT0, ACTIVE, 0, 1)
+
+static void parity_imr_update_irq(XlnxVersalPmcIouSlcr *s)
+{
+ bool pending = s->regs[R_PARITY_ISR] & ~s->regs[R_PARITY_IMR];
+ qemu_set_irq(s->irq_parity_imr, pending);
+}
+
+static void parity_isr_postw(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ parity_imr_update_irq(s);
+}
+
+static uint64_t parity_ier_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t val = val64;
+
+ s->regs[R_PARITY_IMR] &= ~val;
+ parity_imr_update_irq(s);
+ return 0;
+}
+
+static uint64_t parity_idr_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t val = val64;
+
+ s->regs[R_PARITY_IMR] |= val;
+ parity_imr_update_irq(s);
+ return 0;
+}
+
+static uint64_t parity_itr_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t val = val64;
+
+ s->regs[R_PARITY_ISR] |= val;
+ parity_imr_update_irq(s);
+ return 0;
+}
+
+static void imr_update_irq(XlnxVersalPmcIouSlcr *s)
+{
+ bool pending = s->regs[R_ISR] & ~s->regs[R_IMR];
+ qemu_set_irq(s->irq_imr, pending);
+}
+
+static void isr_postw(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ imr_update_irq(s);
+}
+
+static uint64_t ier_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t val = val64;
+
+ s->regs[R_IMR] &= ~val;
+ imr_update_irq(s);
+ return 0;
+}
+
+static uint64_t idr_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t val = val64;
+
+ s->regs[R_IMR] |= val;
+ imr_update_irq(s);
+ return 0;
+}
+
+static uint64_t itr_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t val = val64;
+
+ s->regs[R_ISR] |= val;
+ imr_update_irq(s);
+ return 0;
+}
+
+static uint64_t sd0_ctrl_reg_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t prev = ARRAY_FIELD_EX32(s->regs, SD0_CTRL_REG, SD0_EMMC_SEL);
+
+ if (prev != (val64 & R_SD0_CTRL_REG_SD0_EMMC_SEL_MASK)) {
+ qemu_set_irq(s->sd_emmc_sel[0], !!val64);
+ }
+
+ return val64;
+}
+
+static uint64_t sd1_ctrl_reg_prew(RegisterInfo *reg, uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t prev = ARRAY_FIELD_EX32(s->regs, SD1_CTRL_REG, SD1_EMMC_SEL);
+
+ if (prev != (val64 & R_SD1_CTRL_REG_SD1_EMMC_SEL_MASK)) {
+ qemu_set_irq(s->sd_emmc_sel[1], !!val64);
+ }
+
+ return val64;
+}
+
+static uint64_t ospi_qspi_iou_axi_mux_sel_prew(RegisterInfo *reg,
+ uint64_t val64)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(reg->opaque);
+ uint32_t val32 = (uint32_t) val64;
+ uint8_t ospi_mux_sel = FIELD_EX32(val32, OSPI_QSPI_IOU_AXI_MUX_SEL,
+ OSPI_MUX_SEL);
+ uint8_t qspi_ospi_mux_sel = FIELD_EX32(val32, OSPI_QSPI_IOU_AXI_MUX_SEL,
+ QSPI_OSPI_MUX_SEL);
+
+ if (ospi_mux_sel !=
+ ARRAY_FIELD_EX32(s->regs, OSPI_QSPI_IOU_AXI_MUX_SEL, OSPI_MUX_SEL)) {
+ qemu_set_irq(s->ospi_mux_sel, !!ospi_mux_sel);
+ }
+
+ if (qspi_ospi_mux_sel !=
+ ARRAY_FIELD_EX32(s->regs, OSPI_QSPI_IOU_AXI_MUX_SEL,
+ QSPI_OSPI_MUX_SEL)) {
+ qemu_set_irq(s->qspi_ospi_mux_sel, !!qspi_ospi_mux_sel);
+ }
+
+ return val64;
+}
+
+static RegisterAccessInfo pmc_iou_slcr_regs_info[] = {
+ { .name = "MIO_PIN_0", .addr = A_MIO_PIN_0,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_1", .addr = A_MIO_PIN_1,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_2", .addr = A_MIO_PIN_2,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_3", .addr = A_MIO_PIN_3,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_4", .addr = A_MIO_PIN_4,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_5", .addr = A_MIO_PIN_5,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_6", .addr = A_MIO_PIN_6,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_7", .addr = A_MIO_PIN_7,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_8", .addr = A_MIO_PIN_8,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_9", .addr = A_MIO_PIN_9,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_10", .addr = A_MIO_PIN_10,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_11", .addr = A_MIO_PIN_11,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_12", .addr = A_MIO_PIN_12,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_13", .addr = A_MIO_PIN_13,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_14", .addr = A_MIO_PIN_14,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_15", .addr = A_MIO_PIN_15,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_16", .addr = A_MIO_PIN_16,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_17", .addr = A_MIO_PIN_17,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_18", .addr = A_MIO_PIN_18,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_19", .addr = A_MIO_PIN_19,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_20", .addr = A_MIO_PIN_20,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_21", .addr = A_MIO_PIN_21,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_22", .addr = A_MIO_PIN_22,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_23", .addr = A_MIO_PIN_23,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_24", .addr = A_MIO_PIN_24,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_25", .addr = A_MIO_PIN_25,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_26", .addr = A_MIO_PIN_26,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_27", .addr = A_MIO_PIN_27,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_28", .addr = A_MIO_PIN_28,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_29", .addr = A_MIO_PIN_29,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_30", .addr = A_MIO_PIN_30,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_31", .addr = A_MIO_PIN_31,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_32", .addr = A_MIO_PIN_32,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_33", .addr = A_MIO_PIN_33,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_34", .addr = A_MIO_PIN_34,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_35", .addr = A_MIO_PIN_35,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_36", .addr = A_MIO_PIN_36,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_37", .addr = A_MIO_PIN_37,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_38", .addr = A_MIO_PIN_38,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_39", .addr = A_MIO_PIN_39,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_40", .addr = A_MIO_PIN_40,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_41", .addr = A_MIO_PIN_41,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_42", .addr = A_MIO_PIN_42,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_43", .addr = A_MIO_PIN_43,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_44", .addr = A_MIO_PIN_44,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_45", .addr = A_MIO_PIN_45,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_46", .addr = A_MIO_PIN_46,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_47", .addr = A_MIO_PIN_47,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_48", .addr = A_MIO_PIN_48,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_49", .addr = A_MIO_PIN_49,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_50", .addr = A_MIO_PIN_50,
+ .rsvd = 0xfffffc01,
+ },{ .name = "MIO_PIN_51", .addr = A_MIO_PIN_51,
+ .rsvd = 0xfffffc01,
+ },{ .name = "BNK0_EN_RX", .addr = A_BNK0_EN_RX,
+ .reset = 0x3ffffff,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK0_SEL_RX0", .addr = A_BNK0_SEL_RX0,
+ .reset = 0xffffffff,
+ },{ .name = "BNK0_SEL_RX1", .addr = A_BNK0_SEL_RX1,
+ .reset = 0xfffff,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK0_EN_RX_SCHMITT_HYST", .addr = A_BNK0_EN_RX_SCHMITT_HYST,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK0_EN_WK_PD", .addr = A_BNK0_EN_WK_PD,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK0_EN_WK_PU", .addr = A_BNK0_EN_WK_PU,
+ .reset = 0x3ffffff,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK0_SEL_DRV0", .addr = A_BNK0_SEL_DRV0,
+ .reset = 0xffffffff,
+ },{ .name = "BNK0_SEL_DRV1", .addr = A_BNK0_SEL_DRV1,
+ .reset = 0xfffff,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK0_SEL_SLEW", .addr = A_BNK0_SEL_SLEW,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK0_EN_DFT_OPT_INV", .addr = A_BNK0_EN_DFT_OPT_INV,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK0_EN_PAD2PAD_LOOPBACK",
+ .addr = A_BNK0_EN_PAD2PAD_LOOPBACK,
+ .rsvd = 0xffffe000,
+ },{ .name = "BNK0_RX_SPARE0", .addr = A_BNK0_RX_SPARE0,
+ },{ .name = "BNK0_RX_SPARE1", .addr = A_BNK0_RX_SPARE1,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK0_TX_SPARE0", .addr = A_BNK0_TX_SPARE0,
+ },{ .name = "BNK0_TX_SPARE1", .addr = A_BNK0_TX_SPARE1,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK0_SEL_EN1P8", .addr = A_BNK0_SEL_EN1P8,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK0_EN_B_POR_DETECT", .addr = A_BNK0_EN_B_POR_DETECT,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK0_LPF_BYP_POR_DETECT", .addr = A_BNK0_LPF_BYP_POR_DETECT,
+ .reset = 0x1,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK0_EN_LATCH", .addr = A_BNK0_EN_LATCH,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK0_VBG_LPF_BYP_B", .addr = A_BNK0_VBG_LPF_BYP_B,
+ .reset = 0x1,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK0_EN_AMP_B", .addr = A_BNK0_EN_AMP_B,
+ .rsvd = 0xfffffffc,
+ },{ .name = "BNK0_SPARE_BIAS", .addr = A_BNK0_SPARE_BIAS,
+ .rsvd = 0xfffffff0,
+ },{ .name = "BNK0_DRIVER_BIAS", .addr = A_BNK0_DRIVER_BIAS,
+ .rsvd = 0xffff8000,
+ },{ .name = "BNK0_VMODE", .addr = A_BNK0_VMODE,
+ .rsvd = 0xfffffffe,
+ .ro = 0x1,
+ },{ .name = "BNK0_SEL_AUX_IO_RX", .addr = A_BNK0_SEL_AUX_IO_RX,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK0_EN_TX_HS_MODE", .addr = A_BNK0_EN_TX_HS_MODE,
+ .rsvd = 0xfc000000,
+ },{ .name = "MIO_MST_TRI0", .addr = A_MIO_MST_TRI0,
+ .reset = 0x3ffffff,
+ .rsvd = 0xfc000000,
+ },{ .name = "MIO_MST_TRI1", .addr = A_MIO_MST_TRI1,
+ .reset = 0x3ffffff,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_EN_RX", .addr = A_BNK1_EN_RX,
+ .reset = 0x3ffffff,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_SEL_RX0", .addr = A_BNK1_SEL_RX0,
+ .reset = 0xffffffff,
+ },{ .name = "BNK1_SEL_RX1", .addr = A_BNK1_SEL_RX1,
+ .reset = 0xfffff,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK1_EN_RX_SCHMITT_HYST", .addr = A_BNK1_EN_RX_SCHMITT_HYST,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_EN_WK_PD", .addr = A_BNK1_EN_WK_PD,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_EN_WK_PU", .addr = A_BNK1_EN_WK_PU,
+ .reset = 0x3ffffff,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_SEL_DRV0", .addr = A_BNK1_SEL_DRV0,
+ .reset = 0xffffffff,
+ },{ .name = "BNK1_SEL_DRV1", .addr = A_BNK1_SEL_DRV1,
+ .reset = 0xfffff,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK1_SEL_SLEW", .addr = A_BNK1_SEL_SLEW,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_EN_DFT_OPT_INV", .addr = A_BNK1_EN_DFT_OPT_INV,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_EN_PAD2PAD_LOOPBACK",
+ .addr = A_BNK1_EN_PAD2PAD_LOOPBACK,
+ .rsvd = 0xffffe000,
+ },{ .name = "BNK1_RX_SPARE0", .addr = A_BNK1_RX_SPARE0,
+ },{ .name = "BNK1_RX_SPARE1", .addr = A_BNK1_RX_SPARE1,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK1_TX_SPARE0", .addr = A_BNK1_TX_SPARE0,
+ },{ .name = "BNK1_TX_SPARE1", .addr = A_BNK1_TX_SPARE1,
+ .rsvd = 0xfff00000,
+ },{ .name = "BNK1_SEL_EN1P8", .addr = A_BNK1_SEL_EN1P8,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK1_EN_B_POR_DETECT", .addr = A_BNK1_EN_B_POR_DETECT,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK1_LPF_BYP_POR_DETECT", .addr = A_BNK1_LPF_BYP_POR_DETECT,
+ .reset = 0x1,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK1_EN_LATCH", .addr = A_BNK1_EN_LATCH,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK1_VBG_LPF_BYP_B", .addr = A_BNK1_VBG_LPF_BYP_B,
+ .reset = 0x1,
+ .rsvd = 0xfffffffe,
+ },{ .name = "BNK1_EN_AMP_B", .addr = A_BNK1_EN_AMP_B,
+ .rsvd = 0xfffffffc,
+ },{ .name = "BNK1_SPARE_BIAS", .addr = A_BNK1_SPARE_BIAS,
+ .rsvd = 0xfffffff0,
+ },{ .name = "BNK1_DRIVER_BIAS", .addr = A_BNK1_DRIVER_BIAS,
+ .rsvd = 0xffff8000,
+ },{ .name = "BNK1_VMODE", .addr = A_BNK1_VMODE,
+ .rsvd = 0xfffffffe,
+ .ro = 0x1,
+ },{ .name = "BNK1_SEL_AUX_IO_RX", .addr = A_BNK1_SEL_AUX_IO_RX,
+ .rsvd = 0xfc000000,
+ },{ .name = "BNK1_EN_TX_HS_MODE", .addr = A_BNK1_EN_TX_HS_MODE,
+ .rsvd = 0xfc000000,
+ },{ .name = "SD0_CLK_CTRL", .addr = A_SD0_CLK_CTRL,
+ .rsvd = 0xfffffff8,
+ },{ .name = "SD0_CTRL_REG", .addr = A_SD0_CTRL_REG,
+ .rsvd = 0xfffffffe,
+ .pre_write = sd0_ctrl_reg_prew,
+ },{ .name = "SD0_CONFIG_REG1", .addr = A_SD0_CONFIG_REG1,
+ .reset = 0x3250,
+ .rsvd = 0xffff8000,
+ },{ .name = "SD0_CONFIG_REG2", .addr = A_SD0_CONFIG_REG2,
+ .reset = 0xffc,
+ .rsvd = 0xffffc000,
+ },{ .name = "SD0_CONFIG_REG3", .addr = A_SD0_CONFIG_REG3,
+ .reset = 0x407,
+ .rsvd = 0xfffff800,
+ },{ .name = "SD0_INITPRESET", .addr = A_SD0_INITPRESET,
+ .reset = 0x100,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_DSPPRESET", .addr = A_SD0_DSPPRESET,
+ .reset = 0x4,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_HSPDPRESET", .addr = A_SD0_HSPDPRESET,
+ .reset = 0x2,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_SDR12PRESET", .addr = A_SD0_SDR12PRESET,
+ .reset = 0x4,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_SDR25PRESET", .addr = A_SD0_SDR25PRESET,
+ .reset = 0x2,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_SDR50PRSET", .addr = A_SD0_SDR50PRSET,
+ .reset = 0x1,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_SDR104PRST", .addr = A_SD0_SDR104PRST,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_DDR50PRESET", .addr = A_SD0_DDR50PRESET,
+ .reset = 0x2,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD0_MAXCUR1P8", .addr = A_SD0_MAXCUR1P8,
+ .rsvd = 0xffffff00,
+ },{ .name = "SD0_MAXCUR3P0", .addr = A_SD0_MAXCUR3P0,
+ .rsvd = 0xffffff00,
+ },{ .name = "SD0_MAXCUR3P3", .addr = A_SD0_MAXCUR3P3,
+ .rsvd = 0xffffff00,
+ },{ .name = "SD0_DLL_CTRL", .addr = A_SD0_DLL_CTRL,
+ .reset = 0x1,
+ .rsvd = 0xfffffc00,
+ .ro = 0x19,
+ },{ .name = "SD0_CDN_CTRL", .addr = A_SD0_CDN_CTRL,
+ .rsvd = 0xfffffffe,
+ },{ .name = "SD0_DLL_TEST", .addr = A_SD0_DLL_TEST,
+ .rsvd = 0xff000000,
+ },{ .name = "SD0_RX_TUNING_SEL", .addr = A_SD0_RX_TUNING_SEL,
+ .rsvd = 0xfffffe00,
+ .ro = 0x1ff,
+ },{ .name = "SD0_DLL_DIV_MAP0", .addr = A_SD0_DLL_DIV_MAP0,
+ .reset = 0x50505050,
+ },{ .name = "SD0_DLL_DIV_MAP1", .addr = A_SD0_DLL_DIV_MAP1,
+ .reset = 0x50505050,
+ },{ .name = "SD0_IOU_COHERENT_CTRL", .addr = A_SD0_IOU_COHERENT_CTRL,
+ .rsvd = 0xfffffff0,
+ },{ .name = "SD0_IOU_INTERCONNECT_ROUTE",
+ .addr = A_SD0_IOU_INTERCONNECT_ROUTE,
+ .rsvd = 0xfffffffe,
+ },{ .name = "SD0_IOU_RAM", .addr = A_SD0_IOU_RAM,
+ .reset = 0x24,
+ .rsvd = 0xffffff80,
+ },{ .name = "SD0_IOU_INTERCONNECT_QOS",
+ .addr = A_SD0_IOU_INTERCONNECT_QOS,
+ .rsvd = 0xfffffff0,
+ },{ .name = "SD1_CLK_CTRL", .addr = A_SD1_CLK_CTRL,
+ .rsvd = 0xfffffffc,
+ },{ .name = "SD1_CTRL_REG", .addr = A_SD1_CTRL_REG,
+ .rsvd = 0xfffffffe,
+ .pre_write = sd1_ctrl_reg_prew,
+ },{ .name = "SD1_CONFIG_REG1", .addr = A_SD1_CONFIG_REG1,
+ .reset = 0x3250,
+ .rsvd = 0xffff8000,
+ },{ .name = "SD1_CONFIG_REG2", .addr = A_SD1_CONFIG_REG2,
+ .reset = 0xffc,
+ .rsvd = 0xffffc000,
+ },{ .name = "SD1_CONFIG_REG3", .addr = A_SD1_CONFIG_REG3,
+ .reset = 0x407,
+ .rsvd = 0xfffff800,
+ },{ .name = "SD1_INITPRESET", .addr = A_SD1_INITPRESET,
+ .reset = 0x100,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_DSPPRESET", .addr = A_SD1_DSPPRESET,
+ .reset = 0x4,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_HSPDPRESET", .addr = A_SD1_HSPDPRESET,
+ .reset = 0x2,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_SDR12PRESET", .addr = A_SD1_SDR12PRESET,
+ .reset = 0x4,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_SDR25PRESET", .addr = A_SD1_SDR25PRESET,
+ .reset = 0x2,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_SDR50PRSET", .addr = A_SD1_SDR50PRSET,
+ .reset = 0x1,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_SDR104PRST", .addr = A_SD1_SDR104PRST,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_DDR50PRESET", .addr = A_SD1_DDR50PRESET,
+ .reset = 0x2,
+ .rsvd = 0xffffe000,
+ },{ .name = "SD1_MAXCUR1P8", .addr = A_SD1_MAXCUR1P8,
+ .rsvd = 0xffffff00,
+ },{ .name = "SD1_MAXCUR3P0", .addr = A_SD1_MAXCUR3P0,
+ .rsvd = 0xffffff00,
+ },{ .name = "SD1_MAXCUR3P3", .addr = A_SD1_MAXCUR3P3,
+ .rsvd = 0xffffff00,
+ },{ .name = "SD1_DLL_CTRL", .addr = A_SD1_DLL_CTRL,
+ .reset = 0x1,
+ .rsvd = 0xfffffc00,
+ .ro = 0x19,
+ },{ .name = "SD1_CDN_CTRL", .addr = A_SD1_CDN_CTRL,
+ .rsvd = 0xfffffffe,
+ },{ .name = "SD1_DLL_TEST", .addr = A_SD1_DLL_TEST,
+ .rsvd = 0xff000000,
+ },{ .name = "SD1_RX_TUNING_SEL", .addr = A_SD1_RX_TUNING_SEL,
+ .rsvd = 0xfffffe00,
+ .ro = 0x1ff,
+ },{ .name = "SD1_DLL_DIV_MAP0", .addr = A_SD1_DLL_DIV_MAP0,
+ .reset = 0x50505050,
+ },{ .name = "SD1_DLL_DIV_MAP1", .addr = A_SD1_DLL_DIV_MAP1,
+ .reset = 0x50505050,
+ },{ .name = "SD1_IOU_COHERENT_CTRL", .addr = A_SD1_IOU_COHERENT_CTRL,
+ .rsvd = 0xfffffff0,
+ },{ .name = "SD1_IOU_INTERCONNECT_ROUTE",
+ .addr = A_SD1_IOU_INTERCONNECT_ROUTE,
+ .rsvd = 0xfffffffe,
+ },{ .name = "SD1_IOU_RAM", .addr = A_SD1_IOU_RAM,
+ .reset = 0x24,
+ .rsvd = 0xffffff80,
+ },{ .name = "SD1_IOU_INTERCONNECT_QOS",
+ .addr = A_SD1_IOU_INTERCONNECT_QOS,
+ .rsvd = 0xfffffff0,
+ },{ .name = "OSPI_QSPI_IOU_AXI_MUX_SEL",
+ .addr = A_OSPI_QSPI_IOU_AXI_MUX_SEL,
+ .reset = 0x1,
+ .rsvd = 0xfffffffc,
+ .pre_write = ospi_qspi_iou_axi_mux_sel_prew,
+ },{ .name = "QSPI_IOU_COHERENT_CTRL", .addr = A_QSPI_IOU_COHERENT_CTRL,
+ .rsvd = 0xfffffff0,
+ },{ .name = "QSPI_IOU_INTERCONNECT_ROUTE",
+ .addr = A_QSPI_IOU_INTERCONNECT_ROUTE,
+ .rsvd = 0xfffffffe,
+ },{ .name = "QSPI_IOU_RAM", .addr = A_QSPI_IOU_RAM,
+ .reset = 0x1224,
+ .rsvd = 0xffffc000,
+ },{ .name = "QSPI_IOU_INTERCONNECT_QOS",
+ .addr = A_QSPI_IOU_INTERCONNECT_QOS,
+ .rsvd = 0xfffffff0,
+ },{ .name = "OSPI_IOU_COHERENT_CTRL", .addr = A_OSPI_IOU_COHERENT_CTRL,
+ .rsvd = 0xfffffff0,
+ },{ .name = "OSPI_IOU_INTERCONNECT_ROUTE",
+ .addr = A_OSPI_IOU_INTERCONNECT_ROUTE,
+ .rsvd = 0xfffffffe,
+ },{ .name = "OSPI_IOU_RAM", .addr = A_OSPI_IOU_RAM,
+ .reset = 0xa,
+ .rsvd = 0xffffffc0,
+ },{ .name = "OSPI_IOU_INTERCONNECT_QOS",
+ .addr = A_OSPI_IOU_INTERCONNECT_QOS,
+ .rsvd = 0xfffffff0,
+ },{ .name = "OSPI_REFCLK_DLY_CTRL", .addr = A_OSPI_REFCLK_DLY_CTRL,
+ .reset = 0x13,
+ .rsvd = 0xffffffe0,
+ },{ .name = "CUR_PWR_ST", .addr = A_CUR_PWR_ST,
+ .rsvd = 0xfffffffc,
+ .ro = 0x3,
+ },{ .name = "CONNECT_ST", .addr = A_CONNECT_ST,
+ .rsvd = 0xfffffffe,
+ .ro = 0x1,
+ },{ .name = "PW_STATE_REQ", .addr = A_PW_STATE_REQ,
+ .rsvd = 0xfffffffc,
+ },{ .name = "HOST_U2_PORT_DISABLE", .addr = A_HOST_U2_PORT_DISABLE,
+ .rsvd = 0xfffffffe,
+ },{ .name = "DBG_U2PMU", .addr = A_DBG_U2PMU,
+ .ro = 0xffffffff,
+ },{ .name = "DBG_U2PMU_EXT1", .addr = A_DBG_U2PMU_EXT1,
+ .ro = 0xffffffff,
+ },{ .name = "DBG_U2PMU_EXT2", .addr = A_DBG_U2PMU_EXT2,
+ .rsvd = 0xfffffff0,
+ .ro = 0xf,
+ },{ .name = "PME_GEN_U2PMU", .addr = A_PME_GEN_U2PMU,
+ .rsvd = 0xfffffffe,
+ .ro = 0x1,
+ },{ .name = "PWR_CONFIG_USB2", .addr = A_PWR_CONFIG_USB2,
+ .rsvd = 0xc0000000,
+ },{ .name = "PHY_HUB", .addr = A_PHY_HUB,
+ .rsvd = 0xfffffffc,
+ .ro = 0x2,
+ },{ .name = "CTRL", .addr = A_CTRL,
+ },{ .name = "ISR", .addr = A_ISR,
+ .w1c = 0x1,
+ .post_write = isr_postw,
+ },{ .name = "IMR", .addr = A_IMR,
+ .reset = 0x1,
+ .ro = 0x1,
+ },{ .name = "IER", .addr = A_IER,
+ .pre_write = ier_prew,
+ },{ .name = "IDR", .addr = A_IDR,
+ .pre_write = idr_prew,
+ },{ .name = "ITR", .addr = A_ITR,
+ .pre_write = itr_prew,
+ },{ .name = "PARITY_ISR", .addr = A_PARITY_ISR,
+ .w1c = 0x1fff,
+ .post_write = parity_isr_postw,
+ },{ .name = "PARITY_IMR", .addr = A_PARITY_IMR,
+ .reset = 0x1fff,
+ .ro = 0x1fff,
+ },{ .name = "PARITY_IER", .addr = A_PARITY_IER,
+ .pre_write = parity_ier_prew,
+ },{ .name = "PARITY_IDR", .addr = A_PARITY_IDR,
+ .pre_write = parity_idr_prew,
+ },{ .name = "PARITY_ITR", .addr = A_PARITY_ITR,
+ .pre_write = parity_itr_prew,
+ },{ .name = "WPROT0", .addr = A_WPROT0,
+ .reset = 0x1,
+ }
+};
+
+static void xlnx_versal_pmc_iou_slcr_reset_init(Object *obj, ResetType type)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
+ register_reset(&s->regs_info[i]);
+ }
+}
+
+static void xlnx_versal_pmc_iou_slcr_reset_hold(Object *obj)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj);
+
+ parity_imr_update_irq(s);
+ imr_update_irq(s);
+
+ /*
+ * Setup OSPI_QSPI mux
+ * By default axi slave interface is enabled for ospi-dma
+ */
+ qemu_set_irq(s->ospi_mux_sel, 0);
+ qemu_set_irq(s->qspi_ospi_mux_sel, 1);
+}
+
+static const MemoryRegionOps pmc_iou_slcr_ops = {
+ .read = register_read_memory,
+ .write = register_write_memory,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void xlnx_versal_pmc_iou_slcr_realize(DeviceState *dev, Error **errp)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(dev);
+
+ qdev_init_gpio_out_named(dev, s->sd_emmc_sel, "sd-emmc-sel", 2);
+ qdev_init_gpio_out_named(dev, &s->qspi_ospi_mux_sel,
+ "qspi-ospi-mux-sel", 1);
+ qdev_init_gpio_out_named(dev, &s->ospi_mux_sel, "ospi-mux-sel", 1);
+}
+
+static void xlnx_versal_pmc_iou_slcr_init(Object *obj)
+{
+ XlnxVersalPmcIouSlcr *s = XILINX_VERSAL_PMC_IOU_SLCR(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ RegisterInfoArray *reg_array;
+
+ memory_region_init(&s->iomem, obj, TYPE_XILINX_VERSAL_PMC_IOU_SLCR,
+ XILINX_VERSAL_PMC_IOU_SLCR_R_MAX * 4);
+ reg_array =
+ register_init_block32(DEVICE(obj), pmc_iou_slcr_regs_info,
+ ARRAY_SIZE(pmc_iou_slcr_regs_info),
+ s->regs_info, s->regs,
+ &pmc_iou_slcr_ops,
+ XILINX_VERSAL_PMC_IOU_SLCR_ERR_DEBUG,
+ XILINX_VERSAL_PMC_IOU_SLCR_R_MAX * 4);
+ memory_region_add_subregion(&s->iomem,
+ 0x0,
+ ®_array->mem);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq_parity_imr);
+ sysbus_init_irq(sbd, &s->irq_imr);
+}
+
+static const VMStateDescription vmstate_pmc_iou_slcr = {
+ .name = TYPE_XILINX_VERSAL_PMC_IOU_SLCR,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32_ARRAY(regs, XlnxVersalPmcIouSlcr,
+ XILINX_VERSAL_PMC_IOU_SLCR_R_MAX),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static void xlnx_versal_pmc_iou_slcr_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->realize = xlnx_versal_pmc_iou_slcr_realize;
+ dc->vmsd = &vmstate_pmc_iou_slcr;
+ rc->phases.enter = xlnx_versal_pmc_iou_slcr_reset_init;
+ rc->phases.hold = xlnx_versal_pmc_iou_slcr_reset_hold;
+}
+
+static const TypeInfo xlnx_versal_pmc_iou_slcr_info = {
+ .name = TYPE_XILINX_VERSAL_PMC_IOU_SLCR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XlnxVersalPmcIouSlcr),
+ .class_init = xlnx_versal_pmc_iou_slcr_class_init,
+ .instance_init = xlnx_versal_pmc_iou_slcr_init,
+};
+
+static void xlnx_versal_pmc_iou_slcr_register_types(void)
+{
+ type_register_static(&xlnx_versal_pmc_iou_slcr_info);
+}
+
+type_init(xlnx_versal_pmc_iou_slcr_register_types)
.name = "kvaser_pci",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, KvaserPCIState),
/* Load this before sja_state. */
.name = "mioe3680_pci",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, Mioe3680PCIState),
VMSTATE_STRUCT(sja_state[0], Mioe3680PCIState, 0, vmstate_can_sja,
.name = "pcm3680i_pci",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, Pcm3680iPCIState),
VMSTATE_STRUCT(sja_state[0], Pcm3680iPCIState, 0,
.name = "qemu_can_filter",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(can_id, qemu_can_filter),
VMSTATE_UINT32(can_mask, qemu_can_filter),
.name = "can_sja",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.post_load = can_sja_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT8(mode, CanSJA1000State),
.name = "qemu_ctucan_tx_buffer",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8_ARRAY(data, CtuCanCoreMsgBuffer, CTUCAN_CORE_MSG_MAX_LEN),
VMSTATE_END_OF_LIST()
.name = "ctucan",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.post_load = ctucan_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(mode_settings.u32, CtuCanCoreState),
.name = "ctucan_pci",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, CtuCanPCIState),
VMSTATE_STRUCT(ctucan_state[0], CtuCanPCIState, 0, vmstate_ctucan,
softmmu_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c'))
softmmu_ss.add(when: 'CONFIG_IMX_FEC', if_true: files('imx_fec.c'))
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-emac.c'))
+softmmu_ss.add(when: 'CONFIG_MARVELL_88W8618', if_true: files('mv88w8618_eth.c'))
softmmu_ss.add(when: 'CONFIG_CADENCE', if_true: files('cadence_gem.c'))
softmmu_ss.add(when: 'CONFIG_STELLARIS_ENET', if_true: files('stellaris_enet.c'))
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Marvell MV88W8618 / Freecom MusicPal emulation.
+ *
+ * Copyright (c) 2008 Jan Kiszka
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/qdev-properties.h"
+#include "hw/sysbus.h"
+#include "hw/irq.h"
+#include "hw/net/mv88w8618_eth.h"
+#include "migration/vmstate.h"
+#include "sysemu/dma.h"
+#include "net/net.h"
+
+#define MP_ETH_SIZE 0x00001000
+
+/* Ethernet register offsets */
+#define MP_ETH_SMIR 0x010
+#define MP_ETH_PCXR 0x408
+#define MP_ETH_SDCMR 0x448
+#define MP_ETH_ICR 0x450
+#define MP_ETH_IMR 0x458
+#define MP_ETH_FRDP0 0x480
+#define MP_ETH_FRDP1 0x484
+#define MP_ETH_FRDP2 0x488
+#define MP_ETH_FRDP3 0x48C
+#define MP_ETH_CRDP0 0x4A0
+#define MP_ETH_CRDP1 0x4A4
+#define MP_ETH_CRDP2 0x4A8
+#define MP_ETH_CRDP3 0x4AC
+#define MP_ETH_CTDP0 0x4E0
+#define MP_ETH_CTDP1 0x4E4
+
+/* MII PHY access */
+#define MP_ETH_SMIR_DATA 0x0000FFFF
+#define MP_ETH_SMIR_ADDR 0x03FF0000
+#define MP_ETH_SMIR_OPCODE (1 << 26) /* Read value */
+#define MP_ETH_SMIR_RDVALID (1 << 27)
+
+/* PHY registers */
+#define MP_ETH_PHY1_BMSR 0x00210000
+#define MP_ETH_PHY1_PHYSID1 0x00410000
+#define MP_ETH_PHY1_PHYSID2 0x00610000
+
+#define MP_PHY_BMSR_LINK 0x0004
+#define MP_PHY_BMSR_AUTONEG 0x0008
+
+#define MP_PHY_88E3015 0x01410E20
+
+/* TX descriptor status */
+#define MP_ETH_TX_OWN (1U << 31)
+
+/* RX descriptor status */
+#define MP_ETH_RX_OWN (1U << 31)
+
+/* Interrupt cause/mask bits */
+#define MP_ETH_IRQ_RX_BIT 0
+#define MP_ETH_IRQ_RX (1 << MP_ETH_IRQ_RX_BIT)
+#define MP_ETH_IRQ_TXHI_BIT 2
+#define MP_ETH_IRQ_TXLO_BIT 3
+
+/* Port config bits */
+#define MP_ETH_PCXR_2BSM_BIT 28 /* 2-byte incoming suffix */
+
+/* SDMA command bits */
+#define MP_ETH_CMD_TXHI (1 << 23)
+#define MP_ETH_CMD_TXLO (1 << 22)
+
+typedef struct mv88w8618_tx_desc {
+ uint32_t cmdstat;
+ uint16_t res;
+ uint16_t bytes;
+ uint32_t buffer;
+ uint32_t next;
+} mv88w8618_tx_desc;
+
+typedef struct mv88w8618_rx_desc {
+ uint32_t cmdstat;
+ uint16_t bytes;
+ uint16_t buffer_size;
+ uint32_t buffer;
+ uint32_t next;
+} mv88w8618_rx_desc;
+
+OBJECT_DECLARE_SIMPLE_TYPE(mv88w8618_eth_state, MV88W8618_ETH)
+
+struct mv88w8618_eth_state {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ MemoryRegion iomem;
+ qemu_irq irq;
+ MemoryRegion *dma_mr;
+ AddressSpace dma_as;
+ uint32_t smir;
+ uint32_t icr;
+ uint32_t imr;
+ int mmio_index;
+ uint32_t vlan_header;
+ uint32_t tx_queue[2];
+ uint32_t rx_queue[4];
+ uint32_t frx_queue[4];
+ uint32_t cur_rx[4];
+ NICState *nic;
+ NICConf conf;
+};
+
+static void eth_rx_desc_put(AddressSpace *dma_as, uint32_t addr,
+ mv88w8618_rx_desc *desc)
+{
+ cpu_to_le32s(&desc->cmdstat);
+ cpu_to_le16s(&desc->bytes);
+ cpu_to_le16s(&desc->buffer_size);
+ cpu_to_le32s(&desc->buffer);
+ cpu_to_le32s(&desc->next);
+ dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
+}
+
+static void eth_rx_desc_get(AddressSpace *dma_as, uint32_t addr,
+ mv88w8618_rx_desc *desc)
+{
+ dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
+ le32_to_cpus(&desc->cmdstat);
+ le16_to_cpus(&desc->bytes);
+ le16_to_cpus(&desc->buffer_size);
+ le32_to_cpus(&desc->buffer);
+ le32_to_cpus(&desc->next);
+}
+
+static ssize_t eth_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
+ uint32_t desc_addr;
+ mv88w8618_rx_desc desc;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ desc_addr = s->cur_rx[i];
+ if (!desc_addr) {
+ continue;
+ }
+ do {
+ eth_rx_desc_get(&s->dma_as, desc_addr, &desc);
+ if ((desc.cmdstat & MP_ETH_RX_OWN) && desc.buffer_size >= size) {
+ dma_memory_write(&s->dma_as, desc.buffer + s->vlan_header,
+ buf, size, MEMTXATTRS_UNSPECIFIED);
+ desc.bytes = size + s->vlan_header;
+ desc.cmdstat &= ~MP_ETH_RX_OWN;
+ s->cur_rx[i] = desc.next;
+
+ s->icr |= MP_ETH_IRQ_RX;
+ if (s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ eth_rx_desc_put(&s->dma_as, desc_addr, &desc);
+ return size;
+ }
+ desc_addr = desc.next;
+ } while (desc_addr != s->rx_queue[i]);
+ }
+ return size;
+}
+
+static void eth_tx_desc_put(AddressSpace *dma_as, uint32_t addr,
+ mv88w8618_tx_desc *desc)
+{
+ cpu_to_le32s(&desc->cmdstat);
+ cpu_to_le16s(&desc->res);
+ cpu_to_le16s(&desc->bytes);
+ cpu_to_le32s(&desc->buffer);
+ cpu_to_le32s(&desc->next);
+ dma_memory_write(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
+}
+
+static void eth_tx_desc_get(AddressSpace *dma_as, uint32_t addr,
+ mv88w8618_tx_desc *desc)
+{
+ dma_memory_read(dma_as, addr, desc, sizeof(*desc), MEMTXATTRS_UNSPECIFIED);
+ le32_to_cpus(&desc->cmdstat);
+ le16_to_cpus(&desc->res);
+ le16_to_cpus(&desc->bytes);
+ le32_to_cpus(&desc->buffer);
+ le32_to_cpus(&desc->next);
+}
+
+static void eth_send(mv88w8618_eth_state *s, int queue_index)
+{
+ uint32_t desc_addr = s->tx_queue[queue_index];
+ mv88w8618_tx_desc desc;
+ uint32_t next_desc;
+ uint8_t buf[2048];
+ int len;
+
+ do {
+ eth_tx_desc_get(&s->dma_as, desc_addr, &desc);
+ next_desc = desc.next;
+ if (desc.cmdstat & MP_ETH_TX_OWN) {
+ len = desc.bytes;
+ if (len < 2048) {
+ dma_memory_read(&s->dma_as, desc.buffer, buf, len,
+ MEMTXATTRS_UNSPECIFIED);
+ qemu_send_packet(qemu_get_queue(s->nic), buf, len);
+ }
+ desc.cmdstat &= ~MP_ETH_TX_OWN;
+ s->icr |= 1 << (MP_ETH_IRQ_TXLO_BIT - queue_index);
+ eth_tx_desc_put(&s->dma_as, desc_addr, &desc);
+ }
+ desc_addr = next_desc;
+ } while (desc_addr != s->tx_queue[queue_index]);
+}
+
+static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset,
+ unsigned size)
+{
+ mv88w8618_eth_state *s = opaque;
+
+ switch (offset) {
+ case MP_ETH_SMIR:
+ if (s->smir & MP_ETH_SMIR_OPCODE) {
+ switch (s->smir & MP_ETH_SMIR_ADDR) {
+ case MP_ETH_PHY1_BMSR:
+ return MP_PHY_BMSR_LINK | MP_PHY_BMSR_AUTONEG |
+ MP_ETH_SMIR_RDVALID;
+ case MP_ETH_PHY1_PHYSID1:
+ return (MP_PHY_88E3015 >> 16) | MP_ETH_SMIR_RDVALID;
+ case MP_ETH_PHY1_PHYSID2:
+ return (MP_PHY_88E3015 & 0xFFFF) | MP_ETH_SMIR_RDVALID;
+ default:
+ return MP_ETH_SMIR_RDVALID;
+ }
+ }
+ return 0;
+
+ case MP_ETH_ICR:
+ return s->icr;
+
+ case MP_ETH_IMR:
+ return s->imr;
+
+ case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
+ return s->frx_queue[(offset - MP_ETH_FRDP0) / 4];
+
+ case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
+ return s->rx_queue[(offset - MP_ETH_CRDP0) / 4];
+
+ case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
+ return s->tx_queue[(offset - MP_ETH_CTDP0) / 4];
+
+ default:
+ return 0;
+ }
+}
+
+static void mv88w8618_eth_write(void *opaque, hwaddr offset,
+ uint64_t value, unsigned size)
+{
+ mv88w8618_eth_state *s = opaque;
+
+ switch (offset) {
+ case MP_ETH_SMIR:
+ s->smir = value;
+ break;
+
+ case MP_ETH_PCXR:
+ s->vlan_header = ((value >> MP_ETH_PCXR_2BSM_BIT) & 1) * 2;
+ break;
+
+ case MP_ETH_SDCMR:
+ if (value & MP_ETH_CMD_TXHI) {
+ eth_send(s, 1);
+ }
+ if (value & MP_ETH_CMD_TXLO) {
+ eth_send(s, 0);
+ }
+ if (value & (MP_ETH_CMD_TXHI | MP_ETH_CMD_TXLO) && s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+
+ case MP_ETH_ICR:
+ s->icr &= value;
+ break;
+
+ case MP_ETH_IMR:
+ s->imr = value;
+ if (s->icr & s->imr) {
+ qemu_irq_raise(s->irq);
+ }
+ break;
+
+ case MP_ETH_FRDP0 ... MP_ETH_FRDP3:
+ s->frx_queue[(offset - MP_ETH_FRDP0) / 4] = value;
+ break;
+
+ case MP_ETH_CRDP0 ... MP_ETH_CRDP3:
+ s->rx_queue[(offset - MP_ETH_CRDP0) / 4] =
+ s->cur_rx[(offset - MP_ETH_CRDP0) / 4] = value;
+ break;
+
+ case MP_ETH_CTDP0 ... MP_ETH_CTDP1:
+ s->tx_queue[(offset - MP_ETH_CTDP0) / 4] = value;
+ break;
+ }
+}
+
+static const MemoryRegionOps mv88w8618_eth_ops = {
+ .read = mv88w8618_eth_read,
+ .write = mv88w8618_eth_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void eth_cleanup(NetClientState *nc)
+{
+ mv88w8618_eth_state *s = qemu_get_nic_opaque(nc);
+
+ s->nic = NULL;
+}
+
+static NetClientInfo net_mv88w8618_info = {
+ .type = NET_CLIENT_DRIVER_NIC,
+ .size = sizeof(NICState),
+ .receive = eth_receive,
+ .cleanup = eth_cleanup,
+};
+
+static void mv88w8618_eth_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ DeviceState *dev = DEVICE(sbd);
+ mv88w8618_eth_state *s = MV88W8618_ETH(dev);
+
+ sysbus_init_irq(sbd, &s->irq);
+ memory_region_init_io(&s->iomem, obj, &mv88w8618_eth_ops, s,
+ "mv88w8618-eth", MP_ETH_SIZE);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void mv88w8618_eth_realize(DeviceState *dev, Error **errp)
+{
+ mv88w8618_eth_state *s = MV88W8618_ETH(dev);
+
+ if (!s->dma_mr) {
+ error_setg(errp, TYPE_MV88W8618_ETH " 'dma-memory' link not set");
+ return;
+ }
+
+ address_space_init(&s->dma_as, s->dma_mr, "emac-dma");
+ s->nic = qemu_new_nic(&net_mv88w8618_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->id, s);
+}
+
+static const VMStateDescription mv88w8618_eth_vmsd = {
+ .name = "mv88w8618_eth",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(smir, mv88w8618_eth_state),
+ VMSTATE_UINT32(icr, mv88w8618_eth_state),
+ VMSTATE_UINT32(imr, mv88w8618_eth_state),
+ VMSTATE_UINT32(vlan_header, mv88w8618_eth_state),
+ VMSTATE_UINT32_ARRAY(tx_queue, mv88w8618_eth_state, 2),
+ VMSTATE_UINT32_ARRAY(rx_queue, mv88w8618_eth_state, 4),
+ VMSTATE_UINT32_ARRAY(frx_queue, mv88w8618_eth_state, 4),
+ VMSTATE_UINT32_ARRAY(cur_rx, mv88w8618_eth_state, 4),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property mv88w8618_eth_properties[] = {
+ DEFINE_NIC_PROPERTIES(mv88w8618_eth_state, conf),
+ DEFINE_PROP_LINK("dma-memory", mv88w8618_eth_state, dma_mr,
+ TYPE_MEMORY_REGION, MemoryRegion *),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mv88w8618_eth_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->vmsd = &mv88w8618_eth_vmsd;
+ device_class_set_props(dc, mv88w8618_eth_properties);
+ dc->realize = mv88w8618_eth_realize;
+}
+
+static const TypeInfo mv88w8618_eth_info = {
+ .name = TYPE_MV88W8618_ETH,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(mv88w8618_eth_state),
+ .instance_init = mv88w8618_eth_init,
+ .class_init = mv88w8618_eth_class_init,
+};
+
+static void musicpal_register_types(void)
+{
+ type_register_static(&mv88w8618_eth_info);
+}
+
+type_init(musicpal_register_types)
+
if (sg->flags & NVME_SG_DMA) {
const MemTxAttrs attrs = MEMTXATTRS_UNSPECIFIED;
- uint64_t residual;
+ dma_addr_t residual;
if (dir == NVME_TX_DIRECTION_TO_DEVICE) {
- residual = dma_buf_write(ptr, len, &sg->qsg, attrs);
+ dma_buf_write(ptr, len, &residual, &sg->qsg, attrs);
} else {
- residual = dma_buf_read(ptr, len, &sg->qsg, attrs);
+ dma_buf_read(ptr, len, &residual, &sg->qsg, attrs);
}
if (unlikely(residual)) {
-# QOM interfaces must be available anytime QOM is used.
-qom_ss.add(files('fw_cfg-interface.c'))
+if have_system or have_tools
+ # QOM interfaces must be available anytime QOM is used.
+ qom_ss.add(files('fw_cfg-interface.c'))
+endif
softmmu_ss.add(files('fw_cfg.c'))
softmmu_ss.add(when: 'CONFIG_CHRP_NVRAM', if_true: files('chrp_nvram.c'))
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "qom/object.h"
+#include "sysemu/sysemu.h"
#define phb3_error(phb, fmt, ...) \
qemu_log_mask(LOG_GUEST_ERROR, "phb3[%d:%d]: " fmt "\n", \
sh = tbl_shift * lev + tce_shift;
/* TODO: Multi-level untested */
- while ((lev--) >= 0) {
+ do {
+ lev--;
+
/* Grab the TCE address */
taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3);
if (dma_memory_read(&address_space_memory, taddr, &tce,
}
sh -= tbl_shift;
base = tce & ~0xfffull;
- }
+ } while (lev >= 0);
/* We exit the loop with TCE being the final TCE */
- tce_mask = ~((1ull << tce_shift) - 1);
- tlb->iova = addr & tce_mask;
- tlb->translated_addr = tce & tce_mask;
- tlb->addr_mask = ~tce_mask;
- tlb->perm = tce & 3;
if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) {
phb3_error(phb, "TCE access fault at 0x%"PRIx64, taddr);
phb3_error(phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr,
is_write ? 'W' : 'R', tve);
phb3_error(phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d",
tta, lev, tts, tps);
+ return;
}
+ tce_mask = ~((1ull << tce_shift) - 1);
+ tlb->iova = addr & tce_mask;
+ tlb->translated_addr = tce & tce_mask;
+ tlb->addr_mask = ~tce_mask;
+ tlb->perm = tce & 3;
}
}
/* Power Bus Common Queue */
object_initialize_child(obj, "pbcq", &phb->pbcq, TYPE_PNV_PBCQ);
- /* Root Port */
- object_initialize_child(obj, "root", &phb->root, TYPE_PNV_PHB3_ROOT_PORT);
- qdev_prop_set_int32(DEVICE(&phb->root), "addr", PCI_DEVFN(0, 0));
- qdev_prop_set_bit(DEVICE(&phb->root), "multifunction", false);
}
static void pnv_phb3_realize(DeviceState *dev, Error **errp)
PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
int i;
+ /* User created devices */
+ if (!phb->chip) {
+ Error *local_err = NULL;
+ BusState *s;
+
+ phb->chip = pnv_get_chip(pnv, phb->chip_id);
+ if (!phb->chip) {
+ error_setg(errp, "invalid chip id: %d", phb->chip_id);
+ return;
+ }
+
+ /*
+ * Reparent user created devices to the chip to build
+ * correctly the device tree.
+ */
+ pnv_chip_parent_fixup(phb->chip, OBJECT(phb), phb->phb_id);
+
+ s = qdev_get_parent_bus(DEVICE(phb->chip));
+ if (!qdev_set_parent_bus(DEVICE(phb), s, &local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+
if (phb->phb_id >= PNV_CHIP_GET_CLASS(phb->chip)->num_phbs) {
error_setg(errp, "invalid PHB index: %d", phb->phb_id);
return;
pci_setup_iommu(pci->bus, pnv_phb3_dma_iommu, phb);
- /* Add a single Root port */
- qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id);
- qdev_prop_set_uint16(DEVICE(&phb->root), "slot", phb->phb_id);
- qdev_realize(DEVICE(&phb->root), BUS(pci->bus), &error_fatal);
+ if (defaults_enabled()) {
+ pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb),
+ TYPE_PNV_PHB3_ROOT_PORT);
+ }
}
void pnv_phb3_update_regions(PnvPHB3 *phb)
dc->realize = pnv_phb3_realize;
device_class_set_props(dc, pnv_phb3_properties);
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
- dc->user_creatable = false;
+ dc->user_creatable = true;
}
static const TypeInfo pnv_phb3_type_info = {
static void pnv_phb3_root_port_realize(DeviceState *dev, Error **errp)
{
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
+ PCIDevice *pci = PCI_DEVICE(dev);
+ PCIBus *bus = pci_get_bus(pci);
+ PnvPHB3 *phb = NULL;
Error *local_err = NULL;
+ phb = (PnvPHB3 *) object_dynamic_cast(OBJECT(bus->qbus.parent),
+ TYPE_PNV_PHB3);
+
+ if (!phb) {
+ error_setg(errp,
+"pnv_phb3_root_port devices must be connected to pnv-phb3 buses");
+ return;
+ }
+
+ /* Set unique chassis/slot values for the root port */
+ qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id);
+ qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id);
+
rpc->parent_realize(dev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
device_class_set_parent_realize(dc, pnv_phb3_root_port_realize,
&rpc->parent_realize);
- dc->user_creatable = false;
+ dc->user_creatable = true;
k->vendor_id = PCI_VENDOR_ID_IBM;
k->device_id = 0x03dc;
qemu_log_mask(LOG_GUEST_ERROR, "phb4[%d:%d]: " fmt "\n", \
(phb)->chip_id, (phb)->phb_id, ## __VA_ARGS__)
+#define phb_pec_error(pec, fmt, ...) \
+ qemu_log_mask(LOG_GUEST_ERROR, "phb4_pec[%d:%d]: " fmt "\n", \
+ (pec)->chip_id, (pec)->index, ## __VA_ARGS__)
+
/*
* QEMU version of the GETFIELD/SETFIELD macros
*
}
pdev = pci_find_device(pci->bus, 0, 0);
- assert(pdev);
+ if (!pdev) {
+ phb_error(phb, "rc_config_write device not found\n");
+ return;
+ }
pci_host_config_write_common(pdev, off, PHB_RC_CONFIG_SIZE,
bswap32(val), 4);
}
pdev = pci_find_device(pci->bus, 0, 0);
- assert(pdev);
+ if (!pdev) {
+ phb_error(phb, "rc_config_read device not found\n");
+ return ~0ull;
+ }
val = pci_host_config_read_common(pdev, off, PHB_RC_CONFIG_SIZE, 4);
return bswap32(val);
/* TODO: Figure out how to implemet/decode AOMASK */
/* Check if it matches an enabled MMIO region in the PEC stack */
- if (memory_region_is_mapped(&phb->stack->mmbar0) &&
- base >= phb->stack->mmio0_base &&
- (base + size) <= (phb->stack->mmio0_base + phb->stack->mmio0_size)) {
- parent = &phb->stack->mmbar0;
- base -= phb->stack->mmio0_base;
- } else if (memory_region_is_mapped(&phb->stack->mmbar1) &&
- base >= phb->stack->mmio1_base &&
- (base + size) <= (phb->stack->mmio1_base + phb->stack->mmio1_size)) {
- parent = &phb->stack->mmbar1;
- base -= phb->stack->mmio1_base;
+ if (memory_region_is_mapped(&phb->mmbar0) &&
+ base >= phb->mmio0_base &&
+ (base + size) <= (phb->mmio0_base + phb->mmio0_size)) {
+ parent = &phb->mmbar0;
+ base -= phb->mmio0_base;
+ } else if (memory_region_is_mapped(&phb->mmbar1) &&
+ base >= phb->mmio1_base &&
+ (base + size) <= (phb->mmio1_base + phb->mmio1_size)) {
+ parent = &phb->mmbar1;
+ base -= phb->mmio1_base;
} else {
phb_error(phb, "PHB MBAR %d out of parent bounds", index);
return;
switch (off) {
case PHB_VERSION:
- return phb->version;
+ return PNV_PHB4_PEC_GET_CLASS(phb->pec)->version;
/* Read-only */
case PHB_PHB4_GEN_CAP:
.endianness = DEVICE_BIG_ENDIAN,
};
+static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PnvPHB4 *phb = PNV_PHB4(opaque);
+ uint32_t reg = addr >> 3;
+
+ /* TODO: add list of allowed registers and error out if not */
+ return phb->nest_regs[reg];
+}
+
+/*
+ * Return the 'stack_no' of a PHB4. 'stack_no' is the order
+ * the PHB4 occupies in the PEC. This is the reverse of what
+ * pnv_phb4_pec_get_phb_id() does.
+ *
+ * E.g. a phb with phb_id = 4 and pec->index = 1 (PEC1) will
+ * be the second phb (stack_no = 1) of the PEC.
+ */
+static int pnv_phb4_get_phb_stack_no(PnvPHB4 *phb)
+{
+ PnvPhb4PecState *pec = phb->pec;
+ PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
+ int index = pec->index;
+ int stack_no = phb->phb_id;
+
+ while (index--) {
+ stack_no -= pecc->num_phbs[index];
+ }
+
+ return stack_no;
+}
+
+static void pnv_phb4_update_regions(PnvPHB4 *phb)
+{
+ /* Unmap first always */
+ if (memory_region_is_mapped(&phb->mr_regs)) {
+ memory_region_del_subregion(&phb->phbbar, &phb->mr_regs);
+ }
+ if (memory_region_is_mapped(&phb->xsrc.esb_mmio)) {
+ memory_region_del_subregion(&phb->intbar, &phb->xsrc.esb_mmio);
+ }
+
+ /* Map registers if enabled */
+ if (memory_region_is_mapped(&phb->phbbar)) {
+ memory_region_add_subregion(&phb->phbbar, 0, &phb->mr_regs);
+ }
+
+ /* Map ESB if enabled */
+ if (memory_region_is_mapped(&phb->intbar)) {
+ memory_region_add_subregion(&phb->intbar, 0, &phb->xsrc.esb_mmio);
+ }
+
+ /* Check/update m32 */
+ pnv_phb4_check_all_mbt(phb);
+}
+
+static void pnv_pec_phb_update_map(PnvPHB4 *phb)
+{
+ PnvPhb4PecState *pec = phb->pec;
+ MemoryRegion *sysmem = get_system_memory();
+ uint64_t bar_en = phb->nest_regs[PEC_NEST_STK_BAR_EN];
+ int stack_no = pnv_phb4_get_phb_stack_no(phb);
+ uint64_t bar, mask, size;
+ char name[64];
+
+ /*
+ * NOTE: This will really not work well if those are remapped
+ * after the PHB has created its sub regions. We could do better
+ * if we had a way to resize regions but we don't really care
+ * that much in practice as the stuff below really only happens
+ * once early during boot
+ */
+
+ /* Handle unmaps */
+ if (memory_region_is_mapped(&phb->mmbar0) &&
+ !(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
+ memory_region_del_subregion(sysmem, &phb->mmbar0);
+ }
+ if (memory_region_is_mapped(&phb->mmbar1) &&
+ !(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
+ memory_region_del_subregion(sysmem, &phb->mmbar1);
+ }
+ if (memory_region_is_mapped(&phb->phbbar) &&
+ !(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
+ memory_region_del_subregion(sysmem, &phb->phbbar);
+ }
+ if (memory_region_is_mapped(&phb->intbar) &&
+ !(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
+ memory_region_del_subregion(sysmem, &phb->intbar);
+ }
+
+ /* Update PHB */
+ pnv_phb4_update_regions(phb);
+
+ /* Handle maps */
+ if (!memory_region_is_mapped(&phb->mmbar0) &&
+ (bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
+ bar = phb->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8;
+ mask = phb->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK];
+ size = ((~mask) >> 8) + 1;
+ snprintf(name, sizeof(name), "pec-%d.%d-phb-%d-mmio0",
+ pec->chip_id, pec->index, stack_no);
+ memory_region_init(&phb->mmbar0, OBJECT(phb), name, size);
+ memory_region_add_subregion(sysmem, bar, &phb->mmbar0);
+ phb->mmio0_base = bar;
+ phb->mmio0_size = size;
+ }
+ if (!memory_region_is_mapped(&phb->mmbar1) &&
+ (bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
+ bar = phb->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8;
+ mask = phb->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK];
+ size = ((~mask) >> 8) + 1;
+ snprintf(name, sizeof(name), "pec-%d.%d-phb-%d-mmio1",
+ pec->chip_id, pec->index, stack_no);
+ memory_region_init(&phb->mmbar1, OBJECT(phb), name, size);
+ memory_region_add_subregion(sysmem, bar, &phb->mmbar1);
+ phb->mmio1_base = bar;
+ phb->mmio1_size = size;
+ }
+ if (!memory_region_is_mapped(&phb->phbbar) &&
+ (bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
+ bar = phb->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8;
+ size = PNV_PHB4_NUM_REGS << 3;
+ snprintf(name, sizeof(name), "pec-%d.%d-phb-%d",
+ pec->chip_id, pec->index, stack_no);
+ memory_region_init(&phb->phbbar, OBJECT(phb), name, size);
+ memory_region_add_subregion(sysmem, bar, &phb->phbbar);
+ }
+ if (!memory_region_is_mapped(&phb->intbar) &&
+ (bar_en & PEC_NEST_STK_BAR_EN_INT)) {
+ bar = phb->nest_regs[PEC_NEST_STK_INT_BAR] >> 8;
+ size = PNV_PHB4_MAX_INTs << 16;
+ snprintf(name, sizeof(name), "pec-%d.%d-phb-%d-int",
+ phb->pec->chip_id, phb->pec->index, stack_no);
+ memory_region_init(&phb->intbar, OBJECT(phb), name, size);
+ memory_region_add_subregion(sysmem, bar, &phb->intbar);
+ }
+
+ /* Update PHB */
+ pnv_phb4_update_regions(phb);
+}
+
+static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvPHB4 *phb = PNV_PHB4(opaque);
+ PnvPhb4PecState *pec = phb->pec;
+ uint32_t reg = addr >> 3;
+
+ switch (reg) {
+ case PEC_NEST_STK_PCI_NEST_FIR:
+ phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val;
+ break;
+ case PEC_NEST_STK_PCI_NEST_FIR_CLR:
+ phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val;
+ break;
+ case PEC_NEST_STK_PCI_NEST_FIR_SET:
+ phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val;
+ break;
+ case PEC_NEST_STK_PCI_NEST_FIR_MSK:
+ phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val;
+ break;
+ case PEC_NEST_STK_PCI_NEST_FIR_MSKC:
+ phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val;
+ break;
+ case PEC_NEST_STK_PCI_NEST_FIR_MSKS:
+ phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val;
+ break;
+ case PEC_NEST_STK_PCI_NEST_FIR_ACT0:
+ case PEC_NEST_STK_PCI_NEST_FIR_ACT1:
+ phb->nest_regs[reg] = val;
+ break;
+ case PEC_NEST_STK_PCI_NEST_FIR_WOF:
+ phb->nest_regs[reg] = 0;
+ break;
+ case PEC_NEST_STK_ERR_REPORT_0:
+ case PEC_NEST_STK_ERR_REPORT_1:
+ case PEC_NEST_STK_PBCQ_GNRL_STATUS:
+ /* Flag error ? */
+ break;
+ case PEC_NEST_STK_PBCQ_MODE:
+ phb->nest_regs[reg] = val & 0xff00000000000000ull;
+ break;
+ case PEC_NEST_STK_MMIO_BAR0:
+ case PEC_NEST_STK_MMIO_BAR0_MASK:
+ case PEC_NEST_STK_MMIO_BAR1:
+ case PEC_NEST_STK_MMIO_BAR1_MASK:
+ if (phb->nest_regs[PEC_NEST_STK_BAR_EN] &
+ (PEC_NEST_STK_BAR_EN_MMIO0 |
+ PEC_NEST_STK_BAR_EN_MMIO1)) {
+ phb_pec_error(pec, "Changing enabled BAR unsupported\n");
+ }
+ phb->nest_regs[reg] = val & 0xffffffffff000000ull;
+ break;
+ case PEC_NEST_STK_PHB_REGS_BAR:
+ if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
+ phb_pec_error(pec, "Changing enabled BAR unsupported\n");
+ }
+ phb->nest_regs[reg] = val & 0xffffffffffc00000ull;
+ break;
+ case PEC_NEST_STK_INT_BAR:
+ if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
+ phb_pec_error(pec, "Changing enabled BAR unsupported\n");
+ }
+ phb->nest_regs[reg] = val & 0xfffffff000000000ull;
+ break;
+ case PEC_NEST_STK_BAR_EN:
+ phb->nest_regs[reg] = val & 0xf000000000000000ull;
+ pnv_pec_phb_update_map(phb);
+ break;
+ case PEC_NEST_STK_DATA_FRZ_TYPE:
+ case PEC_NEST_STK_PBCQ_TUN_BAR:
+ /* Not used for now */
+ phb->nest_regs[reg] = val;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx
+ "=%"PRIx64"\n", addr, val);
+ }
+}
+
+static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = {
+ .read = pnv_pec_stk_nest_xscom_read,
+ .write = pnv_pec_stk_nest_xscom_write,
+ .valid.min_access_size = 8,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
+static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr,
+ unsigned size)
+{
+ PnvPHB4 *phb = PNV_PHB4(opaque);
+ uint32_t reg = addr >> 3;
+
+ /* TODO: add list of allowed registers and error out if not */
+ return phb->pci_regs[reg];
+}
+
+static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ PnvPHB4 *phb = PNV_PHB4(opaque);
+ uint32_t reg = addr >> 3;
+
+ switch (reg) {
+ case PEC_PCI_STK_PCI_FIR:
+ phb->pci_regs[reg] = val;
+ break;
+ case PEC_PCI_STK_PCI_FIR_CLR:
+ phb->pci_regs[PEC_PCI_STK_PCI_FIR] &= val;
+ break;
+ case PEC_PCI_STK_PCI_FIR_SET:
+ phb->pci_regs[PEC_PCI_STK_PCI_FIR] |= val;
+ break;
+ case PEC_PCI_STK_PCI_FIR_MSK:
+ phb->pci_regs[reg] = val;
+ break;
+ case PEC_PCI_STK_PCI_FIR_MSKC:
+ phb->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val;
+ break;
+ case PEC_PCI_STK_PCI_FIR_MSKS:
+ phb->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val;
+ break;
+ case PEC_PCI_STK_PCI_FIR_ACT0:
+ case PEC_PCI_STK_PCI_FIR_ACT1:
+ phb->pci_regs[reg] = val;
+ break;
+ case PEC_PCI_STK_PCI_FIR_WOF:
+ phb->pci_regs[reg] = 0;
+ break;
+ case PEC_PCI_STK_ETU_RESET:
+ phb->pci_regs[reg] = val & 0x8000000000000000ull;
+ /* TODO: Implement reset */
+ break;
+ case PEC_PCI_STK_PBAIB_ERR_REPORT:
+ break;
+ case PEC_PCI_STK_PBAIB_TX_CMD_CRED:
+ case PEC_PCI_STK_PBAIB_TX_DAT_CRED:
+ phb->pci_regs[reg] = val;
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx
+ "=%"PRIx64"\n", addr, val);
+ }
+}
+
+static const MemoryRegionOps pnv_pec_stk_pci_xscom_ops = {
+ .read = pnv_pec_stk_pci_xscom_read,
+ .write = pnv_pec_stk_pci_xscom_write,
+ .valid.min_access_size = 8,
+ .valid.max_access_size = 8,
+ .impl.min_access_size = 8,
+ .impl.max_access_size = 8,
+ .endianness = DEVICE_BIG_ENDIAN,
+};
+
static int pnv_phb4_map_irq(PCIDevice *pci_dev, int irq_num)
{
/* Check that out properly ... */
/* TODO: Limit to support IO page sizes */
/* TODO: Multi-level untested */
- while ((lev--) >= 0) {
+ do {
+ lev--;
+
/* Grab the TCE address */
taddr = base | (((addr >> sh) & ((1ul << tbl_shift) - 1)) << 3);
if (dma_memory_read(&address_space_memory, taddr, &tce,
}
sh -= tbl_shift;
base = tce & ~0xfffull;
- }
+ } while (lev >= 0);
/* We exit the loop with TCE being the final TCE */
- tce_mask = ~((1ull << tce_shift) - 1);
- tlb->iova = addr & tce_mask;
- tlb->translated_addr = tce & tce_mask;
- tlb->addr_mask = ~tce_mask;
- tlb->perm = tce & 3;
if ((is_write & !(tce & 2)) || ((!is_write) && !(tce & 1))) {
phb_error(ds->phb, "TCE access fault at 0x%"PRIx64, taddr);
phb_error(ds->phb, " xlate %"PRIx64":%c TVE=%"PRIx64, addr,
is_write ? 'W' : 'R', tve);
phb_error(ds->phb, " tta=%"PRIx64" lev=%d tts=%d tps=%d",
tta, lev, tts, tps);
+ return;
}
+ tce_mask = ~((1ull << tce_shift) - 1);
+ tlb->iova = addr & tce_mask;
+ tlb->translated_addr = tce & tce_mask;
+ tlb->addr_mask = ~tce_mask;
+ tlb->perm = tce & 3;
}
}
.class_init = pnv_phb4_iommu_memory_region_class_init,
};
+/*
+ * Return the index/phb-id of a PHB4 that belongs to a
+ * pec->stacks[stack_index] stack.
+ */
+int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index)
+{
+ PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
+ int index = pec->index;
+ int offset = 0;
+
+ while (index--) {
+ offset += pecc->num_phbs[index];
+ }
+
+ return offset + stack_index;
+}
+
/*
* MSI/MSIX memory region implementation.
* The handler handles both MSI and MSIX.
return &ds->dma_as;
}
+static void pnv_phb4_xscom_realize(PnvPHB4 *phb)
+{
+ PnvPhb4PecState *pec = phb->pec;
+ PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
+ int stack_no = pnv_phb4_get_phb_stack_no(phb);
+ uint32_t pec_nest_base;
+ uint32_t pec_pci_base;
+ char name[64];
+
+ assert(pec);
+
+ /* Initialize the XSCOM regions for the stack registers */
+ snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-phb-%d",
+ pec->chip_id, pec->index, stack_no);
+ pnv_xscom_region_init(&phb->nest_regs_mr, OBJECT(phb),
+ &pnv_pec_stk_nest_xscom_ops, phb, name,
+ PHB4_PEC_NEST_STK_REGS_COUNT);
+
+ snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-phb-%d",
+ pec->chip_id, pec->index, stack_no);
+ pnv_xscom_region_init(&phb->pci_regs_mr, OBJECT(phb),
+ &pnv_pec_stk_pci_xscom_ops, phb, name,
+ PHB4_PEC_PCI_STK_REGS_COUNT);
+
+ /* PHB pass-through */
+ snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-phb-%d",
+ pec->chip_id, pec->index, stack_no);
+ pnv_xscom_region_init(&phb->phb_regs_mr, OBJECT(phb),
+ &pnv_phb4_xscom_ops, phb, name, 0x40);
+
+ pec_nest_base = pecc->xscom_nest_base(pec);
+ pec_pci_base = pecc->xscom_pci_base(pec);
+
+ /* Populate the XSCOM address space. */
+ pnv_xscom_add_subregion(pec->chip,
+ pec_nest_base + 0x40 * (stack_no + 1),
+ &phb->nest_regs_mr);
+ pnv_xscom_add_subregion(pec->chip,
+ pec_pci_base + 0x40 * (stack_no + 1),
+ &phb->pci_regs_mr);
+ pnv_xscom_add_subregion(pec->chip,
+ pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
+ 0x40 * stack_no,
+ &phb->phb_regs_mr);
+}
+
static void pnv_phb4_instance_init(Object *obj)
{
PnvPHB4 *phb = PNV_PHB4(obj);
/* XIVE interrupt source object */
object_initialize_child(obj, "source", &phb->xsrc, TYPE_XIVE_SOURCE);
+}
+
+static PnvPhb4PecState *pnv_phb4_get_pec(PnvChip *chip, PnvPHB4 *phb,
+ Error **errp)
+{
+ Pnv9Chip *chip9 = PNV9_CHIP(chip);
+ int chip_id = phb->chip_id;
+ int index = phb->phb_id;
+ int i, j;
+
+ for (i = 0; i < chip->num_pecs; i++) {
+ /*
+ * For each PEC, check the amount of phbs it supports
+ * and see if the given phb4 index matches an index.
+ */
+ PnvPhb4PecState *pec = &chip9->pecs[i];
- /* Root Port */
- object_initialize_child(obj, "root", &phb->root, TYPE_PNV_PHB4_ROOT_PORT);
+ for (j = 0; j < pec->num_phbs; j++) {
+ if (index == pnv_phb4_pec_get_phb_id(pec, j)) {
+ return pec;
+ }
+ }
+ }
- qdev_prop_set_int32(DEVICE(&phb->root), "addr", PCI_DEVFN(0, 0));
- qdev_prop_set_bit(DEVICE(&phb->root), "multifunction", false);
+ error_setg(errp,
+ "pnv-phb4 chip-id %d index %d didn't match any existing PEC",
+ chip_id, index);
+
+ return NULL;
}
static void pnv_phb4_realize(DeviceState *dev, Error **errp)
PnvPHB4 *phb = PNV_PHB4(dev);
PCIHostState *pci = PCI_HOST_BRIDGE(dev);
XiveSource *xsrc = &phb->xsrc;
+ Error *local_err = NULL;
int nr_irqs;
char name[32];
- assert(phb->stack);
+ /* User created PHB */
+ if (!phb->pec) {
+ PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
+ PnvChip *chip = pnv_get_chip(pnv, phb->chip_id);
+ BusState *s;
+
+ if (!chip) {
+ error_setg(errp, "invalid chip id: %d", phb->chip_id);
+ return;
+ }
+
+ phb->pec = pnv_phb4_get_pec(chip, phb, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+
+ /*
+ * Reparent user created devices to the chip to build
+ * correctly the device tree.
+ */
+ pnv_chip_parent_fixup(chip, OBJECT(phb), phb->phb_id);
+
+ s = qdev_get_parent_bus(DEVICE(chip));
+ if (!qdev_set_parent_bus(DEVICE(phb), s, &local_err)) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
/* Set the "big_phb" flag */
phb->big_phb = phb->phb_id == 0 || phb->phb_id == 3;
pci_setup_iommu(pci->bus, pnv_phb4_dma_iommu, phb);
pci->bus->flags |= PCI_BUS_EXTENDED_CONFIG_SPACE;
- /* Add a single Root port */
- qdev_prop_set_uint8(DEVICE(&phb->root), "chassis", phb->chip_id);
- qdev_prop_set_uint16(DEVICE(&phb->root), "slot", phb->phb_id);
- qdev_realize(DEVICE(&phb->root), BUS(pci->bus), &error_fatal);
-
/* Setup XIVE Source */
if (phb->big_phb) {
nr_irqs = PNV_PHB4_MAX_INTs;
pnv_phb4_update_xsrc(phb);
phb->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc, xsrc->nr_irqs);
+
+ pnv_phb4_xscom_realize(phb);
}
static const char *pnv_phb4_root_bus_path(PCIHostState *host_bridge,
static Property pnv_phb4_properties[] = {
DEFINE_PROP_UINT32("index", PnvPHB4, phb_id, 0),
DEFINE_PROP_UINT32("chip-id", PnvPHB4, chip_id, 0),
- DEFINE_PROP_UINT64("version", PnvPHB4, version, 0),
- DEFINE_PROP_LINK("stack", PnvPHB4, stack, TYPE_PNV_PHB4_PEC_STACK,
- PnvPhb4PecStack *),
+ DEFINE_PROP_LINK("pec", PnvPHB4, pec, TYPE_PNV_PHB4_PEC,
+ PnvPhb4PecState *),
DEFINE_PROP_END_OF_LIST(),
};
dc->realize = pnv_phb4_realize;
device_class_set_props(dc, pnv_phb4_properties);
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
- dc->user_creatable = false;
+ dc->user_creatable = true;
xfc->notify = pnv_phb4_xive_notify;
}
static void pnv_phb4_root_port_realize(DeviceState *dev, Error **errp)
{
PCIERootPortClass *rpc = PCIE_ROOT_PORT_GET_CLASS(dev);
+ PCIDevice *pci = PCI_DEVICE(dev);
+ PCIBus *bus = pci_get_bus(pci);
+ PnvPHB4 *phb = NULL;
Error *local_err = NULL;
+ phb = (PnvPHB4 *) object_dynamic_cast(OBJECT(bus->qbus.parent),
+ TYPE_PNV_PHB4);
+
+ if (!phb) {
+ error_setg(errp, "%s must be connected to pnv-phb4 buses", dev->id);
+ return;
+ }
+
+ /* Set unique chassis/slot values for the root port */
+ qdev_prop_set_uint8(&pci->qdev, "chassis", phb->chip_id);
+ qdev_prop_set_uint16(&pci->qdev, "slot", phb->phb_id);
+
rpc->parent_realize(dev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
PCIERootPortClass *rpc = PCIE_ROOT_PORT_CLASS(klass);
dc->desc = "IBM PHB4 PCIE Root Port";
- dc->user_creatable = false;
+ dc->user_creatable = true;
device_class_set_parent_realize(dc, pnv_phb4_root_port_realize,
&rpc->parent_realize);
type_init(pnv_phb4_register_types);
-void pnv_phb4_update_regions(PnvPhb4PecStack *stack)
-{
- PnvPHB4 *phb = &stack->phb;
-
- /* Unmap first always */
- if (memory_region_is_mapped(&phb->mr_regs)) {
- memory_region_del_subregion(&stack->phbbar, &phb->mr_regs);
- }
- if (memory_region_is_mapped(&phb->xsrc.esb_mmio)) {
- memory_region_del_subregion(&stack->intbar, &phb->xsrc.esb_mmio);
- }
-
- /* Map registers if enabled */
- if (memory_region_is_mapped(&stack->phbbar)) {
- memory_region_add_subregion(&stack->phbbar, 0, &phb->mr_regs);
- }
-
- /* Map ESB if enabled */
- if (memory_region_is_mapped(&stack->intbar)) {
- memory_region_add_subregion(&stack->intbar, 0, &phb->xsrc.esb_mmio);
- }
-
- /* Check/update m32 */
- pnv_phb4_check_all_mbt(phb);
-}
-
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon)
{
uint32_t offset = phb->regs[PHB_INT_NOTIFY_INDEX >> 3];
#include "hw/pci/pci_bus.h"
#include "hw/ppc/pnv.h"
#include "hw/qdev-properties.h"
+#include "sysemu/sysemu.h"
#include <libfdt.h>
.endianness = DEVICE_BIG_ENDIAN,
};
-static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr,
- unsigned size)
+static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec,
+ int stack_no,
+ Error **errp)
{
- PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
- uint32_t reg = addr >> 3;
-
- /* TODO: add list of allowed registers and error out if not */
- return stack->nest_regs[reg];
-}
-
-static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack)
-{
- PnvPhb4PecState *pec = stack->pec;
- MemoryRegion *sysmem = get_system_memory();
- uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN];
- uint64_t bar, mask, size;
- char name[64];
-
- /*
- * NOTE: This will really not work well if those are remapped
- * after the PHB has created its sub regions. We could do better
- * if we had a way to resize regions but we don't really care
- * that much in practice as the stuff below really only happens
- * once early during boot
- */
-
- /* Handle unmaps */
- if (memory_region_is_mapped(&stack->mmbar0) &&
- !(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
- memory_region_del_subregion(sysmem, &stack->mmbar0);
- }
- if (memory_region_is_mapped(&stack->mmbar1) &&
- !(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
- memory_region_del_subregion(sysmem, &stack->mmbar1);
- }
- if (memory_region_is_mapped(&stack->phbbar) &&
- !(bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
- memory_region_del_subregion(sysmem, &stack->phbbar);
- }
- if (memory_region_is_mapped(&stack->intbar) &&
- !(bar_en & PEC_NEST_STK_BAR_EN_INT)) {
- memory_region_del_subregion(sysmem, &stack->intbar);
- }
-
- /* Update PHB */
- pnv_phb4_update_regions(stack);
-
- /* Handle maps */
- if (!memory_region_is_mapped(&stack->mmbar0) &&
- (bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) {
- bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8;
- mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK];
- size = ((~mask) >> 8) + 1;
- snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio0",
- pec->chip_id, pec->index, stack->stack_no);
- memory_region_init(&stack->mmbar0, OBJECT(stack), name, size);
- memory_region_add_subregion(sysmem, bar, &stack->mmbar0);
- stack->mmio0_base = bar;
- stack->mmio0_size = size;
- }
- if (!memory_region_is_mapped(&stack->mmbar1) &&
- (bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) {
- bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8;
- mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK];
- size = ((~mask) >> 8) + 1;
- snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio1",
- pec->chip_id, pec->index, stack->stack_no);
- memory_region_init(&stack->mmbar1, OBJECT(stack), name, size);
- memory_region_add_subregion(sysmem, bar, &stack->mmbar1);
- stack->mmio1_base = bar;
- stack->mmio1_size = size;
- }
- if (!memory_region_is_mapped(&stack->phbbar) &&
- (bar_en & PEC_NEST_STK_BAR_EN_PHB)) {
- bar = stack->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8;
- size = PNV_PHB4_NUM_REGS << 3;
- snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-phb",
- pec->chip_id, pec->index, stack->stack_no);
- memory_region_init(&stack->phbbar, OBJECT(stack), name, size);
- memory_region_add_subregion(sysmem, bar, &stack->phbbar);
- }
- if (!memory_region_is_mapped(&stack->intbar) &&
- (bar_en & PEC_NEST_STK_BAR_EN_INT)) {
- bar = stack->nest_regs[PEC_NEST_STK_INT_BAR] >> 8;
- size = PNV_PHB4_MAX_INTs << 16;
- snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-int",
- stack->pec->chip_id, stack->pec->index, stack->stack_no);
- memory_region_init(&stack->intbar, OBJECT(stack), name, size);
- memory_region_add_subregion(sysmem, bar, &stack->intbar);
- }
-
- /* Update PHB */
- pnv_phb4_update_regions(stack);
-}
-
-static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned size)
-{
- PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
- PnvPhb4PecState *pec = stack->pec;
- uint32_t reg = addr >> 3;
-
- switch (reg) {
- case PEC_NEST_STK_PCI_NEST_FIR:
- stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val;
- break;
- case PEC_NEST_STK_PCI_NEST_FIR_CLR:
- stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val;
- break;
- case PEC_NEST_STK_PCI_NEST_FIR_SET:
- stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val;
- break;
- case PEC_NEST_STK_PCI_NEST_FIR_MSK:
- stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val;
- break;
- case PEC_NEST_STK_PCI_NEST_FIR_MSKC:
- stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val;
- break;
- case PEC_NEST_STK_PCI_NEST_FIR_MSKS:
- stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val;
- break;
- case PEC_NEST_STK_PCI_NEST_FIR_ACT0:
- case PEC_NEST_STK_PCI_NEST_FIR_ACT1:
- stack->nest_regs[reg] = val;
- break;
- case PEC_NEST_STK_PCI_NEST_FIR_WOF:
- stack->nest_regs[reg] = 0;
- break;
- case PEC_NEST_STK_ERR_REPORT_0:
- case PEC_NEST_STK_ERR_REPORT_1:
- case PEC_NEST_STK_PBCQ_GNRL_STATUS:
- /* Flag error ? */
- break;
- case PEC_NEST_STK_PBCQ_MODE:
- stack->nest_regs[reg] = val & 0xff00000000000000ull;
- break;
- case PEC_NEST_STK_MMIO_BAR0:
- case PEC_NEST_STK_MMIO_BAR0_MASK:
- case PEC_NEST_STK_MMIO_BAR1:
- case PEC_NEST_STK_MMIO_BAR1_MASK:
- if (stack->nest_regs[PEC_NEST_STK_BAR_EN] &
- (PEC_NEST_STK_BAR_EN_MMIO0 |
- PEC_NEST_STK_BAR_EN_MMIO1)) {
- phb_pec_error(pec, "Changing enabled BAR unsupported\n");
- }
- stack->nest_regs[reg] = val & 0xffffffffff000000ull;
- break;
- case PEC_NEST_STK_PHB_REGS_BAR:
- if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) {
- phb_pec_error(pec, "Changing enabled BAR unsupported\n");
- }
- stack->nest_regs[reg] = val & 0xffffffffffc00000ull;
- break;
- case PEC_NEST_STK_INT_BAR:
- if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) {
- phb_pec_error(pec, "Changing enabled BAR unsupported\n");
- }
- stack->nest_regs[reg] = val & 0xfffffff000000000ull;
- break;
- case PEC_NEST_STK_BAR_EN:
- stack->nest_regs[reg] = val & 0xf000000000000000ull;
- pnv_pec_stk_update_map(stack);
- break;
- case PEC_NEST_STK_DATA_FRZ_TYPE:
- case PEC_NEST_STK_PBCQ_TUN_BAR:
- /* Not used for now */
- stack->nest_regs[reg] = val;
- break;
- default:
- qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx
- "=%"PRIx64"\n", addr, val);
- }
-}
-
-static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = {
- .read = pnv_pec_stk_nest_xscom_read,
- .write = pnv_pec_stk_nest_xscom_write,
- .valid.min_access_size = 8,
- .valid.max_access_size = 8,
- .impl.min_access_size = 8,
- .impl.max_access_size = 8,
- .endianness = DEVICE_BIG_ENDIAN,
-};
-
-static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr,
- unsigned size)
-{
- PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
- uint32_t reg = addr >> 3;
-
- /* TODO: add list of allowed registers and error out if not */
- return stack->pci_regs[reg];
-}
-
-static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr,
- uint64_t val, unsigned size)
-{
- PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque);
- uint32_t reg = addr >> 3;
-
- switch (reg) {
- case PEC_PCI_STK_PCI_FIR:
- stack->nest_regs[reg] = val;
- break;
- case PEC_PCI_STK_PCI_FIR_CLR:
- stack->nest_regs[PEC_PCI_STK_PCI_FIR] &= val;
- break;
- case PEC_PCI_STK_PCI_FIR_SET:
- stack->nest_regs[PEC_PCI_STK_PCI_FIR] |= val;
- break;
- case PEC_PCI_STK_PCI_FIR_MSK:
- stack->nest_regs[reg] = val;
- break;
- case PEC_PCI_STK_PCI_FIR_MSKC:
- stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val;
- break;
- case PEC_PCI_STK_PCI_FIR_MSKS:
- stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val;
- break;
- case PEC_PCI_STK_PCI_FIR_ACT0:
- case PEC_PCI_STK_PCI_FIR_ACT1:
- stack->nest_regs[reg] = val;
- break;
- case PEC_PCI_STK_PCI_FIR_WOF:
- stack->nest_regs[reg] = 0;
- break;
- case PEC_PCI_STK_ETU_RESET:
- stack->nest_regs[reg] = val & 0x8000000000000000ull;
- /* TODO: Implement reset */
- break;
- case PEC_PCI_STK_PBAIB_ERR_REPORT:
- break;
- case PEC_PCI_STK_PBAIB_TX_CMD_CRED:
- case PEC_PCI_STK_PBAIB_TX_DAT_CRED:
- stack->nest_regs[reg] = val;
- break;
- default:
- qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx
- "=%"PRIx64"\n", addr, val);
- }
-}
-
-static const MemoryRegionOps pnv_pec_stk_pci_xscom_ops = {
- .read = pnv_pec_stk_pci_xscom_read,
- .write = pnv_pec_stk_pci_xscom_write,
- .valid.min_access_size = 8,
- .valid.max_access_size = 8,
- .impl.min_access_size = 8,
- .impl.max_access_size = 8,
- .endianness = DEVICE_BIG_ENDIAN,
-};
+ PnvPHB4 *phb = PNV_PHB4(qdev_new(TYPE_PNV_PHB4));
+ int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no);
-static void pnv_pec_instance_init(Object *obj)
-{
- PnvPhb4PecState *pec = PNV_PHB4_PEC(obj);
- int i;
+ object_property_set_link(OBJECT(phb), "pec", OBJECT(pec),
+ &error_abort);
+ object_property_set_int(OBJECT(phb), "chip-id", pec->chip_id,
+ &error_fatal);
+ object_property_set_int(OBJECT(phb), "index", phb_id,
+ &error_fatal);
- for (i = 0; i < PHB4_PEC_MAX_STACKS; i++) {
- object_initialize_child(obj, "stack[*]", &pec->stacks[i],
- TYPE_PNV_PHB4_PEC_STACK);
+ if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) {
+ return;
}
-}
-static int pnv_pec_phb_offset(PnvPhb4PecState *pec)
-{
- PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
- int index = pec->index;
- int offset = 0;
+ /* Add a single Root port if running with defaults */
+ pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb),
+ PNV_PHB4_PEC_GET_CLASS(pec)->rp_model);
- while (index--) {
- offset += pecc->num_stacks[index];
- }
-
- return offset;
}
static void pnv_pec_realize(DeviceState *dev, Error **errp)
return;
}
- pec->num_stacks = pecc->num_stacks[pec->index];
+ pec->num_phbs = pecc->num_phbs[pec->index];
- /* Create stacks */
- for (i = 0; i < pec->num_stacks; i++) {
- PnvPhb4PecStack *stack = &pec->stacks[i];
- Object *stk_obj = OBJECT(stack);
- int phb_id = pnv_pec_phb_offset(pec) + i;
-
- object_property_set_int(stk_obj, "stack-no", i, &error_abort);
- object_property_set_int(stk_obj, "phb-id", phb_id, &error_abort);
- object_property_set_link(stk_obj, "pec", OBJECT(pec), &error_abort);
- if (!qdev_realize(DEVICE(stk_obj), NULL, errp)) {
- return;
+ /* Create PHBs if running with defaults */
+ if (defaults_enabled()) {
+ for (i = 0; i < pec->num_phbs; i++) {
+ pnv_pec_default_phb_realize(pec, i, errp);
}
}
- for (; i < PHB4_PEC_MAX_STACKS; i++) {
- object_unparent(OBJECT(&pec->stacks[i]));
- }
/* Initialize the XSCOM regions for the PEC registers */
snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest", pec->chip_id,
_FDT((fdt_setprop(fdt, offset, "compatible", pecc->compat,
pecc->compat_size)));
- for (i = 0; i < pec->num_stacks; i++) {
- PnvPhb4PecStack *stack = &pec->stacks[i];
- PnvPHB4 *phb = &stack->phb;
+ for (i = 0; i < pec->num_phbs; i++) {
+ int phb_id = pnv_phb4_pec_get_phb_id(pec, i);
int stk_offset;
name = g_strdup_printf("stack@%x", i);
_FDT((fdt_setprop(fdt, stk_offset, "compatible", pecc->stk_compat,
pecc->stk_compat_size)));
_FDT((fdt_setprop_cell(fdt, stk_offset, "reg", i)));
- _FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb->phb_id)));
+ _FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb_id)));
}
return 0;
}
/*
- * PEC0 -> 1 stack
- * PEC1 -> 2 stacks
- * PEC2 -> 3 stacks
+ * PEC0 -> 1 phb
+ * PEC1 -> 2 phb
+ * PEC2 -> 3 phbs
*/
-static const uint32_t pnv_pec_num_stacks[] = { 1, 2, 3 };
+static const uint32_t pnv_pec_num_phbs[] = { 1, 2, 3 };
static void pnv_pec_class_init(ObjectClass *klass, void *data)
{
pecc->stk_compat = stk_compat;
pecc->stk_compat_size = sizeof(stk_compat);
pecc->version = PNV_PHB4_VERSION;
- pecc->num_stacks = pnv_pec_num_stacks;
+ pecc->num_phbs = pnv_pec_num_phbs;
+ pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT;
}
static const TypeInfo pnv_pec_type_info = {
.name = TYPE_PNV_PHB4_PEC,
.parent = TYPE_DEVICE,
.instance_size = sizeof(PnvPhb4PecState),
- .instance_init = pnv_pec_instance_init,
.class_init = pnv_pec_class_init,
.class_size = sizeof(PnvPhb4PecClass),
.interfaces = (InterfaceInfo[]) {
}
};
-static void pnv_pec_stk_instance_init(Object *obj)
-{
- PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(obj);
-
- object_initialize_child(obj, "phb", &stack->phb, TYPE_PNV_PHB4);
- object_property_add_alias(obj, "phb-id", OBJECT(&stack->phb), "index");
-}
-
-static void pnv_pec_stk_realize(DeviceState *dev, Error **errp)
-{
- PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev);
- PnvPhb4PecState *pec = stack->pec;
- PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec);
- PnvChip *chip = pec->chip;
- uint32_t pec_nest_base;
- uint32_t pec_pci_base;
- char name[64];
-
- assert(pec);
-
- /* Initialize the XSCOM regions for the stack registers */
- snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-stack-%d",
- pec->chip_id, pec->index, stack->stack_no);
- pnv_xscom_region_init(&stack->nest_regs_mr, OBJECT(stack),
- &pnv_pec_stk_nest_xscom_ops, stack, name,
- PHB4_PEC_NEST_STK_REGS_COUNT);
-
- snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d",
- pec->chip_id, pec->index, stack->stack_no);
- pnv_xscom_region_init(&stack->pci_regs_mr, OBJECT(stack),
- &pnv_pec_stk_pci_xscom_ops, stack, name,
- PHB4_PEC_PCI_STK_REGS_COUNT);
-
- /* PHB pass-through */
- snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d-phb",
- pec->chip_id, pec->index, stack->stack_no);
- pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(&stack->phb),
- &pnv_phb4_xscom_ops, &stack->phb, name, 0x40);
-
- object_property_set_int(OBJECT(&stack->phb), "chip-id", pec->chip_id,
- &error_fatal);
- object_property_set_int(OBJECT(&stack->phb), "version", pecc->version,
- &error_fatal);
- object_property_set_link(OBJECT(&stack->phb), "stack", OBJECT(stack),
- &error_abort);
- if (!sysbus_realize(SYS_BUS_DEVICE(&stack->phb), errp)) {
- return;
- }
-
- pec_nest_base = pecc->xscom_nest_base(pec);
- pec_pci_base = pecc->xscom_pci_base(pec);
-
- /* Populate the XSCOM address space. */
- pnv_xscom_add_subregion(chip,
- pec_nest_base + 0x40 * (stack->stack_no + 1),
- &stack->nest_regs_mr);
- pnv_xscom_add_subregion(chip,
- pec_pci_base + 0x40 * (stack->stack_no + 1),
- &stack->pci_regs_mr);
- pnv_xscom_add_subregion(chip,
- pec_pci_base + PNV9_XSCOM_PEC_PCI_STK0 +
- 0x40 * stack->stack_no,
- &stack->phb_regs_mr);
-}
-
-static Property pnv_pec_stk_properties[] = {
- DEFINE_PROP_UINT32("stack-no", PnvPhb4PecStack, stack_no, 0),
- DEFINE_PROP_LINK("pec", PnvPhb4PecStack, pec, TYPE_PNV_PHB4_PEC,
- PnvPhb4PecState *),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pnv_pec_stk_class_init(ObjectClass *klass, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- device_class_set_props(dc, pnv_pec_stk_properties);
- dc->realize = pnv_pec_stk_realize;
- dc->user_creatable = false;
-
- /* TODO: reset regs ? */
-}
-
-static const TypeInfo pnv_pec_stk_type_info = {
- .name = TYPE_PNV_PHB4_PEC_STACK,
- .parent = TYPE_DEVICE,
- .instance_size = sizeof(PnvPhb4PecStack),
- .instance_init = pnv_pec_stk_instance_init,
- .class_init = pnv_pec_stk_class_init,
- .interfaces = (InterfaceInfo[]) {
- { TYPE_PNV_XSCOM_INTERFACE },
- { }
- }
-};
-
static void pnv_pec_register_types(void)
{
type_register_static(&pnv_pec_type_info);
- type_register_static(&pnv_pec_stk_type_info);
}
type_init(pnv_pec_register_types);
static void pnv_chip_power8_instance_init(Object *obj)
{
- PnvChip *chip = PNV_CHIP(obj);
Pnv8Chip *chip8 = PNV8_CHIP(obj);
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj);
int i;
object_initialize_child(obj, "homer", &chip8->homer, TYPE_PNV8_HOMER);
- for (i = 0; i < pcc->num_phbs; i++) {
+ if (defaults_enabled()) {
+ chip8->num_phbs = pcc->num_phbs;
+ }
+
+ for (i = 0; i < chip8->num_phbs; i++) {
object_initialize_child(obj, "phb[*]", &chip8->phbs[i], TYPE_PNV_PHB3);
}
- /*
- * Number of PHBs is the chip default
- */
- chip->num_phbs = pcc->num_phbs;
}
static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp)
}
}
+/* Attach a root port device */
+void pnv_phb_attach_root_port(PCIHostState *pci, const char *name)
+{
+ PCIDevice *root = pci_new(PCI_DEVFN(0, 0), name);
+
+ pci_realize_and_unref(root, pci->bus, &error_fatal);
+}
+
static void pnv_chip_power8_realize(DeviceState *dev, Error **errp)
{
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev);
&chip8->homer.regs);
/* PHB3 controllers */
- for (i = 0; i < chip->num_phbs; i++) {
+ for (i = 0; i < chip8->num_phbs; i++) {
PnvPHB3 *phb = &chip8->phbs[i];
object_property_set_int(OBJECT(phb), "index", i, &error_fatal);
return NULL;
}
+void pnv_chip_parent_fixup(PnvChip *chip, Object *obj, int index)
+{
+ Object *parent = OBJECT(chip);
+ g_autofree char *default_id =
+ g_strdup_printf("%s[%d]", object_get_typename(obj), index);
+
+ if (obj->parent == parent) {
+ return;
+ }
+
+ object_ref(obj);
+ object_unparent(obj);
+ object_property_add_child(
+ parent, DEVICE(obj)->id ? DEVICE(obj)->id : default_id, obj);
+ object_unref(obj);
+}
+
+PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id)
+{
+ int i;
+
+ for (i = 0; i < pnv->num_chips; i++) {
+ PnvChip *chip = pnv->chips[i];
+ if (chip->chip_id == chip_id) {
+ return chip;
+ }
+ }
+ return NULL;
+}
+
static int pnv_ics_resend_child(Object *child, void *opaque)
{
PnvPHB3 *phb3 = (PnvPHB3 *) object_dynamic_cast(child, TYPE_PNV_PHB3);
pmc->compat = compat;
pmc->compat_size = sizeof(compat);
+
+ machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB3);
}
static void pnv_machine_power9_class_init(ObjectClass *oc, void *data)
pmc->compat = compat;
pmc->compat_size = sizeof(compat);
pmc->dt_power_mgt = pnv_dt_power_mgt;
+
+ machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB4);
}
static void pnv_machine_power10_class_init(ObjectClass *oc, void *data)
.name = "timebase",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.pre_save = timebase_pre_save,
.fields = (VMStateField []) {
VMSTATE_UINT64(guest_timebase, PPCTimebase),
*
* Only CPUs for which we create core types in spapr_cpu_core.c
* are possible, and all of those have VMX */
- if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) {
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2)));
- } else {
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1)));
+ if (env->insns_flags & PPC_ALTIVEC) {
+ if (spapr_get_cap(spapr, SPAPR_CAP_VSX) != 0) {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 2)));
+ } else {
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", 1)));
+ }
}
/* Advertise DFP (Decimal Floating Point) if available
VHostSCSICommon *vsc = CAST(VHostSCSICommon, dev, TYPE_VHOST_SCSI_COMMON);
PCIDevice *pcidev = CAST(PCIDevice, dev, TYPE_PCI_DEVICE);
- if (d) {
+ if (d && bus) {
void *spapr = CAST(void, bus->parent, "spapr-vscsi");
VirtIOSCSI *virtio = CAST(VirtIOSCSI, bus->parent, TYPE_VIRTIO_SCSI);
USBDevice *usb = CAST(USBDevice, bus->parent, TYPE_USB_DEVICE);
cpu_reset(cs);
+ /*
+ * "PowerPC Processor binding to IEEE 1275" defines the initial MSR state
+ * as 32bit (MSR_SF=0) in "8.2.1. Initial Register Values".
+ */
+ env->msr &= ~(1ULL << MSR_SF);
env->spr[SPR_HIOR] = 0;
lpcr = env->spr[SPR_LPCR];
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "sysemu/rtc.h"
#include "hw/ppc/spapr.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT,
stack_ptr, spapr->initrd_base,
spapr->initrd_size);
- /* VOF is 32bit BE so enforce MSR here */
- first_ppc_cpu->env.msr &= ~((1ULL << MSR_SF) | (1ULL << MSR_LE));
/*
* At this point the expected allocation map is:
#include "qemu/units.h"
#include "qemu/log.h"
#include "qapi/error.h"
-#include "exec/ram_addr.h"
#include "exec/address-spaces.h"
#include "hw/ppc/vof.h"
#include "hw/ppc/fdt.h"
#include "trace.h"
#include "rdma_utils.h"
-void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen)
+void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len)
{
void *p;
- hwaddr len = plen;
+ dma_addr_t pci_len = len;
if (!addr) {
rdma_error_report("addr is NULL");
return NULL;
}
- p = pci_dma_map(dev, addr, &len, DMA_DIRECTION_TO_DEVICE);
+ p = pci_dma_map(dev, addr, &pci_len, DMA_DIRECTION_TO_DEVICE);
if (!p) {
rdma_error_report("pci_dma_map fail, addr=0x%"PRIx64", len=%"PRId64,
- addr, len);
+ addr, pci_len);
return NULL;
}
- if (len != plen) {
- rdma_pci_dma_unmap(dev, p, len);
+ if (pci_len != len) {
+ rdma_pci_dma_unmap(dev, p, pci_len);
return NULL;
}
- trace_rdma_pci_dma_map(addr, p, len);
+ trace_rdma_pci_dma_map(addr, p, pci_len);
return p;
}
GSList *list;
} RdmaProtectedGSList;
-void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen);
+void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t len);
void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len);
void rdma_protected_gqueue_init(RdmaProtectedGQueue *list);
void rdma_protected_gqueue_destroy(RdmaProtectedGQueue *list);
rdma_rm_modify_qp(uint32_t qpn, uint32_t attr_mask, int qp_state, uint8_t sgid_idx) "qpn=0x%x, attr_mask=0x%x, qp_state=%d, sgid_idx=%d"
# rdma_utils.c
-rdma_pci_dma_map(uint64_t addr, void *vaddr, uint64_t len) "0x%"PRIx64" -> %p (len=%" PRId64")"
+rdma_pci_dma_map(uint64_t addr, void *vaddr, uint64_t len) "0x%"PRIx64" -> %p (len=%" PRIu64")"
rdma_pci_dma_unmap(void *vaddr) "%p"
#include "elf.h"
#include "sysemu/device_tree.h"
#include "sysemu/qtest.h"
+#include "sysemu/kvm.h"
#include <libfdt.h>
CPUState *cs = qemu_get_cpu(i);
CPURISCVState *env = &RISCV_CPU(cs)->env;
- if (riscv_has_ext(env, RVS)) {
+ if (kvm_enabled()) {
+ vals[i] = "S";
+ } else if (riscv_has_ext(env, RVS)) {
vals[i] = "MS";
} else {
vals[i] = "M";
return;
}
+
+void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr)
+{
+ CPUState *cs;
+
+ for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
+ RISCVCPU *riscv_cpu = RISCV_CPU(cs);
+ riscv_cpu->env.kernel_addr = kernel_addr;
+ riscv_cpu->env.fdt_addr = fdt_addr;
+ }
+}
qdev_prop_set_uint32(DEVICE(&s->plic), "priority-base", 0x00);
qdev_prop_set_uint32(DEVICE(&s->plic), "pending-base", 0x1000);
qdev_prop_set_uint32(DEVICE(&s->plic), "enable-base", 0x2000);
- qdev_prop_set_uint32(DEVICE(&s->plic), "enable-stride", 0x18);
+ qdev_prop_set_uint32(DEVICE(&s->plic), "enable-stride", 32);
qdev_prop_set_uint32(DEVICE(&s->plic), "context-base", 0x200000);
qdev_prop_set_uint32(DEVICE(&s->plic), "context-stride", 8);
qdev_prop_set_uint32(DEVICE(&s->plic), "aperture-size", memmap[IBEX_DEV_PLIC].size);
static const MemMapEntry spike_memmap[] = {
[SPIKE_MROM] = { 0x1000, 0xf000 },
+ [SPIKE_HTIF] = { 0x1000000, 0x1000 },
[SPIKE_CLINT] = { 0x2000000, 0x10000 },
[SPIKE_DRAM] = { 0x80000000, 0x0 },
};
qemu_fdt_add_subnode(fdt, "/htif");
qemu_fdt_setprop_string(fdt, "/htif", "compatible", "ucb,htif0");
+ if (!htif_uses_elf_symbols()) {
+ qemu_fdt_setprop_cells(fdt, "/htif", "reg",
+ 0x0, memmap[SPIKE_HTIF].base, 0x0, memmap[SPIKE_HTIF].size);
+ }
qemu_fdt_add_subnode(fdt, "/soc");
qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
if (cmdline) {
qemu_fdt_add_subnode(fdt, "/chosen");
qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline);
+ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", "/htif");
}
}
memory_region_add_subregion(system_memory, memmap[SPIKE_DRAM].base,
machine->ram);
- /* create device tree */
- create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline,
- riscv_is_32bit(&s->soc[0]));
-
/* boot rom */
memory_region_init_rom(mask_rom, NULL, "riscv.spike.mrom",
memmap[SPIKE_MROM].size, &error_fatal);
*/
if (riscv_is_32bit(&s->soc[0])) {
firmware_end_addr = riscv_find_and_load_firmware(machine,
- RISCV32_BIOS_ELF, memmap[SPIKE_DRAM].base,
+ RISCV32_BIOS_BIN, memmap[SPIKE_DRAM].base,
htif_symbol_callback);
} else {
firmware_end_addr = riscv_find_and_load_firmware(machine,
- RISCV64_BIOS_ELF, memmap[SPIKE_DRAM].base,
+ RISCV64_BIOS_BIN, memmap[SPIKE_DRAM].base,
htif_symbol_callback);
}
+ /* Load kernel */
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0],
firmware_end_addr);
kernel_entry = riscv_load_kernel(machine->kernel_filename,
kernel_start_addr,
htif_symbol_callback);
-
- if (machine->initrd_filename) {
- hwaddr start;
- hwaddr end = riscv_load_initrd(machine->initrd_filename,
- machine->ram_size, kernel_entry,
- &start);
- qemu_fdt_setprop_cell(s->fdt, "/chosen",
- "linux,initrd-start", start);
- qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
- end);
- }
} else {
/*
* If dynamic firmware is used, it doesn't know where is the next mode
kernel_entry = 0;
}
+ /* Create device tree */
+ create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline,
+ riscv_is_32bit(&s->soc[0]));
+
+ /* Load initrd */
+ if (machine->kernel_filename && machine->initrd_filename) {
+ hwaddr start;
+ hwaddr end = riscv_load_initrd(machine->initrd_filename,
+ machine->ram_size, kernel_entry,
+ &start);
+ qemu_fdt_setprop_cell(s->fdt, "/chosen",
+ "linux,initrd-start", start);
+ qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
+ end);
+ }
+
/* Compute the fdt load address in dram */
fdt_load_addr = riscv_load_fdt(memmap[SPIKE_DRAM].base,
machine->ram_size, s->fdt);
/* initialize HTIF using symbols found in load_kernel */
htif_mm_init(system_memory, mask_rom,
- &s->soc[0].harts[0].env, serial_hd(0));
+ &s->soc[0].harts[0].env, serial_hd(0),
+ memmap[SPIKE_HTIF].base);
}
static void spike_machine_instance_init(Object *obj)
#include "chardev/char.h"
#include "sysemu/device_tree.h"
#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
#include "hw/pci/pci.h"
#include "hw/pci-host/gpex.h"
#include "hw/display/ramfb.h"
"sifive,plic-1.0.0", "riscv,plic0"
};
- plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
+ if (kvm_enabled()) {
+ plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 2);
+ } else {
+ plic_cells = g_new0(uint32_t, s->soc[socket].num_harts * 4);
+ }
for (cpu = 0; cpu < s->soc[socket].num_harts; cpu++) {
- plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
- plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
- plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
- plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+ if (kvm_enabled()) {
+ plic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_S_EXT);
+ } else {
+ plic_cells[cpu * 4 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cpu * 4 + 1] = cpu_to_be32(IRQ_M_EXT);
+ plic_cells[cpu * 4 + 2] = cpu_to_be32(intc_phandles[cpu]);
+ plic_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_S_EXT);
+ }
}
plic_phandles[socket] = (*phandle)++;
create_fdt_socket_memory(s, memmap, socket);
- if (s->have_aclint) {
- create_fdt_socket_aclint(s, memmap, socket, intc_phandles);
- } else {
- create_fdt_socket_clint(s, memmap, socket, intc_phandles);
+ if (!kvm_enabled()) {
+ if (s->have_aclint) {
+ create_fdt_socket_aclint(s, memmap, socket, intc_phandles);
+ } else {
+ create_fdt_socket_clint(s, memmap, socket, intc_phandles);
+ }
}
create_fdt_socket_plic(s, memmap, socket, phandle,
hart_count, &error_abort);
sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_abort);
- /* Per-socket CLINT */
- riscv_aclint_swi_create(
- memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
- base_hartid, hart_count, false);
- riscv_aclint_mtimer_create(
- memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size +
- RISCV_ACLINT_SWI_SIZE,
- RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count,
- RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
- RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true);
-
- /* Per-socket ACLINT SSWI */
- if (s->have_aclint) {
+ if (!kvm_enabled()) {
+ /* Per-socket CLINT */
riscv_aclint_swi_create(
- memmap[VIRT_ACLINT_SSWI].base +
- i * memmap[VIRT_ACLINT_SSWI].size,
- base_hartid, hart_count, true);
+ memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size,
+ base_hartid, hart_count, false);
+ riscv_aclint_mtimer_create(
+ memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size +
+ RISCV_ACLINT_SWI_SIZE,
+ RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count,
+ RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
+ RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true);
+
+ /* Per-socket ACLINT SSWI */
+ if (s->have_aclint) {
+ riscv_aclint_swi_create(
+ memmap[VIRT_ACLINT_SSWI].base +
+ i * memmap[VIRT_ACLINT_SSWI].size,
+ base_hartid, hart_count, true);
+ }
}
/* Per-socket PLIC hart topology configuration string */
memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base,
mask_rom);
+ /*
+ * Only direct boot kernel is currently supported for KVM VM,
+ * so the "-bios" parameter is ignored and treated like "-bios none"
+ * when KVM is enabled.
+ */
+ if (kvm_enabled()) {
+ g_free(machine->firmware);
+ machine->firmware = g_strdup("none");
+ }
+
if (riscv_is_32bit(&s->soc[0])) {
firmware_end_addr = riscv_find_and_load_firmware(machine,
RISCV32_BIOS_BIN, start_addr, NULL);
virt_memmap[VIRT_MROM].size, kernel_entry,
fdt_load_addr, machine->fdt);
+ /*
+ * Only direct boot kernel is currently supported for KVM VM,
+ * So here setup kernel start address and fdt address.
+ * TODO:Support firmware loading and integrate to TCG start
+ */
+ if (kvm_enabled()) {
+ riscv_setup_direct_kernel(kernel_entry, fdt_load_addr);
+ }
+
/* SiFive Test MMIO device */
sifive_test_create(memmap[VIRT_TEST].base);
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
-#include "qemu-common.h"
#include "hw/qdev-properties.h"
#include "hw/rtc/allwinner-rtc.h"
+#include "sysemu/rtc.h"
#include "trace.h"
/* RTC registers */
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/rtc/aspeed_rtc.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/timer.h"
+#include "sysemu/rtc.h"
#include "trace.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/i2c/i2c.h"
#include "migration/vmstate.h"
#include "qemu/bcd.h"
#include "qemu/module.h"
#include "qom/object.h"
+#include "sysemu/rtc.h"
/* Size of NVRAM including both the user-accessible area and the
* secondary register area.
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/sysbus.h"
#include "hw/arm/exynos4210.h"
#include "qom/object.h"
+#include "sysemu/rtc.h"
#define DEBUG_RTC 0
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/rtc/goldfish_rtc.h"
#include "migration/vmstate.h"
#include "hw/irq.h"
#include "qemu/bitops.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "sysemu/rtc.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "qemu/bcd.h"
#include "hw/i2c/i2c.h"
#include "qom/object.h"
+#include "sysemu/rtc.h"
#define TYPE_M41T80 "m41t80"
OBJECT_DECLARE_SIMPLE_TYPE(M41t80State, M41T80)
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/rtc/m48t59.h"
#include "qemu/timer.h"
#include "sysemu/runstate.h"
+#include "sysemu/rtc.h"
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "qapi/error.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/cutils.h"
#include "qemu/module.h"
#include "qemu/bcd.h"
#include "sysemu/replay.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
+#include "sysemu/rtc.h"
#include "hw/rtc/mc146818rtc.h"
#include "hw/rtc/mc146818rtc_regs.h"
#include "migration/vmstate.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/rtc/pl031.h"
#include "migration/vmstate.h"
#include "hw/irq.h"
#include "hw/sysbus.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "sysemu/rtc.h"
#include "qemu/cutils.h"
#include "qemu/log.h"
#include "qemu/module.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/timer.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#include "migration/qemu-file-types.h"
#include "migration/vmstate.h"
#include "sysemu/sysemu.h"
+#include "sysemu/rtc.h"
#include "qemu/bcd.h"
#include "qemu/module.h"
#include "qom/object.h"
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/sysbus.h"
#include "hw/register.h"
#include "qemu/bitops.h"
#include "hw/irq.h"
#include "qemu/cutils.h"
#include "sysemu/sysemu.h"
+#include "sysemu/rtc.h"
#include "trace.h"
#include "hw/rtc/xlnx-zynqmp-rtc.h"
#include "migration/vmstate.h"
#define KERN_IMAGE_START 0x010000UL
#define LINUX_MAGIC_ADDR 0x010008UL
+#define KERN_PARM_AREA_SIZE_ADDR 0x010430UL
#define KERN_PARM_AREA 0x010480UL
-#define KERN_PARM_AREA_SIZE 0x000380UL
+#define LEGACY_KERN_PARM_AREA_SIZE 0x000380UL
#define INITRD_START 0x800000UL
#define INITRD_PARM_START 0x010408UL
#define PARMFILE_START 0x001000UL
return srcaddr + dstaddr;
}
+static uint64_t get_max_kernel_cmdline_size(void)
+{
+ uint64_t *size_ptr = rom_ptr(KERN_PARM_AREA_SIZE_ADDR, sizeof(*size_ptr));
+
+ if (size_ptr) {
+ uint64_t size;
+
+ size = be64_to_cpu(*size_ptr);
+ if (size) {
+ return size;
+ }
+ }
+ return LEGACY_KERN_PARM_AREA_SIZE;
+}
+
static void s390_ipl_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
ipl->start_addr = KERN_IMAGE_START;
/* Overwrite parameters in the kernel image, which are "rom" */
if (parm_area) {
- if (cmdline_size > KERN_PARM_AREA_SIZE) {
+ uint64_t max_cmdline_size = get_max_kernel_cmdline_size();
+
+ if (cmdline_size > max_cmdline_size) {
error_setg(errp,
- "kernel command line exceeds maximum size: %zu > %lu",
- cmdline_size, KERN_PARM_AREA_SIZE);
+ "kernel command line exceeds maximum size:"
+ " %zu > %" PRIu64,
+ cmdline_size, max_cmdline_size);
return;
}
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qapi/error.h"
#include "hw/s390x/tod.h"
#include "qemu/timer.h"
#include "qemu/module.h"
#include "cpu.h"
#include "tcg/tcg_s390x.h"
+#include "sysemu/rtc.h"
static void qemu_s390_tod_get(const S390TODState *td, S390TOD *tod,
Error **errp)
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "hw/pci/pci.h"
#include "hw/qdev-properties.h"
#include "sysemu/dma.h"
#include "sysemu/block-backend.h"
+#include "sysemu/rtc.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "qemu/iov.h"
cdb[1] = 0x1;
cdb[2] = pg;
}
- cdb[3] = (len >> 8) & 0xff;
- cdb[4] = (len & 0xff);
+ stw_be_p(&cdb[3], len);
return len;
}
} else {
cdb[0] = READ_16;
}
- cdb[2] = (lba >> 56) & 0xff;
- cdb[3] = (lba >> 48) & 0xff;
- cdb[4] = (lba >> 40) & 0xff;
- cdb[5] = (lba >> 32) & 0xff;
- cdb[6] = (lba >> 24) & 0xff;
- cdb[7] = (lba >> 16) & 0xff;
- cdb[8] = (lba >> 8) & 0xff;
- cdb[9] = (lba) & 0xff;
- cdb[10] = (len >> 24) & 0xff;
- cdb[11] = (len >> 16) & 0xff;
- cdb[12] = (len >> 8) & 0xff;
- cdb[13] = (len) & 0xff;
+ stq_be_p(&cdb[2], lba);
+ stl_be_p(&cdb[2 + 8], len);
}
/*
size_t dcmd_size = sizeof(info);
BusChild *kid;
int num_pd_disks = 0;
+ dma_addr_t residual;
memset(&info, 0x0, dcmd_size);
if (cmd->iov_size < dcmd_size) {
MFI_INFO_PDMIX_SATA |
MFI_INFO_PDMIX_LD);
- cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
{
struct mfi_defaults info;
size_t dcmd_size = sizeof(struct mfi_defaults);
+ dma_addr_t residual;
memset(&info, 0x0, dcmd_size);
if (cmd->iov_size < dcmd_size) {
info.disable_preboot_cli = 1;
info.cluster_disable = 1;
- cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
{
struct mfi_bios_data info;
size_t dcmd_size = sizeof(info);
+ dma_addr_t residual;
memset(&info, 0x0, dcmd_size);
if (cmd->iov_size < dcmd_size) {
info.expose_all_drives = 1;
}
- cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
{
uint64_t fw_time;
size_t dcmd_size = sizeof(fw_time);
+ dma_addr_t residual;
fw_time = cpu_to_le64(megasas_fw_time());
- cmd->iov_size -= dma_buf_read(&fw_time, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(&fw_time, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
{
struct mfi_evt_log_state info;
size_t dcmd_size = sizeof(info);
+ dma_addr_t residual;
memset(&info, 0, dcmd_size);
info.shutdown_seq_num = cpu_to_le32(s->shutdown_event);
info.boot_seq_num = cpu_to_le32(s->boot_event);
- cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
size_t dcmd_size = sizeof(info);
BusChild *kid;
uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks;
+ dma_addr_t residual;
memset(&info, 0, dcmd_size);
offset = 8;
info.size = cpu_to_le32(offset);
info.count = cpu_to_le32(num_pd_disks);
- cmd->iov_size -= dma_buf_read(&info, offset, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(&info, offset, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
uint64_t pd_size;
uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
uint8_t cmdbuf[6];
- size_t len, resid;
+ size_t len;
+ dma_addr_t residual;
if (!cmd->iov_buf) {
cmd->iov_buf = g_malloc0(dcmd_size);
info->connected_port_bitmap = 0x1;
info->device_speed = 1;
info->link_speed = 1;
- resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(cmd->iov_buf, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
g_free(cmd->iov_buf);
- cmd->iov_size = dcmd_size - resid;
+ cmd->iov_size = dcmd_size - residual;
cmd->iov_buf = NULL;
return MFI_STAT_OK;
}
static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd)
{
struct mfi_ld_list info;
- size_t dcmd_size = sizeof(info), resid;
+ size_t dcmd_size = sizeof(info);
+ dma_addr_t residual;
uint32_t num_ld_disks = 0, max_ld_disks;
uint64_t ld_size;
BusChild *kid;
info.ld_count = cpu_to_le32(num_ld_disks);
trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks);
- resid = dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
- cmd->iov_size = dcmd_size - resid;
+ dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size = dcmd_size - residual;
return MFI_STAT_OK;
}
{
uint16_t flags;
struct mfi_ld_targetid_list info;
- size_t dcmd_size = sizeof(info), resid;
+ size_t dcmd_size = sizeof(info);
+ dma_addr_t residual;
uint32_t num_ld_disks = 0, max_ld_disks = s->fw_luns;
BusChild *kid;
info.size = dcmd_size;
trace_megasas_dcmd_ld_get_list(cmd->index, num_ld_disks, max_ld_disks);
- resid = dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
- cmd->iov_size = dcmd_size - resid;
+ dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size = dcmd_size - residual;
return MFI_STAT_OK;
}
struct mfi_ld_info *info = cmd->iov_buf;
size_t dcmd_size = sizeof(struct mfi_ld_info);
uint8_t cdb[6];
- ssize_t len, resid;
+ ssize_t len;
+ dma_addr_t residual;
uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF);
uint64_t ld_size;
info->ld_config.span[0].num_blocks = info->size;
info->ld_config.span[0].array_ref = cpu_to_le16(sdev_id);
- resid = dma_buf_read(cmd->iov_buf, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(cmd->iov_buf, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
g_free(cmd->iov_buf);
- cmd->iov_size = dcmd_size - resid;
+ cmd->iov_size = dcmd_size - residual;
cmd->iov_buf = NULL;
return MFI_STAT_OK;
}
struct mfi_config_data *info;
int num_pd_disks = 0, array_offset, ld_offset;
BusChild *kid;
+ dma_addr_t residual;
if (cmd->iov_size > 4096) {
return MFI_STAT_INVALID_PARAMETER;
ld_offset += sizeof(struct mfi_ld_config);
}
- cmd->iov_size -= dma_buf_read(data, info->size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(data, info->size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
{
struct mfi_ctrl_props info;
size_t dcmd_size = sizeof(info);
+ dma_addr_t residual;
memset(&info, 0x0, dcmd_size);
if (cmd->iov_size < dcmd_size) {
info.ecc_bucket_leak_rate = cpu_to_le16(1440);
info.expose_encl_devices = 1;
- cmd->iov_size -= dma_buf_read(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(&info, dcmd_size, &residual, &cmd->qsg,
+ MEMTXATTRS_UNSPECIFIED);
+ cmd->iov_size -= residual;
return MFI_STAT_OK;
}
dcmd_size);
return MFI_STAT_INVALID_PARAMETER;
}
- dma_buf_write(&info, dcmd_size, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_write(&info, dcmd_size, NULL, &cmd->qsg, MEMTXATTRS_UNSPECIFIED);
trace_megasas_dcmd_unsupported(cmd->index, cmd->iov_size);
return MFI_STAT_OK;
}
}
static int megasas_finish_internal_dcmd(MegasasCmd *cmd,
- SCSIRequest *req, size_t resid)
+ SCSIRequest *req, dma_addr_t residual)
{
int retval = MFI_STAT_OK;
int lun = req->lun;
trace_megasas_dcmd_internal_finish(cmd->index, cmd->dcmd_opcode, lun);
- cmd->iov_size -= resid;
+ cmd->iov_size -= residual;
switch (cmd->dcmd_opcode) {
case MFI_DCMD_PD_GET_INFO:
retval = megasas_pd_get_info_submit(req->dev, lun, cmd);
}
}
-static void megasas_command_complete(SCSIRequest *req, size_t resid)
+static void megasas_command_complete(SCSIRequest *req, size_t residual)
{
MegasasCmd *cmd = req->hba_private;
uint8_t cmd_status = MFI_STAT_OK;
- trace_megasas_command_complete(cmd->index, req->status, resid);
+ trace_megasas_command_complete(cmd->index, req->status, residual);
if (req->io_canceled) {
return;
/*
* Internal command complete
*/
- cmd_status = megasas_finish_internal_dcmd(cmd, req, resid);
+ cmd_status = megasas_finish_internal_dcmd(cmd, req, residual);
if (cmd_status == MFI_STAT_INVALID_STATUS) {
return;
}
.name = "megasas-gen2",
.version_id = 0,
.minimum_version_id = 0,
- .minimum_version_id_old = 0,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(parent_obj, MegasasState),
VMSTATE_MSIX(parent_obj, MegasasState),
.name = "mptsas",
.version_id = 0,
.minimum_version_id = 0,
- .minimum_version_id_old = 0,
.post_load = mptsas_post_load,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, MPTSASState),
}
req->cmd = cmd;
- req->resid = req->cmd.xfer;
+ req->residual = req->cmd.xfer;
switch (buf[0]) {
case INQUIRY:
trace_scsi_req_data(req->dev->id, req->lun, req->tag, len);
assert(req->cmd.mode != SCSI_XFER_NONE);
if (!req->sg) {
- req->resid -= len;
+ req->residual -= len;
req->bus->info->transfer_data(req, len);
return;
}
buf = scsi_req_get_buf(req);
if (req->cmd.mode == SCSI_XFER_FROM_DEV) {
- req->resid = dma_buf_read(buf, len, req->sg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_read(buf, len, &req->residual, req->sg,
+ MEMTXATTRS_UNSPECIFIED);
} else {
- req->resid = dma_buf_write(buf, len, req->sg, MEMTXATTRS_UNSPECIFIED);
+ dma_buf_write(buf, len, &req->residual, req->sg,
+ MEMTXATTRS_UNSPECIFIED);
}
scsi_req_continue(req);
}
scsi_req_ref(req);
scsi_req_dequeue(req);
- req->bus->info->complete(req, req->resid);
+ req->bus->info->complete(req, req->residual);
/* Cancelled requests might end up being completed instead of cancelled */
notifier_list_notify(&req->cancel_notifiers, req);
if (r->req.sg) {
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_READ);
- r->req.resid -= r->req.sg->size;
+ r->req.residual -= r->req.sg->size;
r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
r->req.sg, r->sector << BDRV_SECTOR_BITS,
BDRV_SECTOR_SIZE,
if (r->req.sg) {
dma_acct_start(s->qdev.conf.blk, &r->acct, r->req.sg, BLOCK_ACCT_WRITE);
- r->req.resid -= r->req.sg->size;
+ r->req.residual -= r->req.sg->size;
r->req.aiocb = dma_blk_io(blk_get_aio_context(s->qdev.conf.blk),
r->req.sg, r->sector << BDRV_SECTOR_BITS,
BDRV_SECTOR_SIZE,
}
}
-static bool virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev,
- VirtQueue *vq)
-{
- bool progress = false;
- VirtIOSCSI *s = VIRTIO_SCSI(vdev);
-
- virtio_scsi_acquire(s);
- if (!s->dataplane_fenced) {
- assert(s->ctx && s->dataplane_started);
- progress = virtio_scsi_handle_cmd_vq(s, vq);
- }
- virtio_scsi_release(s);
- return progress;
-}
-
-static bool virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev,
- VirtQueue *vq)
-{
- bool progress = false;
- VirtIOSCSI *s = VIRTIO_SCSI(vdev);
-
- virtio_scsi_acquire(s);
- if (!s->dataplane_fenced) {
- assert(s->ctx && s->dataplane_started);
- progress = virtio_scsi_handle_ctrl_vq(s, vq);
- }
- virtio_scsi_release(s);
- return progress;
-}
-
-static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev,
- VirtQueue *vq)
-{
- bool progress = false;
- VirtIOSCSI *s = VIRTIO_SCSI(vdev);
-
- virtio_scsi_acquire(s);
- if (!s->dataplane_fenced) {
- assert(s->ctx && s->dataplane_started);
- progress = virtio_scsi_handle_event_vq(s, vq);
- }
- virtio_scsi_release(s);
- return progress;
-}
-
static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n)
{
BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s)));
VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s);
int i;
- virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL);
- virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL);
+ virtio_queue_aio_detach_host_notifier(vs->ctrl_vq, s->ctx);
+ virtio_queue_aio_detach_host_notifier(vs->event_vq, s->ctx);
for (i = 0; i < vs->conf.num_queues; i++) {
- virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL);
+ virtio_queue_aio_detach_host_notifier(vs->cmd_vqs[i], s->ctx);
}
}
memory_region_transaction_commit();
aio_context_acquire(s->ctx);
- virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx,
- virtio_scsi_data_plane_handle_ctrl);
- virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx,
- virtio_scsi_data_plane_handle_event);
+ virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, s->ctx);
+ virtio_queue_aio_attach_host_notifier(vs->event_vq, s->ctx);
for (i = 0; i < vs->conf.num_queues; i++) {
- virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx,
- virtio_scsi_data_plane_handle_cmd);
+ virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], s->ctx);
}
s->dataplane_starting = false;
/* use non-QOM casts in the data path */
VirtIOSCSI *s = (VirtIOSCSI *)vdev;
- if (s->ctx) {
+ if (s->ctx && !s->dataplane_started) {
virtio_device_start_ioeventfd(vdev);
if (!s->dataplane_fenced) {
return;
softmmu_ss.add(when: 'CONFIG_STM32F2XX_SPI', if_true: files('stm32f2xx_spi.c'))
softmmu_ss.add(when: 'CONFIG_XILINX_SPI', if_true: files('xilinx_spi.c'))
softmmu_ss.add(when: 'CONFIG_XILINX_SPIPS', if_true: files('xilinx_spips.c'))
+softmmu_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal-ospi.c'))
softmmu_ss.add(when: 'CONFIG_IMX', if_true: files('imx_spi.c'))
softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_spi.c'))
--- /dev/null
+/*
+ * QEMU model of Xilinx Versal's OSPI controller.
+ *
+ * Copyright (c) 2021 Xilinx Inc.
+ * Written by Francisco Iglesias <francisco.iglesias@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "hw/qdev-properties.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "hw/irq.h"
+#include "hw/ssi/xlnx-versal-ospi.h"
+
+#ifndef XILINX_VERSAL_OSPI_ERR_DEBUG
+#define XILINX_VERSAL_OSPI_ERR_DEBUG 0
+#endif
+
+REG32(CONFIG_REG, 0x0)
+ FIELD(CONFIG_REG, IDLE_FLD, 31, 1)
+ FIELD(CONFIG_REG, DUAL_BYTE_OPCODE_EN_FLD, 30, 1)
+ FIELD(CONFIG_REG, CRC_ENABLE_FLD, 29, 1)
+ FIELD(CONFIG_REG, CONFIG_RESV2_FLD, 26, 3)
+ FIELD(CONFIG_REG, PIPELINE_PHY_FLD, 25, 1)
+ FIELD(CONFIG_REG, ENABLE_DTR_PROTOCOL_FLD, 24, 1)
+ FIELD(CONFIG_REG, ENABLE_AHB_DECODER_FLD, 23, 1)
+ FIELD(CONFIG_REG, MSTR_BAUD_DIV_FLD, 19, 4)
+ FIELD(CONFIG_REG, ENTER_XIP_MODE_IMM_FLD, 18, 1)
+ FIELD(CONFIG_REG, ENTER_XIP_MODE_FLD, 17, 1)
+ FIELD(CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD, 16, 1)
+ FIELD(CONFIG_REG, ENB_DMA_IF_FLD, 15, 1)
+ FIELD(CONFIG_REG, WR_PROT_FLASH_FLD, 14, 1)
+ FIELD(CONFIG_REG, PERIPH_CS_LINES_FLD, 10, 4)
+ FIELD(CONFIG_REG, PERIPH_SEL_DEC_FLD, 9, 1)
+ FIELD(CONFIG_REG, ENB_LEGACY_IP_MODE_FLD, 8, 1)
+ FIELD(CONFIG_REG, ENB_DIR_ACC_CTLR_FLD, 7, 1)
+ FIELD(CONFIG_REG, RESET_CFG_FLD, 6, 1)
+ FIELD(CONFIG_REG, RESET_PIN_FLD, 5, 1)
+ FIELD(CONFIG_REG, HOLD_PIN_FLD, 4, 1)
+ FIELD(CONFIG_REG, PHY_MODE_ENABLE_FLD, 3, 1)
+ FIELD(CONFIG_REG, SEL_CLK_PHASE_FLD, 2, 1)
+ FIELD(CONFIG_REG, SEL_CLK_POL_FLD, 1, 1)
+ FIELD(CONFIG_REG, ENB_SPI_FLD, 0, 1)
+REG32(DEV_INSTR_RD_CONFIG_REG, 0x4)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV5_FLD, 29, 3)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, DUMMY_RD_CLK_CYCLES_FLD, 24, 5)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV4_FLD, 21, 3)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, MODE_BIT_ENABLE_FLD, 20, 1)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV3_FLD, 18, 2)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, DATA_XFER_TYPE_EXT_MODE_FLD, 16, 2)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, RD_INSTR_RESV2_FLD, 14, 2)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, ADDR_XFER_TYPE_STD_MODE_FLD, 12, 2)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, PRED_DIS_FLD, 11, 1)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, DDR_EN_FLD, 10, 1)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, INSTR_TYPE_FLD, 8, 2)
+ FIELD(DEV_INSTR_RD_CONFIG_REG, RD_OPCODE_NON_XIP_FLD, 0, 8)
+REG32(DEV_INSTR_WR_CONFIG_REG, 0x8)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV4_FLD, 29, 3)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, DUMMY_WR_CLK_CYCLES_FLD, 24, 5)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV3_FLD, 18, 6)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, DATA_XFER_TYPE_EXT_MODE_FLD, 16, 2)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV2_FLD, 14, 2)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, ADDR_XFER_TYPE_STD_MODE_FLD, 12, 2)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, WR_INSTR_RESV1_FLD, 9, 3)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD, 8, 1)
+ FIELD(DEV_INSTR_WR_CONFIG_REG, WR_OPCODE_FLD, 0, 8)
+REG32(DEV_DELAY_REG, 0xc)
+ FIELD(DEV_DELAY_REG, D_NSS_FLD, 24, 8)
+ FIELD(DEV_DELAY_REG, D_BTWN_FLD, 16, 8)
+ FIELD(DEV_DELAY_REG, D_AFTER_FLD, 8, 8)
+ FIELD(DEV_DELAY_REG, D_INIT_FLD, 0, 8)
+REG32(RD_DATA_CAPTURE_REG, 0x10)
+ FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV3_FLD, 20, 12)
+ FIELD(RD_DATA_CAPTURE_REG, DDR_READ_DELAY_FLD, 16, 4)
+ FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV2_FLD, 9, 7)
+ FIELD(RD_DATA_CAPTURE_REG, DQS_ENABLE_FLD, 8, 1)
+ FIELD(RD_DATA_CAPTURE_REG, RD_DATA_RESV1_FLD, 6, 2)
+ FIELD(RD_DATA_CAPTURE_REG, SAMPLE_EDGE_SEL_FLD, 5, 1)
+ FIELD(RD_DATA_CAPTURE_REG, DELAY_FLD, 1, 4)
+ FIELD(RD_DATA_CAPTURE_REG, BYPASS_FLD, 0, 1)
+REG32(DEV_SIZE_CONFIG_REG, 0x14)
+ FIELD(DEV_SIZE_CONFIG_REG, DEV_SIZE_RESV_FLD, 29, 3)
+ FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS3_FLD, 27, 2)
+ FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS2_FLD, 25, 2)
+ FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS1_FLD, 23, 2)
+ FIELD(DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS0_FLD, 21, 2)
+ FIELD(DEV_SIZE_CONFIG_REG, BYTES_PER_SUBSECTOR_FLD, 16, 5)
+ FIELD(DEV_SIZE_CONFIG_REG, BYTES_PER_DEVICE_PAGE_FLD, 4, 12)
+ FIELD(DEV_SIZE_CONFIG_REG, NUM_ADDR_BYTES_FLD, 0, 4)
+REG32(SRAM_PARTITION_CFG_REG, 0x18)
+ FIELD(SRAM_PARTITION_CFG_REG, SRAM_PARTITION_RESV_FLD, 8, 24)
+ FIELD(SRAM_PARTITION_CFG_REG, ADDR_FLD, 0, 8)
+REG32(IND_AHB_ADDR_TRIGGER_REG, 0x1c)
+REG32(DMA_PERIPH_CONFIG_REG, 0x20)
+ FIELD(DMA_PERIPH_CONFIG_REG, DMA_PERIPH_RESV2_FLD, 12, 20)
+ FIELD(DMA_PERIPH_CONFIG_REG, NUM_BURST_REQ_BYTES_FLD, 8, 4)
+ FIELD(DMA_PERIPH_CONFIG_REG, DMA_PERIPH_RESV1_FLD, 4, 4)
+ FIELD(DMA_PERIPH_CONFIG_REG, NUM_SINGLE_REQ_BYTES_FLD, 0, 4)
+REG32(REMAP_ADDR_REG, 0x24)
+REG32(MODE_BIT_CONFIG_REG, 0x28)
+ FIELD(MODE_BIT_CONFIG_REG, RX_CRC_DATA_LOW_FLD, 24, 8)
+ FIELD(MODE_BIT_CONFIG_REG, RX_CRC_DATA_UP_FLD, 16, 8)
+ FIELD(MODE_BIT_CONFIG_REG, CRC_OUT_ENABLE_FLD, 15, 1)
+ FIELD(MODE_BIT_CONFIG_REG, MODE_BIT_RESV1_FLD, 11, 4)
+ FIELD(MODE_BIT_CONFIG_REG, CHUNK_SIZE_FLD, 8, 3)
+ FIELD(MODE_BIT_CONFIG_REG, MODE_FLD, 0, 8)
+REG32(SRAM_FILL_REG, 0x2c)
+ FIELD(SRAM_FILL_REG, SRAM_FILL_INDAC_WRITE_FLD, 16, 16)
+ FIELD(SRAM_FILL_REG, SRAM_FILL_INDAC_READ_FLD, 0, 16)
+REG32(TX_THRESH_REG, 0x30)
+ FIELD(TX_THRESH_REG, TX_THRESH_RESV_FLD, 5, 27)
+ FIELD(TX_THRESH_REG, LEVEL_FLD, 0, 5)
+REG32(RX_THRESH_REG, 0x34)
+ FIELD(RX_THRESH_REG, RX_THRESH_RESV_FLD, 5, 27)
+ FIELD(RX_THRESH_REG, LEVEL_FLD, 0, 5)
+REG32(WRITE_COMPLETION_CTRL_REG, 0x38)
+ FIELD(WRITE_COMPLETION_CTRL_REG, POLL_REP_DELAY_FLD, 24, 8)
+ FIELD(WRITE_COMPLETION_CTRL_REG, POLL_COUNT_FLD, 16, 8)
+ FIELD(WRITE_COMPLETION_CTRL_REG, ENABLE_POLLING_EXP_FLD, 15, 1)
+ FIELD(WRITE_COMPLETION_CTRL_REG, DISABLE_POLLING_FLD, 14, 1)
+ FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_POLARITY_FLD, 13, 1)
+ FIELD(WRITE_COMPLETION_CTRL_REG, WR_COMP_CTRL_RESV1_FLD, 12, 1)
+ FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_ADDR_EN_FLD, 11, 1)
+ FIELD(WRITE_COMPLETION_CTRL_REG, POLLING_BIT_INDEX_FLD, 8, 3)
+ FIELD(WRITE_COMPLETION_CTRL_REG, OPCODE_FLD, 0, 8)
+REG32(NO_OF_POLLS_BEF_EXP_REG, 0x3c)
+REG32(IRQ_STATUS_REG, 0x40)
+ FIELD(IRQ_STATUS_REG, IRQ_STAT_RESV_FLD, 20, 12)
+ FIELD(IRQ_STATUS_REG, ECC_FAIL_FLD, 19, 1)
+ FIELD(IRQ_STATUS_REG, TX_CRC_CHUNK_BRK_FLD, 18, 1)
+ FIELD(IRQ_STATUS_REG, RX_CRC_DATA_VAL_FLD, 17, 1)
+ FIELD(IRQ_STATUS_REG, RX_CRC_DATA_ERR_FLD, 16, 1)
+ FIELD(IRQ_STATUS_REG, IRQ_STAT_RESV1_FLD, 15, 1)
+ FIELD(IRQ_STATUS_REG, STIG_REQ_INT_FLD, 14, 1)
+ FIELD(IRQ_STATUS_REG, POLL_EXP_INT_FLD, 13, 1)
+ FIELD(IRQ_STATUS_REG, INDRD_SRAM_FULL_FLD, 12, 1)
+ FIELD(IRQ_STATUS_REG, RX_FIFO_FULL_FLD, 11, 1)
+ FIELD(IRQ_STATUS_REG, RX_FIFO_NOT_EMPTY_FLD, 10, 1)
+ FIELD(IRQ_STATUS_REG, TX_FIFO_FULL_FLD, 9, 1)
+ FIELD(IRQ_STATUS_REG, TX_FIFO_NOT_FULL_FLD, 8, 1)
+ FIELD(IRQ_STATUS_REG, RECV_OVERFLOW_FLD, 7, 1)
+ FIELD(IRQ_STATUS_REG, INDIRECT_XFER_LEVEL_BREACH_FLD, 6, 1)
+ FIELD(IRQ_STATUS_REG, ILLEGAL_ACCESS_DET_FLD, 5, 1)
+ FIELD(IRQ_STATUS_REG, PROT_WR_ATTEMPT_FLD, 4, 1)
+ FIELD(IRQ_STATUS_REG, INDIRECT_TRANSFER_REJECT_FLD, 3, 1)
+ FIELD(IRQ_STATUS_REG, INDIRECT_OP_DONE_FLD, 2, 1)
+ FIELD(IRQ_STATUS_REG, UNDERFLOW_DET_FLD, 1, 1)
+ FIELD(IRQ_STATUS_REG, MODE_M_FAIL_FLD, 0, 1)
+REG32(IRQ_MASK_REG, 0x44)
+ FIELD(IRQ_MASK_REG, IRQ_MASK_RESV_FLD, 20, 12)
+ FIELD(IRQ_MASK_REG, ECC_FAIL_MASK_FLD, 19, 1)
+ FIELD(IRQ_MASK_REG, TX_CRC_CHUNK_BRK_MASK_FLD, 18, 1)
+ FIELD(IRQ_MASK_REG, RX_CRC_DATA_VAL_MASK_FLD, 17, 1)
+ FIELD(IRQ_MASK_REG, RX_CRC_DATA_ERR_MASK_FLD, 16, 1)
+ FIELD(IRQ_MASK_REG, IRQ_MASK_RESV1_FLD, 15, 1)
+ FIELD(IRQ_MASK_REG, STIG_REQ_MASK_FLD, 14, 1)
+ FIELD(IRQ_MASK_REG, POLL_EXP_INT_MASK_FLD, 13, 1)
+ FIELD(IRQ_MASK_REG, INDRD_SRAM_FULL_MASK_FLD, 12, 1)
+ FIELD(IRQ_MASK_REG, RX_FIFO_FULL_MASK_FLD, 11, 1)
+ FIELD(IRQ_MASK_REG, RX_FIFO_NOT_EMPTY_MASK_FLD, 10, 1)
+ FIELD(IRQ_MASK_REG, TX_FIFO_FULL_MASK_FLD, 9, 1)
+ FIELD(IRQ_MASK_REG, TX_FIFO_NOT_FULL_MASK_FLD, 8, 1)
+ FIELD(IRQ_MASK_REG, RECV_OVERFLOW_MASK_FLD, 7, 1)
+ FIELD(IRQ_MASK_REG, INDIRECT_XFER_LEVEL_BREACH_MASK_FLD, 6, 1)
+ FIELD(IRQ_MASK_REG, ILLEGAL_ACCESS_DET_MASK_FLD, 5, 1)
+ FIELD(IRQ_MASK_REG, PROT_WR_ATTEMPT_MASK_FLD, 4, 1)
+ FIELD(IRQ_MASK_REG, INDIRECT_TRANSFER_REJECT_MASK_FLD, 3, 1)
+ FIELD(IRQ_MASK_REG, INDIRECT_OP_DONE_MASK_FLD, 2, 1)
+ FIELD(IRQ_MASK_REG, UNDERFLOW_DET_MASK_FLD, 1, 1)
+ FIELD(IRQ_MASK_REG, MODE_M_FAIL_MASK_FLD, 0, 1)
+REG32(LOWER_WR_PROT_REG, 0x50)
+REG32(UPPER_WR_PROT_REG, 0x54)
+REG32(WR_PROT_CTRL_REG, 0x58)
+ FIELD(WR_PROT_CTRL_REG, WR_PROT_CTRL_RESV_FLD, 2, 30)
+ FIELD(WR_PROT_CTRL_REG, ENB_FLD, 1, 1)
+ FIELD(WR_PROT_CTRL_REG, INV_FLD, 0, 1)
+REG32(INDIRECT_READ_XFER_CTRL_REG, 0x60)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, INDIR_RD_XFER_RESV_FLD, 8, 24)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, NUM_IND_OPS_DONE_FLD, 6, 2)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, IND_OPS_DONE_STATUS_FLD, 5, 1)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, RD_QUEUED_FLD, 4, 1)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, SRAM_FULL_FLD, 3, 1)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, RD_STATUS_FLD, 2, 1)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD, 1, 1)
+ FIELD(INDIRECT_READ_XFER_CTRL_REG, START_FLD, 0, 1)
+REG32(INDIRECT_READ_XFER_WATERMARK_REG, 0x64)
+REG32(INDIRECT_READ_XFER_START_REG, 0x68)
+REG32(INDIRECT_READ_XFER_NUM_BYTES_REG, 0x6c)
+REG32(INDIRECT_WRITE_XFER_CTRL_REG, 0x70)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, INDIR_WR_XFER_RESV2_FLD, 8, 24)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, NUM_IND_OPS_DONE_FLD, 6, 2)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, IND_OPS_DONE_STATUS_FLD, 5, 1)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, WR_QUEUED_FLD, 4, 1)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, INDIR_WR_XFER_RESV1_FLD, 3, 1)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, WR_STATUS_FLD, 2, 1)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD, 1, 1)
+ FIELD(INDIRECT_WRITE_XFER_CTRL_REG, START_FLD, 0, 1)
+REG32(INDIRECT_WRITE_XFER_WATERMARK_REG, 0x74)
+REG32(INDIRECT_WRITE_XFER_START_REG, 0x78)
+REG32(INDIRECT_WRITE_XFER_NUM_BYTES_REG, 0x7c)
+REG32(INDIRECT_TRIGGER_ADDR_RANGE_REG, 0x80)
+ FIELD(INDIRECT_TRIGGER_ADDR_RANGE_REG, IND_RANGE_RESV1_FLD, 4, 28)
+ FIELD(INDIRECT_TRIGGER_ADDR_RANGE_REG, IND_RANGE_WIDTH_FLD, 0, 4)
+REG32(FLASH_COMMAND_CTRL_MEM_REG, 0x8c)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV1_FLD, 29, 3)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_ADDR_FLD, 20, 9)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV2_FLD, 19, 1)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, NB_OF_STIG_READ_BYTES_FLD, 16, 3)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_READ_DATA_FLD, 8, 8)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, FLASH_COMMAND_CTRL_MEM_RESV3_FLD, 2, 6)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_REQ_IN_PROGRESS_FLD, 1, 1)
+ FIELD(FLASH_COMMAND_CTRL_MEM_REG, TRIGGER_MEM_BANK_REQ_FLD, 0, 1)
+REG32(FLASH_CMD_CTRL_REG, 0x90)
+ FIELD(FLASH_CMD_CTRL_REG, CMD_OPCODE_FLD, 24, 8)
+ FIELD(FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD, 23, 1)
+ FIELD(FLASH_CMD_CTRL_REG, NUM_RD_DATA_BYTES_FLD, 20, 3)
+ FIELD(FLASH_CMD_CTRL_REG, ENB_COMD_ADDR_FLD, 19, 1)
+ FIELD(FLASH_CMD_CTRL_REG, ENB_MODE_BIT_FLD, 18, 1)
+ FIELD(FLASH_CMD_CTRL_REG, NUM_ADDR_BYTES_FLD, 16, 2)
+ FIELD(FLASH_CMD_CTRL_REG, ENB_WRITE_DATA_FLD, 15, 1)
+ FIELD(FLASH_CMD_CTRL_REG, NUM_WR_DATA_BYTES_FLD, 12, 3)
+ FIELD(FLASH_CMD_CTRL_REG, NUM_DUMMY_CYCLES_FLD, 7, 5)
+ FIELD(FLASH_CMD_CTRL_REG, FLASH_CMD_CTRL_RESV1_FLD, 3, 4)
+ FIELD(FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD, 2, 1)
+ FIELD(FLASH_CMD_CTRL_REG, CMD_EXEC_STATUS_FLD, 1, 1)
+ FIELD(FLASH_CMD_CTRL_REG, CMD_EXEC_FLD, 0, 1)
+REG32(FLASH_CMD_ADDR_REG, 0x94)
+REG32(FLASH_RD_DATA_LOWER_REG, 0xa0)
+REG32(FLASH_RD_DATA_UPPER_REG, 0xa4)
+REG32(FLASH_WR_DATA_LOWER_REG, 0xa8)
+REG32(FLASH_WR_DATA_UPPER_REG, 0xac)
+REG32(POLLING_FLASH_STATUS_REG, 0xb0)
+ FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_RSVD_FLD2, 21, 11)
+ FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_NB_DUMMY, 16, 5)
+ FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_RSVD_FLD1, 9, 7)
+ FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_VALID_FLD, 8, 1)
+ FIELD(POLLING_FLASH_STATUS_REG, DEVICE_STATUS_FLD, 0, 8)
+REG32(PHY_CONFIGURATION_REG, 0xb4)
+ FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESYNC_FLD, 31, 1)
+ FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESET_FLD, 30, 1)
+ FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RX_DLL_BYPASS_FLD, 29, 1)
+ FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESV2_FLD, 23, 6)
+ FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_TX_DLL_DELAY_FLD, 16, 7)
+ FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RESV1_FLD, 7, 9)
+ FIELD(PHY_CONFIGURATION_REG, PHY_CONFIG_RX_DLL_DELAY_FLD, 0, 7)
+REG32(PHY_MASTER_CONTROL_REG, 0xb8)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV3_FLD, 25, 7)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_LOCK_MODE_FLD, 24, 1)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_BYPASS_MODE_FLD, 23, 1)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_PHASE_DETECT_SELECTOR_FLD, 20, 3)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV2_FLD, 19, 1)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_NB_INDICATIONS_FLD, 16, 3)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_CONTROL_RESV1_FLD, 7, 9)
+ FIELD(PHY_MASTER_CONTROL_REG, PHY_MASTER_INITIAL_DELAY_FLD, 0, 7)
+REG32(DLL_OBSERVABLE_LOWER_REG, 0xbc)
+ FIELD(DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_DLL_LOCK_INC_FLD, 24, 8)
+ FIELD(DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_DLL_LOCK_DEC_FLD, 16, 8)
+ FIELD(DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_LOOPBACK_LOCK_FLD, 15, 1)
+ FIELD(DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_LOCK_VALUE_FLD, 8, 7)
+ FIELD(DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_UNLOCK_COUNTER_FLD, 3, 5)
+ FIELD(DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_LOCK_MODE_FLD, 1, 2)
+ FIELD(DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_DLL_LOCK_FLD, 0, 1)
+REG32(DLL_OBSERVABLE_UPPER_REG, 0xc0)
+ FIELD(DLL_OBSERVABLE_UPPER_REG,
+ DLL_OBSERVABLE_UPPER_RESV2_FLD, 23, 9)
+ FIELD(DLL_OBSERVABLE_UPPER_REG,
+ DLL_OBSERVABLE_UPPER_TX_DECODER_OUTPUT_FLD, 16, 7)
+ FIELD(DLL_OBSERVABLE_UPPER_REG,
+ DLL_OBSERVABLE_UPPER_RESV1_FLD, 7, 9)
+ FIELD(DLL_OBSERVABLE_UPPER_REG,
+ DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD, 0, 7)
+REG32(OPCODE_EXT_LOWER_REG, 0xe0)
+ FIELD(OPCODE_EXT_LOWER_REG, EXT_READ_OPCODE_FLD, 24, 8)
+ FIELD(OPCODE_EXT_LOWER_REG, EXT_WRITE_OPCODE_FLD, 16, 8)
+ FIELD(OPCODE_EXT_LOWER_REG, EXT_POLL_OPCODE_FLD, 8, 8)
+ FIELD(OPCODE_EXT_LOWER_REG, EXT_STIG_OPCODE_FLD, 0, 8)
+REG32(OPCODE_EXT_UPPER_REG, 0xe4)
+ FIELD(OPCODE_EXT_UPPER_REG, WEL_OPCODE_FLD, 24, 8)
+ FIELD(OPCODE_EXT_UPPER_REG, EXT_WEL_OPCODE_FLD, 16, 8)
+ FIELD(OPCODE_EXT_UPPER_REG, OPCODE_EXT_UPPER_RESV1_FLD, 0, 16)
+REG32(MODULE_ID_REG, 0xfc)
+ FIELD(MODULE_ID_REG, FIX_PATCH_FLD, 24, 8)
+ FIELD(MODULE_ID_REG, MODULE_ID_FLD, 8, 16)
+ FIELD(MODULE_ID_REG, MODULE_ID_RESV_FLD, 2, 6)
+ FIELD(MODULE_ID_REG, CONF_FLD, 0, 2)
+
+#define RXFF_SZ 1024
+#define TXFF_SZ 1024
+
+#define MAX_RX_DEC_OUT 8
+
+#define SZ_512MBIT (512 * 1024 * 1024)
+#define SZ_1GBIT (1024 * 1024 * 1024)
+#define SZ_2GBIT (2ULL * SZ_1GBIT)
+#define SZ_4GBIT (4ULL * SZ_1GBIT)
+
+#define IS_IND_DMA_START(op) (op->done_bytes == 0)
+/*
+ * Bit field size of R_INDIRECT_WRITE_XFER_CTRL_REG_NUM_IND_OPS_DONE_FLD
+ * is 2 bits, which can record max of 3 indac operations.
+ */
+#define IND_OPS_DONE_MAX 3
+
+typedef enum {
+ WREN = 0x6,
+} FlashCMD;
+
+static unsigned int ospi_stig_addr_len(XlnxVersalOspi *s)
+{
+ /* Num address bytes is NUM_ADDR_BYTES_FLD + 1 */
+ return ARRAY_FIELD_EX32(s->regs,
+ FLASH_CMD_CTRL_REG, NUM_ADDR_BYTES_FLD) + 1;
+}
+
+static unsigned int ospi_stig_wr_data_len(XlnxVersalOspi *s)
+{
+ /* Num write data bytes is NUM_WR_DATA_BYTES_FLD + 1 */
+ return ARRAY_FIELD_EX32(s->regs,
+ FLASH_CMD_CTRL_REG, NUM_WR_DATA_BYTES_FLD) + 1;
+}
+
+static unsigned int ospi_stig_rd_data_len(XlnxVersalOspi *s)
+{
+ /* Num read data bytes is NUM_RD_DATA_BYTES_FLD + 1 */
+ return ARRAY_FIELD_EX32(s->regs,
+ FLASH_CMD_CTRL_REG, NUM_RD_DATA_BYTES_FLD) + 1;
+}
+
+/*
+ * Status bits in R_IRQ_STATUS_REG are set when the event occurs and the
+ * interrupt is enabled in the mask register ([1] Section 2.3.17)
+ */
+static void set_irq(XlnxVersalOspi *s, uint32_t set_mask)
+{
+ s->regs[R_IRQ_STATUS_REG] |= s->regs[R_IRQ_MASK_REG] & set_mask;
+}
+
+static void ospi_update_irq_line(XlnxVersalOspi *s)
+{
+ qemu_set_irq(s->irq, !!(s->regs[R_IRQ_STATUS_REG] &
+ s->regs[R_IRQ_MASK_REG]));
+}
+
+static uint8_t ospi_get_wr_opcode(XlnxVersalOspi *s)
+{
+ return ARRAY_FIELD_EX32(s->regs,
+ DEV_INSTR_WR_CONFIG_REG, WR_OPCODE_FLD);
+}
+
+static uint8_t ospi_get_rd_opcode(XlnxVersalOspi *s)
+{
+ return ARRAY_FIELD_EX32(s->regs,
+ DEV_INSTR_RD_CONFIG_REG, RD_OPCODE_NON_XIP_FLD);
+}
+
+static uint32_t ospi_get_num_addr_bytes(XlnxVersalOspi *s)
+{
+ /* Num address bytes is NUM_ADDR_BYTES_FLD + 1 */
+ return ARRAY_FIELD_EX32(s->regs,
+ DEV_SIZE_CONFIG_REG, NUM_ADDR_BYTES_FLD) + 1;
+}
+
+static void ospi_stig_membank_req(XlnxVersalOspi *s)
+{
+ int idx = ARRAY_FIELD_EX32(s->regs,
+ FLASH_COMMAND_CTRL_MEM_REG, MEM_BANK_ADDR_FLD);
+
+ ARRAY_FIELD_DP32(s->regs, FLASH_COMMAND_CTRL_MEM_REG,
+ MEM_BANK_READ_DATA_FLD, s->stig_membank[idx]);
+}
+
+static int ospi_stig_membank_rd_bytes(XlnxVersalOspi *s)
+{
+ int rd_data_fld = ARRAY_FIELD_EX32(s->regs, FLASH_COMMAND_CTRL_MEM_REG,
+ NB_OF_STIG_READ_BYTES_FLD);
+ static const int sizes[6] = { 16, 32, 64, 128, 256, 512 };
+ return (rd_data_fld < 6) ? sizes[rd_data_fld] : 0;
+}
+
+static uint32_t ospi_get_page_sz(XlnxVersalOspi *s)
+{
+ return ARRAY_FIELD_EX32(s->regs,
+ DEV_SIZE_CONFIG_REG, BYTES_PER_DEVICE_PAGE_FLD);
+}
+
+static bool ospi_ind_rd_watermark_enabled(XlnxVersalOspi *s)
+{
+ return s->regs[R_INDIRECT_READ_XFER_WATERMARK_REG];
+}
+
+static void ind_op_advance(IndOp *op, unsigned int len)
+{
+ op->done_bytes += len;
+ assert(op->done_bytes <= op->num_bytes);
+ if (op->done_bytes == op->num_bytes) {
+ op->completed = true;
+ }
+}
+
+static uint32_t ind_op_next_byte(IndOp *op)
+{
+ return op->flash_addr + op->done_bytes;
+}
+
+static uint32_t ind_op_end_byte(IndOp *op)
+{
+ return op->flash_addr + op->num_bytes;
+}
+
+static void ospi_ind_op_next(IndOp *op)
+{
+ op[0] = op[1];
+ op[1].completed = true;
+}
+
+static void ind_op_setup(IndOp *op, uint32_t flash_addr, uint32_t num_bytes)
+{
+ if (num_bytes & 0x3) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "OSPI indirect op num bytes not word aligned\n");
+ }
+ op->flash_addr = flash_addr;
+ op->num_bytes = num_bytes;
+ op->done_bytes = 0;
+ op->completed = false;
+}
+
+static bool ospi_ind_op_completed(IndOp *op)
+{
+ return op->completed;
+}
+
+static bool ospi_ind_op_all_completed(XlnxVersalOspi *s)
+{
+ return s->rd_ind_op[0].completed && s->wr_ind_op[0].completed;
+}
+
+static void ospi_ind_op_cancel(IndOp *op)
+{
+ op[0].completed = true;
+ op[1].completed = true;
+}
+
+static bool ospi_ind_op_add(IndOp *op, Fifo8 *fifo,
+ uint32_t flash_addr, uint32_t num_bytes)
+{
+ /* Check if first indirect op has been completed */
+ if (op->completed) {
+ fifo8_reset(fifo);
+ ind_op_setup(op, flash_addr, num_bytes);
+ return false;
+ }
+
+ /* Check if second indirect op has been completed */
+ op++;
+ if (op->completed) {
+ ind_op_setup(op, flash_addr, num_bytes);
+ return false;
+ }
+ return true;
+}
+
+static void ospi_ind_op_queue_up_rd(XlnxVersalOspi *s)
+{
+ uint32_t num_bytes = s->regs[R_INDIRECT_READ_XFER_NUM_BYTES_REG];
+ uint32_t flash_addr = s->regs[R_INDIRECT_READ_XFER_START_REG];
+ bool failed;
+
+ failed = ospi_ind_op_add(s->rd_ind_op, &s->rx_sram, flash_addr, num_bytes);
+ /* If two already queued set rd reject interrupt */
+ if (failed) {
+ set_irq(s, R_IRQ_STATUS_REG_INDIRECT_TRANSFER_REJECT_FLD_MASK);
+ }
+}
+
+static void ospi_ind_op_queue_up_wr(XlnxVersalOspi *s)
+{
+ uint32_t num_bytes = s->regs[R_INDIRECT_WRITE_XFER_NUM_BYTES_REG];
+ uint32_t flash_addr = s->regs[R_INDIRECT_WRITE_XFER_START_REG];
+ bool failed;
+
+ failed = ospi_ind_op_add(s->wr_ind_op, &s->tx_sram, flash_addr, num_bytes);
+ /* If two already queued set rd reject interrupt */
+ if (failed) {
+ set_irq(s, R_IRQ_STATUS_REG_INDIRECT_TRANSFER_REJECT_FLD_MASK);
+ }
+}
+
+static uint64_t flash_sz(XlnxVersalOspi *s, unsigned int cs)
+{
+ /* Flash sizes in MB */
+ static const uint64_t sizes[4] = { SZ_512MBIT / 8, SZ_1GBIT / 8,
+ SZ_2GBIT / 8, SZ_4GBIT / 8 };
+ uint32_t v = s->regs[R_DEV_SIZE_CONFIG_REG];
+
+ v >>= cs * R_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS0_FLD_LENGTH;
+ return sizes[FIELD_EX32(v, DEV_SIZE_CONFIG_REG, MEM_SIZE_ON_CS0_FLD)];
+}
+
+static unsigned int ospi_get_block_sz(XlnxVersalOspi *s)
+{
+ unsigned int block_fld = ARRAY_FIELD_EX32(s->regs,
+ DEV_SIZE_CONFIG_REG,
+ BYTES_PER_SUBSECTOR_FLD);
+ return 1 << block_fld;
+}
+
+static unsigned int flash_blocks(XlnxVersalOspi *s, unsigned int cs)
+{
+ unsigned int b_sz = ospi_get_block_sz(s);
+ unsigned int f_sz = flash_sz(s, cs);
+
+ return f_sz / b_sz;
+}
+
+static int ospi_ahb_decoder_cs(XlnxVersalOspi *s, hwaddr addr)
+{
+ uint64_t end_addr = 0;
+ int cs;
+
+ for (cs = 0; cs < s->num_cs; cs++) {
+ end_addr += flash_sz(s, cs);
+ if (addr < end_addr) {
+ break;
+ }
+ }
+
+ if (cs == s->num_cs) {
+ /* Address is out of range */
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "OSPI flash address does not fit in configuration\n");
+ return -1;
+ }
+ return cs;
+}
+
+static void ospi_ahb_decoder_enable_cs(XlnxVersalOspi *s, hwaddr addr)
+{
+ int cs = ospi_ahb_decoder_cs(s, addr);
+
+ if (cs >= 0) {
+ for (int i = 0; i < s->num_cs; i++) {
+ qemu_set_irq(s->cs_lines[i], cs != i);
+ }
+ }
+}
+
+static unsigned int single_cs(XlnxVersalOspi *s)
+{
+ unsigned int field = ARRAY_FIELD_EX32(s->regs,
+ CONFIG_REG, PERIPH_CS_LINES_FLD);
+
+ /*
+ * Below one liner is a trick that finds the rightmost zero and makes sure
+ * all other bits are turned to 1. It is a variant of the 'Isolate the
+ * rightmost 0-bit' trick found below at the time of writing:
+ *
+ * https://emre.me/computer-science/bit-manipulation-tricks/
+ *
+ * 4'bXXX0 -> 4'b1110
+ * 4'bXX01 -> 4'b1101
+ * 4'bX011 -> 4'b1011
+ * 4'b0111 -> 4'b0111
+ * 4'b1111 -> 4'b1111
+ */
+ return (field | ~(field + 1)) & 0xf;
+}
+
+static void ospi_update_cs_lines(XlnxVersalOspi *s)
+{
+ unsigned int all_cs;
+ int i;
+
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, PERIPH_SEL_DEC_FLD)) {
+ all_cs = ARRAY_FIELD_EX32(s->regs, CONFIG_REG, PERIPH_CS_LINES_FLD);
+ } else {
+ all_cs = single_cs(s);
+ }
+
+ for (i = 0; i < s->num_cs; i++) {
+ bool cs = (all_cs >> i) & 1;
+
+ qemu_set_irq(s->cs_lines[i], cs);
+ }
+}
+
+static void ospi_dac_cs(XlnxVersalOspi *s, hwaddr addr)
+{
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENABLE_AHB_DECODER_FLD)) {
+ ospi_ahb_decoder_enable_cs(s, addr);
+ } else {
+ ospi_update_cs_lines(s);
+ }
+}
+
+static void ospi_disable_cs(XlnxVersalOspi *s)
+{
+ int i;
+
+ for (i = 0; i < s->num_cs; i++) {
+ qemu_set_irq(s->cs_lines[i], 1);
+ }
+}
+
+static void ospi_flush_txfifo(XlnxVersalOspi *s)
+{
+ while (!fifo8_is_empty(&s->tx_fifo)) {
+ uint32_t tx_rx = fifo8_pop(&s->tx_fifo);
+
+ tx_rx = ssi_transfer(s->spi, tx_rx);
+ fifo8_push(&s->rx_fifo, tx_rx);
+ }
+}
+
+static void ospi_tx_fifo_push_address_raw(XlnxVersalOspi *s,
+ uint32_t flash_addr,
+ unsigned int addr_bytes)
+{
+ /* Push write address */
+ if (addr_bytes == 4) {
+ fifo8_push(&s->tx_fifo, flash_addr >> 24);
+ }
+ if (addr_bytes >= 3) {
+ fifo8_push(&s->tx_fifo, flash_addr >> 16);
+ }
+ if (addr_bytes >= 2) {
+ fifo8_push(&s->tx_fifo, flash_addr >> 8);
+ }
+ fifo8_push(&s->tx_fifo, flash_addr);
+}
+
+static void ospi_tx_fifo_push_address(XlnxVersalOspi *s, uint32_t flash_addr)
+{
+ /* Push write address */
+ int addr_bytes = ospi_get_num_addr_bytes(s);
+
+ ospi_tx_fifo_push_address_raw(s, flash_addr, addr_bytes);
+}
+
+static void ospi_tx_fifo_push_stig_addr(XlnxVersalOspi *s)
+{
+ uint32_t flash_addr = s->regs[R_FLASH_CMD_ADDR_REG];
+ unsigned int addr_bytes = ospi_stig_addr_len(s);
+
+ ospi_tx_fifo_push_address_raw(s, flash_addr, addr_bytes);
+}
+
+static void ospi_tx_fifo_push_rd_op_addr(XlnxVersalOspi *s, uint32_t flash_addr)
+{
+ uint8_t inst_code = ospi_get_rd_opcode(s);
+
+ fifo8_reset(&s->tx_fifo);
+
+ /* Push read opcode */
+ fifo8_push(&s->tx_fifo, inst_code);
+
+ /* Push read address */
+ ospi_tx_fifo_push_address(s, flash_addr);
+}
+
+static void ospi_tx_fifo_push_stig_wr_data(XlnxVersalOspi *s)
+{
+ uint64_t data = s->regs[R_FLASH_WR_DATA_LOWER_REG];
+ int wr_data_len = ospi_stig_wr_data_len(s);
+ int i;
+
+ data |= (uint64_t) s->regs[R_FLASH_WR_DATA_UPPER_REG] << 32;
+ for (i = 0; i < wr_data_len; i++) {
+ int shift = i * 8;
+ fifo8_push(&s->tx_fifo, data >> shift);
+ }
+}
+
+static void ospi_tx_fifo_push_stig_rd_data(XlnxVersalOspi *s)
+{
+ int rd_data_len;
+ int i;
+
+ if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD)) {
+ rd_data_len = ospi_stig_membank_rd_bytes(s);
+ } else {
+ rd_data_len = ospi_stig_rd_data_len(s);
+ }
+
+ /* transmit second part (data) */
+ for (i = 0; i < rd_data_len; ++i) {
+ fifo8_push(&s->tx_fifo, 0);
+ }
+}
+
+static void ospi_rx_fifo_pop_stig_rd_data(XlnxVersalOspi *s)
+{
+ int size = ospi_stig_rd_data_len(s);
+ uint8_t bytes[8] = {};
+ int i;
+
+ size = MIN(fifo8_num_used(&s->rx_fifo), size);
+
+ assert(size <= 8);
+
+ for (i = 0; i < size; i++) {
+ bytes[i] = fifo8_pop(&s->rx_fifo);
+ }
+
+ s->regs[R_FLASH_RD_DATA_LOWER_REG] = ldl_le_p(bytes);
+ s->regs[R_FLASH_RD_DATA_UPPER_REG] = ldl_le_p(bytes + 4);
+}
+
+static void ospi_ind_read(XlnxVersalOspi *s, uint32_t flash_addr, uint32_t len)
+{
+ int i;
+
+ /* Create first section of read cmd */
+ ospi_tx_fifo_push_rd_op_addr(s, flash_addr);
+
+ /* transmit first part */
+ ospi_update_cs_lines(s);
+ ospi_flush_txfifo(s);
+
+ fifo8_reset(&s->rx_fifo);
+
+ /* transmit second part (data) */
+ for (i = 0; i < len; ++i) {
+ fifo8_push(&s->tx_fifo, 0);
+ }
+ ospi_flush_txfifo(s);
+
+ for (i = 0; i < len; ++i) {
+ fifo8_push(&s->rx_sram, fifo8_pop(&s->rx_fifo));
+ }
+
+ /* done */
+ ospi_disable_cs(s);
+}
+
+static unsigned int ospi_dma_burst_size(XlnxVersalOspi *s)
+{
+ return 1 << ARRAY_FIELD_EX32(s->regs,
+ DMA_PERIPH_CONFIG_REG,
+ NUM_BURST_REQ_BYTES_FLD);
+}
+
+static unsigned int ospi_dma_single_size(XlnxVersalOspi *s)
+{
+ return 1 << ARRAY_FIELD_EX32(s->regs,
+ DMA_PERIPH_CONFIG_REG,
+ NUM_SINGLE_REQ_BYTES_FLD);
+}
+
+static void ind_rd_inc_num_done(XlnxVersalOspi *s)
+{
+ unsigned int done = ARRAY_FIELD_EX32(s->regs,
+ INDIRECT_READ_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD);
+ if (done < IND_OPS_DONE_MAX) {
+ done++;
+ }
+ done &= 0x3;
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD, done);
+}
+
+static void ospi_ind_rd_completed(XlnxVersalOspi *s)
+{
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG,
+ IND_OPS_DONE_STATUS_FLD, 1);
+
+ ind_rd_inc_num_done(s);
+ ospi_ind_op_next(s->rd_ind_op);
+ if (ospi_ind_op_all_completed(s)) {
+ set_irq(s, R_IRQ_STATUS_REG_INDIRECT_OP_DONE_FLD_MASK);
+ }
+}
+
+static void ospi_dma_read(XlnxVersalOspi *s)
+{
+ IndOp *op = s->rd_ind_op;
+ uint32_t dma_len = op->num_bytes;
+ uint32_t burst_sz = ospi_dma_burst_size(s);
+ uint32_t single_sz = ospi_dma_single_size(s);
+ uint32_t ind_trig_range;
+ uint32_t remainder;
+ XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_GET_CLASS(s->dma_src);
+
+ ind_trig_range = (1 << ARRAY_FIELD_EX32(s->regs,
+ INDIRECT_TRIGGER_ADDR_RANGE_REG,
+ IND_RANGE_WIDTH_FLD));
+ remainder = dma_len % burst_sz;
+ remainder = remainder % single_sz;
+ if (burst_sz > ind_trig_range || single_sz > ind_trig_range ||
+ remainder != 0) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "OSPI DMA burst size / single size config error\n");
+ }
+
+ s->src_dma_inprog = true;
+ if (xcdc->read(s->dma_src, 0, dma_len) != MEMTX_OK) {
+ qemu_log_mask(LOG_GUEST_ERROR, "OSPI DMA configuration error\n");
+ }
+ s->src_dma_inprog = false;
+}
+
+static void ospi_do_ind_read(XlnxVersalOspi *s)
+{
+ IndOp *op = s->rd_ind_op;
+ uint32_t next_b;
+ uint32_t end_b;
+ uint32_t len;
+ bool start_dma = IS_IND_DMA_START(op) && !s->src_dma_inprog;
+
+ /* Continue to read flash until we run out of space in sram */
+ while (!ospi_ind_op_completed(op) &&
+ !fifo8_is_full(&s->rx_sram)) {
+ /* Read reqested number of bytes, max bytes limited to size of sram */
+ next_b = ind_op_next_byte(op);
+ end_b = next_b + fifo8_num_free(&s->rx_sram);
+ end_b = MIN(end_b, ind_op_end_byte(op));
+
+ len = end_b - next_b;
+ ospi_ind_read(s, next_b, len);
+ ind_op_advance(op, len);
+
+ if (ospi_ind_rd_watermark_enabled(s)) {
+ ARRAY_FIELD_DP32(s->regs, IRQ_STATUS_REG,
+ INDIRECT_XFER_LEVEL_BREACH_FLD, 1);
+ set_irq(s,
+ R_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_FLD_MASK);
+ }
+
+ if (!s->src_dma_inprog &&
+ ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD)) {
+ ospi_dma_read(s);
+ }
+ }
+
+ /* Set sram full */
+ if (fifo8_num_used(&s->rx_sram) == RXFF_SZ) {
+ ARRAY_FIELD_DP32(s->regs,
+ INDIRECT_READ_XFER_CTRL_REG, SRAM_FULL_FLD, 1);
+ set_irq(s, R_IRQ_STATUS_REG_INDRD_SRAM_FULL_FLD_MASK);
+ }
+
+ /* Signal completion if done, unless inside recursion via ospi_dma_read */
+ if (!ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD) || start_dma) {
+ if (ospi_ind_op_completed(op)) {
+ ospi_ind_rd_completed(s);
+ }
+ }
+}
+
+/* Transmit write enable instruction */
+static void ospi_transmit_wel(XlnxVersalOspi *s, bool ahb_decoder_cs,
+ hwaddr addr)
+{
+ fifo8_reset(&s->tx_fifo);
+ fifo8_push(&s->tx_fifo, WREN);
+
+ if (ahb_decoder_cs) {
+ ospi_ahb_decoder_enable_cs(s, addr);
+ } else {
+ ospi_update_cs_lines(s);
+ }
+
+ ospi_flush_txfifo(s);
+ ospi_disable_cs(s);
+
+ fifo8_reset(&s->rx_fifo);
+}
+
+static void ospi_ind_write(XlnxVersalOspi *s, uint32_t flash_addr, uint32_t len)
+{
+ bool ahb_decoder_cs = false;
+ uint8_t inst_code;
+ int i;
+
+ assert(fifo8_num_used(&s->tx_sram) >= len);
+
+ if (!ARRAY_FIELD_EX32(s->regs, DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD)) {
+ ospi_transmit_wel(s, ahb_decoder_cs, 0);
+ }
+
+ /* reset fifos */
+ fifo8_reset(&s->tx_fifo);
+ fifo8_reset(&s->rx_fifo);
+
+ /* Push write opcode */
+ inst_code = ospi_get_wr_opcode(s);
+ fifo8_push(&s->tx_fifo, inst_code);
+
+ /* Push write address */
+ ospi_tx_fifo_push_address(s, flash_addr);
+
+ /* data */
+ for (i = 0; i < len; i++) {
+ fifo8_push(&s->tx_fifo, fifo8_pop(&s->tx_sram));
+ }
+
+ /* transmit */
+ ospi_update_cs_lines(s);
+ ospi_flush_txfifo(s);
+
+ /* done */
+ ospi_disable_cs(s);
+ fifo8_reset(&s->rx_fifo);
+}
+
+static void ind_wr_inc_num_done(XlnxVersalOspi *s)
+{
+ unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD);
+ if (done < IND_OPS_DONE_MAX) {
+ done++;
+ }
+ done &= 0x3;
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD, done);
+}
+
+static void ospi_ind_wr_completed(XlnxVersalOspi *s)
+{
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG,
+ IND_OPS_DONE_STATUS_FLD, 1);
+ ind_wr_inc_num_done(s);
+ ospi_ind_op_next(s->wr_ind_op);
+ /* Set indirect op done interrupt if enabled */
+ if (ospi_ind_op_all_completed(s)) {
+ set_irq(s, R_IRQ_STATUS_REG_INDIRECT_OP_DONE_FLD_MASK);
+ }
+}
+
+static void ospi_do_indirect_write(XlnxVersalOspi *s)
+{
+ uint32_t write_watermark = s->regs[R_INDIRECT_WRITE_XFER_WATERMARK_REG];
+ uint32_t pagesz = ospi_get_page_sz(s);
+ uint32_t page_mask = ~(pagesz - 1);
+ IndOp *op = s->wr_ind_op;
+ uint32_t next_b;
+ uint32_t end_b;
+ uint32_t len;
+
+ /* Write out tx_fifo in maximum page sz chunks */
+ while (!ospi_ind_op_completed(op) && fifo8_num_used(&s->tx_sram) > 0) {
+ next_b = ind_op_next_byte(op);
+ end_b = next_b + MIN(fifo8_num_used(&s->tx_sram), pagesz);
+
+ /* Dont cross page boundary */
+ if ((end_b & page_mask) > next_b) {
+ end_b &= page_mask;
+ }
+
+ len = end_b - next_b;
+ len = MIN(len, op->num_bytes - op->done_bytes);
+ ospi_ind_write(s, next_b, len);
+ ind_op_advance(op, len);
+ }
+
+ /*
+ * Always set indirect transfer level breached interrupt if enabled
+ * (write watermark > 0) since the tx_sram always will be emptied
+ */
+ if (write_watermark > 0) {
+ set_irq(s, R_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_FLD_MASK);
+ }
+
+ /* Signal completions if done */
+ if (ospi_ind_op_completed(op)) {
+ ospi_ind_wr_completed(s);
+ }
+}
+
+static void ospi_stig_fill_membank(XlnxVersalOspi *s)
+{
+ int num_rd_bytes = ospi_stig_membank_rd_bytes(s);
+ int idx = num_rd_bytes - 8; /* first of last 8 */
+ int i;
+
+ for (i = 0; i < num_rd_bytes; i++) {
+ s->stig_membank[i] = fifo8_pop(&s->rx_fifo);
+ }
+
+ g_assert((idx + 4) < ARRAY_SIZE(s->stig_membank));
+
+ /* Fill in lower upper regs */
+ s->regs[R_FLASH_RD_DATA_LOWER_REG] = ldl_le_p(&s->stig_membank[idx]);
+ s->regs[R_FLASH_RD_DATA_UPPER_REG] = ldl_le_p(&s->stig_membank[idx + 4]);
+}
+
+static void ospi_stig_cmd_exec(XlnxVersalOspi *s)
+{
+ uint8_t inst_code;
+
+ /* Reset fifos */
+ fifo8_reset(&s->tx_fifo);
+ fifo8_reset(&s->rx_fifo);
+
+ /* Push write opcode */
+ inst_code = ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, CMD_OPCODE_FLD);
+ fifo8_push(&s->tx_fifo, inst_code);
+
+ /* Push address if enabled */
+ if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_COMD_ADDR_FLD)) {
+ ospi_tx_fifo_push_stig_addr(s);
+ }
+
+ /* Enable cs */
+ ospi_update_cs_lines(s);
+
+ /* Data */
+ if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_WRITE_DATA_FLD)) {
+ ospi_tx_fifo_push_stig_wr_data(s);
+ } else if (ARRAY_FIELD_EX32(s->regs,
+ FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD)) {
+ /* transmit first part */
+ ospi_flush_txfifo(s);
+ fifo8_reset(&s->rx_fifo);
+ ospi_tx_fifo_push_stig_rd_data(s);
+ }
+
+ /* Transmit */
+ ospi_flush_txfifo(s);
+ ospi_disable_cs(s);
+
+ if (ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, ENB_READ_DATA_FLD)) {
+ if (ARRAY_FIELD_EX32(s->regs,
+ FLASH_CMD_CTRL_REG, STIG_MEM_BANK_EN_FLD)) {
+ ospi_stig_fill_membank(s);
+ } else {
+ ospi_rx_fifo_pop_stig_rd_data(s);
+ }
+ }
+}
+
+static uint32_t ospi_block_address(XlnxVersalOspi *s, unsigned int block)
+{
+ unsigned int block_sz = ospi_get_block_sz(s);
+ unsigned int cs = 0;
+ uint32_t addr = 0;
+
+ while (cs < s->num_cs && block >= flash_blocks(s, cs)) {
+ block -= flash_blocks(s, 0);
+ addr += flash_sz(s, cs);
+ }
+ addr += block * block_sz;
+ return addr;
+}
+
+static uint32_t ospi_get_wr_prot_addr_low(XlnxVersalOspi *s)
+{
+ unsigned int block = s->regs[R_LOWER_WR_PROT_REG];
+
+ return ospi_block_address(s, block);
+}
+
+static uint32_t ospi_get_wr_prot_addr_upper(XlnxVersalOspi *s)
+{
+ unsigned int block = s->regs[R_UPPER_WR_PROT_REG];
+
+ /* Get address of first block out of defined range */
+ return ospi_block_address(s, block + 1);
+}
+
+static bool ospi_is_write_protected(XlnxVersalOspi *s, hwaddr addr)
+{
+ uint32_t wr_prot_addr_upper = ospi_get_wr_prot_addr_upper(s);
+ uint32_t wr_prot_addr_low = ospi_get_wr_prot_addr_low(s);
+ bool in_range = false;
+
+ if (addr >= wr_prot_addr_low && addr < wr_prot_addr_upper) {
+ in_range = true;
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, WR_PROT_CTRL_REG, INV_FLD)) {
+ in_range = !in_range;
+ }
+ return in_range;
+}
+
+static uint64_t ospi_rx_sram_read(XlnxVersalOspi *s, unsigned int size)
+{
+ uint8_t bytes[8] = {};
+ int i;
+
+ if (size < 4 && fifo8_num_used(&s->rx_sram) >= 4) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "OSPI only last read of internal "
+ "sram is allowed to be < 32 bits\n");
+ }
+
+ size = MIN(fifo8_num_used(&s->rx_sram), size);
+
+ assert(size <= 8);
+
+ for (i = 0; i < size; i++) {
+ bytes[i] = fifo8_pop(&s->rx_sram);
+ }
+
+ return ldq_le_p(bytes);
+}
+
+static void ospi_tx_sram_write(XlnxVersalOspi *s, uint64_t value,
+ unsigned int size)
+{
+ int i;
+ for (i = 0; i < size && !fifo8_is_full(&s->tx_sram); i++) {
+ fifo8_push(&s->tx_sram, value >> 8 * i);
+ }
+}
+
+static uint64_t ospi_do_dac_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque);
+ uint8_t bytes[8] = {};
+ int i;
+
+ /* Create first section of read cmd */
+ ospi_tx_fifo_push_rd_op_addr(s, (uint32_t) addr);
+
+ /* Enable cs and transmit first part */
+ ospi_dac_cs(s, addr);
+ ospi_flush_txfifo(s);
+
+ fifo8_reset(&s->rx_fifo);
+
+ /* transmit second part (data) */
+ for (i = 0; i < size; ++i) {
+ fifo8_push(&s->tx_fifo, 0);
+ }
+ ospi_flush_txfifo(s);
+
+ /* fill in result */
+ size = MIN(fifo8_num_used(&s->rx_fifo), size);
+
+ assert(size <= 8);
+
+ for (i = 0; i < size; i++) {
+ bytes[i] = fifo8_pop(&s->rx_fifo);
+ }
+
+ /* done */
+ ospi_disable_cs(s);
+
+ return ldq_le_p(bytes);
+}
+
+static void ospi_do_dac_write(void *opaque,
+ hwaddr addr,
+ uint64_t value,
+ unsigned int size)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque);
+ bool ahb_decoder_cs = ARRAY_FIELD_EX32(s->regs, CONFIG_REG,
+ ENABLE_AHB_DECODER_FLD);
+ uint8_t inst_code;
+ unsigned int i;
+
+ if (!ARRAY_FIELD_EX32(s->regs, DEV_INSTR_WR_CONFIG_REG, WEL_DIS_FLD)) {
+ ospi_transmit_wel(s, ahb_decoder_cs, addr);
+ }
+
+ /* reset fifos */
+ fifo8_reset(&s->tx_fifo);
+ fifo8_reset(&s->rx_fifo);
+
+ /* Push write opcode */
+ inst_code = ospi_get_wr_opcode(s);
+ fifo8_push(&s->tx_fifo, inst_code);
+
+ /* Push write address */
+ ospi_tx_fifo_push_address(s, addr);
+
+ /* data */
+ for (i = 0; i < size; i++) {
+ fifo8_push(&s->tx_fifo, value >> 8 * i);
+ }
+
+ /* Enable cs and transmit */
+ ospi_dac_cs(s, addr);
+ ospi_flush_txfifo(s);
+ ospi_disable_cs(s);
+
+ fifo8_reset(&s->rx_fifo);
+}
+
+static void flash_cmd_ctrl_mem_reg_post_write(RegisterInfo *reg,
+ uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) {
+ if (ARRAY_FIELD_EX32(s->regs,
+ FLASH_COMMAND_CTRL_MEM_REG,
+ TRIGGER_MEM_BANK_REQ_FLD)) {
+ ospi_stig_membank_req(s);
+ ARRAY_FIELD_DP32(s->regs, FLASH_COMMAND_CTRL_MEM_REG,
+ TRIGGER_MEM_BANK_REQ_FLD, 0);
+ }
+ }
+}
+
+static void flash_cmd_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD) &&
+ ARRAY_FIELD_EX32(s->regs, FLASH_CMD_CTRL_REG, CMD_EXEC_FLD)) {
+ ospi_stig_cmd_exec(s);
+ set_irq(s, R_IRQ_STATUS_REG_STIG_REQ_INT_FLD_MASK);
+ ARRAY_FIELD_DP32(s->regs, FLASH_CMD_CTRL_REG, CMD_EXEC_FLD, 0);
+ }
+}
+
+static uint64_t ind_wr_dec_num_done(XlnxVersalOspi *s, uint64_t val)
+{
+ unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD);
+ done--;
+ done &= 0x3;
+ val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD, done);
+ return val;
+}
+
+static bool ind_wr_clearing_op_done(XlnxVersalOspi *s, uint64_t new_val)
+{
+ bool set_in_reg = ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG,
+ IND_OPS_DONE_STATUS_FLD);
+ bool set_in_new_val = FIELD_EX32(new_val, INDIRECT_WRITE_XFER_CTRL_REG,
+ IND_OPS_DONE_STATUS_FLD);
+ /* return true if clearing bit */
+ return set_in_reg && !set_in_new_val;
+}
+
+static uint64_t ind_wr_xfer_ctrl_reg_pre_write(RegisterInfo *reg,
+ uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+
+ if (ind_wr_clearing_op_done(s, val)) {
+ val = ind_wr_dec_num_done(s, val);
+ }
+ return val;
+}
+
+static void ind_wr_xfer_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+
+ if (s->ind_write_disabled) {
+ return;
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, START_FLD)) {
+ ospi_ind_op_queue_up_wr(s);
+ ospi_do_indirect_write(s);
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, START_FLD, 0);
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD)) {
+ ospi_ind_op_cancel(s->wr_ind_op);
+ fifo8_reset(&s->tx_sram);
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_WRITE_XFER_CTRL_REG, CANCEL_FLD, 0);
+ }
+}
+
+static uint64_t ind_wr_xfer_ctrl_reg_post_read(RegisterInfo *reg,
+ uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+ IndOp *op = s->wr_ind_op;
+
+ /* Check if ind ops is ongoing */
+ if (!ospi_ind_op_completed(&op[0])) {
+ /* Check if two ind ops are queued */
+ if (!ospi_ind_op_completed(&op[1])) {
+ val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG,
+ WR_QUEUED_FLD, 1);
+ }
+ val = FIELD_DP32(val, INDIRECT_WRITE_XFER_CTRL_REG, WR_STATUS_FLD, 1);
+ }
+ return val;
+}
+
+static uint64_t ind_rd_dec_num_done(XlnxVersalOspi *s, uint64_t val)
+{
+ unsigned int done = ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD);
+ done--;
+ done &= 0x3;
+ val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG,
+ NUM_IND_OPS_DONE_FLD, done);
+ return val;
+}
+
+static uint64_t ind_rd_xfer_ctrl_reg_pre_write(RegisterInfo *reg,
+ uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+
+ if (FIELD_EX32(val, INDIRECT_READ_XFER_CTRL_REG,
+ IND_OPS_DONE_STATUS_FLD)) {
+ val = ind_rd_dec_num_done(s, val);
+ val &= ~R_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_FLD_MASK;
+ }
+ return val;
+}
+
+static void ind_rd_xfer_ctrl_reg_post_write(RegisterInfo *reg, uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+
+ if (ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, START_FLD)) {
+ ospi_ind_op_queue_up_rd(s);
+ ospi_do_ind_read(s);
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, START_FLD, 0);
+ }
+
+ if (ARRAY_FIELD_EX32(s->regs, INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD)) {
+ ospi_ind_op_cancel(s->rd_ind_op);
+ fifo8_reset(&s->rx_sram);
+ ARRAY_FIELD_DP32(s->regs, INDIRECT_READ_XFER_CTRL_REG, CANCEL_FLD, 0);
+ }
+}
+
+static uint64_t ind_rd_xfer_ctrl_reg_post_read(RegisterInfo *reg,
+ uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+ IndOp *op = s->rd_ind_op;
+
+ /* Check if ind ops is ongoing */
+ if (!ospi_ind_op_completed(&op[0])) {
+ /* Check if two ind ops are queued */
+ if (!ospi_ind_op_completed(&op[1])) {
+ val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG,
+ RD_QUEUED_FLD, 1);
+ }
+ val = FIELD_DP32(val, INDIRECT_READ_XFER_CTRL_REG, RD_STATUS_FLD, 1);
+ }
+ return val;
+}
+
+static uint64_t sram_fill_reg_post_read(RegisterInfo *reg, uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+ val = ((fifo8_num_used(&s->tx_sram) & 0xFFFF) << 16) |
+ (fifo8_num_used(&s->rx_sram) & 0xFFFF);
+ return val;
+}
+
+static uint64_t dll_obs_upper_reg_post_read(RegisterInfo *reg, uint64_t val)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(reg->opaque);
+ uint32_t rx_dec_out;
+
+ rx_dec_out = FIELD_EX32(val, DLL_OBSERVABLE_UPPER_REG,
+ DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD);
+
+ if (rx_dec_out < MAX_RX_DEC_OUT) {
+ ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_UPPER_REG,
+ DLL_OBSERVABLE__UPPER_RX_DECODER_OUTPUT_FLD,
+ rx_dec_out + 1);
+ }
+
+ return val;
+}
+
+
+static void xlnx_versal_ospi_reset(DeviceState *dev)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(dev);
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
+ register_reset(&s->regs_info[i]);
+ }
+
+ fifo8_reset(&s->rx_fifo);
+ fifo8_reset(&s->tx_fifo);
+ fifo8_reset(&s->rx_sram);
+ fifo8_reset(&s->tx_sram);
+
+ s->rd_ind_op[0].completed = true;
+ s->rd_ind_op[1].completed = true;
+ s->wr_ind_op[0].completed = true;
+ s->wr_ind_op[1].completed = true;
+ ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_DLL_LOCK_FLD, 1);
+ ARRAY_FIELD_DP32(s->regs, DLL_OBSERVABLE_LOWER_REG,
+ DLL_OBSERVABLE_LOWER_LOOPBACK_LOCK_FLD, 1);
+}
+
+static RegisterAccessInfo ospi_regs_info[] = {
+ { .name = "CONFIG_REG",
+ .addr = A_CONFIG_REG,
+ .reset = 0x80780081,
+ .ro = 0x9c000000,
+ },{ .name = "DEV_INSTR_RD_CONFIG_REG",
+ .addr = A_DEV_INSTR_RD_CONFIG_REG,
+ .reset = 0x3,
+ .ro = 0xe0ecc800,
+ },{ .name = "DEV_INSTR_WR_CONFIG_REG",
+ .addr = A_DEV_INSTR_WR_CONFIG_REG,
+ .reset = 0x2,
+ .ro = 0xe0fcce00,
+ },{ .name = "DEV_DELAY_REG",
+ .addr = A_DEV_DELAY_REG,
+ },{ .name = "RD_DATA_CAPTURE_REG",
+ .addr = A_RD_DATA_CAPTURE_REG,
+ .reset = 0x1,
+ .ro = 0xfff0fec0,
+ },{ .name = "DEV_SIZE_CONFIG_REG",
+ .addr = A_DEV_SIZE_CONFIG_REG,
+ .reset = 0x101002,
+ .ro = 0xe0000000,
+ },{ .name = "SRAM_PARTITION_CFG_REG",
+ .addr = A_SRAM_PARTITION_CFG_REG,
+ .reset = 0x80,
+ .ro = 0xffffff00,
+ },{ .name = "IND_AHB_ADDR_TRIGGER_REG",
+ .addr = A_IND_AHB_ADDR_TRIGGER_REG,
+ },{ .name = "DMA_PERIPH_CONFIG_REG",
+ .addr = A_DMA_PERIPH_CONFIG_REG,
+ .ro = 0xfffff0f0,
+ },{ .name = "REMAP_ADDR_REG",
+ .addr = A_REMAP_ADDR_REG,
+ },{ .name = "MODE_BIT_CONFIG_REG",
+ .addr = A_MODE_BIT_CONFIG_REG,
+ .reset = 0x200,
+ .ro = 0xffff7800,
+ },{ .name = "SRAM_FILL_REG",
+ .addr = A_SRAM_FILL_REG,
+ .ro = 0xffffffff,
+ .post_read = sram_fill_reg_post_read,
+ },{ .name = "TX_THRESH_REG",
+ .addr = A_TX_THRESH_REG,
+ .reset = 0x1,
+ .ro = 0xffffffe0,
+ },{ .name = "RX_THRESH_REG",
+ .addr = A_RX_THRESH_REG,
+ .reset = 0x1,
+ .ro = 0xffffffe0,
+ },{ .name = "WRITE_COMPLETION_CTRL_REG",
+ .addr = A_WRITE_COMPLETION_CTRL_REG,
+ .reset = 0x10005,
+ .ro = 0x1800,
+ },{ .name = "NO_OF_POLLS_BEF_EXP_REG",
+ .addr = A_NO_OF_POLLS_BEF_EXP_REG,
+ .reset = 0xffffffff,
+ },{ .name = "IRQ_STATUS_REG",
+ .addr = A_IRQ_STATUS_REG,
+ .ro = 0xfff08000,
+ .w1c = 0xf7fff,
+ },{ .name = "IRQ_MASK_REG",
+ .addr = A_IRQ_MASK_REG,
+ .ro = 0xfff08000,
+ },{ .name = "LOWER_WR_PROT_REG",
+ .addr = A_LOWER_WR_PROT_REG,
+ },{ .name = "UPPER_WR_PROT_REG",
+ .addr = A_UPPER_WR_PROT_REG,
+ },{ .name = "WR_PROT_CTRL_REG",
+ .addr = A_WR_PROT_CTRL_REG,
+ .ro = 0xfffffffc,
+ },{ .name = "INDIRECT_READ_XFER_CTRL_REG",
+ .addr = A_INDIRECT_READ_XFER_CTRL_REG,
+ .ro = 0xffffffd4,
+ .w1c = 0x08,
+ .pre_write = ind_rd_xfer_ctrl_reg_pre_write,
+ .post_write = ind_rd_xfer_ctrl_reg_post_write,
+ .post_read = ind_rd_xfer_ctrl_reg_post_read,
+ },{ .name = "INDIRECT_READ_XFER_WATERMARK_REG",
+ .addr = A_INDIRECT_READ_XFER_WATERMARK_REG,
+ },{ .name = "INDIRECT_READ_XFER_START_REG",
+ .addr = A_INDIRECT_READ_XFER_START_REG,
+ },{ .name = "INDIRECT_READ_XFER_NUM_BYTES_REG",
+ .addr = A_INDIRECT_READ_XFER_NUM_BYTES_REG,
+ },{ .name = "INDIRECT_WRITE_XFER_CTRL_REG",
+ .addr = A_INDIRECT_WRITE_XFER_CTRL_REG,
+ .ro = 0xffffffdc,
+ .w1c = 0x20,
+ .pre_write = ind_wr_xfer_ctrl_reg_pre_write,
+ .post_write = ind_wr_xfer_ctrl_reg_post_write,
+ .post_read = ind_wr_xfer_ctrl_reg_post_read,
+ },{ .name = "INDIRECT_WRITE_XFER_WATERMARK_REG",
+ .addr = A_INDIRECT_WRITE_XFER_WATERMARK_REG,
+ .reset = 0xffffffff,
+ },{ .name = "INDIRECT_WRITE_XFER_START_REG",
+ .addr = A_INDIRECT_WRITE_XFER_START_REG,
+ },{ .name = "INDIRECT_WRITE_XFER_NUM_BYTES_REG",
+ .addr = A_INDIRECT_WRITE_XFER_NUM_BYTES_REG,
+ },{ .name = "INDIRECT_TRIGGER_ADDR_RANGE_REG",
+ .addr = A_INDIRECT_TRIGGER_ADDR_RANGE_REG,
+ .reset = 0x4,
+ .ro = 0xfffffff0,
+ },{ .name = "FLASH_COMMAND_CTRL_MEM_REG",
+ .addr = A_FLASH_COMMAND_CTRL_MEM_REG,
+ .ro = 0xe008fffe,
+ .post_write = flash_cmd_ctrl_mem_reg_post_write,
+ },{ .name = "FLASH_CMD_CTRL_REG",
+ .addr = A_FLASH_CMD_CTRL_REG,
+ .ro = 0x7a,
+ .post_write = flash_cmd_ctrl_reg_post_write,
+ },{ .name = "FLASH_CMD_ADDR_REG",
+ .addr = A_FLASH_CMD_ADDR_REG,
+ },{ .name = "FLASH_RD_DATA_LOWER_REG",
+ .addr = A_FLASH_RD_DATA_LOWER_REG,
+ .ro = 0xffffffff,
+ },{ .name = "FLASH_RD_DATA_UPPER_REG",
+ .addr = A_FLASH_RD_DATA_UPPER_REG,
+ .ro = 0xffffffff,
+ },{ .name = "FLASH_WR_DATA_LOWER_REG",
+ .addr = A_FLASH_WR_DATA_LOWER_REG,
+ },{ .name = "FLASH_WR_DATA_UPPER_REG",
+ .addr = A_FLASH_WR_DATA_UPPER_REG,
+ },{ .name = "POLLING_FLASH_STATUS_REG",
+ .addr = A_POLLING_FLASH_STATUS_REG,
+ .ro = 0xfff0ffff,
+ },{ .name = "PHY_CONFIGURATION_REG",
+ .addr = A_PHY_CONFIGURATION_REG,
+ .reset = 0x40000000,
+ .ro = 0x1f80ff80,
+ },{ .name = "PHY_MASTER_CONTROL_REG",
+ .addr = A_PHY_MASTER_CONTROL_REG,
+ .reset = 0x800000,
+ .ro = 0xfe08ff80,
+ },{ .name = "DLL_OBSERVABLE_LOWER_REG",
+ .addr = A_DLL_OBSERVABLE_LOWER_REG,
+ .ro = 0xffffffff,
+ },{ .name = "DLL_OBSERVABLE_UPPER_REG",
+ .addr = A_DLL_OBSERVABLE_UPPER_REG,
+ .ro = 0xffffffff,
+ .post_read = dll_obs_upper_reg_post_read,
+ },{ .name = "OPCODE_EXT_LOWER_REG",
+ .addr = A_OPCODE_EXT_LOWER_REG,
+ .reset = 0x13edfa00,
+ },{ .name = "OPCODE_EXT_UPPER_REG",
+ .addr = A_OPCODE_EXT_UPPER_REG,
+ .reset = 0x6f90000,
+ .ro = 0xffff,
+ },{ .name = "MODULE_ID_REG",
+ .addr = A_MODULE_ID_REG,
+ .reset = 0x300,
+ .ro = 0xffffffff,
+ }
+};
+
+/* Return dev-obj from reg-region created by register_init_block32 */
+static XlnxVersalOspi *xilinx_ospi_of_mr(void *mr_accessor)
+{
+ RegisterInfoArray *reg_array = mr_accessor;
+ Object *dev;
+
+ dev = reg_array->mem.owner;
+ assert(dev);
+
+ return XILINX_VERSAL_OSPI(dev);
+}
+
+static void ospi_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned int size)
+{
+ XlnxVersalOspi *s = xilinx_ospi_of_mr(opaque);
+
+ register_write_memory(opaque, addr, value, size);
+ ospi_update_irq_line(s);
+}
+
+static const MemoryRegionOps ospi_ops = {
+ .read = register_read_memory,
+ .write = ospi_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static uint64_t ospi_indac_read(void *opaque, unsigned int size)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque);
+ uint64_t ret = ospi_rx_sram_read(s, size);
+
+ if (!ospi_ind_op_completed(s->rd_ind_op)) {
+ ospi_do_ind_read(s);
+ }
+ return ret;
+}
+
+static void ospi_indac_write(void *opaque, uint64_t value, unsigned int size)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque);
+
+ g_assert(!s->ind_write_disabled);
+
+ if (!ospi_ind_op_completed(s->wr_ind_op)) {
+ ospi_tx_sram_write(s, value, size);
+ ospi_do_indirect_write(s);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "OSPI wr into indac area while no ongoing indac wr\n");
+ }
+}
+
+static bool is_inside_indac_range(XlnxVersalOspi *s, hwaddr addr)
+{
+ uint32_t range_start;
+ uint32_t range_end;
+
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DMA_IF_FLD)) {
+ return true;
+ }
+
+ range_start = s->regs[R_IND_AHB_ADDR_TRIGGER_REG];
+ range_end = range_start +
+ (1 << ARRAY_FIELD_EX32(s->regs,
+ INDIRECT_TRIGGER_ADDR_RANGE_REG,
+ IND_RANGE_WIDTH_FLD));
+
+ addr += s->regs[R_IND_AHB_ADDR_TRIGGER_REG] & 0xF0000000;
+
+ return addr >= range_start && addr < range_end;
+}
+
+static bool ospi_is_indac_active(XlnxVersalOspi *s)
+{
+ /*
+ * When dac and indac cannot be active at the same time,
+ * return true when dac is disabled.
+ */
+ return s->dac_with_indac || !s->dac_enable;
+}
+
+static uint64_t ospi_dac_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque);
+
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) {
+ if (ospi_is_indac_active(s) &&
+ is_inside_indac_range(s, addr)) {
+ return ospi_indac_read(s, size);
+ }
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DIR_ACC_CTLR_FLD)
+ && s->dac_enable) {
+ if (ARRAY_FIELD_EX32(s->regs,
+ CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD)) {
+ addr += s->regs[R_REMAP_ADDR_REG];
+ }
+ return ospi_do_dac_read(opaque, addr, size);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB rd while DAC disabled\n");
+ }
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB rd while OSPI disabled\n");
+ }
+
+ return 0;
+}
+
+static void ospi_dac_write(void *opaque, hwaddr addr, uint64_t value,
+ unsigned int size)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque);
+
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_SPI_FLD)) {
+ if (ospi_is_indac_active(s) &&
+ !s->ind_write_disabled &&
+ is_inside_indac_range(s, addr)) {
+ return ospi_indac_write(s, value, size);
+ }
+ if (ARRAY_FIELD_EX32(s->regs, CONFIG_REG, ENB_DIR_ACC_CTLR_FLD) &&
+ s->dac_enable) {
+ if (ARRAY_FIELD_EX32(s->regs,
+ CONFIG_REG, ENB_AHB_ADDR_REMAP_FLD)) {
+ addr += s->regs[R_REMAP_ADDR_REG];
+ }
+ /* Check if addr is write protected */
+ if (ARRAY_FIELD_EX32(s->regs, WR_PROT_CTRL_REG, ENB_FLD) &&
+ ospi_is_write_protected(s, addr)) {
+ set_irq(s, R_IRQ_STATUS_REG_PROT_WR_ATTEMPT_FLD_MASK);
+ ospi_update_irq_line(s);
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "OSPI writing into write protected area\n");
+ return;
+ }
+ ospi_do_dac_write(opaque, addr, value, size);
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB wr while DAC disabled\n");
+ }
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR, "OSPI AHB wr while OSPI disabled\n");
+ }
+}
+
+static const MemoryRegionOps ospi_dac_ops = {
+ .read = ospi_dac_read,
+ .write = ospi_dac_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
+};
+
+static void ospi_update_dac_status(void *opaque, int n, int level)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(opaque);
+
+ s->dac_enable = level;
+}
+
+static void xlnx_versal_ospi_realize(DeviceState *dev, Error **errp)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(dev);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ s->num_cs = 4;
+ s->spi = ssi_create_bus(dev, "spi0");
+ s->cs_lines = g_new0(qemu_irq, s->num_cs);
+ for (int i = 0; i < s->num_cs; ++i) {
+ sysbus_init_irq(sbd, &s->cs_lines[i]);
+ }
+
+ fifo8_create(&s->rx_fifo, RXFF_SZ);
+ fifo8_create(&s->tx_fifo, TXFF_SZ);
+ fifo8_create(&s->rx_sram, RXFF_SZ);
+ fifo8_create(&s->tx_sram, TXFF_SZ);
+}
+
+static void xlnx_versal_ospi_init(Object *obj)
+{
+ XlnxVersalOspi *s = XILINX_VERSAL_OSPI(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ DeviceState *dev = DEVICE(obj);
+ RegisterInfoArray *reg_array;
+
+ memory_region_init(&s->iomem, obj, TYPE_XILINX_VERSAL_OSPI,
+ XILINX_VERSAL_OSPI_R_MAX * 4);
+ reg_array =
+ register_init_block32(DEVICE(obj), ospi_regs_info,
+ ARRAY_SIZE(ospi_regs_info),
+ s->regs_info, s->regs,
+ &ospi_ops,
+ XILINX_VERSAL_OSPI_ERR_DEBUG,
+ XILINX_VERSAL_OSPI_R_MAX * 4);
+ memory_region_add_subregion(&s->iomem, 0x0, ®_array->mem);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ memory_region_init_io(&s->iomem_dac, obj, &ospi_dac_ops, s,
+ TYPE_XILINX_VERSAL_OSPI "-dac", 0x20000000);
+ sysbus_init_mmio(sbd, &s->iomem_dac);
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ object_property_add_link(obj, "dma-src", TYPE_XLNX_CSU_DMA,
+ (Object **)&s->dma_src,
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+
+ qdev_init_gpio_in_named(dev, ospi_update_dac_status, "ospi-mux-sel", 1);
+}
+
+static const VMStateDescription vmstate_ind_op = {
+ .name = "OSPIIndOp",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(flash_addr, IndOp),
+ VMSTATE_UINT32(num_bytes, IndOp),
+ VMSTATE_UINT32(done_bytes, IndOp),
+ VMSTATE_BOOL(completed, IndOp),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_xlnx_versal_ospi = {
+ .name = TYPE_XILINX_VERSAL_OSPI,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_FIFO8(rx_fifo, XlnxVersalOspi),
+ VMSTATE_FIFO8(tx_fifo, XlnxVersalOspi),
+ VMSTATE_FIFO8(rx_sram, XlnxVersalOspi),
+ VMSTATE_FIFO8(tx_sram, XlnxVersalOspi),
+ VMSTATE_BOOL(ind_write_disabled, XlnxVersalOspi),
+ VMSTATE_BOOL(dac_with_indac, XlnxVersalOspi),
+ VMSTATE_BOOL(dac_enable, XlnxVersalOspi),
+ VMSTATE_BOOL(src_dma_inprog, XlnxVersalOspi),
+ VMSTATE_STRUCT_ARRAY(rd_ind_op, XlnxVersalOspi, 2, 1,
+ vmstate_ind_op, IndOp),
+ VMSTATE_STRUCT_ARRAY(wr_ind_op, XlnxVersalOspi, 2, 1,
+ vmstate_ind_op, IndOp),
+ VMSTATE_UINT32_ARRAY(regs, XlnxVersalOspi, XILINX_VERSAL_OSPI_R_MAX),
+ VMSTATE_UINT8_ARRAY(stig_membank, XlnxVersalOspi, 512),
+ VMSTATE_END_OF_LIST(),
+ }
+};
+
+static Property xlnx_versal_ospi_properties[] = {
+ DEFINE_PROP_BOOL("dac-with-indac", XlnxVersalOspi, dac_with_indac, false),
+ DEFINE_PROP_BOOL("indac-write-disabled", XlnxVersalOspi,
+ ind_write_disabled, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void xlnx_versal_ospi_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->reset = xlnx_versal_ospi_reset;
+ dc->realize = xlnx_versal_ospi_realize;
+ dc->vmsd = &vmstate_xlnx_versal_ospi;
+ device_class_set_props(dc, xlnx_versal_ospi_properties);
+}
+
+static const TypeInfo xlnx_versal_ospi_info = {
+ .name = TYPE_XILINX_VERSAL_OSPI,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(XlnxVersalOspi),
+ .class_init = xlnx_versal_ospi_class_init,
+ .instance_init = xlnx_versal_ospi_init,
+};
+
+static void xlnx_versal_ospi_register_types(void)
+{
+ type_register_static(&xlnx_versal_ospi_info);
+}
+
+type_init(xlnx_versal_ospi_register_types)
#include "hw/sysbus.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
+#include "migration/vmstate.h"
#include "qemu/module.h"
#include "qemu/timer.h"
#include "hw/irq.h"
ptimer_state *ptimer_t1;
ptimer_state *ptimer_wd;
- int wd_hits;
+ uint32_t wd_hits;
/* Control registers. */
uint32_t rw_tmr0_div;
uint32_t r_masked_intr;
};
+static const VMStateDescription vmstate_etraxfs = {
+ .name = "etraxfs",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(ptimer_t0, ETRAXTimerState),
+ VMSTATE_PTIMER(ptimer_t1, ETRAXTimerState),
+ VMSTATE_PTIMER(ptimer_wd, ETRAXTimerState),
+
+ VMSTATE_UINT32(wd_hits, ETRAXTimerState),
+
+ VMSTATE_UINT32(rw_tmr0_div, ETRAXTimerState),
+ VMSTATE_UINT32(r_tmr0_data, ETRAXTimerState),
+ VMSTATE_UINT32(rw_tmr0_ctrl, ETRAXTimerState),
+
+ VMSTATE_UINT32(rw_tmr1_div, ETRAXTimerState),
+ VMSTATE_UINT32(r_tmr1_data, ETRAXTimerState),
+ VMSTATE_UINT32(rw_tmr1_ctrl, ETRAXTimerState),
+
+ VMSTATE_UINT32(rw_wd_ctrl, ETRAXTimerState),
+
+ VMSTATE_UINT32(rw_intr_mask, ETRAXTimerState),
+ VMSTATE_UINT32(rw_ack_intr, ETRAXTimerState),
+ VMSTATE_UINT32(r_intr, ETRAXTimerState),
+ VMSTATE_UINT32(r_masked_intr, ETRAXTimerState),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
static uint64_t
timer_read(void *opaque, hwaddr addr, unsigned int size)
{
ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->realize = etraxfs_timer_realize;
+ dc->vmsd = &vmstate_etraxfs;
rc->phases.enter = etraxfs_timer_reset_enter;
rc->phases.hold = etraxfs_timer_reset_hold;
}
#include "target/riscv/cpu.h"
#include "migration/vmstate.h"
-REG32(CTRL, 0x00)
+REG32(ALERT_TEST, 0x00)
+ FIELD(ALERT_TEST, FATAL_FAULT, 0, 1)
+REG32(CTRL, 0x04)
FIELD(CTRL, ACTIVE, 0, 1)
REG32(CFG0, 0x100)
FIELD(CFG0, PRESCALE, 0, 12)
s->timer_compare_upper0 = 0xFFFFFFFF;
s->timer_intr_enable = 0x00000000;
s->timer_intr_state = 0x00000000;
- s->timer_intr_test = 0x00000000;
ibex_timer_update_irqs(s);
}
uint64_t retvalue = 0;
switch (addr >> 2) {
+ case R_ALERT_TEST:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Attempted to read ALERT_TEST, a write only register");
+ break;
case R_CTRL:
retvalue = s->timer_ctrl;
break;
retvalue = s->timer_intr_state;
break;
case R_INTR_TEST:
- retvalue = s->timer_intr_test;
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "Attempted to read INTR_TEST, a write only register");
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
uint32_t val = val64;
switch (addr >> 2) {
+ case R_ALERT_TEST:
+ qemu_log_mask(LOG_UNIMP, "Alert triggering not supported");
+ break;
case R_CTRL:
s->timer_ctrl = val;
break;
s->timer_intr_state &= ~val;
break;
case R_INTR_TEST:
- s->timer_intr_test = val;
- if (s->timer_intr_enable &
- s->timer_intr_test &
- R_INTR_ENABLE_IE_0_MASK) {
+ if (s->timer_intr_enable & val & R_INTR_ENABLE_IE_0_MASK) {
s->timer_intr_state |= R_INTR_STATE_IS_0_MASK;
qemu_set_irq(s->irq, true);
}
static const VMStateDescription vmstate_ibex_timer = {
.name = TYPE_IBEX_TIMER,
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.post_load = ibex_timer_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(timer_ctrl, IbexTimerState),
VMSTATE_UINT32(timer_compare_upper0, IbexTimerState),
VMSTATE_UINT32(timer_intr_enable, IbexTimerState),
VMSTATE_UINT32(timer_intr_state, IbexTimerState),
- VMSTATE_UINT32(timer_intr_test, IbexTimerState),
VMSTATE_END_OF_LIST()
}
};
bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
const USBDesc *desc = usb_device_get_usb_desc(dev);
const USBDescDevice *other_dev;
- uint8_t buf[256];
+ size_t buflen = USB_DESC_MAX_LEN;
+ g_autofree uint8_t *buf = g_malloc(buflen);
uint8_t type = value >> 8;
uint8_t index = value & 0xff;
int flags, ret = -1;
switch(type) {
case USB_DT_DEVICE:
- ret = usb_desc_device(&desc->id, dev->device, msos, buf, sizeof(buf));
+ ret = usb_desc_device(&desc->id, dev->device, msos, buf, buflen);
trace_usb_desc_device(dev->addr, len, ret);
break;
case USB_DT_CONFIG:
if (index < dev->device->bNumConfigurations) {
ret = usb_desc_config(dev->device->confs + index, flags,
- buf, sizeof(buf));
+ buf, buflen);
}
trace_usb_desc_config(dev->addr, index, len, ret);
break;
case USB_DT_STRING:
- ret = usb_desc_string(dev, index, buf, sizeof(buf));
+ ret = usb_desc_string(dev, index, buf, buflen);
trace_usb_desc_string(dev->addr, index, len, ret);
break;
case USB_DT_DEVICE_QUALIFIER:
if (other_dev != NULL) {
- ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
+ ret = usb_desc_device_qualifier(other_dev, buf, buflen);
}
trace_usb_desc_device_qualifier(dev->addr, len, ret);
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (other_dev != NULL && index < other_dev->bNumConfigurations) {
ret = usb_desc_config(other_dev->confs + index, flags,
- buf, sizeof(buf));
+ buf, buflen);
buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
}
trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
break;
case USB_DT_BOS:
- ret = usb_desc_bos(desc, buf, sizeof(buf));
+ ret = usb_desc_bos(desc, buf, buflen);
trace_usb_desc_bos(dev->addr, len, ret);
break;
const USBDescMSOS *msos;
};
+#define USB_DESC_MAX_LEN 8192
#define USB_DESC_FLAG_SUPER (1 << 1)
/* little helpers */
p->status = USB_RET_STALL;
break;
}
+ return;
err_stream:
error_report("%s: invalid stream %d", __func__, p->stream);
[STR_SERIALNUMBER] = "1",
};
+static const uint8_t qemu_wacom_hid_report_descriptor[] = {
+ 0x05, 0x01, /* Usage Page (Desktop) */
+ 0x09, 0x02, /* Usage (Mouse) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x85, 0x01, /* Report ID (1) */
+ 0x09, 0x01, /* Usage (Pointer) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x05, 0x09, /* Usage Page (Button) */
+ 0x19, 0x01, /* Usage Minimum (01h) */
+ 0x29, 0x03, /* Usage Maximum (03h) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x25, 0x01, /* Logical Maximum (1) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x75, 0x01, /* Report Size (1) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0x75, 0x05, /* Report Size (5) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0x05, 0x01, /* Usage Page (Desktop) */
+ 0x09, 0x30, /* Usage (X) */
+ 0x09, 0x31, /* Usage (Y) */
+ 0x09, 0x38, /* Usage (Wheel) */
+ 0x15, 0x81, /* Logical Minimum (-127) */
+ 0x25, 0x7f, /* Logical Maximum (127) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x06, /* Input (Data, Variable, Relative) */
+ 0x95, 0x03, /* Report Count (3) */
+ 0x81, 0x01, /* Input (Constant) */
+ 0xc0, /* End Collection */
+ 0xc0, /* End Collection */
+ 0x05, 0x0d, /* Usage Page (Digitizer) */
+ 0x09, 0x01, /* Usage (Digitizer) */
+ 0xa1, 0x01, /* Collection (Application) */
+ 0x85, 0x02, /* Report ID (2) */
+ 0xa1, 0x00, /* Collection (Physical) */
+ 0x06, 0x00, 0xff,/* Usage Page (ff00h), vendor-defined */
+ 0x09, 0x01, /* Usage (01h) */
+ 0x15, 0x00, /* Logical Minimum (0) */
+ 0x26, 0xff, 0x00,/* Logical Maximum (255) */
+ 0x75, 0x08, /* Report Size (8) */
+ 0x95, 0x07, /* Report Count (7) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0xc0, /* End Collection */
+ 0x09, 0x01, /* Usage (01h) */
+ 0x85, 0x63, /* Report ID (99) */
+ 0x95, 0x07, /* Report Count (7) */
+ 0x81, 0x02, /* Input (Data, Variable, Absolute) */
+ 0x09, 0x01, /* Usage (01h) */
+ 0x85, 0x02, /* Report ID (2) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xb1, 0x02, /* Feature (Variable) */
+ 0x09, 0x01, /* Usage (01h) */
+ 0x85, 0x03, /* Report ID (3) */
+ 0x95, 0x01, /* Report Count (1) */
+ 0xb1, 0x02, /* Feature (Variable) */
+ 0xc0 /* End Collection */
+};
+
static const USBDescIface desc_iface_wacom = {
.bInterfaceNumber = 0,
.bNumEndpoints = 1,
0x00, /* u8 country_code */
0x01, /* u8 num_descriptors */
USB_DT_REPORT, /* u8 type: Report */
- 0x6e, 0, /* u16 len */
+ sizeof(qemu_wacom_hid_report_descriptor), 0, /* u16 len */
},
},
},
}
switch (request) {
+ case InterfaceRequest | USB_REQ_GET_DESCRIPTOR:
+ switch (value >> 8) {
+ case 0x22:
+ memcpy(data, qemu_wacom_hid_report_descriptor,
+ sizeof(qemu_wacom_hid_report_descriptor));
+ p->actual_length = sizeof(qemu_wacom_hid_report_descriptor);
+ break;
+ default:
+ return;
+ }
+ break;
case WACOM_SET_REPORT:
if (s->mouse_grabbed) {
qemu_remove_mouse_event_handler(s->eh_entry);
*/
#define VIRTIO_MEM_MIN_BLOCK_SIZE ((uint32_t)(1 * MiB))
-#if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) || \
- defined(__powerpc64__)
-#define VIRTIO_MEM_DEFAULT_THP_SIZE ((uint32_t)(2 * MiB))
-#else
- /* fallback to 1 MiB (e.g., the THP size on s390x) */
-#define VIRTIO_MEM_DEFAULT_THP_SIZE VIRTIO_MEM_MIN_BLOCK_SIZE
+static uint32_t virtio_mem_default_thp_size(void)
+{
+ uint32_t default_thp_size = VIRTIO_MEM_MIN_BLOCK_SIZE;
+
+#if defined(__x86_64__) || defined(__arm__) || defined(__powerpc64__)
+ default_thp_size = 2 * MiB;
+#elif defined(__aarch64__)
+ if (qemu_real_host_page_size == 4 * KiB) {
+ default_thp_size = 2 * MiB;
+ } else if (qemu_real_host_page_size == 16 * KiB) {
+ default_thp_size = 32 * MiB;
+ } else if (qemu_real_host_page_size == 64 * KiB) {
+ default_thp_size = 512 * MiB;
+ }
#endif
+ return default_thp_size;
+}
+
/*
* We want to have a reasonable default block size such that
* 1. We avoid splitting THPs when unplugging memory, which degrades
if (g_file_get_contents(HPAGE_PMD_SIZE_PATH, &content, NULL, NULL) &&
!qemu_strtou64(content, &endptr, 0, &tmp) &&
(!endptr || *endptr == '\n')) {
- /*
- * Sanity-check the value, if it's too big (e.g., aarch64 with 64k base
- * pages) or weird, fallback to something smaller.
- */
- if (!tmp || !is_power_of_2(tmp) || tmp > 16 * MiB) {
+ /* Sanity-check the value and fallback to something reasonable. */
+ if (!tmp || !is_power_of_2(tmp)) {
warn_report("Read unsupported THP size: %" PRIx64, tmp);
} else {
thp_size = tmp;
}
if (!thp_size) {
- thp_size = VIRTIO_MEM_DEFAULT_THP_SIZE;
+ thp_size = virtio_mem_default_thp_size();
warn_report("Could not detect THP size, falling back to %" PRIx64
" MiB.", thp_size / MiB);
}
* The memory block size corresponds mostly to the section size.
*
* This allows e.g., to add 20MB with a section size of 128MB on x86_64, and
- * a section size of 1GB on arm64 (as long as the start address is properly
+ * a section size of 512MB on arm64 (as long as the start address is properly
* aligned, similar to ordinary DIMMs).
*
* We can change this at any time and maybe even make it configurable if
*/
#if defined(TARGET_X86_64) || defined(TARGET_I386)
#define VIRTIO_MEM_USABLE_EXTENT (2 * (128 * MiB))
+#elif defined(TARGET_ARM)
+#define VIRTIO_MEM_USABLE_EXTENT (2 * (512 * MiB))
#else
#error VIRTIO_MEM_USABLE_EXTENT not defined
#endif
.name = "virtio_mmio",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
},
.name = "virtio_pci",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
},
uint16_t vector;
VirtIOHandleOutput handle_output;
- VirtIOHandleAIOOutput handle_aio_output;
VirtIODevice *vdev;
EventNotifier guest_notifier;
EventNotifier host_notifier;
}
}
-static bool virtio_queue_notify_aio_vq(VirtQueue *vq)
-{
- bool ret = false;
-
- if (vq->vring.desc && vq->handle_aio_output) {
- VirtIODevice *vdev = vq->vdev;
-
- trace_virtio_queue_notify(vdev, vq - vdev->vq, vq);
- ret = vq->handle_aio_output(vdev, vq);
-
- if (unlikely(vdev->start_on_kick)) {
- virtio_set_started(vdev, true);
- }
- }
-
- return ret;
-}
-
static void virtio_queue_notify_vq(VirtQueue *vq)
{
if (vq->vring.desc && vq->handle_output) {
vdev->vq[i].vring.num_default = queue_size;
vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN;
vdev->vq[i].handle_output = handle_output;
- vdev->vq[i].handle_aio_output = NULL;
vdev->vq[i].used_elems = g_malloc0(sizeof(VirtQueueElement) *
queue_size);
vq->vring.num = 0;
vq->vring.num_default = 0;
vq->handle_output = NULL;
- vq->handle_aio_output = NULL;
g_free(vq->used_elems);
vq->used_elems = NULL;
virtio_virtqueue_reset_region_cache(vq);
.name = "virtio",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
},
return &vq->guest_notifier;
}
-static void virtio_queue_host_notifier_aio_read(EventNotifier *n)
-{
- VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
- if (event_notifier_test_and_clear(n)) {
- virtio_queue_notify_aio_vq(vq);
- }
-}
-
static void virtio_queue_host_notifier_aio_poll_begin(EventNotifier *n)
{
VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
EventNotifier *n = opaque;
VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
- if (!vq->vring.desc || virtio_queue_empty(vq)) {
- return false;
- }
+ return vq->vring.desc && !virtio_queue_empty(vq);
+}
+
+static void virtio_queue_host_notifier_aio_poll_ready(EventNotifier *n)
+{
+ VirtQueue *vq = container_of(n, VirtQueue, host_notifier);
- return virtio_queue_notify_aio_vq(vq);
+ virtio_queue_notify_vq(vq);
}
static void virtio_queue_host_notifier_aio_poll_end(EventNotifier *n)
virtio_queue_set_notification(vq, 1);
}
-void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx,
- VirtIOHandleAIOOutput handle_output)
+void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx)
{
- if (handle_output) {
- vq->handle_aio_output = handle_output;
- aio_set_event_notifier(ctx, &vq->host_notifier, true,
- virtio_queue_host_notifier_aio_read,
- virtio_queue_host_notifier_aio_poll);
- aio_set_event_notifier_poll(ctx, &vq->host_notifier,
- virtio_queue_host_notifier_aio_poll_begin,
- virtio_queue_host_notifier_aio_poll_end);
- } else {
- aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL, NULL);
- /* Test and clear notifier before after disabling event,
- * in case poll callback didn't have time to run. */
- virtio_queue_host_notifier_aio_read(&vq->host_notifier);
- vq->handle_aio_output = NULL;
- }
+ aio_set_event_notifier(ctx, &vq->host_notifier, true,
+ virtio_queue_host_notifier_read,
+ virtio_queue_host_notifier_aio_poll,
+ virtio_queue_host_notifier_aio_poll_ready);
+ aio_set_event_notifier_poll(ctx, &vq->host_notifier,
+ virtio_queue_host_notifier_aio_poll_begin,
+ virtio_queue_host_notifier_aio_poll_end);
+}
+
+void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx)
+{
+ aio_set_event_notifier(ctx, &vq->host_notifier, true, NULL, NULL, NULL);
+ /* Test and clear notifier before after disabling event,
+ * in case poll callback didn't have time to run. */
+ virtio_queue_host_notifier_read(&vq->host_notifier);
}
void virtio_queue_host_notifier_read(EventNotifier *n)
if (channel->ctx)
aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
channel->ctx = ctx;
aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true,
- xen_device_event, NULL, xen_device_poll, channel);
+ xen_device_event, NULL, xen_device_poll, NULL, channel);
}
XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev,
QLIST_REMOVE(channel, list);
aio_set_fd_handler(channel->ctx, xenevtchn_fd(channel->xeh), true,
- NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL);
if (xenevtchn_unbind(channel->xeh, channel->local_port) < 0) {
error_setg_errno(errp, errno, "xenevtchn_unbind failed");
IOHandler *io_read,
IOHandler *io_write,
AioPollFn *io_poll,
+ IOHandler *io_poll_ready,
void *opaque);
/* Set polling begin/end callbacks for a file descriptor that has already been
EventNotifier *notifier,
bool is_external,
EventNotifierHandler *io_read,
- AioPollFn *io_poll);
+ AioPollFn *io_poll,
+ EventNotifierHandler *io_poll_ready);
/* Set polling begin/end callbacks for an event notifier that has already been
* registered with aio_set_event_notifier. Do nothing if the event notifier is
BLK_PERM_RESIZE = 0x08,
/**
- * This permission is required to change the node that this BdrvChild
- * points to.
+ * There was a now-removed bit BLK_PERM_GRAPH_MOD, with value of 0x10. QEMU
+ * 6.1 and earlier may still lock the corresponding byte in block/file-posix
+ * locking. So, implementing some new permission should be very careful to
+ * not interfere with this old unused thing.
*/
- BLK_PERM_GRAPH_MOD = 0x10,
- BLK_PERM_ALL = 0x1f,
+ BLK_PERM_ALL = 0x0f,
DEFAULT_PERM_PASSTHROUGH = BLK_PERM_CONSISTENT_READ
| BLK_PERM_WRITE
char *bdrv_perm_names(uint64_t perm);
uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm);
-/* disk I/O throttling */
void bdrv_init(void);
void bdrv_init_with_whitelist(void);
bool bdrv_uses_whitelist(void);
void bdrv_parse_filename_strip_prefix(const char *filename, const char *prefix,
QDict *options);
-bool bdrv_backing_overridden(BlockDriverState *bs);
-
-
/**
* bdrv_add_aio_context_notifier:
*
#define TARGET_PAGE_ALIGN(addr) ROUND_UP((addr), TARGET_PAGE_SIZE)
-/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even
- * when intptr_t is 32-bit and we are aligning a long long.
- */
-extern uintptr_t qemu_host_page_size;
-extern intptr_t qemu_host_page_mask;
-
-#define HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_host_page_size)
-#define REAL_HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_real_host_page_size)
-
/* same as PROT_xxx */
#define PAGE_READ 0x0001
#define PAGE_WRITE 0x0002
#include "exec/hwaddr.h"
#endif
+/* Using intptr_t ensures that qemu_*_page_mask is sign-extended even
+ * when intptr_t is 32-bit and we are aligning a long long.
+ */
+extern uintptr_t qemu_host_page_size;
+extern intptr_t qemu_host_page_mask;
+
+#define HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_host_page_size)
+#define REAL_HOST_PAGE_ALIGN(addr) ROUND_UP((addr), qemu_real_host_page_size)
+
/* The CPU list lock nests outside page_(un)lock or mmap_(un)lock */
void qemu_init_cpu_list(void);
void cpu_list_lock(void);
const MemoryRegionOps *ops;
void *opaque;
MemoryRegion *container;
+ int mapped_via_alias; /* Mapped via an alias, container might be NULL */
Int128 size;
hwaddr addr;
void (*destructor)(MemoryRegion *mr);
/**
* memory_region_is_mapped: returns true if #MemoryRegion is mapped
- * into any address space.
+ * into another memory region, which does not necessarily imply that it is
+ * mapped into an address space.
*
* @mr: a #MemoryRegion which should be checked if it's mapped
*/
}
}
+/**
+ * address_space_set: Fill address space with a constant byte.
+ *
+ * Return a MemTxResult indicating whether the operation succeeded
+ * or failed (eg unassigned memory, device rejected the transaction,
+ * IOMMU fault).
+ *
+ * @as: #AddressSpace to be accessed
+ * @addr: address within that address space
+ * @c: constant byte to fill the memory
+ * @len: the number of bytes to fill with the constant byte
+ * @attrs: memory transaction attributes
+ */
+MemTxResult address_space_set(AddressSpace *as, hwaddr addr,
+ uint8_t c, hwaddr len, MemTxAttrs attrs);
+
#ifdef NEED_CPU_H
/* enum device_endian to MemOp. */
static inline MemOp devend_memop(enum device_endian end)
* int g_foo(const char *wibble)
*
* We must define a static inline function with the same signature that does
- * what we need, but with a "_qemu" suffix e.g.
+ * what we need, but with a "_compat" suffix e.g.
*
- * static inline void g_foo_qemu(const char *wibble)
+ * static inline void g_foo_compat(const char *wibble)
* {
* #if GLIB_CHECK_VERSION(X, Y, 0)
* g_foo(wibble)
* ensuring this wrapper function impl doesn't trigger the compiler warning
* about using too new glib APIs. Finally we can do
*
- * #define g_foo(a) g_foo_qemu(a)
+ * #define g_foo(a) g_foo_compat(a)
*
* So now the code elsewhere in QEMU, which *does* have the
* -Wdeprecated-declarations warning active, can call g_foo(...) as normal,
#include "hw/timer/aspeed_timer.h"
#include "hw/rtc/aspeed_rtc.h"
#include "hw/i2c/aspeed_i2c.h"
+#include "hw/misc/aspeed_i3c.h"
#include "hw/ssi/aspeed_smc.h"
#include "hw/misc/aspeed_hace.h"
#include "hw/watchdog/wdt_aspeed.h"
AspeedRtcState rtc;
AspeedTimerCtrlState timerctrl;
AspeedI2CState i2c;
+ AspeedI3CState i3c;
AspeedSCUState scu;
AspeedHACEState hace;
AspeedXDMAState xdma;
ASPEED_DEV_HACE,
ASPEED_DEV_DPMCU,
ASPEED_DEV_DP,
+ ASPEED_DEV_I3C,
};
#endif /* ASPEED_SOC_H */
bool secure;
bool highmem;
bool highmem_ecam;
+ bool highmem_mmio;
+ bool highmem_redists;
bool its;
bool tcg_its;
bool virt;
bool ras;
bool mte;
+ bool dtb_kaslr_seed;
OnOffAuto acpi;
VirtGICType gic_version;
VirtIOMMUType iommu;
assert(vms->gic_version == VIRT_GIC_VERSION_3);
- return MACHINE(vms)->smp.cpus > redist0_capacity ? 2 : 1;
+ return (MACHINE(vms)->smp.cpus > redist0_capacity &&
+ vms->highmem_redists) ? 2 : 1;
}
#endif /* QEMU_ARM_VIRT_H */
#include "hw/misc/xlnx-versal-xramc.h"
#include "hw/nvram/xlnx-bbram.h"
#include "hw/nvram/xlnx-versal-efuse.h"
+#include "hw/ssi/xlnx-versal-ospi.h"
+#include "hw/dma/xlnx_csu_dma.h"
+#include "hw/misc/xlnx-versal-pmc-iou-slcr.h"
#define TYPE_XLNX_VERSAL "xlnx-versal"
OBJECT_DECLARE_SIMPLE_TYPE(Versal, XLNX_VERSAL)
struct {
struct {
SDHCIState sd[XLNX_VERSAL_NR_SDS];
+ XlnxVersalPmcIouSlcr slcr;
+
+ struct {
+ XlnxVersalOspi ospi;
+ XlnxCSUDMA dma_src;
+ XlnxCSUDMA dma_dst;
+ MemoryRegion linear_mr;
+ qemu_or_irq irq_orgate;
+ } ospi;
} iou;
XlnxZynqMPRTC rtc;
XlnxEFuse efuse;
XlnxVersalEFuseCtrl efuse_ctrl;
XlnxVersalEFuseCache efuse_cache;
+
+ qemu_or_irq apb_irq_orgate;
} pmc;
struct {
#define VERSAL_GEM1_WAKE_IRQ_0 59
#define VERSAL_ADMA_IRQ_0 60
#define VERSAL_XRAM_IRQ_0 79
-#define VERSAL_BBRAM_APB_IRQ_0 121
-#define VERSAL_RTC_APB_ERR_IRQ 121
+#define VERSAL_PMC_APB_IRQ 121
+#define VERSAL_OSPI_IRQ 124
#define VERSAL_SD0_IRQ_0 126
#define VERSAL_EFUSE_IRQ 139
#define VERSAL_RTC_ALARM_IRQ 142
#define MM_FPD_FPD_APU 0xfd5c0000
#define MM_FPD_FPD_APU_SIZE 0x100
+#define MM_PMC_PMC_IOU_SLCR 0xf1060000
+#define MM_PMC_PMC_IOU_SLCR_SIZE 0x10000
+
+#define MM_PMC_OSPI 0xf1010000
+#define MM_PMC_OSPI_SIZE 0x10000
+
+#define MM_PMC_OSPI_DAC 0xc0000000
+#define MM_PMC_OSPI_DAC_SIZE 0x20000000
+
+#define MM_PMC_OSPI_DMA_DST 0xf1011800
+#define MM_PMC_OSPI_DMA_SRC 0xf1011000
+
#define MM_PMC_SD0 0xf1040000U
#define MM_PMC_SD0_SIZE 0x10000
#define MM_PMC_BBRAM_CTRL 0xf11f0000
void htif_symbol_callback(const char *st_name, int st_info, uint64_t st_value,
uint64_t st_size);
+/* Check if HTIF uses ELF symbols */
+bool htif_uses_elf_symbols(void);
+
/* legacy pre qom */
HTIFState *htif_mm_init(MemoryRegion *address_space, MemoryRegion *main_mem,
- CPURISCVState *env, Chardev *chr);
+ CPURISCVState *env, Chardev *chr, uint64_t nonelf_base);
#endif
#ifndef QEMU_HW_DISPLAY_VGA_H
#define QEMU_HW_DISPLAY_VGA_H
-#include "exec/hwaddr.h"
-
/*
* modules can reference this symbol to avoid being loaded
* into system emulators without vga support
extern enum vga_retrace_method vga_retrace_method;
-int isa_vga_mm_init(hwaddr vram_base,
- hwaddr ctrl_base, int it_shift,
- MemoryRegion *address_space);
+#define TYPE_VGA_MMIO "vga-mmio"
#endif
#ifndef XLNX_CSU_DMA_H
#define XLNX_CSU_DMA_H
+#include "hw/sysbus.h"
+#include "hw/register.h"
+#include "hw/ptimer.h"
+#include "hw/stream.h"
+
#define TYPE_XLNX_CSU_DMA "xlnx.csu_dma"
#define XLNX_CSU_DMA_R_MAX (0x2c / 4)
RegisterInfo regs_info[XLNX_CSU_DMA_R_MAX];
} XlnxCSUDMA;
-#define XLNX_CSU_DMA(obj) \
- OBJECT_CHECK(XlnxCSUDMA, (obj), TYPE_XLNX_CSU_DMA)
+OBJECT_DECLARE_TYPE(XlnxCSUDMA, XlnxCSUDMAClass, XLNX_CSU_DMA)
+
+struct XlnxCSUDMAClass {
+ SysBusDeviceClass parent_class;
+
+ /*
+ * read: Start a read transfer on a Xilinx CSU DMA engine
+ *
+ * @s: the Xilinx CSU DMA engine to start the transfer on
+ * @addr: the address to read
+ * @len: the number of bytes to read at 'addr'
+ *
+ * @return a MemTxResult indicating whether the operation succeeded ('len'
+ * bytes were read) or failed.
+ */
+ MemTxResult (*read)(XlnxCSUDMA *s, hwaddr addr, uint32_t len);
+};
#endif
if (res != MEMTX_OK) {
goto fail;
}
+ /*
+ * We need to zero'ify the space that is not copied
+ * from file
+ */
+ if (file_size < mem_size) {
+ res = address_space_set(as ? as : &address_space_memory,
+ addr + file_size, 0,
+ mem_size - file_size,
+ MEMTXATTRS_UNSPECIFIED);
+ if (res != MEMTX_OK) {
+ goto fail;
+ }
+ }
}
}
void ioapic_reset_common(DeviceState *dev);
-void ioapic_print_redtbl(Monitor *mon, IOAPICCommonState *s);
void ioapic_stat_update_irq(IOAPICCommonState *s, int irq, int level);
#endif /* QEMU_IOAPIC_INTERNAL_H */
uint16_t entry_sz;
uint32_t page_sz;
uint32_t num_entries;
- uint32_t num_ids;
uint64_t base_addr;
} TableDesc;
void pic_reset_common(PICCommonState *s);
ISADevice *i8259_init_chip(const char *name, ISABus *bus, bool master);
void pic_stat_update_irq(int irq, int level);
-bool pic_get_statistics(InterruptStatsProvider *obj,
- uint64_t **irq_counts, unsigned int *nb_irqs);
-void pic_print_info(InterruptStatsProvider *obj, Monitor *mon);
#endif /* QEMU_I8259_INTERNAL_H */
--- /dev/null
+/*
+ * ASPEED I3C Controller
+ *
+ * Copyright (C) 2021 ASPEED Technology Inc.
+ *
+ * This code is licensed under the GPL version 2 or later. See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef ASPEED_I3C_H
+#define ASPEED_I3C_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_ASPEED_I3C "aspeed.i3c"
+#define TYPE_ASPEED_I3C_DEVICE "aspeed.i3c.device"
+OBJECT_DECLARE_TYPE(AspeedI3CState, AspeedI3CClass, ASPEED_I3C)
+
+#define ASPEED_I3C_NR_REGS (0x70 >> 2)
+#define ASPEED_I3C_DEVICE_NR_REGS (0x300 >> 2)
+#define ASPEED_I3C_NR_DEVICES 6
+
+OBJECT_DECLARE_SIMPLE_TYPE(AspeedI3CDevice, ASPEED_I3C_DEVICE)
+typedef struct AspeedI3CDevice {
+ /* <private> */
+ SysBusDevice parent;
+
+ /* <public> */
+ MemoryRegion mr;
+ qemu_irq irq;
+
+ uint8_t id;
+ uint32_t regs[ASPEED_I3C_DEVICE_NR_REGS];
+} AspeedI3CDevice;
+
+typedef struct AspeedI3CState {
+ /* <private> */
+ SysBusDevice parent;
+
+ /* <public> */
+ MemoryRegion iomem;
+ MemoryRegion iomem_container;
+ qemu_irq irq;
+
+ uint32_t regs[ASPEED_I3C_NR_REGS];
+ AspeedI3CDevice devices[ASPEED_I3C_NR_DEVICES];
+} AspeedI3CState;
+#endif /* ASPEED_I3C_H */
--- /dev/null
+/*
+ * Header file for the Xilinx Versal's PMC IOU SLCR
+ *
+ * Copyright (C) 2021 Xilinx Inc
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * This is a model of Xilinx Versal's PMC I/O Peripheral Control and Status
+ * module documented in Versal's Technical Reference manual [1] and the Versal
+ * ACAP Register reference [2].
+ *
+ * References:
+ *
+ * [1] Versal ACAP Technical Reference Manual,
+ * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf
+ *
+ * [2] Versal ACAP Register Reference,
+ * https://www.xilinx.com/html_docs/registers/am012/am012-versal-register-reference.html#mod___pmc_iop_slcr.html
+ *
+ * QEMU interface:
+ * + sysbus MMIO region 0: MemoryRegion for the device's registers
+ * + sysbus IRQ 0: PMC (AXI and APB) parity error interrupt detected by the PMC
+ * I/O peripherals.
+ * + sysbus IRQ 1: Device interrupt.
+ * + Named GPIO output "sd-emmc-sel[0]": Enables 0: SD mode or 1: eMMC mode on
+ * SD/eMMC controller 0.
+ * + Named GPIO output "sd-emmc-sel[1]": Enables 0: SD mode or 1: eMMC mode on
+ * SD/eMMC controller 1.
+ * + Named GPIO output "qspi-ospi-mux-sel": Selects 0: QSPI linear region or 1:
+ * OSPI linear region.
+ * + Named GPIO output "ospi-mux-sel": Selects 0: OSPI Indirect access mode or
+ * 1: OSPI direct access mode.
+ */
+
+#ifndef XILINX_VERSAL_PMC_IOU_SLCR_H
+#define XILINX_VERSAL_PMC_IOU_SLCR_H
+
+#include "hw/register.h"
+
+#define TYPE_XILINX_VERSAL_PMC_IOU_SLCR "xlnx.versal-pmc-iou-slcr"
+
+OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalPmcIouSlcr, XILINX_VERSAL_PMC_IOU_SLCR)
+
+#define XILINX_VERSAL_PMC_IOU_SLCR_R_MAX (0x828 / 4 + 1)
+
+struct XlnxVersalPmcIouSlcr {
+ SysBusDevice parent_obj;
+ MemoryRegion iomem;
+ qemu_irq irq_parity_imr;
+ qemu_irq irq_imr;
+ qemu_irq sd_emmc_sel[2];
+ qemu_irq qspi_ospi_mux_sel;
+ qemu_irq ospi_mux_sel;
+
+ uint32_t regs[XILINX_VERSAL_PMC_IOU_SLCR_R_MAX];
+ RegisterInfo regs_info[XILINX_VERSAL_PMC_IOU_SLCR_R_MAX];
+};
+
+#endif /* XILINX_VERSAL_PMC_IOU_SLCR_H */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Marvell MV88W8618 / Freecom MusicPal emulation.
+ *
+ * Copyright (c) 2008-2021 QEMU contributors
+ */
+#ifndef HW_NET_MV88W8618_H
+#define HW_NET_MV88W8618_H
+
+#define TYPE_MV88W8618_ETH "mv88w8618_eth"
+
+#endif
/*
* PHB3 PCIe Root port
*/
-#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root-bus"
+#define TYPE_PNV_PHB3_ROOT_BUS "pnv-phb3-root"
#define TYPE_PNV_PHB3_ROOT_PORT "pnv-phb3-root-port"
PnvPBCQState pbcq;
- PnvPHB3RootPort root;
-
QLIST_HEAD(, PnvPhb3DMASpace) dma_spaces;
PnvChip *chip;
#include "hw/ppc/xive.h"
#include "qom/object.h"
+typedef struct PnvPhb4PecState PnvPhb4PecState;
typedef struct PnvPhb4PecStack PnvPhb4PecStack;
typedef struct PnvPHB4 PnvPHB4;
typedef struct PnvChip PnvChip;
/*
* PHB4 PCIe Root port
*/
-#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root-bus"
+#define TYPE_PNV_PHB4_ROOT_BUS "pnv-phb4-root"
#define TYPE_PNV_PHB4_ROOT_PORT "pnv-phb4-root-port"
typedef struct PnvPHB4RootPort {
struct PnvPHB4 {
PCIExpressHost parent_obj;
- PnvPHB4RootPort root;
-
uint32_t chip_id;
uint32_t phb_id;
uint64_t version;
+ /* The owner PEC */
+ PnvPhb4PecState *pec;
+
char bus_path[8];
/* Main register images */
MemoryRegion pci_mmio;
MemoryRegion pci_io;
+ /* PCI registers (excluding pass-through) */
+#define PHB4_PEC_PCI_STK_REGS_COUNT 0xf
+ uint64_t pci_regs[PHB4_PEC_PCI_STK_REGS_COUNT];
+ MemoryRegion pci_regs_mr;
+
+ /* Nest registers */
+#define PHB4_PEC_NEST_STK_REGS_COUNT 0x17
+ uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT];
+ MemoryRegion nest_regs_mr;
+
+ /* PHB pass-through XSCOM */
+ MemoryRegion phb_regs_mr;
+
+ /* Memory windows from PowerBus to PHB */
+ MemoryRegion phbbar;
+ MemoryRegion intbar;
+ MemoryRegion mmbar0;
+ MemoryRegion mmbar1;
+ uint64_t mmio0_base;
+ uint64_t mmio0_size;
+ uint64_t mmio1_base;
+ uint64_t mmio1_size;
+
/* On-chip IODA tables */
uint64_t ioda_LIST[PNV_PHB4_MAX_LSIs];
uint64_t ioda_MIST[PNV_PHB4_MAX_MIST];
XiveSource xsrc;
qemu_irq *qirqs;
- PnvPhb4PecStack *stack;
-
QLIST_HEAD(, PnvPhb4DMASpace) dma_spaces;
};
void pnv_phb4_pic_print_info(PnvPHB4 *phb, Monitor *mon);
-void pnv_phb4_update_regions(PnvPhb4PecStack *stack);
+int pnv_phb4_pec_get_phb_id(PnvPhb4PecState *pec, int stack_index);
extern const MemoryRegionOps pnv_phb4_xscom_ops;
/*
#define TYPE_PNV_PHB4_PEC "pnv-phb4-pec"
OBJECT_DECLARE_TYPE(PnvPhb4PecState, PnvPhb4PecClass, PNV_PHB4_PEC)
-#define TYPE_PNV_PHB4_PEC_STACK "pnv-phb4-pec-stack"
-OBJECT_DECLARE_SIMPLE_TYPE(PnvPhb4PecStack, PNV_PHB4_PEC_STACK)
-
-/* Per-stack data */
-struct PnvPhb4PecStack {
- DeviceState parent;
-
- /* My own stack number */
- uint32_t stack_no;
-
- /* Nest registers */
-#define PHB4_PEC_NEST_STK_REGS_COUNT 0x17
- uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT];
- MemoryRegion nest_regs_mr;
-
- /* PCI registers (excluding pass-through) */
-#define PHB4_PEC_PCI_STK_REGS_COUNT 0xf
- uint64_t pci_regs[PHB4_PEC_PCI_STK_REGS_COUNT];
- MemoryRegion pci_regs_mr;
-
- /* PHB pass-through XSCOM */
- MemoryRegion phb_regs_mr;
-
- /* Memory windows from PowerBus to PHB */
- MemoryRegion mmbar0;
- MemoryRegion mmbar1;
- MemoryRegion phbbar;
- MemoryRegion intbar;
- uint64_t mmio0_base;
- uint64_t mmio0_size;
- uint64_t mmio1_base;
- uint64_t mmio1_size;
-
- /* The owner PEC */
- PnvPhb4PecState *pec;
-
- /* The actual PHB */
- PnvPHB4 phb;
-};
-
struct PnvPhb4PecState {
DeviceState parent;
uint64_t pci_regs[PHB4_PEC_PCI_REGS_COUNT];
MemoryRegion pci_regs_mr;
- /* Stacks */
- #define PHB4_PEC_MAX_STACKS 3
- uint32_t num_stacks;
- PnvPhb4PecStack stacks[PHB4_PEC_MAX_STACKS];
+ /* PHBs */
+ uint32_t num_phbs;
PnvChip *chip;
};
const char *stk_compat;
int stk_compat_size;
uint64_t version;
- const uint32_t *num_stacks;
+ const uint32_t *num_phbs;
+ const char *rp_model;
};
#endif /* PCI_HOST_PNV_PHB4_H */
#undef PCI_DMA_DEFINE_LDST
+/**
+ * pci_dma_map: Map device PCI address space range into host virtual address
+ * @dev: #PCIDevice to be accessed
+ * @addr: address within that device's address space
+ * @plen: pointer to length of buffer; updated on return to indicate
+ * if only a subset of the requested range has been mapped
+ * @dir: indicates the transfer direction
+ *
+ * Return: A host pointer, or %NULL if the resources needed to
+ * perform the mapping are exhausted (in that case *@plen
+ * is set to zero).
+ */
static inline void *pci_dma_map(PCIDevice *dev, dma_addr_t addr,
dma_addr_t *plen, DMADirection dir)
{
uint64_t cores_mask;
PnvCore **cores;
- uint32_t num_phbs;
uint32_t num_pecs;
MemoryRegion xscom_mmio;
#define PNV8_CHIP_PHB3_MAX 4
PnvPHB3 phbs[PNV8_CHIP_PHB3_MAX];
+ uint32_t num_phbs;
XICSFabric *xics;
};
/*< public >*/
uint64_t chip_cfam_id;
uint64_t cores_mask;
- uint32_t num_phbs;
uint32_t num_pecs;
+ uint32_t num_phbs;
DeviceRealize parent_realize;
TYPE_PNV_CHIP_POWER10)
PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir);
+void pnv_phb_attach_root_port(PCIHostState *pci, const char *name);
+void pnv_chip_parent_fixup(PnvChip *chip, Object *obj, int index);
#define TYPE_PNV_MACHINE MACHINE_TYPE_NAME("powernv")
typedef struct PnvMachineClass PnvMachineClass;
hwaddr fw_load_addr;
};
+PnvChip *pnv_get_chip(PnvMachineState *pnv, uint32_t chip_id);
+
#define PNV_FDT_ADDR 0x01000000
#define PNV_TIMEBASE_FREQ 512000000ULL
#ifndef HW_VOF_H
#define HW_VOF_H
+#include "qom/object.h"
+#include "exec/address-spaces.h"
+#include "exec/memory.h"
+#include "cpu.h"
+
typedef struct Vof {
uint64_t top_addr; /* copied from rma_size */
GArray *claimed; /* array of SpaprOfClaimed */
* qemu_irqs at once, or to connect multiple outbound GPIOs to the
* same qemu_irq. (Warning: there is no assertion or other guard to
* catch this error: the model will just not do the right thing.)
- * Instead, for fan-out you can use the TYPE_IRQ_SPLIT device: connect
+ * Instead, for fan-out you can use the TYPE_SPLIT_IRQ device: connect
* a device's outbound GPIO to the splitter's input, and connect each
* of the splitter's outputs to a different device. For fan-in you
* can use the TYPE_OR_IRQ device, which is a model of a logical OR
#include "hw/riscv/riscv_hart.h"
#define RISCV32_BIOS_BIN "opensbi-riscv32-generic-fw_dynamic.bin"
-#define RISCV32_BIOS_ELF "opensbi-riscv32-generic-fw_dynamic.elf"
#define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin"
-#define RISCV64_BIOS_ELF "opensbi-riscv64-generic-fw_dynamic.elf"
bool riscv_is_32bit(RISCVHartArrayState *harts);
hwaddr rom_size,
uint32_t reset_vec_size,
uint64_t kernel_entry);
+void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr);
#endif /* RISCV_BOOT_H */
enum {
SPIKE_MROM,
+ SPIKE_HTIF,
SPIKE_CLINT,
SPIKE_DRAM
};
#define PMCW_FLAGS_MASK_MP 0x0004
#define PMCW_FLAGS_MASK_TF 0x0002
#define PMCW_FLAGS_MASK_DNV 0x0001
-#define PMCW_FLAGS_MASK_INVALID 0x0700
+#define PMCW_FLAGS_MASK_INVALID 0xc300
#define PMCW_CHARS_MASK_ST 0x00e00000
#define PMCW_CHARS_MASK_MBFC 0x00000004
int16_t status;
int16_t host_status;
void *hba_private;
- size_t resid;
+ uint64_t residual;
SCSICommand cmd;
NotifierList cancel_notifiers;
void *hba_private);
void (*transfer_data)(SCSIRequest *req, uint32_t arg);
void (*fail)(SCSIRequest *req);
- void (*complete)(SCSIRequest *req, size_t resid);
+ void (*complete)(SCSIRequest *req, size_t residual);
void (*cancel)(SCSIRequest *req);
void (*change)(SCSIBus *bus, SCSIDevice *dev, SCSISense sense);
QEMUSGList *(*get_sg_list)(SCSIRequest *req);
--- /dev/null
+/*
+ * Header file for the Xilinx Versal's OSPI controller
+ *
+ * Copyright (C) 2021 Xilinx Inc
+ * Written by Francisco Iglesias <francisco.iglesias@xilinx.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * This is a model of Xilinx Versal's Octal SPI flash memory controller
+ * documented in Versal's Technical Reference manual [1] and the Versal ACAP
+ * Register reference [2].
+ *
+ * References:
+ *
+ * [1] Versal ACAP Technical Reference Manual,
+ * https://www.xilinx.com/support/documentation/architecture-manuals/am011-versal-acap-trm.pdf
+ *
+ * [2] Versal ACAP Register Reference,
+ * https://www.xilinx.com/html_docs/registers/am012/am012-versal-register-reference.html#mod___ospi.html
+ *
+ *
+ * QEMU interface:
+ * + sysbus MMIO region 0: MemoryRegion for the device's registers
+ * + sysbus MMIO region 1: MemoryRegion for flash memory linear address space
+ * (data transfer).
+ * + sysbus IRQ 0: Device interrupt.
+ * + Named GPIO input "ospi-mux-sel": 0: enables indirect access mode
+ * and 1: enables direct access mode.
+ * + Property "dac-with-indac": Allow both direct accesses and indirect
+ * accesses simultaneously.
+ * + Property "indac-write-disabled": Disable indirect access writes.
+ */
+
+#ifndef XILINX_VERSAL_OSPI_H
+#define XILINX_VERSAL_OSPI_H
+
+#include "hw/register.h"
+#include "hw/ssi/ssi.h"
+#include "qemu/fifo8.h"
+#include "hw/dma/xlnx_csu_dma.h"
+
+#define TYPE_XILINX_VERSAL_OSPI "xlnx.versal-ospi"
+
+OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalOspi, XILINX_VERSAL_OSPI)
+
+#define XILINX_VERSAL_OSPI_R_MAX (0xfc / 4 + 1)
+
+/*
+ * Indirect operations
+ */
+typedef struct IndOp {
+ uint32_t flash_addr;
+ uint32_t num_bytes;
+ uint32_t done_bytes;
+ bool completed;
+} IndOp;
+
+struct XlnxVersalOspi {
+ SysBusDevice parent_obj;
+
+ MemoryRegion iomem;
+ MemoryRegion iomem_dac;
+
+ uint8_t num_cs;
+ qemu_irq *cs_lines;
+
+ SSIBus *spi;
+
+ Fifo8 rx_fifo;
+ Fifo8 tx_fifo;
+
+ Fifo8 rx_sram;
+ Fifo8 tx_sram;
+
+ qemu_irq irq;
+
+ XlnxCSUDMA *dma_src;
+ bool ind_write_disabled;
+ bool dac_with_indac;
+ bool dac_enable;
+ bool src_dma_inprog;
+
+ IndOp rd_ind_op[2];
+ IndOp wr_ind_op[2];
+
+ uint32_t regs[XILINX_VERSAL_OSPI_R_MAX];
+ RegisterInfo regs_info[XILINX_VERSAL_OSPI_R_MAX];
+
+ /* Maximum inferred membank size is 512 bytes */
+ uint8_t stig_membank[512];
+};
+
+#endif /* XILINX_VERSAL_OSPI_H */
uint32_t timer_compare_upper0;
uint32_t timer_intr_enable;
uint32_t timer_intr_state;
- uint32_t timer_intr_test;
uint32_t timebase_freq;
bool is_write;
} MultiReqBuffer;
-bool virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq);
+void virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq);
void virtio_blk_process_queued_requests(VirtIOBlock *s, bool is_bh);
#endif
DEFINE_PROP_UINT32("max_outputs", _state, _conf.max_outputs, 1), \
DEFINE_PROP_BIT("edid", _state, _conf.flags, \
VIRTIO_GPU_FLAG_EDID_ENABLED, true), \
- DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1024), \
- DEFINE_PROP_UINT32("yres", _state, _conf.yres, 768)
+ DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1280), \
+ DEFINE_PROP_UINT32("yres", _state, _conf.yres, 800)
typedef struct VGPUDMABuf {
QemuDmaBuf buf;
void virtio_device_set_child_bus_name(VirtIODevice *vdev, char *bus_name);
typedef void (*VirtIOHandleOutput)(VirtIODevice *, VirtQueue *);
-typedef bool (*VirtIOHandleAIOOutput)(VirtIODevice *, VirtQueue *);
VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size,
VirtIOHandleOutput handle_output);
EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq);
void virtio_queue_set_host_notifier_enabled(VirtQueue *vq, bool enabled);
void virtio_queue_host_notifier_read(EventNotifier *n);
-void virtio_queue_aio_set_host_notifier_handler(VirtQueue *vq, AioContext *ctx,
- VirtIOHandleAIOOutput handle_output);
+void virtio_queue_aio_attach_host_notifier(VirtQueue *vq, AioContext *ctx);
+void virtio_queue_aio_detach_host_notifier(VirtQueue *vq, AioContext *ctx);
VirtQueue *virtio_vector_first_queue(VirtIODevice *vdev, uint16_t vector);
VirtQueue *virtio_vector_next_queue(VirtQueue *vq);
#define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
/* Copyright string for -version arguments, About dialogs, etc */
-#define QEMU_COPYRIGHT "Copyright (c) 2003-2021 " \
+#define QEMU_COPYRIGHT "Copyright (c) 2003-2022 " \
"Fabrice Bellard and the QEMU Project developers"
/* Bug reporting information for --help arguments, About dialogs, etc */
int qemu_main(int argc, char **argv, char **envp);
#endif
-void qemu_get_timedate(struct tm *tm, int offset);
-int qemu_timedate_diff(struct tm *tm);
-
void *qemu_oom_check(void *ptr);
ssize_t qemu_write_full(int fd, const void *buf, size_t count)
const VuDevIface *vu_iface;
/* Protected by ctx lock */
+ unsigned int refcount;
+ bool wait_idle;
VuDev vu_dev;
QIOChannel *ioc; /* The I/O channel with the client */
QIOChannelSocket *sioc; /* The underlying data channel with the client */
void vhost_user_server_stop(VuServer *server);
+void vhost_user_server_ref(VuServer *server);
+void vhost_user_server_unref(VuServer *server);
+
void vhost_user_server_attach_aio_context(VuServer *server, AioContext *ctx);
void vhost_user_server_detach_aio_context(VuServer *server);
void override_max_devs(BlockInterfaceType type, int max_devs);
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
-void drive_mark_claimed_by_board(void);
void drive_check_orphaned(void);
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
int drive_get_max_bus(BlockInterfaceType type);
-int drive_get_max_devs(BlockInterfaceType type);
-QemuOpts *drive_def(const char *optstr);
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
const char *optstr);
DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type,
#include "block/block.h"
#include "block/accounting.h"
-typedef struct ScatterGatherEntry ScatterGatherEntry;
-
typedef enum {
DMA_DIRECTION_TO_DEVICE = 0,
DMA_DIRECTION_FROM_DEVICE = 1,
} DMADirection;
-struct QEMUSGList {
- ScatterGatherEntry *sg;
- int nsg;
- int nalloc;
- size_t size;
- DeviceState *dev;
- AddressSpace *as;
-};
-
-#ifndef CONFIG_USER_ONLY
-
/*
* When an IOMMU is present, bus addresses become distinct from
* CPU/memory physical addresses and may be a different size. Because
#define DMA_ADDR_BITS 64
#define DMA_ADDR_FMT "%" PRIx64
+typedef struct ScatterGatherEntry ScatterGatherEntry;
+
+struct QEMUSGList {
+ ScatterGatherEntry *sg;
+ int nsg;
+ int nalloc;
+ dma_addr_t size;
+ DeviceState *dev;
+ AddressSpace *as;
+};
+
static inline void dma_barrier(AddressSpace *as, DMADirection dir)
{
/*
AddressSpace *as);
void qemu_sglist_add(QEMUSGList *qsg, dma_addr_t base, dma_addr_t len);
void qemu_sglist_destroy(QEMUSGList *qsg);
-#endif
typedef BlockAIOCB *DMAIOFunc(int64_t offset, QEMUIOVector *iov,
BlockCompletionFunc *cb, void *cb_opaque,
BlockAIOCB *dma_blk_write(BlockBackend *blk,
QEMUSGList *sg, uint64_t offset, uint32_t align,
BlockCompletionFunc *cb, void *opaque);
-uint64_t dma_buf_read(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs);
-uint64_t dma_buf_write(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs);
+MemTxResult dma_buf_read(void *ptr, dma_addr_t len, dma_addr_t *residual,
+ QEMUSGList *sg, MemTxAttrs attrs);
+MemTxResult dma_buf_write(void *ptr, dma_addr_t len, dma_addr_t *residual,
+ QEMUSGList *sg, MemTxAttrs attrs);
void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
QEMUSGList *sg, enum BlockAcctType type);
--- /dev/null
+/*
+ * RTC configuration and clock read
+ *
+ * Copyright (c) 2003-2021 QEMU contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef SYSEMU_RTC_H
+#define SYSEMU_RTC_H
+
+/**
+ * qemu_get_timedate: Get the current RTC time
+ * @tm: struct tm to fill in with RTC time
+ * @offset: offset in seconds to adjust the RTC time by before
+ * converting to struct tm format.
+ *
+ * This function fills in @tm with the current RTC time, as adjusted
+ * by @offset (for example, if @offset is 3600 then the returned time/date
+ * will be one hour further ahead than the current RTC time).
+ *
+ * The usual use is by RTC device models, which should call this function
+ * to find the time/date value that they should return to the guest
+ * when it reads the RTC registers.
+ *
+ * The behaviour of the clock whose value this function returns will
+ * depend on the -rtc command line option passed by the user.
+ */
+void qemu_get_timedate(struct tm *tm, int offset);
+
+/**
+ * qemu_timedate_diff: Return difference between a struct tm and the RTC
+ * @tm: struct tm containing the date/time to compare against
+ *
+ * Returns the difference in seconds between the RTC clock time
+ * and the date/time specified in @tm. For example, if @tm specifies
+ * a timestamp one hour further ahead than the current RTC time
+ * then this function will return 3600.
+ */
+int qemu_timedate_diff(struct tm *tm);
+
+#endif
#define SPICE_NEEDS_SET_MM_TIME 0
#endif
+#if defined(SPICE_SERVER_VERSION) && (SPICE_SERVER_VERSION >= 0x000f00)
+#define SPICE_HAS_ATTACHED_WORKER 1
+#else
+#define SPICE_HAS_ATTACHED_WORKER 0
+#endif
+
#else /* CONFIG_SPICE */
#include "qemu/error-report.h"
#undef WIN32_LEAN_AND_MEAN
#include <SDL.h>
+
+/* with Alpine / muslc SDL headers pull in directfb headers
+ * which in turn trigger warning about redundant decls for
+ * direct_waitqueue_deinit.
+ */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+
#include <SDL_syswm.h>
+
+#pragma GCC diagnostic pop
+
#ifdef CONFIG_SDL_IMAGE
# include <SDL_image.h>
#endif
void *opaque)
{
QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
- aio_set_fd_handler(ctx, cioc->readfd, false, io_read, NULL, NULL, opaque);
- aio_set_fd_handler(ctx, cioc->writefd, false, NULL, io_write, NULL, opaque);
+ aio_set_fd_handler(ctx, cioc->readfd, false,
+ io_read, NULL, NULL, NULL, opaque);
+ aio_set_fd_handler(ctx, cioc->writefd, false,
+ NULL, io_write, NULL, NULL, opaque);
}
void *opaque)
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
- aio_set_fd_handler(ctx, fioc->fd, false, io_read, io_write, NULL, opaque);
+ aio_set_fd_handler(ctx, fioc->fd, false, io_read, io_write,
+ NULL, NULL, opaque);
}
static GSource *qio_channel_file_create_watch(QIOChannel *ioc,
void *opaque)
{
QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
- aio_set_fd_handler(ctx, sioc->fd, false, io_read, io_write, NULL, opaque);
+ aio_set_fd_handler(ctx, sioc->fd, false,
+ io_read, io_write, NULL, NULL, opaque);
}
static GSource *qio_channel_socket_create_watch(QIOChannel *ioc,
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ */
+
+#ifndef __LINUX_KVM_RISCV_H
+#define __LINUX_KVM_RISCV_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <asm/ptrace.h>
+
+#define __KVM_HAVE_READONLY_MEM
+
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+
+#define KVM_INTERRUPT_SET -1U
+#define KVM_INTERRUPT_UNSET -2U
+
+/* for KVM_GET_REGS and KVM_SET_REGS */
+struct kvm_regs {
+};
+
+/* for KVM_GET_FPU and KVM_SET_FPU */
+struct kvm_fpu {
+};
+
+/* KVM Debug exit structure */
+struct kvm_debug_exit_arch {
+};
+
+/* for KVM_SET_GUEST_DEBUG */
+struct kvm_guest_debug_arch {
+};
+
+/* definition of registers in kvm_run */
+struct kvm_sync_regs {
+};
+
+/* for KVM_GET_SREGS and KVM_SET_SREGS */
+struct kvm_sregs {
+};
+
+/* CONFIG registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
+struct kvm_riscv_config {
+ unsigned long isa;
+};
+
+/* CORE registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
+struct kvm_riscv_core {
+ struct user_regs_struct regs;
+ unsigned long mode;
+};
+
+/* Possible privilege modes for kvm_riscv_core */
+#define KVM_RISCV_MODE_S 1
+#define KVM_RISCV_MODE_U 0
+
+/* CSR registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
+struct kvm_riscv_csr {
+ unsigned long sstatus;
+ unsigned long sie;
+ unsigned long stvec;
+ unsigned long sscratch;
+ unsigned long sepc;
+ unsigned long scause;
+ unsigned long stval;
+ unsigned long sip;
+ unsigned long satp;
+ unsigned long scounteren;
+};
+
+/* TIMER registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */
+struct kvm_riscv_timer {
+ __u64 frequency;
+ __u64 time;
+ __u64 compare;
+ __u64 state;
+};
+
+/* Possible states for kvm_riscv_timer */
+#define KVM_RISCV_TIMER_STATE_OFF 0
+#define KVM_RISCV_TIMER_STATE_ON 1
+
+#define KVM_REG_SIZE(id) \
+ (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT))
+
+/* If you need to interpret the index values, here is the key: */
+#define KVM_REG_RISCV_TYPE_MASK 0x00000000FF000000
+#define KVM_REG_RISCV_TYPE_SHIFT 24
+
+/* Config registers are mapped as type 1 */
+#define KVM_REG_RISCV_CONFIG (0x01 << KVM_REG_RISCV_TYPE_SHIFT)
+#define KVM_REG_RISCV_CONFIG_REG(name) \
+ (offsetof(struct kvm_riscv_config, name) / sizeof(unsigned long))
+
+/* Core registers are mapped as type 2 */
+#define KVM_REG_RISCV_CORE (0x02 << KVM_REG_RISCV_TYPE_SHIFT)
+#define KVM_REG_RISCV_CORE_REG(name) \
+ (offsetof(struct kvm_riscv_core, name) / sizeof(unsigned long))
+
+/* Control and status registers are mapped as type 3 */
+#define KVM_REG_RISCV_CSR (0x03 << KVM_REG_RISCV_TYPE_SHIFT)
+#define KVM_REG_RISCV_CSR_REG(name) \
+ (offsetof(struct kvm_riscv_csr, name) / sizeof(unsigned long))
+
+/* Timer registers are mapped as type 4 */
+#define KVM_REG_RISCV_TIMER (0x04 << KVM_REG_RISCV_TYPE_SHIFT)
+#define KVM_REG_RISCV_TIMER_REG(name) \
+ (offsetof(struct kvm_riscv_timer, name) / sizeof(__u64))
+
+/* F extension registers are mapped as type 5 */
+#define KVM_REG_RISCV_FP_F (0x05 << KVM_REG_RISCV_TYPE_SHIFT)
+#define KVM_REG_RISCV_FP_F_REG(name) \
+ (offsetof(struct __riscv_f_ext_state, name) / sizeof(__u32))
+
+/* D extension registers are mapped as type 6 */
+#define KVM_REG_RISCV_FP_D (0x06 << KVM_REG_RISCV_TYPE_SHIFT)
+#define KVM_REG_RISCV_FP_D_REG(name) \
+ (offsetof(struct __riscv_d_ext_state, name) / sizeof(__u64))
+
+#endif
+
+#endif /* __LINUX_KVM_RISCV_H */
case 0x6: /* Access flag fault, level 2 */
case 0x9: /* Domain fault, level 1 */
case 0xb: /* Domain fault, level 2 */
- case 0xd: /* Permision fault, level 1 */
- case 0xf: /* Permision fault, level 2 */
+ case 0xd: /* Permission fault, level 1 */
+ case 0xf: /* Permission fault, level 2 */
si_signo = TARGET_SIGSEGV;
si_code = TARGET_SEGV_ACCERR;
break;
if (test != addr) {
pgb_fail_in_use(image_name);
}
+ qemu_log_mask(CPU_LOG_PAGE,
+ "%s: base @ %p for " TARGET_ABI_FMT_ld " bytes\n",
+ __func__, addr, guest_hiaddr - guest_loaddr);
}
/**
if (mmap_start != MAP_FAILED) {
munmap(mmap_start, guest_size);
if (mmap_start == (void *) align_start) {
+ qemu_log_mask(CPU_LOG_PAGE,
+ "%s: base @ %p for %" PRIdPTR" bytes\n",
+ __func__, mmap_start + offset, guest_size);
return (uintptr_t) mmap_start + offset;
}
}
brk = (uintptr_t)sbrk(0);
if (!maps) {
- ret = pgd_find_hole_fallback(guest_size, brk, align, offset);
- return ret == -1 ? -1 : ret - guest_loaddr;
+ return pgd_find_hole_fallback(guest_size, brk, align, offset);
}
/* The first hole is before the first map entry. */
/* Record the lowest successful match. */
if (ret < 0) {
- ret = align_start - guest_loaddr;
+ ret = align_start;
}
/* If this hole contains the identity map, select it. */
if (align_start <= guest_loaddr &&
}
free_self_maps(maps);
+ if (ret != -1) {
+ qemu_log_mask(CPU_LOG_PAGE, "%s: base @ %" PRIxPTR
+ " for %" PRIuPTR " bytes\n",
+ __func__, ret, guest_size);
+ }
+
return ret;
}
}
guest_base = addr;
+
+ qemu_log_mask(CPU_LOG_PAGE, "%s: base @ %"PRIxPTR" for %" PRIuPTR" bytes\n",
+ __func__, addr, hiaddr - loaddr);
}
static void pgb_dynamic(const char *image_name, long align)
"using -R option)", reserved_va, test, strerror(errno));
exit(EXIT_FAILURE);
}
+
+ qemu_log_mask(CPU_LOG_PAGE, "%s: base @ %p for %lu bytes\n",
+ __func__, addr, reserved_va);
}
void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
* and the stack, lest they be placed immediately after
* the data segment and block allocation from the brk.
*
- * 16MB is chosen as "large enough" without being so large
- * as to allow the result to not fit with a 32-bit guest on
- * a 32-bit host.
+ * 16MB is chosen as "large enough" without being so large as
+ * to allow the result to not fit with a 32-bit guest on a
+ * 32-bit host. However some 64 bit guests (e.g. s390x)
+ * attempt to place their heap further ahead and currently
+ * nothing stops them smashing into QEMUs address space.
*/
+#if TARGET_LONG_BITS == 64
+ info->reserve_brk = 32 * MiB;
+#else
info->reserve_brk = 16 * MiB;
+#endif
hiaddr += info->reserve_brk;
if (ehdr->e_type == ET_EXEC) {
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef AARCH64_HOST_SIGNAL_H
-#define AARCH64_HOST_SIGNAL_H
-
-/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
-#ifndef ESR_MAGIC
-#define ESR_MAGIC 0x45535201
-struct esr_context {
- struct _aarch64_ctx head;
- uint64_t esr;
-};
-#endif
-
-static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
-{
- return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
-}
-
-static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
-{
- return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
-}
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.pc;
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.pc = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- struct _aarch64_ctx *hdr;
- uint32_t insn;
-
- /* Find the esr_context, which has the WnR bit in it */
- for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
- if (hdr->magic == ESR_MAGIC) {
- struct esr_context const *ec = (struct esr_context const *)hdr;
- uint64_t esr = ec->esr;
-
- /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
- return extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
- }
- }
-
- /*
- * Fall back to parsing instructions; will only be needed
- * for really ancient (pre-3.16) kernels.
- */
- insn = *(uint32_t *)host_signal_pc(uc);
-
- return (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
- || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
- || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
- || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
- || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
- || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
- || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
- /* Ignore bits 10, 11 & 21, controlling indexing. */
- || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
- || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
- /* Ignore bits 23 & 24, controlling indexing. */
- || (insn & 0x3a400000) == 0x28000000; /* C3.3.7,14-16 */
-}
-
-#endif
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef ALPHA_HOST_SIGNAL_H
-#define ALPHA_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.sc_pc;
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.sc_pc = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- uint32_t *pc = (uint32_t *)host_signal_pc(uc);
- uint32_t insn = *pc;
-
- /* XXX: need kernel patch to get write flag faster */
- switch (insn >> 26) {
- case 0x0d: /* stw */
- case 0x0e: /* stb */
- case 0x0f: /* stq_u */
- case 0x24: /* stf */
- case 0x25: /* stg */
- case 0x26: /* sts */
- case 0x27: /* stt */
- case 0x2c: /* stl */
- case 0x2d: /* stq */
- case 0x2e: /* stl_c */
- case 0x2f: /* stq_c */
- return true;
- }
- return false;
-}
-
-#endif
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef ARM_HOST_SIGNAL_H
-#define ARM_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.arm_pc;
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.arm_pc = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- /*
- * In the FSR, bit 11 is WnR, assuming a v6 or
- * later processor. On v5 we will always report
- * this as a read, which will fail later.
- */
- uint32_t fsr = uc->uc_mcontext.error_code;
- return extract32(fsr, 11, 1);
-}
-
-#endif
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef I386_HOST_SIGNAL_H
-#define I386_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.gregs[REG_EIP];
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.gregs[REG_EIP] = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe
- && (uc->uc_mcontext.gregs[REG_ERR] & 0x2);
-}
-
-#endif
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 WANG Xuerui <git@xen0n.name>
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef LOONGARCH64_HOST_SIGNAL_H
-#define LOONGARCH64_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.__pc;
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.__pc = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- const uint32_t *pinsn = (const uint32_t *)host_signal_pc(uc);
- uint32_t insn = pinsn[0];
-
- /* Detect store by reading the instruction at the program counter. */
- switch ((insn >> 26) & 0b111111) {
- case 0b001000: /* {ll,sc}.[wd] */
- switch ((insn >> 24) & 0b11) {
- case 0b01: /* sc.w */
- case 0b11: /* sc.d */
- return true;
- }
- break;
- case 0b001001: /* {ld,st}ox4.[wd] ({ld,st}ptr.[wd]) */
- switch ((insn >> 24) & 0b11) {
- case 0b01: /* stox4.w (stptr.w) */
- case 0b11: /* stox4.d (stptr.d) */
- return true;
- }
- break;
- case 0b001010: /* {ld,st}.* family */
- switch ((insn >> 22) & 0b1111) {
- case 0b0100: /* st.b */
- case 0b0101: /* st.h */
- case 0b0110: /* st.w */
- case 0b0111: /* st.d */
- case 0b1101: /* fst.s */
- case 0b1111: /* fst.d */
- return true;
- }
- break;
- case 0b001110: /* indexed, atomic, bounds-checking memory operations */
- switch ((insn >> 15) & 0b11111111111) {
- case 0b00000100000: /* stx.b */
- case 0b00000101000: /* stx.h */
- case 0b00000110000: /* stx.w */
- case 0b00000111000: /* stx.d */
- case 0b00001110000: /* fstx.s */
- case 0b00001111000: /* fstx.d */
- case 0b00011101100: /* fstgt.s */
- case 0b00011101101: /* fstgt.d */
- case 0b00011101110: /* fstle.s */
- case 0b00011101111: /* fstle.d */
- case 0b00011111000: /* stgt.b */
- case 0b00011111001: /* stgt.h */
- case 0b00011111010: /* stgt.w */
- case 0b00011111011: /* stgt.d */
- case 0b00011111100: /* stle.b */
- case 0b00011111101: /* stle.h */
- case 0b00011111110: /* stle.w */
- case 0b00011111111: /* stle.d */
- case 0b00011000000 ... 0b00011100011: /* am* insns */
- return true;
- }
- break;
- }
-
- return false;
-}
-
-#endif
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef MIPS_HOST_SIGNAL_H
-#define MIPS_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.pc;
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.pc = pc;
-}
-
-#if defined(__misp16) || defined(__mips_micromips)
-#error "Unsupported encoding"
-#endif
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- uint32_t insn = *(uint32_t *)host_signal_pc(uc);
-
- /* Detect all store instructions at program counter. */
- switch ((insn >> 26) & 077) {
- case 050: /* SB */
- case 051: /* SH */
- case 052: /* SWL */
- case 053: /* SW */
- case 054: /* SDL */
- case 055: /* SDR */
- case 056: /* SWR */
- case 070: /* SC */
- case 071: /* SWC1 */
- case 074: /* SCD */
- case 075: /* SDC1 */
- case 077: /* SD */
-#if !defined(__mips_isa_rev) || __mips_isa_rev < 6
- case 072: /* SWC2 */
- case 076: /* SDC2 */
-#endif
- return true;
- case 023: /* COP1X */
- /*
- * Required in all versions of MIPS64 since
- * MIPS64r1 and subsequent versions of MIPS32r2.
- */
- switch (insn & 077) {
- case 010: /* SWXC1 */
- case 011: /* SDXC1 */
- case 015: /* SUXC1 */
- return true;
- }
- break;
- }
- return false;
-}
-
-#endif
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef PPC_HOST_SIGNAL_H
-#define PPC_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.regs->nip;
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.regs->nip = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- return uc->uc_mcontext.regs->trap != 0x400
- && (uc->uc_mcontext.regs->dsisr & 0x02000000);
-}
-
-#endif
+++ /dev/null
-#include "../ppc/host-signal.h"
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef RISCV_HOST_SIGNAL_H
-#define RISCV_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.__gregs[REG_PC];
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.__gregs[REG_PC] = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- /*
- * Detect store by reading the instruction at the program counter.
- * Do not read more than 16 bits, because we have not yet determined
- * the size of the instruction.
- */
- const uint16_t *pinsn = (const uint16_t *)host_signal_pc(uc);
- uint16_t insn = pinsn[0];
-
- /* 16-bit instructions */
- switch (insn & 0xe003) {
- case 0xa000: /* c.fsd */
- case 0xc000: /* c.sw */
- case 0xe000: /* c.sd (rv64) / c.fsw (rv32) */
- case 0xa002: /* c.fsdsp */
- case 0xc002: /* c.swsp */
- case 0xe002: /* c.sdsp (rv64) / c.fswsp (rv32) */
- return true;
- }
-
- /* 32-bit instructions, major opcodes */
- switch (insn & 0x7f) {
- case 0x23: /* store */
- case 0x27: /* store-fp */
- return true;
- case 0x2f: /* amo */
- /*
- * The AMO function code is in bits 25-31, unread as yet.
- * The AMO functions are LR (read), SC (write), and the
- * rest are all read-modify-write.
- */
- insn = pinsn[1];
- return (insn >> 11) != 2; /* LR */
- }
-
- return false;
-}
-
-#endif
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef S390_HOST_SIGNAL_H
-#define S390_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.psw.addr;
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.psw.addr = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- uint16_t *pinsn = (uint16_t *)host_signal_pc(uc);
-
- /*
- * ??? On linux, the non-rt signal handler has 4 (!) arguments instead
- * of the normal 2 arguments. The 4th argument contains the "Translation-
- * Exception Identification for DAT Exceptions" from the hardware (aka
- * "int_parm_long"), which does in fact contain the is_write value.
- * The rt signal handler, as far as I can tell, does not give this value
- * at all. Not that we could get to it from here even if it were.
- * So fall back to parsing instructions. Treat read-modify-write ones as
- * writes, which is not fully correct, but for tracking self-modifying code
- * this is better than treating them as reads. Checking si_addr page flags
- * might be a viable improvement, albeit a racy one.
- */
- /* ??? This is not even close to complete. */
- switch (pinsn[0] >> 8) {
- case 0x50: /* ST */
- case 0x42: /* STC */
- case 0x40: /* STH */
- case 0xba: /* CS */
- case 0xbb: /* CDS */
- return true;
- case 0xc4: /* RIL format insns */
- switch (pinsn[0] & 0xf) {
- case 0xf: /* STRL */
- case 0xb: /* STGRL */
- case 0x7: /* STHRL */
- return true;
- }
- break;
- case 0xc8: /* SSF format insns */
- switch (pinsn[0] & 0xf) {
- case 0x2: /* CSST */
- return true;
- }
- break;
- case 0xe3: /* RXY format insns */
- switch (pinsn[2] & 0xff) {
- case 0x50: /* STY */
- case 0x24: /* STG */
- case 0x72: /* STCY */
- case 0x70: /* STHY */
- case 0x8e: /* STPQ */
- case 0x3f: /* STRVH */
- case 0x3e: /* STRV */
- case 0x2f: /* STRVG */
- return true;
- }
- break;
- case 0xeb: /* RSY format insns */
- switch (pinsn[2] & 0xff) {
- case 0x14: /* CSY */
- case 0x30: /* CSG */
- case 0x31: /* CDSY */
- case 0x3e: /* CDSG */
- case 0xe4: /* LANG */
- case 0xe6: /* LAOG */
- case 0xe7: /* LAXG */
- case 0xe8: /* LAAG */
- case 0xea: /* LAALG */
- case 0xf4: /* LAN */
- case 0xf6: /* LAO */
- case 0xf7: /* LAX */
- case 0xfa: /* LAAL */
- case 0xf8: /* LAA */
- return true;
- }
- break;
- }
- return false;
-}
-
-#endif
+++ /dev/null
-#include "../s390/host-signal.h"
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (c) 2003-2005 Fabrice Bellard
- * Copyright (c) 2021 Linaro Limited
- *
- * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
- * See the COPYING file in the top-level directory.
- */
-
-#ifndef SPARC_HOST_SIGNAL_H
-#define SPARC_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
-#ifdef __arch64__
- return uc->uc_mcontext.mc_gregs[MC_PC];
-#else
- return uc->uc_mcontext.gregs[REG_PC];
-#endif
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
-#ifdef __arch64__
- uc->uc_mcontext.mc_gregs[MC_PC] = pc;
-#else
- uc->uc_mcontext.gregs[REG_PC] = pc;
-#endif
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- uint32_t insn = *(uint32_t *)host_signal_pc(uc);
-
- if ((insn >> 30) == 3) {
- switch ((insn >> 19) & 0x3f) {
- case 0x05: /* stb */
- case 0x15: /* stba */
- case 0x06: /* sth */
- case 0x16: /* stha */
- case 0x04: /* st */
- case 0x14: /* sta */
- case 0x07: /* std */
- case 0x17: /* stda */
- case 0x0e: /* stx */
- case 0x1e: /* stxa */
- case 0x24: /* stf */
- case 0x34: /* stfa */
- case 0x27: /* stdf */
- case 0x37: /* stdfa */
- case 0x26: /* stqf */
- case 0x36: /* stqfa */
- case 0x25: /* stfsr */
- case 0x3c: /* casa */
- case 0x3e: /* casxa */
- return true;
- }
- }
- return false;
-}
-
-#endif
+++ /dev/null
-#include "../sparc/host-signal.h"
+++ /dev/null
-#include "../x86_64/host-signal.h"
+++ /dev/null
-/*
- * host-signal.h: signal info dependent on the host architecture
- *
- * Copyright (C) 2021 Linaro Limited
- *
- * 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 X86_64_HOST_SIGNAL_H
-#define X86_64_HOST_SIGNAL_H
-
-static inline uintptr_t host_signal_pc(ucontext_t *uc)
-{
- return uc->uc_mcontext.gregs[REG_RIP];
-}
-
-static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
-{
- uc->uc_mcontext.gregs[REG_RIP] = pc;
-}
-
-static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
-{
- return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe
- && (uc->uc_mcontext.gregs[REG_ERR] & 0x2);
-}
-
-#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef AARCH64_HOST_SIGNAL_H
+#define AARCH64_HOST_SIGNAL_H
+
+/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */
+#ifndef ESR_MAGIC
+#define ESR_MAGIC 0x45535201
+struct esr_context {
+ struct _aarch64_ctx head;
+ uint64_t esr;
+};
+#endif
+
+static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc)
+{
+ return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved;
+}
+
+static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr)
+{
+ return (struct _aarch64_ctx *)((char *)hdr + hdr->size);
+}
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.pc;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.pc = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ struct _aarch64_ctx *hdr;
+ uint32_t insn;
+
+ /* Find the esr_context, which has the WnR bit in it */
+ for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) {
+ if (hdr->magic == ESR_MAGIC) {
+ struct esr_context const *ec = (struct esr_context const *)hdr;
+ uint64_t esr = ec->esr;
+
+ /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */
+ return extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1;
+ }
+ }
+
+ /*
+ * Fall back to parsing instructions; will only be needed
+ * for really ancient (pre-3.16) kernels.
+ */
+ insn = *(uint32_t *)host_signal_pc(uc);
+
+ return (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */
+ || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */
+ || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */
+ || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */
+ || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */
+ || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */
+ || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */
+ /* Ignore bits 10, 11 & 21, controlling indexing. */
+ || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */
+ || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */
+ /* Ignore bits 23 & 24, controlling indexing. */
+ || (insn & 0x3a400000) == 0x28000000; /* C3.3.7,14-16 */
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef ALPHA_HOST_SIGNAL_H
+#define ALPHA_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.sc_pc;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.sc_pc = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint32_t *pc = (uint32_t *)host_signal_pc(uc);
+ uint32_t insn = *pc;
+
+ /* XXX: need kernel patch to get write flag faster */
+ switch (insn >> 26) {
+ case 0x0d: /* stw */
+ case 0x0e: /* stb */
+ case 0x0f: /* stq_u */
+ case 0x24: /* stf */
+ case 0x25: /* stg */
+ case 0x26: /* sts */
+ case 0x27: /* stt */
+ case 0x2c: /* stl */
+ case 0x2d: /* stq */
+ case 0x2e: /* stl_c */
+ case 0x2f: /* stq_c */
+ return true;
+ }
+ return false;
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef ARM_HOST_SIGNAL_H
+#define ARM_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.arm_pc;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.arm_pc = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ /*
+ * In the FSR, bit 11 is WnR, assuming a v6 or
+ * later processor. On v5 we will always report
+ * this as a read, which will fail later.
+ */
+ uint32_t fsr = uc->uc_mcontext.error_code;
+ return extract32(fsr, 11, 1);
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef I386_HOST_SIGNAL_H
+#define I386_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_EIP];
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.gregs[REG_EIP] = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe
+ && (uc->uc_mcontext.gregs[REG_ERR] & 0x2);
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 WANG Xuerui <git@xen0n.name>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef LOONGARCH64_HOST_SIGNAL_H
+#define LOONGARCH64_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.__pc;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.__pc = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ const uint32_t *pinsn = (const uint32_t *)host_signal_pc(uc);
+ uint32_t insn = pinsn[0];
+
+ /* Detect store by reading the instruction at the program counter. */
+ switch ((insn >> 26) & 0b111111) {
+ case 0b001000: /* {ll,sc}.[wd] */
+ switch ((insn >> 24) & 0b11) {
+ case 0b01: /* sc.w */
+ case 0b11: /* sc.d */
+ return true;
+ }
+ break;
+ case 0b001001: /* {ld,st}ox4.[wd] ({ld,st}ptr.[wd]) */
+ switch ((insn >> 24) & 0b11) {
+ case 0b01: /* stox4.w (stptr.w) */
+ case 0b11: /* stox4.d (stptr.d) */
+ return true;
+ }
+ break;
+ case 0b001010: /* {ld,st}.* family */
+ switch ((insn >> 22) & 0b1111) {
+ case 0b0100: /* st.b */
+ case 0b0101: /* st.h */
+ case 0b0110: /* st.w */
+ case 0b0111: /* st.d */
+ case 0b1101: /* fst.s */
+ case 0b1111: /* fst.d */
+ return true;
+ }
+ break;
+ case 0b001110: /* indexed, atomic, bounds-checking memory operations */
+ switch ((insn >> 15) & 0b11111111111) {
+ case 0b00000100000: /* stx.b */
+ case 0b00000101000: /* stx.h */
+ case 0b00000110000: /* stx.w */
+ case 0b00000111000: /* stx.d */
+ case 0b00001110000: /* fstx.s */
+ case 0b00001111000: /* fstx.d */
+ case 0b00011101100: /* fstgt.s */
+ case 0b00011101101: /* fstgt.d */
+ case 0b00011101110: /* fstle.s */
+ case 0b00011101111: /* fstle.d */
+ case 0b00011111000: /* stgt.b */
+ case 0b00011111001: /* stgt.h */
+ case 0b00011111010: /* stgt.w */
+ case 0b00011111011: /* stgt.d */
+ case 0b00011111100: /* stle.b */
+ case 0b00011111101: /* stle.h */
+ case 0b00011111110: /* stle.w */
+ case 0b00011111111: /* stle.d */
+ case 0b00011000000 ... 0b00011100011: /* am* insns */
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef MIPS_HOST_SIGNAL_H
+#define MIPS_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.pc;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.pc = pc;
+}
+
+#if defined(__misp16) || defined(__mips_micromips)
+#error "Unsupported encoding"
+#endif
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint32_t insn = *(uint32_t *)host_signal_pc(uc);
+
+ /* Detect all store instructions at program counter. */
+ switch ((insn >> 26) & 077) {
+ case 050: /* SB */
+ case 051: /* SH */
+ case 052: /* SWL */
+ case 053: /* SW */
+ case 054: /* SDL */
+ case 055: /* SDR */
+ case 056: /* SWR */
+ case 070: /* SC */
+ case 071: /* SWC1 */
+ case 074: /* SCD */
+ case 075: /* SDC1 */
+ case 077: /* SD */
+#if !defined(__mips_isa_rev) || __mips_isa_rev < 6
+ case 072: /* SWC2 */
+ case 076: /* SDC2 */
+#endif
+ return true;
+ case 023: /* COP1X */
+ /*
+ * Required in all versions of MIPS64 since
+ * MIPS64r1 and subsequent versions of MIPS32r2.
+ */
+ switch (insn & 077) {
+ case 010: /* SWXC1 */
+ case 011: /* SDXC1 */
+ case 015: /* SUXC1 */
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef PPC_HOST_SIGNAL_H
+#define PPC_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.regs->nip;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.regs->nip = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ return uc->uc_mcontext.regs->trap != 0x400
+ && (uc->uc_mcontext.regs->dsisr & 0x02000000);
+}
+
+#endif
--- /dev/null
+#include "../ppc/host-signal.h"
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef RISCV_HOST_SIGNAL_H
+#define RISCV_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.__gregs[REG_PC];
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.__gregs[REG_PC] = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ /*
+ * Detect store by reading the instruction at the program counter.
+ * Do not read more than 16 bits, because we have not yet determined
+ * the size of the instruction.
+ */
+ const uint16_t *pinsn = (const uint16_t *)host_signal_pc(uc);
+ uint16_t insn = pinsn[0];
+
+ /* 16-bit instructions */
+ switch (insn & 0xe003) {
+ case 0xa000: /* c.fsd */
+ case 0xc000: /* c.sw */
+ case 0xe000: /* c.sd (rv64) / c.fsw (rv32) */
+ case 0xa002: /* c.fsdsp */
+ case 0xc002: /* c.swsp */
+ case 0xe002: /* c.sdsp (rv64) / c.fswsp (rv32) */
+ return true;
+ }
+
+ /* 32-bit instructions, major opcodes */
+ switch (insn & 0x7f) {
+ case 0x23: /* store */
+ case 0x27: /* store-fp */
+ return true;
+ case 0x2f: /* amo */
+ /*
+ * The AMO function code is in bits 25-31, unread as yet.
+ * The AMO functions are LR (read), SC (write), and the
+ * rest are all read-modify-write.
+ */
+ insn = pinsn[1];
+ return (insn >> 11) != 2; /* LR */
+ }
+
+ return false;
+}
+
+#endif
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef S390_HOST_SIGNAL_H
+#define S390_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.psw.addr;
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.psw.addr = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint16_t *pinsn = (uint16_t *)host_signal_pc(uc);
+
+ /*
+ * ??? On linux, the non-rt signal handler has 4 (!) arguments instead
+ * of the normal 2 arguments. The 4th argument contains the "Translation-
+ * Exception Identification for DAT Exceptions" from the hardware (aka
+ * "int_parm_long"), which does in fact contain the is_write value.
+ * The rt signal handler, as far as I can tell, does not give this value
+ * at all. Not that we could get to it from here even if it were.
+ * So fall back to parsing instructions. Treat read-modify-write ones as
+ * writes, which is not fully correct, but for tracking self-modifying code
+ * this is better than treating them as reads. Checking si_addr page flags
+ * might be a viable improvement, albeit a racy one.
+ */
+ /* ??? This is not even close to complete. */
+ switch (pinsn[0] >> 8) {
+ case 0x50: /* ST */
+ case 0x42: /* STC */
+ case 0x40: /* STH */
+ case 0xba: /* CS */
+ case 0xbb: /* CDS */
+ return true;
+ case 0xc4: /* RIL format insns */
+ switch (pinsn[0] & 0xf) {
+ case 0xf: /* STRL */
+ case 0xb: /* STGRL */
+ case 0x7: /* STHRL */
+ return true;
+ }
+ break;
+ case 0xc8: /* SSF format insns */
+ switch (pinsn[0] & 0xf) {
+ case 0x2: /* CSST */
+ return true;
+ }
+ break;
+ case 0xe3: /* RXY format insns */
+ switch (pinsn[2] & 0xff) {
+ case 0x50: /* STY */
+ case 0x24: /* STG */
+ case 0x72: /* STCY */
+ case 0x70: /* STHY */
+ case 0x8e: /* STPQ */
+ case 0x3f: /* STRVH */
+ case 0x3e: /* STRV */
+ case 0x2f: /* STRVG */
+ return true;
+ }
+ break;
+ case 0xeb: /* RSY format insns */
+ switch (pinsn[2] & 0xff) {
+ case 0x14: /* CSY */
+ case 0x30: /* CSG */
+ case 0x31: /* CDSY */
+ case 0x3e: /* CDSG */
+ case 0xe4: /* LANG */
+ case 0xe6: /* LAOG */
+ case 0xe7: /* LAXG */
+ case 0xe8: /* LAAG */
+ case 0xea: /* LAALG */
+ case 0xf4: /* LAN */
+ case 0xf6: /* LAO */
+ case 0xf7: /* LAX */
+ case 0xfa: /* LAAL */
+ case 0xf8: /* LAA */
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+#endif
--- /dev/null
+#include "../s390/host-signal.h"
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (c) 2003-2005 Fabrice Bellard
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef SPARC_HOST_SIGNAL_H
+#define SPARC_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+#ifdef __arch64__
+ return uc->uc_mcontext.mc_gregs[MC_PC];
+#else
+ return uc->uc_mcontext.gregs[REG_PC];
+#endif
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+#ifdef __arch64__
+ uc->uc_mcontext.mc_gregs[MC_PC] = pc;
+#else
+ uc->uc_mcontext.gregs[REG_PC] = pc;
+#endif
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ uint32_t insn = *(uint32_t *)host_signal_pc(uc);
+
+ if ((insn >> 30) == 3) {
+ switch ((insn >> 19) & 0x3f) {
+ case 0x05: /* stb */
+ case 0x15: /* stba */
+ case 0x06: /* sth */
+ case 0x16: /* stha */
+ case 0x04: /* st */
+ case 0x14: /* sta */
+ case 0x07: /* std */
+ case 0x17: /* stda */
+ case 0x0e: /* stx */
+ case 0x1e: /* stxa */
+ case 0x24: /* stf */
+ case 0x34: /* stfa */
+ case 0x27: /* stdf */
+ case 0x37: /* stdfa */
+ case 0x26: /* stqf */
+ case 0x36: /* stqfa */
+ case 0x25: /* stfsr */
+ case 0x3c: /* casa */
+ case 0x3e: /* casxa */
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
--- /dev/null
+#include "../sparc/host-signal.h"
--- /dev/null
+#include "../x86_64/host-signal.h"
--- /dev/null
+/*
+ * host-signal.h: signal info dependent on the host architecture
+ *
+ * Copyright (C) 2021 Linaro Limited
+ *
+ * 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 X86_64_HOST_SIGNAL_H
+#define X86_64_HOST_SIGNAL_H
+
+static inline uintptr_t host_signal_pc(ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_RIP];
+}
+
+static inline void host_signal_set_pc(ucontext_t *uc, uintptr_t pc)
+{
+ uc->uc_mcontext.gregs[REG_RIP] = pc;
+}
+
+static inline bool host_signal_write(siginfo_t *info, ucontext_t *uc)
+{
+ return uc->uc_mcontext.gregs[REG_TRAPNO] == 0xe
+ && (uc->uc_mcontext.gregs[REG_ERR] & 0x2);
+}
+
+#endif
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU internal errno values for implementing user-only POSIX.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2021 Linaro, Ltd.
+ */
+
+#ifndef SPECIAL_ERRNO_H
+#define SPECIAL_ERRNO_H
+
+/*
+ * All of these are QEMU internal, not visible to the guest.
+ * They should be chosen so as to not overlap with any host
+ * or guest errno.
+ */
+
+/*
+ * This is returned when a system call should be restarted, to tell the
+ * main loop that it should wind the guest PC backwards so it will
+ * re-execute the syscall after handling any pending signals.
+ */
+#define QEMU_ERESTARTSYS 512
+
+/*
+ * This is returned after a successful sigreturn syscall, to indicate
+ * that it has correctly set the guest registers and so the main loop
+ * should not touch them.
+ */
+#define QEMU_ESIGRETURN 513
+
+#endif /* SPECIAL_ERRNO_H */
linux_user_ss = ss.source_set()
-common_user_inc += include_directories('host/' / host_arch)
-common_user_inc += include_directories('.')
+common_user_inc += include_directories('include/host/' / host_arch)
+common_user_inc += include_directories('include')
linux_user_ss.add(files(
'elfload.c',
#include "nwfpe/fpa11.h"
#endif
-#define MAX_SIGQUEUE_SIZE 1024
-
struct emulated_sigtable {
int pending; /* true if signal is pending */
target_siginfo_t info;
void process_pending_signals(CPUArchState *cpu_env);
void signal_init(void);
-int queue_signal(CPUArchState *env, int sig, int si_type,
- target_siginfo_t *info);
+void queue_signal(CPUArchState *env, int sig, int si_type,
+ target_siginfo_t *info);
void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
int target_to_host_signal(int sig);
struct sigaction act;
host_sig = target_to_host_signal(target_sig);
- trace_user_force_sig(env, target_sig, host_sig);
+ trace_user_dump_core_and_abort(env, target_sig, host_sig);
gdb_signalled(env, target_sig);
/* dump core if supported by target binary format */
/* queue a signal so that it will be send to the virtual CPU as soon
as possible */
-int queue_signal(CPUArchState *env, int sig, int si_type,
- target_siginfo_t *info)
+void queue_signal(CPUArchState *env, int sig, int si_type,
+ target_siginfo_t *info)
{
CPUState *cpu = env_cpu(env);
TaskState *ts = cpu->opaque;
ts->sync_signal.pending = sig;
/* signal that a new signal is pending */
qatomic_set(&ts->signal_pending, 1);
- return 1; /* indicates that the signal was queued */
}
+++ /dev/null
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * QEMU internal errno values for implementing user-only POSIX.
- *
- * Copyright (c) 2003 Fabrice Bellard
- * Copyright (c) 2021 Linaro, Ltd.
- */
-
-#ifndef SPECIAL_ERRNO_H
-#define SPECIAL_ERRNO_H
-
-/*
- * All of these are QEMU internal, not visible to the guest.
- * They should be chosen so as to not overlap with any host
- * or guest errno.
- */
-
-/*
- * This is returned when a system call should be restarted, to tell the
- * main loop that it should wind the guest PC backwards so it will
- * re-execute the syscall after handling any pending signals.
- */
-#define QEMU_ERESTARTSYS 512
-
-/*
- * This is returned after a successful sigreturn syscall, to indicate
- * that it has correctly set the guest registers and so the main loop
- * should not touch them.
- */
-#define QEMU_ESIGRETURN 513
-
-#endif /* SPECIAL_ERRNO_H */
user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
user_do_rt_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
user_do_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64
-user_force_sig(void *env, int target_sig, int host_sig) "env=%p signal %d (host %d)"
+user_dump_core_and_abort(void *env, int target_sig, int host_sig) "env=%p signal %d (host %d)"
user_handle_signal(void *env, int target_sig) "env=%p signal %d"
user_host_signal(void *env, int host_sig, int target_sig) "env=%p signal %d (target %d)"
user_queue_signal(void *env, int target_sig) "env=%p signal %d"
'b_staticpic=false', 'stdsplit=false'],
version: files('VERSION'))
-add_test_setup('quick', exclude_suites: 'slow', is_default: true)
-add_test_setup('slow', env: ['G_TEST_SLOW=1', 'SPEED=slow'])
+add_test_setup('quick', exclude_suites: ['block', 'slow', 'thorough'], is_default: true)
+add_test_setup('slow', exclude_suites: ['block', 'thorough'], env: ['G_TEST_SLOW=1', 'SPEED=slow'])
+add_test_setup('thorough', exclude_suites: ['block'], env: ['G_TEST_SLOW=1', 'SPEED=thorough'])
not_found = dependency('', required: false)
keyval = import('keyval')
config_host_data = configuration_data()
genh = []
+qapi_trace_events = []
target_dirs = config_host['TARGET_DIRS'].split()
have_linux_user = false
kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
elif cpu in ['mips', 'mips64']
kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu']
+elif cpu in ['riscv']
+ kvm_targets = ['riscv32-softmmu', 'riscv64-softmmu']
else
kvm_targets = []
endif
# Target-specific checks and dependencies #
###########################################
+# Fuzzing
if get_option('fuzzing') and get_option('fuzzing_engine') == '' and \
not cc.links('''
#include <stdint.h>
error('Your compiler does not support -fsanitize=fuzzer')
endif
+# Tracing backends
if 'ftrace' in get_option('trace_backends') and targetos != 'linux'
error('ftrace is supported only on Linux')
endif
error('syslog is not supported on this system')
endif
+# Miscellaneous Linux-only features
if targetos != 'linux' and get_option('mpath').enabled()
error('Multipath is supported only on Linux')
endif
endif
multiprocess_allowed = targetos == 'linux' and not get_option('multiprocess').disabled()
+# Target-specific libraries and flags
libm = cc.find_library('m', required: false)
threads = dependency('threads')
util = cc.find_library('util', required: false)
endif
endif
+# Target-specific configuration of accelerators
accelerators = []
if not get_option('kvm').disabled() and targetos == 'linux'
accelerators += 'CONFIG_KVM'
endif
linux_io_uring = not_found
if not get_option('linux_io_uring').auto() or have_block
- linux_io_uring = dependency('liburing', required: get_option('linux_io_uring'),
+ linux_io_uring = dependency('liburing', version: '>=0.3',
+ required: get_option('linux_io_uring'),
method: 'pkg-config', kwargs: static_kwargs)
endif
libxml2 = not_found
'CONFIG_ALL': true,
}
+target_configs_h = []
+foreach target: target_dirs
+ target_configs_h += config_target_h[target]
+ target_configs_h += config_devices_h.get(target, [])
+endforeach
+genh += custom_target('config-poison.h',
+ input: [target_configs_h],
+ output: 'config-poison.h',
+ capture: true,
+ command: [find_program('scripts/make-config-poison.sh'),
+ target_configs_h])
+
##############
# Submodules #
##############
if fdt.found() and cc.links('''
#include <libfdt.h>
#include <libfdt_env.h>
- int main(void) { fdt_check_full(NULL, 0); return 0; }''',
+ int main(void) { fdt_find_max_phandle(NULL, NULL); return 0; }''',
dependencies: fdt)
fdt_opt = 'system'
elif fdt_opt == 'system'
block_ss = ss.source_set()
chardev_ss = ss.source_set()
common_ss = ss.source_set()
-common_user_ss = ss.source_set()
crypto_ss = ss.source_set()
hwcore_ss = ss.source_set()
io_ss = ss.source_set()
'monitor',
'util',
]
-if have_user
+if have_linux_user
trace_events_subdirs += [ 'linux-user' ]
endif
+if have_bsd_user
+ trace_events_subdirs += [ 'bsd-user' ]
+endif
if have_block
trace_events_subdirs += [
'authz',
vhost_user = libvhost_user.get_variable('vhost_user_dep')
endif
+# NOTE: the trace/ subdirectory needs the qapi_trace_events variable
+# that is filled in by qapi/.
subdir('qapi')
subdir('qobject')
subdir('stubs')
subdir('bsd-user')
subdir('linux-user')
-common_user_ss = common_user_ss.apply(config_all, strict: false)
-common_user = static_library('common-user',
- sources: common_user_ss.sources(),
- dependencies: common_user_ss.dependencies(),
- include_directories: common_user_inc,
- name_suffix: 'fa',
- build_by_default: false)
-common_user = declare_dependency(link_with: common_user)
-
-user_ss.add(common_user)
-
# needed for fuzzing binaries
subdir('tests/qtest/libqos')
subdir('tests/qtest/fuzz')
# Targets #
###########
+emulator_modules = []
foreach m : block_mods + softmmu_mods
- shared_module(m.name(),
+ emulator_modules += shared_module(m.name(),
+ build_by_default: true,
name_prefix: '',
link_whole: m,
install: true,
common_all = static_library('common',
build_by_default: false,
sources: common_all.sources() + genh,
+ include_directories: common_user_inc,
implicit_include_directories: false,
dependencies: common_all.dependencies(),
name_suffix: 'fa')
else
abi = config_target['TARGET_ABI_DIR']
target_type='user'
+ target_inc += common_user_inc
qemu_target_name = 'qemu-' + target_name
if target_base_arch in target_user_arch
t = target_user_arch[target_base_arch].apply(config_target, strict: false)
endif
if 'CONFIG_LINUX_USER' in config_target
base_dir = 'linux-user'
- target_inc += include_directories('linux-user/host/' / host_arch)
endif
if 'CONFIG_BSD_USER' in config_target
base_dir = 'bsd-user'
target_inc += include_directories('bsd-user/' / targetos)
+ target_inc += include_directories('bsd-user/host/' / host_arch)
dir = base_dir / abi
arch_srcs += files(dir / 'signal.c', dir / 'target_arch_cpu.c')
endif
install_input += meson.current_source_dir() / entitlements
endif
+ entitlement = find_program('scripts/entitlement.sh')
emulators += {exe['name'] : custom_target(exe['name'],
input: build_input,
output: exe['name'],
- command: [
- files('scripts/entitlement.sh'),
- '@OUTPUT@',
- '@INPUT@'
- ])
+ command: [entitlement, '@OUTPUT@', '@INPUT@'])
}
- meson.add_install_script('scripts/entitlement.sh', '--install',
+ meson.add_install_script(entitlement, '--install',
get_option('bindir') / exe['name'],
install_input)
else
summary_info += {' spice server support': spice}
endif
summary_info += {'rbd support': rbd}
-summary_info += {'xfsctl support': config_host.has_key('CONFIG_XFS')}
summary_info += {'smartcard support': cacard}
summary_info += {'U2F support': u2f}
summary_info += {'libusb': libusb}
info->ram->page_size = page_size;
info->ram->multifd_bytes = ram_counters.multifd_bytes;
info->ram->pages_per_second = s->pages_per_second;
+ info->ram->precopy_bytes = ram_counters.precopy_bytes;
+ info->ram->downtime_bytes = ram_counters.downtime_bytes;
+ info->ram->postcopy_bytes = ram_counters.postcopy_bytes;
if (migrate_use_xbzrle()) {
info->has_xbzrle_cache = true;
* that are dirty
*/
if (migrate_postcopy_ram()) {
- if (ram_postcopy_send_discard_bitmap(ms)) {
- error_report("postcopy send discard bitmap failed");
- goto fail;
- }
+ ram_postcopy_send_discard_bitmap(ms);
}
/*
qemu_mutex_unlock_iothread();
trace_migration_completion_postcopy_end_after_complete();
- } else if (s->state == MIGRATION_STATUS_CANCELLING) {
+ } else {
goto fail;
}
goto fail_invalidate;
}
- if (!migrate_colo_enabled()) {
+ if (migrate_colo_enabled() && s->state == MIGRATION_STATUS_ACTIVE) {
+ /* COLO does not support postcopy */
+ migrate_set_state(&s->state, MIGRATION_STATUS_ACTIVE,
+ MIGRATION_STATUS_COLO);
+ } else {
migrate_set_state(&s->state, current_active_state,
MIGRATION_STATUS_COMPLETED);
}
"COLO enabled", __func__);
}
migrate_start_colo_process(s);
- /*
- * Fixme: we will run VM in COLO no matter its old running state.
- * After exited COLO, we will keep running.
- */
- /* Fallthrough */
- case MIGRATION_STATUS_ACTIVE:
- /*
- * We should really assert here, but since it's during
- * migration, let's try to reduce the usage of assertions.
- */
s->vm_was_running = true;
/* Fallthrough */
case MIGRATION_STATUS_FAILED:
zs->opaque = Z_NULL;
if (deflateInit(zs, migrate_multifd_zlib_level()) != Z_OK) {
g_free(z);
- error_setg(errp, "multifd %d: deflate init failed", p->id);
+ error_setg(errp, "multifd %u: deflate init failed", p->id);
return -1;
}
- /* To be safe, we reserve twice the size of the packet */
- z->zbuff_len = MULTIFD_PACKET_SIZE * 2;
+ /* This is the maxium size of the compressed buffer */
+ z->zbuff_len = compressBound(MULTIFD_PACKET_SIZE);
z->zbuff = g_try_malloc(z->zbuff_len);
if (!z->zbuff) {
deflateEnd(&z->zs);
g_free(z);
- error_setg(errp, "multifd %d: out of memory for zbuff", p->id);
+ error_setg(errp, "multifd %u: out of memory for zbuff", p->id);
return -1;
}
p->data = z;
int ret;
uint32_t i;
- for (i = 0; i < p->pages->num; i++) {
+ for (i = 0; i < p->normal_num; i++) {
uint32_t available = z->zbuff_len - out_size;
int flush = Z_NO_FLUSH;
- if (i == p->pages->num - 1) {
+ if (i == p->normal_num - 1) {
flush = Z_SYNC_FLUSH;
}
zs->avail_in = page_size;
- zs->next_in = p->pages->block->host + p->pages->offset[i];
+ zs->next_in = p->pages->block->host + p->normal[i];
zs->avail_out = available;
zs->next_out = z->zbuff + out_size;
ret = deflate(zs, flush);
} while (ret == Z_OK && zs->avail_in && zs->avail_out);
if (ret == Z_OK && zs->avail_in) {
- error_setg(errp, "multifd %d: deflate failed to compress all input",
+ error_setg(errp, "multifd %u: deflate failed to compress all input",
p->id);
return -1;
}
if (ret != Z_OK) {
- error_setg(errp, "multifd %d: deflate returned %d instead of Z_OK",
+ error_setg(errp, "multifd %u: deflate returned %d instead of Z_OK",
p->id, ret);
return -1;
}
out_size += available - zs->avail_out;
}
+ p->iov[p->iovs_num].iov_base = z->zbuff;
+ p->iov[p->iovs_num].iov_len = out_size;
+ p->iovs_num++;
p->next_packet_size = out_size;
p->flags |= MULTIFD_FLAG_ZLIB;
return 0;
}
-/**
- * zlib_send_write: do the actual write of the data
- *
- * Do the actual write of the comprresed buffer.
- *
- * Returns 0 for success or -1 for error
- *
- * @p: Params for the channel that we are using
- * @used: number of pages used
- * @errp: pointer to an error
- */
-static int zlib_send_write(MultiFDSendParams *p, uint32_t used, Error **errp)
-{
- struct zlib_data *z = p->data;
-
- return qio_channel_write_all(p->c, (void *)z->zbuff, p->next_packet_size,
- errp);
-}
-
/**
* zlib_recv_setup: setup receive side
*
zs->avail_in = 0;
zs->next_in = Z_NULL;
if (inflateInit(zs) != Z_OK) {
- error_setg(errp, "multifd %d: inflate init failed", p->id);
+ error_setg(errp, "multifd %u: inflate init failed", p->id);
return -1;
}
/* To be safe, we reserve twice the size of the packet */
z->zbuff = g_try_malloc(z->zbuff_len);
if (!z->zbuff) {
inflateEnd(zs);
- error_setg(errp, "multifd %d: out of memory for zbuff", p->id);
+ error_setg(errp, "multifd %u: out of memory for zbuff", p->id);
return -1;
}
return 0;
uint32_t in_size = p->next_packet_size;
/* we measure the change of total_out */
uint32_t out_size = zs->total_out;
- uint32_t expected_size = p->pages->num * qemu_target_page_size();
+ uint32_t expected_size = p->normal_num * page_size;
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
int ret;
int i;
if (flags != MULTIFD_FLAG_ZLIB) {
- error_setg(errp, "multifd %d: flags received %x flags expected %x",
+ error_setg(errp, "multifd %u: flags received %x flags expected %x",
p->id, flags, MULTIFD_FLAG_ZLIB);
return -1;
}
zs->avail_in = in_size;
zs->next_in = z->zbuff;
- for (i = 0; i < p->pages->num; i++) {
+ for (i = 0; i < p->normal_num; i++) {
int flush = Z_NO_FLUSH;
unsigned long start = zs->total_out;
- if (i == p->pages->num - 1) {
+ if (i == p->normal_num - 1) {
flush = Z_SYNC_FLUSH;
}
zs->avail_out = page_size;
- zs->next_out = p->pages->block->host + p->pages->offset[i];
+ zs->next_out = p->host + p->normal[i];
/*
* Welcome to inflate semantics
} while (ret == Z_OK && zs->avail_in
&& (zs->total_out - start) < page_size);
if (ret == Z_OK && (zs->total_out - start) < page_size) {
- error_setg(errp, "multifd %d: inflate generated too few output",
+ error_setg(errp, "multifd %u: inflate generated too few output",
p->id);
return -1;
}
if (ret != Z_OK) {
- error_setg(errp, "multifd %d: inflate returned %d instead of Z_OK",
+ error_setg(errp, "multifd %u: inflate returned %d instead of Z_OK",
p->id, ret);
return -1;
}
}
out_size = zs->total_out - out_size;
if (out_size != expected_size) {
- error_setg(errp, "multifd %d: packet size received %d size expected %d",
+ error_setg(errp, "multifd %u: packet size received %u size expected %u",
p->id, out_size, expected_size);
return -1;
}
.send_setup = zlib_send_setup,
.send_cleanup = zlib_send_cleanup,
.send_prepare = zlib_send_prepare,
- .send_write = zlib_send_write,
.recv_setup = zlib_recv_setup,
.recv_cleanup = zlib_recv_cleanup,
.recv_pages = zlib_recv_pages
z->zcs = ZSTD_createCStream();
if (!z->zcs) {
g_free(z);
- error_setg(errp, "multifd %d: zstd createCStream failed", p->id);
+ error_setg(errp, "multifd %u: zstd createCStream failed", p->id);
return -1;
}
if (ZSTD_isError(res)) {
ZSTD_freeCStream(z->zcs);
g_free(z);
- error_setg(errp, "multifd %d: initCStream failed with error %s",
+ error_setg(errp, "multifd %u: initCStream failed with error %s",
p->id, ZSTD_getErrorName(res));
return -1;
}
- /* To be safe, we reserve twice the size of the packet */
- z->zbuff_len = MULTIFD_PACKET_SIZE * 2;
+ /* This is the maxium size of the compressed buffer */
+ z->zbuff_len = ZSTD_compressBound(MULTIFD_PACKET_SIZE);
z->zbuff = g_try_malloc(z->zbuff_len);
if (!z->zbuff) {
ZSTD_freeCStream(z->zcs);
g_free(z);
- error_setg(errp, "multifd %d: out of memory for zbuff", p->id);
+ error_setg(errp, "multifd %u: out of memory for zbuff", p->id);
return -1;
}
return 0;
z->out.size = z->zbuff_len;
z->out.pos = 0;
- for (i = 0; i < p->pages->num; i++) {
+ for (i = 0; i < p->normal_num; i++) {
ZSTD_EndDirective flush = ZSTD_e_continue;
- if (i == p->pages->num - 1) {
+ if (i == p->normal_num - 1) {
flush = ZSTD_e_flush;
}
- z->in.src = p->pages->block->host + p->pages->offset[i];
+ z->in.src = p->pages->block->host + p->normal[i];
z->in.size = page_size;
z->in.pos = 0;
} while (ret > 0 && (z->in.size - z->in.pos > 0)
&& (z->out.size - z->out.pos > 0));
if (ret > 0 && (z->in.size - z->in.pos > 0)) {
- error_setg(errp, "multifd %d: compressStream buffer too small",
+ error_setg(errp, "multifd %u: compressStream buffer too small",
p->id);
return -1;
}
if (ZSTD_isError(ret)) {
- error_setg(errp, "multifd %d: compressStream error %s",
+ error_setg(errp, "multifd %u: compressStream error %s",
p->id, ZSTD_getErrorName(ret));
return -1;
}
}
+ p->iov[p->iovs_num].iov_base = z->zbuff;
+ p->iov[p->iovs_num].iov_len = z->out.pos;
+ p->iovs_num++;
p->next_packet_size = z->out.pos;
p->flags |= MULTIFD_FLAG_ZSTD;
return 0;
}
-/**
- * zstd_send_write: do the actual write of the data
- *
- * Do the actual write of the comprresed buffer.
- *
- * Returns 0 for success or -1 for error
- *
- * @p: Params for the channel that we are using
- * @used: number of pages used
- * @errp: pointer to an error
- */
-static int zstd_send_write(MultiFDSendParams *p, uint32_t used, Error **errp)
-{
- struct zstd_data *z = p->data;
-
- return qio_channel_write_all(p->c, (void *)z->zbuff, p->next_packet_size,
- errp);
-}
-
/**
* zstd_recv_setup: setup receive side
*
z->zds = ZSTD_createDStream();
if (!z->zds) {
g_free(z);
- error_setg(errp, "multifd %d: zstd createDStream failed", p->id);
+ error_setg(errp, "multifd %u: zstd createDStream failed", p->id);
return -1;
}
if (ZSTD_isError(ret)) {
ZSTD_freeDStream(z->zds);
g_free(z);
- error_setg(errp, "multifd %d: initDStream failed with error %s",
+ error_setg(errp, "multifd %u: initDStream failed with error %s",
p->id, ZSTD_getErrorName(ret));
return -1;
}
if (!z->zbuff) {
ZSTD_freeDStream(z->zds);
g_free(z);
- error_setg(errp, "multifd %d: out of memory for zbuff", p->id);
+ error_setg(errp, "multifd %u: out of memory for zbuff", p->id);
return -1;
}
return 0;
uint32_t in_size = p->next_packet_size;
uint32_t out_size = 0;
size_t page_size = qemu_target_page_size();
- uint32_t expected_size = p->pages->num * page_size;
+ uint32_t expected_size = p->normal_num * page_size;
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
struct zstd_data *z = p->data;
int ret;
int i;
if (flags != MULTIFD_FLAG_ZSTD) {
- error_setg(errp, "multifd %d: flags received %x flags expected %x",
+ error_setg(errp, "multifd %u: flags received %x flags expected %x",
p->id, flags, MULTIFD_FLAG_ZSTD);
return -1;
}
z->in.size = in_size;
z->in.pos = 0;
- for (i = 0; i < p->pages->num; i++) {
- z->out.dst = p->pages->block->host + p->pages->offset[i];
+ for (i = 0; i < p->normal_num; i++) {
+ z->out.dst = p->host + p->normal[i];
z->out.size = page_size;
z->out.pos = 0;
} while (ret > 0 && (z->in.size - z->in.pos > 0)
&& (z->out.pos < page_size));
if (ret > 0 && (z->out.pos < page_size)) {
- error_setg(errp, "multifd %d: decompressStream buffer too small",
+ error_setg(errp, "multifd %u: decompressStream buffer too small",
p->id);
return -1;
}
if (ZSTD_isError(ret)) {
- error_setg(errp, "multifd %d: decompressStream returned %s",
+ error_setg(errp, "multifd %u: decompressStream returned %s",
p->id, ZSTD_getErrorName(ret));
return ret;
}
out_size += z->out.pos;
}
if (out_size != expected_size) {
- error_setg(errp, "multifd %d: packet size received %d size expected %d",
+ error_setg(errp, "multifd %u: packet size received %u size expected %u",
p->id, out_size, expected_size);
return -1;
}
.send_setup = zstd_send_setup,
.send_cleanup = zstd_send_cleanup,
.send_prepare = zstd_send_prepare,
- .send_write = zstd_send_write,
.recv_setup = zstd_recv_setup,
.recv_cleanup = zstd_recv_cleanup,
.recv_pages = zstd_recv_pages
*/
static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp)
{
- p->next_packet_size = p->pages->num * qemu_target_page_size();
+ MultiFDPages_t *pages = p->pages;
+ size_t page_size = qemu_target_page_size();
+
+ for (int i = 0; i < p->normal_num; i++) {
+ p->iov[p->iovs_num].iov_base = pages->block->host + p->normal[i];
+ p->iov[p->iovs_num].iov_len = page_size;
+ p->iovs_num++;
+ }
+
+ p->next_packet_size = p->normal_num * page_size;
p->flags |= MULTIFD_FLAG_NOCOMP;
return 0;
}
-/**
- * nocomp_send_write: do the actual write of the data
- *
- * For no compression we just have to write the data.
- *
- * Returns 0 for success or -1 for error
- *
- * @p: Params for the channel that we are using
- * @used: number of pages used
- * @errp: pointer to an error
- */
-static int nocomp_send_write(MultiFDSendParams *p, uint32_t used, Error **errp)
-{
- return qio_channel_writev_all(p->c, p->pages->iov, used, errp);
-}
-
/**
* nocomp_recv_setup: setup receive side
*
static int nocomp_recv_pages(MultiFDRecvParams *p, Error **errp)
{
uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK;
+ size_t page_size = qemu_target_page_size();
if (flags != MULTIFD_FLAG_NOCOMP) {
- error_setg(errp, "multifd %d: flags received %x flags expected %x",
+ error_setg(errp, "multifd %u: flags received %x flags expected %x",
p->id, flags, MULTIFD_FLAG_NOCOMP);
return -1;
}
- return qio_channel_readv_all(p->c, p->pages->iov, p->pages->num, errp);
+ for (int i = 0; i < p->normal_num; i++) {
+ p->iov[i].iov_base = p->host + p->normal[i];
+ p->iov[i].iov_len = page_size;
+ }
+ return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp);
}
static MultiFDMethods multifd_nocomp_ops = {
.send_setup = nocomp_send_setup,
.send_cleanup = nocomp_send_cleanup,
.send_prepare = nocomp_send_prepare,
- .send_write = nocomp_send_write,
.recv_setup = nocomp_recv_setup,
.recv_cleanup = nocomp_recv_cleanup,
.recv_pages = nocomp_recv_pages
}
if (msg.version != MULTIFD_VERSION) {
- error_setg(errp, "multifd: received packet version %d "
- "expected %d", msg.version, MULTIFD_VERSION);
+ error_setg(errp, "multifd: received packet version %u "
+ "expected %u", msg.version, MULTIFD_VERSION);
return -1;
}
}
if (msg.id > migrate_multifd_channels()) {
- error_setg(errp, "multifd: received channel version %d "
- "expected %d", msg.version, MULTIFD_VERSION);
+ error_setg(errp, "multifd: received channel version %u "
+ "expected %u", msg.version, MULTIFD_VERSION);
return -1;
}
MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1);
pages->allocated = size;
- pages->iov = g_new0(struct iovec, size);
pages->offset = g_new0(ram_addr_t, size);
return pages;
pages->allocated = 0;
pages->packet_num = 0;
pages->block = NULL;
- g_free(pages->iov);
- pages->iov = NULL;
g_free(pages->offset);
pages->offset = NULL;
g_free(pages);
packet->flags = cpu_to_be32(p->flags);
packet->pages_alloc = cpu_to_be32(p->pages->allocated);
- packet->pages_used = cpu_to_be32(p->pages->num);
+ packet->normal_pages = cpu_to_be32(p->normal_num);
packet->next_packet_size = cpu_to_be32(p->next_packet_size);
packet->packet_num = cpu_to_be64(p->packet_num);
strncpy(packet->ramblock, p->pages->block->idstr, 256);
}
- for (i = 0; i < p->pages->num; i++) {
+ for (i = 0; i < p->normal_num; i++) {
/* there are architectures where ram_addr_t is 32 bit */
- uint64_t temp = p->pages->offset[i];
+ uint64_t temp = p->normal[i];
packet->offset[i] = cpu_to_be64(temp);
}
{
MultiFDPacket_t *packet = p->packet;
size_t page_size = qemu_target_page_size();
- uint32_t pages_max = MULTIFD_PACKET_SIZE / page_size;
+ uint32_t page_count = MULTIFD_PACKET_SIZE / page_size;
RAMBlock *block;
int i;
packet->version = be32_to_cpu(packet->version);
if (packet->version != MULTIFD_VERSION) {
error_setg(errp, "multifd: received packet "
- "version %d and expected version %d",
+ "version %u and expected version %u",
packet->version, MULTIFD_VERSION);
return -1;
}
* If we received a packet that is 100 times bigger than expected
* just stop migration. It is a magic number.
*/
- if (packet->pages_alloc > pages_max * 100) {
+ if (packet->pages_alloc > page_count) {
error_setg(errp, "multifd: received packet "
- "with size %d and expected a maximum size of %d",
- packet->pages_alloc, pages_max * 100) ;
+ "with size %u and expected a size of %u",
+ packet->pages_alloc, page_count) ;
return -1;
}
- /*
- * We received a packet that is bigger than expected but inside
- * reasonable limits (see previous comment). Just reallocate.
- */
- if (packet->pages_alloc > p->pages->allocated) {
- multifd_pages_clear(p->pages);
- p->pages = multifd_pages_init(packet->pages_alloc);
- }
- p->pages->num = be32_to_cpu(packet->pages_used);
- if (p->pages->num > packet->pages_alloc) {
+ p->normal_num = be32_to_cpu(packet->normal_pages);
+ if (p->normal_num > packet->pages_alloc) {
error_setg(errp, "multifd: received packet "
- "with %d pages and expected maximum pages are %d",
- p->pages->num, packet->pages_alloc) ;
+ "with %u pages and expected maximum pages are %u",
+ p->normal_num, packet->pages_alloc) ;
return -1;
}
p->next_packet_size = be32_to_cpu(packet->next_packet_size);
p->packet_num = be64_to_cpu(packet->packet_num);
- if (p->pages->num == 0) {
+ if (p->normal_num == 0) {
return 0;
}
return -1;
}
- p->pages->block = block;
- for (i = 0; i < p->pages->num; i++) {
+ p->host = block->host;
+ for (i = 0; i < p->normal_num; i++) {
uint64_t offset = be64_to_cpu(packet->offset[i]);
if (offset > (block->used_length - page_size)) {
offset, block->used_length);
return -1;
}
- p->pages->offset[i] = offset;
- p->pages->iov[i].iov_base = block->host + offset;
- p->pages->iov[i].iov_len = page_size;
+ p->normal[i] = offset;
}
return 0;
if (pages->block == block) {
pages->offset[pages->num] = offset;
- pages->iov[pages->num].iov_base = block->host + offset;
- pages->iov[pages->num].iov_len = qemu_target_page_size();
pages->num++;
if (pages->num < pages->allocated) {
p->packet_len = 0;
g_free(p->packet);
p->packet = NULL;
+ g_free(p->iov);
+ p->iov = NULL;
+ g_free(p->normal);
+ p->normal = NULL;
multifd_send_state->ops->send_cleanup(p, &local_err);
if (local_err) {
migrate_set_error(migrate_get_current(), local_err);
qemu_mutex_lock(&p->mutex);
if (p->pending_job) {
- uint32_t used = p->pages->num;
uint64_t packet_num = p->packet_num;
uint32_t flags = p->flags;
+ p->iovs_num = 1;
+ p->normal_num = 0;
+
+ for (int i = 0; i < p->pages->num; i++) {
+ p->normal[p->normal_num] = p->pages->offset[i];
+ p->normal_num++;
+ }
- if (used) {
+ if (p->normal_num) {
ret = multifd_send_state->ops->send_prepare(p, &local_err);
if (ret != 0) {
qemu_mutex_unlock(&p->mutex);
multifd_send_fill_packet(p);
p->flags = 0;
p->num_packets++;
- p->num_pages += used;
+ p->total_normal_pages += p->normal_num;
p->pages->num = 0;
p->pages->block = NULL;
qemu_mutex_unlock(&p->mutex);
- trace_multifd_send(p->id, packet_num, used, flags,
+ trace_multifd_send(p->id, packet_num, p->normal_num, flags,
p->next_packet_size);
- ret = qio_channel_write_all(p->c, (void *)p->packet,
- p->packet_len, &local_err);
+ p->iov[0].iov_len = p->packet_len;
+ p->iov[0].iov_base = p->packet;
+
+ ret = qio_channel_writev_all(p->c, p->iov, p->iovs_num,
+ &local_err);
if (ret != 0) {
break;
}
- if (used) {
- ret = multifd_send_state->ops->send_write(p, used, &local_err);
- if (ret != 0) {
- break;
- }
- }
-
qemu_mutex_lock(&p->mutex);
p->pending_job--;
qemu_mutex_unlock(&p->mutex);
qemu_mutex_unlock(&p->mutex);
rcu_unregister_thread();
- trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages);
+ trace_multifd_send_thread_end(p->id, p->num_packets, p->total_normal_pages);
return NULL;
}
p->packet->version = cpu_to_be32(MULTIFD_VERSION);
p->name = g_strdup_printf("multifdsend_%d", i);
p->tls_hostname = g_strdup(s->hostname);
+ /* We need one extra place for the packet header */
+ p->iov = g_new0(struct iovec, page_count + 1);
+ p->normal = g_new0(ram_addr_t, page_count);
socket_send_channel_create(multifd_new_send_channel_async, p);
}
qemu_sem_destroy(&p->sem_sync);
g_free(p->name);
p->name = NULL;
- multifd_pages_clear(p->pages);
- p->pages = NULL;
p->packet_len = 0;
g_free(p->packet);
p->packet = NULL;
+ g_free(p->iov);
+ p->iov = NULL;
+ g_free(p->normal);
+ p->normal = NULL;
multifd_recv_state->ops->recv_cleanup(p);
}
qemu_sem_destroy(&multifd_recv_state->sem_sync);
rcu_register_thread();
while (true) {
- uint32_t used;
uint32_t flags;
if (p->quit) {
break;
}
- used = p->pages->num;
flags = p->flags;
/* recv methods don't know how to handle the SYNC flag */
p->flags &= ~MULTIFD_FLAG_SYNC;
- trace_multifd_recv(p->id, p->packet_num, used, flags,
+ trace_multifd_recv(p->id, p->packet_num, p->normal_num, flags,
p->next_packet_size);
p->num_packets++;
- p->num_pages += used;
+ p->total_normal_pages += p->normal_num;
qemu_mutex_unlock(&p->mutex);
- if (used) {
+ if (p->normal_num) {
ret = multifd_recv_state->ops->recv_pages(p, &local_err);
if (ret != 0) {
break;
qemu_mutex_unlock(&p->mutex);
rcu_unregister_thread();
- trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages);
+ trace_multifd_recv_thread_end(p->id, p->num_packets, p->total_normal_pages);
return NULL;
}
qemu_sem_init(&p->sem_sync, 0);
p->quit = false;
p->id = i;
- p->pages = multifd_pages_init(page_count);
p->packet_len = sizeof(MultiFDPacket_t)
+ sizeof(uint64_t) * page_count;
p->packet = g_malloc0(p->packet_len);
p->name = g_strdup_printf("multifdrecv_%d", i);
+ p->iov = g_new0(struct iovec, page_count);
+ p->normal = g_new0(ram_addr_t, page_count);
}
for (i = 0; i < thread_count; i++) {
uint32_t flags;
/* maximum number of allocated pages */
uint32_t pages_alloc;
- uint32_t pages_used;
+ /* non zero pages */
+ uint32_t normal_pages;
/* size of the next packet that contains pages */
uint32_t next_packet_size;
uint64_t packet_num;
uint64_t packet_num;
/* offset of each page */
ram_addr_t *offset;
- /* pointer to each page */
- struct iovec *iov;
RAMBlock *block;
} MultiFDPages_t;
/* thread local variables */
/* packets sent through this channel */
uint64_t num_packets;
- /* pages sent through this channel */
- uint64_t num_pages;
+ /* non zero pages sent through this channel */
+ uint64_t total_normal_pages;
/* syncs main thread and channels */
QemuSemaphore sem_sync;
+ /* buffers to send */
+ struct iovec *iov;
+ /* number of iovs used */
+ uint32_t iovs_num;
+ /* Pages that are not zero */
+ ram_addr_t *normal;
+ /* num of non zero pages */
+ uint32_t normal_num;
/* used for compression methods */
void *data;
} MultiFDSendParams;
bool running;
/* should this thread finish */
bool quit;
- /* array of pages to receive */
- MultiFDPages_t *pages;
+ /* ramblock host address */
+ uint8_t *host;
/* packet allocated len */
uint32_t packet_len;
/* pointer to the packet */
uint32_t next_packet_size;
/* packets sent through this channel */
uint64_t num_packets;
- /* pages sent through this channel */
- uint64_t num_pages;
+ /* non zero pages recv through this channel */
+ uint64_t total_normal_pages;
/* syncs main thread and channels */
QemuSemaphore sem_sync;
+ /* buffers to recv */
+ struct iovec *iov;
+ /* Pages that are not zero */
+ ram_addr_t *normal;
+ /* num of non zero pages */
+ uint32_t normal_num;
/* used for de-compression methods */
void *data;
} MultiFDRecvParams;
void (*send_cleanup)(MultiFDSendParams *p, Error **errp);
/* Prepare the send packet */
int (*send_prepare)(MultiFDSendParams *p, Error **errp);
- /* Write the send packet */
- int (*send_write)(MultiFDSendParams *p, uint32_t used, Error **errp);
/* Setup for receiving side */
int (*recv_setup)(MultiFDRecvParams *p, Error **errp);
/* Cleanup for receiving side */
}
#ifdef UFFD_FEATURE_THREAD_ID
- if (migrate_postcopy_blocktime() && mis &&
- UFFD_FEATURE_THREAD_ID & supported_features) {
- /* kernel supports that feature */
- /* don't create blocktime_context if it exists */
- if (!mis->blocktime_ctx) {
- mis->blocktime_ctx = blocktime_context_new();
- }
-
+ if (UFFD_FEATURE_THREAD_ID & supported_features) {
asked_features |= UFFD_FEATURE_THREAD_ID;
+ if (migrate_postcopy_blocktime()) {
+ if (!mis->blocktime_ctx) {
+ mis->blocktime_ctx = blocktime_context_new();
+ }
+ }
}
#endif
return 0;
}
+static void postcopy_temp_pages_cleanup(MigrationIncomingState *mis)
+{
+ if (mis->postcopy_tmp_page) {
+ munmap(mis->postcopy_tmp_page, mis->largest_page_size);
+ mis->postcopy_tmp_page = NULL;
+ }
+
+ if (mis->postcopy_tmp_zero_page) {
+ munmap(mis->postcopy_tmp_zero_page, mis->largest_page_size);
+ mis->postcopy_tmp_zero_page = NULL;
+ }
+}
+
/*
* At the end of a migration where postcopy_ram_incoming_init was called.
*/
}
}
- if (mis->postcopy_tmp_page) {
- munmap(mis->postcopy_tmp_page, mis->largest_page_size);
- mis->postcopy_tmp_page = NULL;
- }
- if (mis->postcopy_tmp_zero_page) {
- munmap(mis->postcopy_tmp_zero_page, mis->largest_page_size);
- mis->postcopy_tmp_zero_page = NULL;
- }
+ postcopy_temp_pages_cleanup(mis);
+
trace_postcopy_ram_incoming_cleanup_blocktime(
get_postcopy_total_blocktime());
return NULL;
}
+static int postcopy_temp_pages_setup(MigrationIncomingState *mis)
+{
+ int err;
+
+ mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (mis->postcopy_tmp_page == MAP_FAILED) {
+ err = errno;
+ mis->postcopy_tmp_page = NULL;
+ error_report("%s: Failed to map postcopy_tmp_page %s",
+ __func__, strerror(err));
+ return -err;
+ }
+
+ /*
+ * Map large zero page when kernel can't use UFFDIO_ZEROPAGE for hugepages
+ */
+ mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (mis->postcopy_tmp_zero_page == MAP_FAILED) {
+ err = errno;
+ mis->postcopy_tmp_zero_page = NULL;
+ error_report("%s: Failed to map large zero page %s",
+ __func__, strerror(err));
+ return -err;
+ }
+
+ memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
+
+ return 0;
+}
+
int postcopy_ram_incoming_setup(MigrationIncomingState *mis)
{
/* Open the fd for the kernel to give us userfaults */
return -1;
}
- mis->postcopy_tmp_page = mmap(NULL, mis->largest_page_size,
- PROT_READ | PROT_WRITE, MAP_PRIVATE |
- MAP_ANONYMOUS, -1, 0);
- if (mis->postcopy_tmp_page == MAP_FAILED) {
- mis->postcopy_tmp_page = NULL;
- error_report("%s: Failed to map postcopy_tmp_page %s",
- __func__, strerror(errno));
+ if (postcopy_temp_pages_setup(mis)) {
+ /* Error dumped in the sub-function */
return -1;
}
- /*
- * Map large zero page when kernel can't use UFFDIO_ZEROPAGE for hugepages
- */
- mis->postcopy_tmp_zero_page = mmap(NULL, mis->largest_page_size,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1, 0);
- if (mis->postcopy_tmp_zero_page == MAP_FAILED) {
- int e = errno;
- mis->postcopy_tmp_zero_page = NULL;
- error_report("%s: Failed to map large zero page %s",
- __func__, strerror(e));
- return -e;
- }
- memset(mis->postcopy_tmp_zero_page, '\0', mis->largest_page_size);
-
trace_postcopy_ram_enable_notify();
return 0;
uint64_t xbzrle_bytes_prev;
/* Start using XBZRLE (e.g., after the first round). */
bool xbzrle_enabled;
-
+ /* Are we on the last stage of migration */
+ bool last_stage;
/* compression statistics since the beginning of the period */
/* amount of count that no free thread to compress data */
uint64_t compress_thread_busy_prev;
static NotifierWithReturnList precopy_notifier_list;
+/* Whether postcopy has queued requests? */
+static bool postcopy_has_request(RAMState *rs)
+{
+ return !QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests);
+}
+
void precopy_infrastructure_init(void)
{
notifier_with_return_list_init(&precopy_notifier_list);
MigrationStats ram_counters;
+static void ram_transferred_add(uint64_t bytes)
+{
+ if (runstate_is_running()) {
+ ram_counters.precopy_bytes += bytes;
+ } else if (migration_in_postcopy()) {
+ ram_counters.postcopy_bytes += bytes;
+ } else {
+ ram_counters.downtime_bytes += bytes;
+ }
+ ram_counters.transferred += bytes;
+}
+
/* used by the search for pages to send */
struct PageSearchStatus {
/* Current block being searched */
* @current_addr: addr of the page
* @block: block that contains the page we want to send
* @offset: offset inside the block for the page
- * @last_stage: if we are at the completion stage
*/
static int save_xbzrle_page(RAMState *rs, uint8_t **current_data,
ram_addr_t current_addr, RAMBlock *block,
- ram_addr_t offset, bool last_stage)
+ ram_addr_t offset)
{
int encoded_len = 0, bytes_xbzrle;
uint8_t *prev_cached_page;
if (!cache_is_cached(XBZRLE.cache, current_addr,
ram_counters.dirty_sync_count)) {
xbzrle_counters.cache_miss++;
- if (!last_stage) {
+ if (!rs->last_stage) {
if (cache_insert(XBZRLE.cache, current_addr, *current_data,
ram_counters.dirty_sync_count) == -1) {
return -1;
* Update the cache contents, so that it corresponds to the data
* sent, in all cases except where we skip the page.
*/
- if (!last_stage && encoded_len != 0) {
+ if (!rs->last_stage && encoded_len != 0) {
memcpy(prev_cached_page, XBZRLE.current_buf, TARGET_PAGE_SIZE);
/*
* In the case where we couldn't compress, ensure that the caller
* RAM_SAVE_FLAG_CONTINUE.
*/
xbzrle_counters.bytes += bytes_xbzrle - 8;
- ram_counters.transferred += bytes_xbzrle;
+ ram_transferred_add(bytes_xbzrle);
return 1;
}
}
}
+static void ram_release_page(const char *rbname, uint64_t offset)
+{
+ if (!migrate_release_ram() || !migration_in_postcopy()) {
+ return;
+ }
+
+ ram_discard_range(rbname, offset, TARGET_PAGE_SIZE);
+}
+
/**
* save_zero_page_to_file: send the zero page to the file
*
len += save_page_header(rs, file, block, offset | RAM_SAVE_FLAG_ZERO);
qemu_put_byte(file, 0);
len += 1;
+ ram_release_page(block->idstr, offset);
}
return len;
}
if (len) {
ram_counters.duplicate++;
- ram_counters.transferred += len;
+ ram_transferred_add(len);
return 1;
}
return -1;
}
-static void ram_release_pages(const char *rbname, uint64_t offset, int pages)
-{
- if (!migrate_release_ram() || !migration_in_postcopy()) {
- return;
- }
-
- ram_discard_range(rbname, offset, ((ram_addr_t)pages) << TARGET_PAGE_BITS);
-}
-
/*
* @pages: the number of pages written by the control path,
* < 0 - error
}
if (bytes_xmit) {
- ram_counters.transferred += bytes_xmit;
+ ram_transferred_add(bytes_xmit);
*pages = 1;
}
static int save_normal_page(RAMState *rs, RAMBlock *block, ram_addr_t offset,
uint8_t *buf, bool async)
{
- ram_counters.transferred += save_page_header(rs, rs->f, block,
- offset | RAM_SAVE_FLAG_PAGE);
+ ram_transferred_add(save_page_header(rs, rs->f, block,
+ offset | RAM_SAVE_FLAG_PAGE));
if (async) {
qemu_put_buffer_async(rs->f, buf, TARGET_PAGE_SIZE,
migrate_release_ram() &
} else {
qemu_put_buffer(rs->f, buf, TARGET_PAGE_SIZE);
}
- ram_counters.transferred += TARGET_PAGE_SIZE;
+ ram_transferred_add(TARGET_PAGE_SIZE);
ram_counters.normal++;
return 1;
}
* @rs: current RAM state
* @block: block that contains the page we want to send
* @offset: offset inside the block for the page
- * @last_stage: if we are at the completion stage
*/
-static int ram_save_page(RAMState *rs, PageSearchStatus *pss, bool last_stage)
+static int ram_save_page(RAMState *rs, PageSearchStatus *pss)
{
int pages = -1;
uint8_t *p;
XBZRLE_cache_lock();
if (rs->xbzrle_enabled && !migration_in_postcopy()) {
pages = save_xbzrle_page(rs, &p, current_addr, block,
- offset, last_stage);
- if (!last_stage) {
+ offset);
+ if (!rs->last_stage) {
/* Can't send this cached data async, since the cache page
* might get updated before it gets to the wire
*/
ram_addr_t offset, uint8_t *source_buf)
{
RAMState *rs = ram_state;
- uint8_t *p = block->host + (offset & TARGET_PAGE_MASK);
- bool zero_page = false;
+ uint8_t *p = block->host + offset;
int ret;
if (save_zero_page_to_file(rs, f, block, offset)) {
- zero_page = true;
- goto exit;
+ return true;
}
save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE);
if (ret < 0) {
qemu_file_set_error(migrate_get_current()->to_dst_file, ret);
error_report("compressed data failed!");
- return false;
}
-
-exit:
- ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1);
- return zero_page;
+ return false;
}
static void
update_compress_thread_counts(const CompressParam *param, int bytes_xmit)
{
- ram_counters.transferred += bytes_xmit;
+ ram_transferred_add(bytes_xmit);
if (param->zero_page) {
ram_counters.duplicate++;
*/
static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset)
{
+ struct RAMSrcPageRequest *entry;
RAMBlock *block = NULL;
+ size_t page_size;
- if (QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests)) {
+ if (!postcopy_has_request(rs)) {
return NULL;
}
QEMU_LOCK_GUARD(&rs->src_page_req_mutex);
- if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
- struct RAMSrcPageRequest *entry =
- QSIMPLEQ_FIRST(&rs->src_page_requests);
- block = entry->rb;
- *offset = entry->offset;
-
- if (entry->len > TARGET_PAGE_SIZE) {
- entry->len -= TARGET_PAGE_SIZE;
- entry->offset += TARGET_PAGE_SIZE;
- } else {
- memory_region_unref(block->mr);
- QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req);
- g_free(entry);
- migration_consume_urgent_request();
- }
+
+ /*
+ * This should _never_ change even after we take the lock, because no one
+ * should be taking anything off the request list other than us.
+ */
+ assert(postcopy_has_request(rs));
+
+ entry = QSIMPLEQ_FIRST(&rs->src_page_requests);
+ block = entry->rb;
+ *offset = entry->offset;
+ page_size = qemu_ram_pagesize(block);
+ /* Each page request should only be multiple page size of the ramblock */
+ assert((entry->len % page_size) == 0);
+
+ if (entry->len > page_size) {
+ entry->len -= page_size;
+ entry->offset += page_size;
+ } else {
+ memory_region_unref(block->mr);
+ QSIMPLEQ_REMOVE_HEAD(&rs->src_page_requests, next_req);
+ g_free(entry);
+ migration_consume_urgent_request();
}
+ trace_unqueue_page(block->idstr, *offset,
+ test_bit((*offset >> TARGET_PAGE_BITS), block->bmap));
+
return block;
}
/* Check if page is from UFFD-managed region. */
if (pss->block->flags & RAM_UF_WRITEPROTECT) {
void *page_address = pss->block->host + (start_page << TARGET_PAGE_BITS);
- uint64_t run_length = (pss->page - start_page + 1) << TARGET_PAGE_BITS;
+ uint64_t run_length = (pss->page - start_page) << TARGET_PAGE_BITS;
/* Flush async buffers before un-protect. */
qemu_fflush(rs->f);
{
RAMBlock *block;
ram_addr_t offset;
- bool dirty;
-
- do {
- block = unqueue_page(rs, &offset);
- /*
- * We're sending this page, and since it's postcopy nothing else
- * will dirty it, and we must make sure it doesn't get sent again
- * even if this queue request was received after the background
- * search already sent it.
- */
- if (block) {
- unsigned long page;
- page = offset >> TARGET_PAGE_BITS;
- dirty = test_bit(page, block->bmap);
- if (!dirty) {
- trace_get_queued_page_not_dirty(block->idstr, (uint64_t)offset,
- page);
- } else {
- trace_get_queued_page(block->idstr, (uint64_t)offset, page);
- }
- }
-
- } while (block && !dirty);
+ block = unqueue_page(rs, &offset);
if (!block) {
/*
*
* @rs: current RAM state
* @pss: data about the page we want to send
- * @last_stage: if we are at the completion stage
*/
-static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss,
- bool last_stage)
+static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss)
{
RAMBlock *block = pss->block;
ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS;
xbzrle_cache_zero_page(rs, block->offset + offset);
XBZRLE_cache_unlock();
}
- ram_release_pages(block->idstr, offset, res);
return res;
}
return ram_save_multifd_page(rs, block, offset);
}
- return ram_save_page(rs, pss, last_stage);
+ return ram_save_page(rs, pss);
}
/**
* Returns the number of pages written or negative on error
*
* @rs: current RAM state
- * @ms: current migration state
* @pss: data about the page we want to send
- * @last_stage: if we are at the completion stage
*/
-static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss,
- bool last_stage)
+static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss)
{
int tmppages, pages = 0;
size_t pagesize_bits =
do {
/* Check the pages is dirty and if it is send it */
if (migration_bitmap_clear_dirty(rs, pss->block, pss->page)) {
- tmppages = ram_save_target_page(rs, pss, last_stage);
+ tmppages = ram_save_target_page(rs, pss);
if (tmppages < 0) {
return tmppages;
}
offset_in_ramblock(pss->block,
((ram_addr_t)pss->page) << TARGET_PAGE_BITS));
/* The offset we leave with is the min boundary of host page and block */
- pss->page = MIN(pss->page, hostpage_boundary) - 1;
+ pss->page = MIN(pss->page, hostpage_boundary);
res = ram_save_release_protection(rs, pss, start_page);
return (res < 0 ? res : pages);
* or negative on error
*
* @rs: current RAM state
- * @last_stage: if we are at the completion stage
*
* On systems where host-page-size > target-page-size it will send all the
* pages in a host page that are dirty.
*/
-
-static int ram_find_and_save_block(RAMState *rs, bool last_stage)
+static int ram_find_and_save_block(RAMState *rs)
{
PageSearchStatus pss;
int pages = 0;
}
if (found) {
- pages = ram_save_host_page(rs, &pss, last_stage);
+ pages = ram_save_host_page(rs, &pss);
}
} while (!pages && again);
ram_counters.duplicate += pages;
} else {
ram_counters.normal += pages;
- ram_counters.transferred += size;
+ ram_transferred_add(size);
qemu_update_position(f, size);
}
}
#define MAX_WAIT 50 /* ms, half buffered_file limit */
-/*
- * 'expected' is the value you expect the bitmap mostly to be full
- * of; it won't bother printing lines that are all this value.
- * If 'todump' is null the migration bitmap is dumped.
- */
-void ram_debug_dump_bitmap(unsigned long *todump, bool expected,
- unsigned long pages)
-{
- int64_t cur;
- int64_t linelen = 128;
- char linebuf[129];
-
- for (cur = 0; cur < pages; cur += linelen) {
- int64_t curb;
- bool found = false;
- /*
- * Last line; catch the case where the line length
- * is longer than remaining ram
- */
- if (cur + linelen > pages) {
- linelen = pages - cur;
- }
- for (curb = 0; curb < linelen; curb++) {
- bool thisbit = test_bit(cur + curb, todump);
- linebuf[curb] = thisbit ? '1' : '.';
- found = found || (thisbit != expected);
- }
- if (found) {
- linebuf[curb] = '\0';
- fprintf(stderr, "0x%08" PRIx64 " : %s\n", cur, linebuf);
- }
- }
-}
-
/* **** functions for postcopy ***** */
void ram_postcopy_migrated_memory_release(MigrationState *ms)
/**
* postcopy_send_discard_bm_ram: discard a RAMBlock
*
- * Returns zero on success
- *
* Callback from postcopy_each_ram_send_discard for each RAMBlock
*
* @ms: current migration state
* @block: RAMBlock to discard
*/
-static int postcopy_send_discard_bm_ram(MigrationState *ms, RAMBlock *block)
+static void postcopy_send_discard_bm_ram(MigrationState *ms, RAMBlock *block)
{
unsigned long end = block->used_length >> TARGET_PAGE_BITS;
unsigned long current;
postcopy_discard_send_range(ms, one, discard_length);
current = one + discard_length;
}
-
- return 0;
}
+static void postcopy_chunk_hostpages_pass(MigrationState *ms, RAMBlock *block);
+
/**
* postcopy_each_ram_send_discard: discard all RAMBlocks
*
- * Returns 0 for success or negative for error
- *
* Utility for the outgoing postcopy code.
* Calls postcopy_send_discard_bm_ram for each RAMBlock
* passing it bitmap indexes and name.
*
* @ms: current migration state
*/
-static int postcopy_each_ram_send_discard(MigrationState *ms)
+static void postcopy_each_ram_send_discard(MigrationState *ms)
{
struct RAMBlock *block;
- int ret;
RAMBLOCK_FOREACH_NOT_IGNORED(block) {
postcopy_discard_send_init(ms, block->idstr);
+ /*
+ * Deal with TPS != HPS and huge pages. It discard any partially sent
+ * host-page size chunks, mark any partially dirty host-page size
+ * chunks as all dirty. In this case the host-page is the host-page
+ * for the particular RAMBlock, i.e. it might be a huge page.
+ */
+ postcopy_chunk_hostpages_pass(ms, block);
+
/*
* Postcopy sends chunks of bitmap over the wire, but it
* just needs indexes at this point, avoids it having
* target page specific code.
*/
- ret = postcopy_send_discard_bm_ram(ms, block);
+ postcopy_send_discard_bm_ram(ms, block);
postcopy_discard_send_finish(ms);
- if (ret) {
- return ret;
- }
}
-
- return 0;
}
/**
}
}
-/**
- * postcopy_chunk_hostpages: discard any partially sent host page
- *
- * Utility for the outgoing postcopy code.
- *
- * Discard any partially sent host-page size chunks, mark any partially
- * dirty host-page size chunks as all dirty. In this case the host-page
- * is the host-page for the particular RAMBlock, i.e. it might be a huge page
- *
- * Returns zero on success
- *
- * @ms: current migration state
- * @block: block we want to work with
- */
-static int postcopy_chunk_hostpages(MigrationState *ms, RAMBlock *block)
-{
- postcopy_discard_send_init(ms, block->idstr);
-
- /*
- * Ensure that all partially dirty host pages are made fully dirty.
- */
- postcopy_chunk_hostpages_pass(ms, block);
-
- postcopy_discard_send_finish(ms);
- return 0;
-}
-
/**
* ram_postcopy_send_discard_bitmap: transmit the discard bitmap
*
- * Returns zero on success
- *
* Transmit the set of pages to be discarded after precopy to the target
* these are pages that:
* a) Have been previously transmitted but are now dirty again
*
* @ms: current migration state
*/
-int ram_postcopy_send_discard_bitmap(MigrationState *ms)
+void ram_postcopy_send_discard_bitmap(MigrationState *ms)
{
RAMState *rs = ram_state;
- RAMBlock *block;
- int ret;
RCU_READ_LOCK_GUARD();
rs->last_sent_block = NULL;
rs->last_page = 0;
- RAMBLOCK_FOREACH_NOT_IGNORED(block) {
- /* Deal with TPS != HPS and huge pages */
- ret = postcopy_chunk_hostpages(ms, block);
- if (ret) {
- return ret;
- }
+ postcopy_each_ram_send_discard(ms);
-#ifdef DEBUG_POSTCOPY
- ram_debug_dump_bitmap(block->bmap, true,
- block->used_length >> TARGET_PAGE_BITS);
-#endif
- }
trace_ram_postcopy_send_discard_bitmap();
-
- return postcopy_each_ram_send_discard(ms);
}
/**
t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
i = 0;
while ((ret = qemu_file_rate_limit(f)) == 0 ||
- !QSIMPLEQ_EMPTY(&rs->src_page_requests)) {
+ postcopy_has_request(rs)) {
int pages;
if (qemu_file_get_error(f)) {
break;
}
- pages = ram_find_and_save_block(rs, false);
+ pages = ram_find_and_save_block(rs);
/* no more pages to sent */
if (pages == 0) {
done = 1;
multifd_send_sync_main(rs->f);
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
qemu_fflush(f);
- ram_counters.transferred += 8;
+ ram_transferred_add(8);
ret = qemu_file_get_error(f);
}
RAMState *rs = *temp;
int ret = 0;
+ rs->last_stage = !migration_in_colo_state();
+
WITH_RCU_READ_LOCK_GUARD() {
if (!migration_in_postcopy()) {
migration_bitmap_sync_precopy(rs);
while (true) {
int pages;
- pages = ram_find_and_save_block(rs, !migration_in_colo_state());
+ pages = ram_find_and_save_block(rs);
/* no more blocks to sent */
if (pages == 0) {
break;
uint64_t ram_pagesize_summary(void);
int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len);
void acct_update_position(QEMUFile *f, size_t size, bool zero);
-void ram_debug_dump_bitmap(unsigned long *todump, bool expected,
- unsigned long pages);
void ram_postcopy_migrated_memory_release(MigrationState *ms);
/* For outgoing discard bitmap */
-int ram_postcopy_send_discard_bitmap(MigrationState *ms);
+void ram_postcopy_send_discard_bitmap(MigrationState *ms);
/* For incoming postcopy discard */
int ram_discard_range(const char *block_name, uint64_t start, size_t length);
int ram_postcopy_incoming_init(MigrationIncomingState *mis);
QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc);
if (io_read) {
aio_set_fd_handler(ctx, rioc->rdmain->recv_comp_channel->fd,
- false, io_read, io_write, NULL, opaque);
+ false, io_read, io_write, NULL, NULL, opaque);
aio_set_fd_handler(ctx, rioc->rdmain->send_comp_channel->fd,
- false, io_read, io_write, NULL, opaque);
+ false, io_read, io_write, NULL, NULL, opaque);
} else {
aio_set_fd_handler(ctx, rioc->rdmaout->recv_comp_channel->fd,
- false, io_read, io_write, NULL, opaque);
+ false, io_read, io_write, NULL, NULL, opaque);
aio_set_fd_handler(ctx, rioc->rdmaout->send_comp_channel->fd,
- false, io_read, io_write, NULL, opaque);
+ false, io_read, io_write, NULL, NULL, opaque);
}
}
save_section_footer(f, se);
if (ret < 0) {
- error_report("failed to save SaveStateEntry with id(name): %d(%s)",
- se->section_id, se->idstr);
+ error_report("failed to save SaveStateEntry with id(name): "
+ "%d(%s): %d",
+ se->section_id, se->idstr, ret);
qemu_file_set_error(f, ret);
}
if (ret <= 0) {
qemu_file_fclose(void) ""
# ram.c
-get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx"
-get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx"
migration_bitmap_sync_start(void) ""
migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64
migration_bitmap_clear_dirty(char *str, uint64_t start, uint64_t size, unsigned long page) "rb %s start 0x%"PRIx64" size 0x%"PRIx64" page 0x%lx"
ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64
ram_write_tracking_ramblock_start(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu"
ram_write_tracking_ramblock_stop(const char *block_id, size_t page_size, void *addr, size_t length) "%s: page_size: %zu addr: %p length: %zu"
+unqueue_page(char *block, uint64_t offset, bool dirty) "ramblock '%s' offset 0x%"PRIx64" dirty %d"
# multifd.c
-multifd_new_send_channel_async(uint8_t id) "channel %d"
-multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d"
-multifd_recv_new_channel(uint8_t id) "channel %d"
+multifd_new_send_channel_async(uint8_t id) "channel %u"
+multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " pages %u flags 0x%x next packet size %u"
+multifd_recv_new_channel(uint8_t id) "channel %u"
multifd_recv_sync_main(long packet_num) "packet num %ld"
-multifd_recv_sync_main_signal(uint8_t id) "channel %d"
-multifd_recv_sync_main_wait(uint8_t id) "channel %d"
+multifd_recv_sync_main_signal(uint8_t id) "channel %u"
+multifd_recv_sync_main_wait(uint8_t id) "channel %u"
multifd_recv_terminate_threads(bool error) "error %d"
-multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64
-multifd_recv_thread_start(uint8_t id) "%d"
-multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d"
-multifd_send_error(uint8_t id) "channel %d"
+multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %u packets %" PRIu64 " pages %" PRIu64
+multifd_recv_thread_start(uint8_t id) "%u"
+multifd_send(uint8_t id, uint64_t packet_num, uint32_t normal, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u flags 0x%x next packet size %u"
+multifd_send_error(uint8_t id) "channel %u"
multifd_send_sync_main(long packet_num) "packet num %ld"
-multifd_send_sync_main_signal(uint8_t id) "channel %d"
-multifd_send_sync_main_wait(uint8_t id) "channel %d"
+multifd_send_sync_main_signal(uint8_t id) "channel %u"
+multifd_send_sync_main_wait(uint8_t id) "channel %u"
multifd_send_terminate_threads(bool error) "error %d"
-multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64
-multifd_send_thread_start(uint8_t id) "%d"
+multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64
+multifd_send_thread_start(uint8_t id) "%u"
multifd_tls_outgoing_handshake_start(void *ioc, void *tioc, const char *hostname) "ioc=%p tioc=%p hostname=%s"
multifd_tls_outgoing_handshake_error(void *ioc, const char *err) "ioc=%p err=%s"
multifd_tls_outgoing_handshake_complete(void *ioc) "ioc=%p"
monitor_printf(mon, "postcopy request count: %" PRIu64 "\n",
info->ram->postcopy_requests);
}
+ if (info->ram->precopy_bytes) {
+ monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n",
+ info->ram->precopy_bytes >> 10);
+ }
+ if (info->ram->downtime_bytes) {
+ monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n",
+ info->ram->downtime_bytes >> 10);
+ }
+ if (info->ram->postcopy_bytes) {
+ monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n",
+ info->ram->postcopy_bytes >> 10);
+ }
}
if (info->has_disk) {
}
}
-#ifdef CONFIG_PROFILER
-
-int64_t dev_time;
-
-HumanReadableText *qmp_x_query_profile(Error **errp)
-{
- g_autoptr(GString) buf = g_string_new("");
- static int64_t last_cpu_exec_time;
- int64_t cpu_exec_time;
- int64_t delta;
-
- cpu_exec_time = tcg_cpu_exec_time();
- delta = cpu_exec_time - last_cpu_exec_time;
-
- g_string_append_printf(buf, "async time %" PRId64 " (%0.3f)\n",
- dev_time, dev_time / (double)NANOSECONDS_PER_SECOND);
- g_string_append_printf(buf, "qemu time %" PRId64 " (%0.3f)\n",
- delta, delta / (double)NANOSECONDS_PER_SECOND);
- last_cpu_exec_time = cpu_exec_time;
- dev_time = 0;
-
- return human_readable_text_from_str(buf);
-}
-#else
-HumanReadableText *qmp_x_query_profile(Error **errp)
-{
- error_setg(errp, "Internal profiler not compiled");
- return NULL;
-}
-#endif
-
static int qmp_x_query_rdma_foreach(Object *obj, void *opaque)
{
RdmaProvider *rdma;
typedef struct NBDRequestData NBDRequestData;
struct NBDRequestData {
- QSIMPLEQ_ENTRY(NBDRequestData) entry;
NBDClient *client;
uint8_t *data;
bool complete;
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "clients.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "qapi/visitor.h"
#include "net/filter.h"
#include "qom/object.h"
+#include "sysemu/rtc.h"
typedef struct DumpState {
int64_t start_ts;
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
https://github.com/aik/SLOF, and the image currently in qemu is
- built from git tag qemu-slof-20211112.
+ built from git tag qemu-slof-20220110.
- VOF (Virtual Open Firmware) is a minimalistic firmware to work with
-machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and
'hppa-firmware.img',
'opensbi-riscv32-generic-fw_dynamic.bin',
'opensbi-riscv64-generic-fw_dynamic.bin',
- 'opensbi-riscv32-generic-fw_dynamic.elf',
- 'opensbi-riscv64-generic-fw_dynamic.elf',
'npcm7xx_bootrom.bin',
)
s390-ccw.elf: $(OBJECTS)
$(call quiet-command,$(CC) $(LDFLAGS) -o $@ $(OBJECTS),"BUILD","$(TARGET_DIR)$@")
-STRIP ?= strip
-
s390-ccw.img: s390-ccw.elf
$(call quiet-command,$(STRIP) --strip-unneeded $< -o $@,"STRIP","$(TARGET_DIR)$@")
plugin_ldflags = []
# Modules need more symbols than just those in plugins/qemu-plugins.symbols
if not enable_modules
- if 'CONFIG_HAS_LD_DYNAMIC_LIST' in config_host
- plugin_ldflags = ['-Wl,--dynamic-list=qemu-plugins-ld.symbols']
- elif 'CONFIG_HAS_LD_EXPORTED_SYMBOLS_LIST' in config_host
- plugin_ldflags = ['-Wl,-exported_symbols_list,qemu-plugins-ld64.symbols']
+ if targetos == 'darwin'
+ qemu_plugins_symbols_list = configure_file(
+ input: files('qemu-plugins.symbols'),
+ output: 'qemu-plugins-ld64.symbols',
+ capture: true,
+ command: ['sed', '-ne', 's/^[[:space:]]*\\(qemu_.*\\);/_\\1/p', '@INPUT@'])
+ plugin_ldflags = ['-Wl,-exported_symbols_list,plugins/qemu-plugins-ld64.symbols']
+ else
+ plugin_ldflags = ['-Xlinker', '--dynamic-list=' + (meson.project_source_root() / 'plugins/qemu-plugins.symbols')]
endif
endif
echo "ACTIVATE $(QEMU_VENV_DIR)"; \
. $(QEMU_VENV_DIR)/bin/activate; \
echo "INSTALL qemu[devel] $(QEMU_VENV_DIR)"; \
+ pip install --disable-pip-version-check \
+ "setuptools<60.0.0" 1>/dev/null; \
make develop 1>/dev/null; \
)
@touch $(QEMU_VENV_DIR)
so that tools like ``qmp-shell`` are always available via $PATH. To
invoke them without installation, you can invoke e.g.:
-``> PYTHONPATH=~/src/qemu/python python3 -m qemu.qmp.qmp_shell``
+``> PYTHONPATH=~/src/qemu/python python3 -m qemu.aqmp.qmp_shell``
The mappings between console script name and python module path can be
found in ``setup.cfg``.
[run]
-test_runner = runner
+test_runner = nrunner
[simpletests]
# Don't show stdout/stderr in the test *summary*
QEMU Guest Agent, and the QEMU Storage Daemon.
`QMPClient` provides the main functionality of this package. All errors
-raised by this library dervive from `AQMPError`, see `aqmp.error` for
+raised by this library derive from `QMPError`, see `aqmp.error` for
additional detail. See `aqmp.events` for an in-depth tutorial on
managing QMP events.
"""
import logging
-from .error import AQMPError
+from .error import QMPError
from .events import EventListener
from .message import Message
-from .protocol import ConnectError, Runstate, StateError
+from .protocol import (
+ ConnectError,
+ Runstate,
+ SocketAddrT,
+ StateError,
+)
from .qmp_client import ExecInterruptedError, ExecuteError, QMPClient
'Runstate',
# Exceptions, most generic to most explicit
- 'AQMPError',
+ 'QMPError',
'StateError',
'ConnectError',
'ExecuteError',
'ExecInterruptedError',
+
+ # Type aliases
+ 'SocketAddrT',
)
"""
-AQMP Error Classes
+QMP Error Classes
This package seeks to provide semantic error classes that are intended
to be used directly by clients when they would like to handle particular
semantic failures (e.g. "failed to connect") without needing to know the
enumeration of possible reasons for that failure.
-AQMPError serves as the ancestor for all exceptions raised by this
+QMPError serves as the ancestor for all exceptions raised by this
package, and is suitable for use in handling semantic errors from this
library. In most cases, individual public methods will attempt to catch
and re-encapsulate various exceptions to provide a semantic
error-handling interface.
-.. admonition:: AQMP Exception Hierarchy Reference
+.. admonition:: QMP Exception Hierarchy Reference
| `Exception`
- | +-- `AQMPError`
+ | +-- `QMPError`
| +-- `ConnectError`
| +-- `StateError`
| +-- `ExecInterruptedError`
"""
-class AQMPError(Exception):
+class QMPError(Exception):
"""Abstract error class for all errors originating from this package."""
-class ProtocolError(AQMPError):
+class ProtocolError(QMPError):
"""
Abstract error class for protocol failures.
Union,
)
-from .error import AQMPError
+from .error import QMPError
from .message import Message
EventFilter = Callable[[Message], bool]
-class ListenerError(AQMPError):
+class ListenerError(QMPError):
"""
Generic error class for `EventListener`-related problems.
"""
import asyncio
from typing import (
+ Any,
Awaitable,
+ Dict,
List,
Optional,
TypeVar,
)
import qemu.qmp
-from qemu.qmp import QMPMessage, QMPReturnValue, SocketAddrT
+from .error import QMPError
+from .protocol import Runstate, SocketAddrT
from .qmp_client import QMPClient
+# (Temporarily) Re-export QMPBadPortError
+QMPBadPortError = qemu.qmp.QMPBadPortError
+
+#: QMPMessage is an entire QMP message of any kind.
+QMPMessage = Dict[str, Any]
+
+#: QMPReturnValue is the 'return' value of a command.
+QMPReturnValue = object
+
+#: QMPObject is any object in a QMP message.
+QMPObject = Dict[str, object]
+
+# QMPMessage can be outgoing commands or incoming events/returns.
+# QMPReturnValue is usually a dict/json object, but due to QAPI's
+# 'returns-whitelist', it can actually be anything.
+#
+# {'return': {}} is a QMPMessage,
+# {} is the QMPReturnValue.
+
+
# pylint: disable=missing-docstring
def send_fd_scm(self, fd: int) -> None:
self._aqmp.send_fd_scm(fd)
+
+ def __del__(self) -> None:
+ if self._aqmp.runstate == Runstate.IDLE:
+ return
+
+ if not self._aloop.is_running():
+ self.close()
+ else:
+ # Garbage collection ran while the event loop was running.
+ # Nothing we can do about it now, but if we don't raise our
+ # own error, the user will be treated to a lot of traceback
+ # they might not understand.
+ raise QMPError(
+ "QEMUMonitorProtocol.close()"
+ " was not called before object was garbage collected"
+ )
cast,
)
-from .error import AQMPError
+from .error import QMPError
from .util import (
bottom_half,
create_task,
_U = TypeVar('_U')
_TaskFN = Callable[[], Awaitable[None]] # aka ``async def func() -> None``
+InternetAddrT = Tuple[str, int]
+UnixAddrT = str
+SocketAddrT = Union[UnixAddrT, InternetAddrT]
+
class Runstate(Enum):
"""Protocol session runstate."""
DISCONNECTING = 3
-class ConnectError(AQMPError):
+class ConnectError(QMPError):
"""
Raised when the initial connection process has failed.
return f"{self.error_message}: {cause}"
-class StateError(AQMPError):
+class StateError(QMPError):
"""
An API command (connect, execute, etc) was issued at an inappropriate time.
@upper_half
@require(Runstate.IDLE)
- async def accept(self, address: Union[str, Tuple[str, int]],
+ async def accept(self, address: SocketAddrT,
ssl: Optional[SSLContext] = None) -> None:
"""
Accept a connection and begin processing message queues.
@upper_half
@require(Runstate.IDLE)
- async def connect(self, address: Union[str, Tuple[str, int]],
+ async def connect(self, address: SocketAddrT,
ssl: Optional[SSLContext] = None) -> None:
"""
Connect to the server and begin processing message queues.
@upper_half
async def _new_session(self,
- address: Union[str, Tuple[str, int]],
+ address: SocketAddrT,
ssl: Optional[SSLContext] = None,
accept: bool = False) -> None:
"""
This exception will wrap a more concrete one. In most cases,
the wrapped exception will be `OSError` or `EOFError`. If a
protocol-level failure occurs while establishing a new
- session, the wrapped error may also be an `AQMPError`.
+ session, the wrapped error may also be an `QMPError`.
"""
assert self.runstate == Runstate.IDLE
@upper_half
async def _establish_connection(
self,
- address: Union[str, Tuple[str, int]],
+ address: SocketAddrT,
ssl: Optional[SSLContext] = None,
accept: bool = False
) -> None:
await self._do_connect(address, ssl)
@upper_half
- async def _do_accept(self, address: Union[str, Tuple[str, int]],
+ async def _do_accept(self, address: SocketAddrT,
ssl: Optional[SSLContext] = None) -> None:
"""
Acting as the transport server, accept a single connection.
self.logger.debug("Connection accepted.")
@upper_half
- async def _do_connect(self, address: Union[str, Tuple[str, int]],
+ async def _do_connect(self, address: SocketAddrT,
ssl: Optional[SSLContext] = None) -> None:
"""
Acting as the transport client, initiate a connection to a server.
cast,
)
-from .error import AQMPError, ProtocolError
+from .error import ProtocolError, QMPError
from .events import Events
from .message import Message
from .models import ErrorResponse, Greeting
"""
-class ExecuteError(AQMPError):
+class ExecuteError(QMPError):
"""
Exception raised by `QMPClient.execute()` on RPC failure.
self.error_class: str = error_response.error.class_
-class ExecInterruptedError(AQMPError):
+class ExecInterruptedError(QMPError):
"""
Exception raised by `execute()` (et al) when an RPC is interrupted.
msg_id = msg['id']
self._pending[msg_id] = asyncio.Queue(maxsize=1)
- await self._outgoing.put(msg)
+ try:
+ await self._outgoing.put(msg)
+ except:
+ del self._pending[msg_id]
+ raise
return msg_id
was lost, or some other problem.
"""
queue = self._pending[msg_id]
- result = await queue.get()
try:
+ result = await queue.get()
if isinstance(result, ExecInterruptedError):
raise result
return result
sock = self._writer.transport.get_extra_info('socket')
if sock.family != socket.AF_UNIX:
- raise AQMPError("Sending file descriptors requires a UNIX socket.")
+ raise QMPError("Sending file descriptors requires a UNIX socket.")
if not hasattr(sock, 'sendmsg'):
# We need to void the warranty sticker.
--- /dev/null
+#
+# Copyright (C) 2009, 2010 Red Hat Inc.
+#
+# Authors:
+# Luiz Capitulino <lcapitulino@redhat.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+#
+
+"""
+Low-level QEMU shell on top of QMP.
+
+usage: qmp-shell [-h] [-H] [-N] [-v] [-p] qmp_server
+
+positional arguments:
+ qmp_server < UNIX socket path | TCP address:port >
+
+optional arguments:
+ -h, --help show this help message and exit
+ -H, --hmp Use HMP interface
+ -N, --skip-negotiation
+ Skip negotiate (for qemu-ga)
+ -v, --verbose Verbose (echo commands sent and received)
+ -p, --pretty Pretty-print JSON
+
+
+Start QEMU with:
+
+# qemu [...] -qmp unix:./qmp-sock,server
+
+Run the shell:
+
+$ qmp-shell ./qmp-sock
+
+Commands have the following format:
+
+ < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+
+For example:
+
+(QEMU) device_add driver=e1000 id=net1
+{'return': {}}
+(QEMU)
+
+key=value pairs also support Python or JSON object literal subset notations,
+without spaces. Dictionaries/objects {} are supported as are arrays [].
+
+ example-command arg-name1={'key':'value','obj'={'prop':"value"}}
+
+Both JSON and Python formatting should work, including both styles of
+string literal quotes. Both paradigms of literal values should work,
+including null/true/false for JSON and None/True/False for Python.
+
+
+Transactions have the following multi-line format:
+
+ transaction(
+ action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ]
+ ...
+ action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ]
+ )
+
+One line transactions are also supported:
+
+ transaction( action-name1 ... )
+
+For example:
+
+ (QEMU) transaction(
+ TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1
+ TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0
+ TRANS> )
+ {"return": {}}
+ (QEMU)
+
+Use the -v and -p options to activate the verbose and pretty-print options,
+which will echo back the properly formatted JSON-compliant QMP that is being
+sent to QEMU, which is useful for debugging and documentation generation.
+"""
+
+import argparse
+import ast
+import json
+import logging
+import os
+import re
+import readline
+import sys
+from typing import (
+ Iterator,
+ List,
+ NoReturn,
+ Optional,
+ Sequence,
+)
+
+from qemu.aqmp import ConnectError, QMPError, SocketAddrT
+from qemu.aqmp.legacy import (
+ QEMUMonitorProtocol,
+ QMPBadPortError,
+ QMPMessage,
+ QMPObject,
+)
+
+
+LOG = logging.getLogger(__name__)
+
+
+class QMPCompleter:
+ """
+ QMPCompleter provides a readline library tab-complete behavior.
+ """
+ # NB: Python 3.9+ will probably allow us to subclass list[str] directly,
+ # but pylint as of today does not know that List[str] is simply 'list'.
+ def __init__(self) -> None:
+ self._matches: List[str] = []
+
+ def append(self, value: str) -> None:
+ """Append a new valid completion to the list of possibilities."""
+ return self._matches.append(value)
+
+ def complete(self, text: str, state: int) -> Optional[str]:
+ """readline.set_completer() callback implementation."""
+ for cmd in self._matches:
+ if cmd.startswith(text):
+ if state == 0:
+ return cmd
+ state -= 1
+ return None
+
+
+class QMPShellError(QMPError):
+ """
+ QMP Shell Base error class.
+ """
+
+
+class FuzzyJSON(ast.NodeTransformer):
+ """
+ This extension of ast.NodeTransformer filters literal "true/false/null"
+ values in a Python AST and replaces them by proper "True/False/None" values
+ that Python can properly evaluate.
+ """
+
+ @classmethod
+ def visit_Name(cls, # pylint: disable=invalid-name
+ node: ast.Name) -> ast.AST:
+ """
+ Transform Name nodes with certain values into Constant (keyword) nodes.
+ """
+ if node.id == 'true':
+ return ast.Constant(value=True)
+ if node.id == 'false':
+ return ast.Constant(value=False)
+ if node.id == 'null':
+ return ast.Constant(value=None)
+ return node
+
+
+class QMPShell(QEMUMonitorProtocol):
+ """
+ QMPShell provides a basic readline-based QMP shell.
+
+ :param address: Address of the QMP server.
+ :param pretty: Pretty-print QMP messages.
+ :param verbose: Echo outgoing QMP messages to console.
+ """
+ def __init__(self, address: SocketAddrT,
+ pretty: bool = False, verbose: bool = False):
+ super().__init__(address)
+ self._greeting: Optional[QMPMessage] = None
+ self._completer = QMPCompleter()
+ self._transmode = False
+ self._actions: List[QMPMessage] = []
+ self._histfile = os.path.join(os.path.expanduser('~'),
+ '.qmp-shell_history')
+ self.pretty = pretty
+ self.verbose = verbose
+
+ def close(self) -> None:
+ # Hook into context manager of parent to save shell history.
+ self._save_history()
+ super().close()
+
+ def _fill_completion(self) -> None:
+ cmds = self.cmd('query-commands')
+ if 'error' in cmds:
+ return
+ for cmd in cmds['return']:
+ self._completer.append(cmd['name'])
+
+ def _completer_setup(self) -> None:
+ self._completer = QMPCompleter()
+ self._fill_completion()
+ readline.set_history_length(1024)
+ readline.set_completer(self._completer.complete)
+ readline.parse_and_bind("tab: complete")
+ # NB: default delimiters conflict with some command names
+ # (eg. query-), clearing everything as it doesn't seem to matter
+ readline.set_completer_delims('')
+ try:
+ readline.read_history_file(self._histfile)
+ except FileNotFoundError:
+ pass
+ except IOError as err:
+ msg = f"Failed to read history '{self._histfile}': {err!s}"
+ LOG.warning(msg)
+
+ def _save_history(self) -> None:
+ try:
+ readline.write_history_file(self._histfile)
+ except IOError as err:
+ msg = f"Failed to save history file '{self._histfile}': {err!s}"
+ LOG.warning(msg)
+
+ @classmethod
+ def _parse_value(cls, val: str) -> object:
+ try:
+ return int(val)
+ except ValueError:
+ pass
+
+ if val.lower() == 'true':
+ return True
+ if val.lower() == 'false':
+ return False
+ if val.startswith(('{', '[')):
+ # Try first as pure JSON:
+ try:
+ return json.loads(val)
+ except ValueError:
+ pass
+ # Try once again as FuzzyJSON:
+ try:
+ tree = ast.parse(val, mode='eval')
+ transformed = FuzzyJSON().visit(tree)
+ return ast.literal_eval(transformed)
+ except (SyntaxError, ValueError):
+ pass
+ return val
+
+ def _cli_expr(self,
+ tokens: Sequence[str],
+ parent: QMPObject) -> None:
+ for arg in tokens:
+ (key, sep, val) = arg.partition('=')
+ if sep != '=':
+ raise QMPShellError(
+ f"Expected a key=value pair, got '{arg!s}'"
+ )
+
+ value = self._parse_value(val)
+ optpath = key.split('.')
+ curpath = []
+ for path in optpath[:-1]:
+ curpath.append(path)
+ obj = parent.get(path, {})
+ if not isinstance(obj, dict):
+ msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
+ raise QMPShellError(msg.format('.'.join(curpath)))
+ parent[path] = obj
+ parent = obj
+ if optpath[-1] in parent:
+ if isinstance(parent[optpath[-1]], dict):
+ msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
+ raise QMPShellError(msg.format('.'.join(curpath)))
+ raise QMPShellError(f'Cannot set "{key}" multiple times')
+ parent[optpath[-1]] = value
+
+ def _build_cmd(self, cmdline: str) -> Optional[QMPMessage]:
+ """
+ Build a QMP input object from a user provided command-line in the
+ following format:
+
+ < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
+ """
+ argument_regex = r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+'''
+ cmdargs = re.findall(argument_regex, cmdline)
+ qmpcmd: QMPMessage
+
+ # Transactional CLI entry:
+ if cmdargs and cmdargs[0] == 'transaction(':
+ self._transmode = True
+ self._actions = []
+ cmdargs.pop(0)
+
+ # Transactional CLI exit:
+ if cmdargs and cmdargs[0] == ')' and self._transmode:
+ self._transmode = False
+ if len(cmdargs) > 1:
+ msg = 'Unexpected input after close of Transaction sub-shell'
+ raise QMPShellError(msg)
+ qmpcmd = {
+ 'execute': 'transaction',
+ 'arguments': {'actions': self._actions}
+ }
+ return qmpcmd
+
+ # No args, or no args remaining
+ if not cmdargs:
+ return None
+
+ if self._transmode:
+ # Parse and cache this Transactional Action
+ finalize = False
+ action = {'type': cmdargs[0], 'data': {}}
+ if cmdargs[-1] == ')':
+ cmdargs.pop(-1)
+ finalize = True
+ self._cli_expr(cmdargs[1:], action['data'])
+ self._actions.append(action)
+ return self._build_cmd(')') if finalize else None
+
+ # Standard command: parse and return it to be executed.
+ qmpcmd = {'execute': cmdargs[0], 'arguments': {}}
+ self._cli_expr(cmdargs[1:], qmpcmd['arguments'])
+ return qmpcmd
+
+ def _print(self, qmp_message: object) -> None:
+ jsobj = json.dumps(qmp_message,
+ indent=4 if self.pretty else None,
+ sort_keys=self.pretty)
+ print(str(jsobj))
+
+ def _execute_cmd(self, cmdline: str) -> bool:
+ try:
+ qmpcmd = self._build_cmd(cmdline)
+ except QMPShellError as err:
+ print(
+ f"Error while parsing command line: {err!s}\n"
+ "command format: <command-name> "
+ "[arg-name1=arg1] ... [arg-nameN=argN",
+ file=sys.stderr
+ )
+ return True
+ # For transaction mode, we may have just cached the action:
+ if qmpcmd is None:
+ return True
+ if self.verbose:
+ self._print(qmpcmd)
+ resp = self.cmd_obj(qmpcmd)
+ if resp is None:
+ print('Disconnected')
+ return False
+ self._print(resp)
+ return True
+
+ def connect(self, negotiate: bool = True) -> None:
+ self._greeting = super().connect(negotiate)
+ self._completer_setup()
+
+ def show_banner(self,
+ msg: str = 'Welcome to the QMP low-level shell!') -> None:
+ """
+ Print to stdio a greeting, and the QEMU version if available.
+ """
+ print(msg)
+ if not self._greeting:
+ print('Connected')
+ return
+ version = self._greeting['QMP']['version']['qemu']
+ print("Connected to QEMU {major}.{minor}.{micro}\n".format(**version))
+
+ @property
+ def prompt(self) -> str:
+ """
+ Return the current shell prompt, including a trailing space.
+ """
+ if self._transmode:
+ return 'TRANS> '
+ return '(QEMU) '
+
+ def read_exec_command(self) -> bool:
+ """
+ Read and execute a command.
+
+ @return True if execution was ok, return False if disconnected.
+ """
+ try:
+ cmdline = input(self.prompt)
+ except EOFError:
+ print()
+ return False
+
+ if cmdline == '':
+ for event in self.get_events():
+ print(event)
+ return True
+
+ return self._execute_cmd(cmdline)
+
+ def repl(self) -> Iterator[None]:
+ """
+ Return an iterator that implements the REPL.
+ """
+ self.show_banner()
+ while self.read_exec_command():
+ yield
+ self.close()
+
+
+class HMPShell(QMPShell):
+ """
+ HMPShell provides a basic readline-based HMP shell, tunnelled via QMP.
+
+ :param address: Address of the QMP server.
+ :param pretty: Pretty-print QMP messages.
+ :param verbose: Echo outgoing QMP messages to console.
+ """
+ def __init__(self, address: SocketAddrT,
+ pretty: bool = False, verbose: bool = False):
+ super().__init__(address, pretty, verbose)
+ self._cpu_index = 0
+
+ def _cmd_completion(self) -> None:
+ for cmd in self._cmd_passthrough('help')['return'].split('\r\n'):
+ if cmd and cmd[0] != '[' and cmd[0] != '\t':
+ name = cmd.split()[0] # drop help text
+ if name == 'info':
+ continue
+ if name.find('|') != -1:
+ # Command in the form 'foobar|f' or 'f|foobar', take the
+ # full name
+ opt = name.split('|')
+ if len(opt[0]) == 1:
+ name = opt[1]
+ else:
+ name = opt[0]
+ self._completer.append(name)
+ self._completer.append('help ' + name) # help completion
+
+ def _info_completion(self) -> None:
+ for cmd in self._cmd_passthrough('info')['return'].split('\r\n'):
+ if cmd:
+ self._completer.append('info ' + cmd.split()[1])
+
+ def _other_completion(self) -> None:
+ # special cases
+ self._completer.append('help info')
+
+ def _fill_completion(self) -> None:
+ self._cmd_completion()
+ self._info_completion()
+ self._other_completion()
+
+ def _cmd_passthrough(self, cmdline: str,
+ cpu_index: int = 0) -> QMPMessage:
+ return self.cmd_obj({
+ 'execute': 'human-monitor-command',
+ 'arguments': {
+ 'command-line': cmdline,
+ 'cpu-index': cpu_index
+ }
+ })
+
+ def _execute_cmd(self, cmdline: str) -> bool:
+ if cmdline.split()[0] == "cpu":
+ # trap the cpu command, it requires special setting
+ try:
+ idx = int(cmdline.split()[1])
+ if 'return' not in self._cmd_passthrough('info version', idx):
+ print('bad CPU index')
+ return True
+ self._cpu_index = idx
+ except ValueError:
+ print('cpu command takes an integer argument')
+ return True
+ resp = self._cmd_passthrough(cmdline, self._cpu_index)
+ if resp is None:
+ print('Disconnected')
+ return False
+ assert 'return' in resp or 'error' in resp
+ if 'return' in resp:
+ # Success
+ if len(resp['return']) > 0:
+ print(resp['return'], end=' ')
+ else:
+ # Error
+ print('%s: %s' % (resp['error']['class'], resp['error']['desc']))
+ return True
+
+ def show_banner(self, msg: str = 'Welcome to the HMP shell!') -> None:
+ QMPShell.show_banner(self, msg)
+
+
+def die(msg: str) -> NoReturn:
+ """Write an error to stderr, then exit with a return code of 1."""
+ sys.stderr.write('ERROR: %s\n' % msg)
+ sys.exit(1)
+
+
+def main() -> None:
+ """
+ qmp-shell entry point: parse command line arguments and start the REPL.
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-H', '--hmp', action='store_true',
+ help='Use HMP interface')
+ parser.add_argument('-N', '--skip-negotiation', action='store_true',
+ help='Skip negotiate (for qemu-ga)')
+ parser.add_argument('-v', '--verbose', action='store_true',
+ help='Verbose (echo commands sent and received)')
+ parser.add_argument('-p', '--pretty', action='store_true',
+ help='Pretty-print JSON')
+
+ default_server = os.environ.get('QMP_SOCKET')
+ parser.add_argument('qmp_server', action='store',
+ default=default_server,
+ help='< UNIX socket path | TCP address:port >')
+
+ args = parser.parse_args()
+ if args.qmp_server is None:
+ parser.error("QMP socket or TCP address must be specified")
+
+ shell_class = HMPShell if args.hmp else QMPShell
+
+ try:
+ address = shell_class.parse_address(args.qmp_server)
+ except QMPBadPortError:
+ parser.error(f"Bad port number: {args.qmp_server}")
+ return # pycharm doesn't know error() is noreturn
+
+ with shell_class(address, args.pretty, args.verbose) as qemu:
+ try:
+ qemu.connect(negotiate=not args.skip_negotiation)
+ except ConnectError as err:
+ if isinstance(err.exc, OSError):
+ die(f"Couldn't connect to {args.qmp_server}: {err!s}")
+ die(str(err))
+
+ for _ in qemu.repl():
+ pass
+
+
+if __name__ == '__main__':
+ main()
+++ /dev/null
-"""
-QEMU Guest Agent Client
-
-Usage:
-
-Start QEMU with:
-
-# qemu [...] -chardev socket,path=/tmp/qga.sock,server,wait=off,id=qga0 \
- -device virtio-serial \
- -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
-
-Run the script:
-
-$ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
-
-or
-
-$ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
-$ qemu-ga-client <command> [args...]
-
-For example:
-
-$ qemu-ga-client cat /etc/resolv.conf
-# Generated by NetworkManager
-nameserver 10.0.2.3
-$ qemu-ga-client fsfreeze status
-thawed
-$ qemu-ga-client fsfreeze freeze
-2 filesystems frozen
-
-See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
-"""
-
-# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2. See
-# the COPYING file in the top-level directory.
-
-import argparse
-import base64
-import errno
-import os
-import random
-import sys
-from typing import (
- Any,
- Callable,
- Dict,
- Optional,
- Sequence,
-)
-
-from qemu import qmp
-from qemu.qmp import SocketAddrT
-
-
-# This script has not seen many patches or careful attention in quite
-# some time. If you would like to improve it, please review the design
-# carefully and add docstrings at that point in time. Until then:
-
-# pylint: disable=missing-docstring
-
-
-class QemuGuestAgent(qmp.QEMUMonitorProtocol):
- def __getattr__(self, name: str) -> Callable[..., Any]:
- def wrapper(**kwds: object) -> object:
- return self.command('guest-' + name.replace('_', '-'), **kwds)
- return wrapper
-
-
-class QemuGuestAgentClient:
- def __init__(self, address: SocketAddrT):
- self.qga = QemuGuestAgent(address)
- self.qga.connect(negotiate=False)
-
- def sync(self, timeout: Optional[float] = 3) -> None:
- # Avoid being blocked forever
- if not self.ping(timeout):
- raise EnvironmentError('Agent seems not alive')
- uid = random.randint(0, (1 << 32) - 1)
- while True:
- ret = self.qga.sync(id=uid)
- if isinstance(ret, int) and int(ret) == uid:
- break
-
- def __file_read_all(self, handle: int) -> bytes:
- eof = False
- data = b''
- while not eof:
- ret = self.qga.file_read(handle=handle, count=1024)
- _data = base64.b64decode(ret['buf-b64'])
- data += _data
- eof = ret['eof']
- return data
-
- def read(self, path: str) -> bytes:
- handle = self.qga.file_open(path=path)
- try:
- data = self.__file_read_all(handle)
- finally:
- self.qga.file_close(handle=handle)
- return data
-
- def info(self) -> str:
- info = self.qga.info()
-
- msgs = []
- msgs.append('version: ' + info['version'])
- msgs.append('supported_commands:')
- enabled = [c['name'] for c in info['supported_commands']
- if c['enabled']]
- msgs.append('\tenabled: ' + ', '.join(enabled))
- disabled = [c['name'] for c in info['supported_commands']
- if not c['enabled']]
- msgs.append('\tdisabled: ' + ', '.join(disabled))
-
- return '\n'.join(msgs)
-
- @classmethod
- def __gen_ipv4_netmask(cls, prefixlen: int) -> str:
- mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
- return '.'.join([str(mask >> 24),
- str((mask >> 16) & 0xff),
- str((mask >> 8) & 0xff),
- str(mask & 0xff)])
-
- def ifconfig(self) -> str:
- nifs = self.qga.network_get_interfaces()
-
- msgs = []
- for nif in nifs:
- msgs.append(nif['name'] + ':')
- if 'ip-addresses' in nif:
- for ipaddr in nif['ip-addresses']:
- if ipaddr['ip-address-type'] == 'ipv4':
- addr = ipaddr['ip-address']
- mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
- msgs.append(f"\tinet {addr} netmask {mask}")
- elif ipaddr['ip-address-type'] == 'ipv6':
- addr = ipaddr['ip-address']
- prefix = ipaddr['prefix']
- msgs.append(f"\tinet6 {addr} prefixlen {prefix}")
- if nif['hardware-address'] != '00:00:00:00:00:00':
- msgs.append("\tether " + nif['hardware-address'])
-
- return '\n'.join(msgs)
-
- def ping(self, timeout: Optional[float]) -> bool:
- self.qga.settimeout(timeout)
- try:
- self.qga.ping()
- except TimeoutError:
- return False
- return True
-
- def fsfreeze(self, cmd: str) -> object:
- if cmd not in ['status', 'freeze', 'thaw']:
- raise Exception('Invalid command: ' + cmd)
- # Can be int (freeze, thaw) or GuestFsfreezeStatus (status)
- return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
-
- def fstrim(self, minimum: int) -> Dict[str, object]:
- # returns GuestFilesystemTrimResponse
- ret = getattr(self.qga, 'fstrim')(minimum=minimum)
- assert isinstance(ret, dict)
- return ret
-
- def suspend(self, mode: str) -> None:
- if mode not in ['disk', 'ram', 'hybrid']:
- raise Exception('Invalid mode: ' + mode)
-
- try:
- getattr(self.qga, 'suspend' + '_' + mode)()
- # On error exception will raise
- except TimeoutError:
- # On success command will timed out
- return
-
- def shutdown(self, mode: str = 'powerdown') -> None:
- if mode not in ['powerdown', 'halt', 'reboot']:
- raise Exception('Invalid mode: ' + mode)
-
- try:
- self.qga.shutdown(mode=mode)
- except TimeoutError:
- pass
-
-
-def _cmd_cat(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- if len(args) != 1:
- print('Invalid argument')
- print('Usage: cat <file>')
- sys.exit(1)
- print(client.read(args[0]))
-
-
-def _cmd_fsfreeze(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- usage = 'Usage: fsfreeze status|freeze|thaw'
- if len(args) != 1:
- print('Invalid argument')
- print(usage)
- sys.exit(1)
- if args[0] not in ['status', 'freeze', 'thaw']:
- print('Invalid command: ' + args[0])
- print(usage)
- sys.exit(1)
- cmd = args[0]
- ret = client.fsfreeze(cmd)
- if cmd == 'status':
- print(ret)
- return
-
- assert isinstance(ret, int)
- verb = 'frozen' if cmd == 'freeze' else 'thawed'
- print(f"{ret:d} filesystems {verb}")
-
-
-def _cmd_fstrim(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- if len(args) == 0:
- minimum = 0
- else:
- minimum = int(args[0])
- print(client.fstrim(minimum))
-
-
-def _cmd_ifconfig(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- assert not args
- print(client.ifconfig())
-
-
-def _cmd_info(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- assert not args
- print(client.info())
-
-
-def _cmd_ping(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- timeout = 3.0 if len(args) == 0 else float(args[0])
- alive = client.ping(timeout)
- if not alive:
- print("Not responded in %s sec" % args[0])
- sys.exit(1)
-
-
-def _cmd_suspend(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- usage = 'Usage: suspend disk|ram|hybrid'
- if len(args) != 1:
- print('Less argument')
- print(usage)
- sys.exit(1)
- if args[0] not in ['disk', 'ram', 'hybrid']:
- print('Invalid command: ' + args[0])
- print(usage)
- sys.exit(1)
- client.suspend(args[0])
-
-
-def _cmd_shutdown(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- assert not args
- client.shutdown()
-
-
-_cmd_powerdown = _cmd_shutdown
-
-
-def _cmd_halt(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- assert not args
- client.shutdown('halt')
-
-
-def _cmd_reboot(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
- assert not args
- client.shutdown('reboot')
-
-
-commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
-
-
-def send_command(address: str, cmd: str, args: Sequence[str]) -> None:
- if not os.path.exists(address):
- print('%s not found' % address)
- sys.exit(1)
-
- if cmd not in commands:
- print('Invalid command: ' + cmd)
- print('Available commands: ' + ', '.join(commands))
- sys.exit(1)
-
- try:
- client = QemuGuestAgentClient(address)
- except OSError as err:
- print(err)
- if err.errno == errno.ECONNREFUSED:
- print('Hint: qemu is not running?')
- sys.exit(1)
-
- if cmd == 'fsfreeze' and args[0] == 'freeze':
- client.sync(60)
- elif cmd != 'ping':
- client.sync()
-
- globals()['_cmd_' + cmd](client, args)
-
-
-def main() -> None:
- address = os.environ.get('QGA_CLIENT_ADDRESS')
-
- parser = argparse.ArgumentParser()
- parser.add_argument('--address', action='store',
- default=address,
- help='Specify a ip:port pair or a unix socket path')
- parser.add_argument('command', choices=commands)
- parser.add_argument('args', nargs='*')
-
- args = parser.parse_args()
- if args.address is None:
- parser.error('address is not specified')
- sys.exit(1)
-
- send_command(args.address, args.command, args.args)
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-#
-# Copyright (C) 2009, 2010 Red Hat Inc.
-#
-# Authors:
-# Luiz Capitulino <lcapitulino@redhat.com>
-#
-# This work is licensed under the terms of the GNU GPL, version 2. See
-# the COPYING file in the top-level directory.
-#
-
-"""
-Low-level QEMU shell on top of QMP.
-
-usage: qmp-shell [-h] [-H] [-N] [-v] [-p] qmp_server
-
-positional arguments:
- qmp_server < UNIX socket path | TCP address:port >
-
-optional arguments:
- -h, --help show this help message and exit
- -H, --hmp Use HMP interface
- -N, --skip-negotiation
- Skip negotiate (for qemu-ga)
- -v, --verbose Verbose (echo commands sent and received)
- -p, --pretty Pretty-print JSON
-
-
-Start QEMU with:
-
-# qemu [...] -qmp unix:./qmp-sock,server
-
-Run the shell:
-
-$ qmp-shell ./qmp-sock
-
-Commands have the following format:
-
- < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
-
-For example:
-
-(QEMU) device_add driver=e1000 id=net1
-{'return': {}}
-(QEMU)
-
-key=value pairs also support Python or JSON object literal subset notations,
-without spaces. Dictionaries/objects {} are supported as are arrays [].
-
- example-command arg-name1={'key':'value','obj'={'prop':"value"}}
-
-Both JSON and Python formatting should work, including both styles of
-string literal quotes. Both paradigms of literal values should work,
-including null/true/false for JSON and None/True/False for Python.
-
-
-Transactions have the following multi-line format:
-
- transaction(
- action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ]
- ...
- action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ]
- )
-
-One line transactions are also supported:
-
- transaction( action-name1 ... )
-
-For example:
-
- (QEMU) transaction(
- TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1
- TRANS> block-dirty-bitmap-clear node=drive0 name=bitmap0
- TRANS> )
- {"return": {}}
- (QEMU)
-
-Use the -v and -p options to activate the verbose and pretty-print options,
-which will echo back the properly formatted JSON-compliant QMP that is being
-sent to QEMU, which is useful for debugging and documentation generation.
-"""
-
-import argparse
-import ast
-import json
-import logging
-import os
-import re
-import readline
-import sys
-from typing import (
- Iterator,
- List,
- NoReturn,
- Optional,
- Sequence,
-)
-
-from qemu import qmp
-from qemu.qmp import QMPMessage
-
-
-LOG = logging.getLogger(__name__)
-
-
-class QMPCompleter:
- """
- QMPCompleter provides a readline library tab-complete behavior.
- """
- # NB: Python 3.9+ will probably allow us to subclass list[str] directly,
- # but pylint as of today does not know that List[str] is simply 'list'.
- def __init__(self) -> None:
- self._matches: List[str] = []
-
- def append(self, value: str) -> None:
- """Append a new valid completion to the list of possibilities."""
- return self._matches.append(value)
-
- def complete(self, text: str, state: int) -> Optional[str]:
- """readline.set_completer() callback implementation."""
- for cmd in self._matches:
- if cmd.startswith(text):
- if state == 0:
- return cmd
- state -= 1
- return None
-
-
-class QMPShellError(qmp.QMPError):
- """
- QMP Shell Base error class.
- """
-
-
-class FuzzyJSON(ast.NodeTransformer):
- """
- This extension of ast.NodeTransformer filters literal "true/false/null"
- values in a Python AST and replaces them by proper "True/False/None" values
- that Python can properly evaluate.
- """
-
- @classmethod
- def visit_Name(cls, # pylint: disable=invalid-name
- node: ast.Name) -> ast.AST:
- """
- Transform Name nodes with certain values into Constant (keyword) nodes.
- """
- if node.id == 'true':
- return ast.Constant(value=True)
- if node.id == 'false':
- return ast.Constant(value=False)
- if node.id == 'null':
- return ast.Constant(value=None)
- return node
-
-
-class QMPShell(qmp.QEMUMonitorProtocol):
- """
- QMPShell provides a basic readline-based QMP shell.
-
- :param address: Address of the QMP server.
- :param pretty: Pretty-print QMP messages.
- :param verbose: Echo outgoing QMP messages to console.
- """
- def __init__(self, address: qmp.SocketAddrT,
- pretty: bool = False, verbose: bool = False):
- super().__init__(address)
- self._greeting: Optional[QMPMessage] = None
- self._completer = QMPCompleter()
- self._transmode = False
- self._actions: List[QMPMessage] = []
- self._histfile = os.path.join(os.path.expanduser('~'),
- '.qmp-shell_history')
- self.pretty = pretty
- self.verbose = verbose
-
- def close(self) -> None:
- # Hook into context manager of parent to save shell history.
- self._save_history()
- super().close()
-
- def _fill_completion(self) -> None:
- cmds = self.cmd('query-commands')
- if 'error' in cmds:
- return
- for cmd in cmds['return']:
- self._completer.append(cmd['name'])
-
- def _completer_setup(self) -> None:
- self._completer = QMPCompleter()
- self._fill_completion()
- readline.set_history_length(1024)
- readline.set_completer(self._completer.complete)
- readline.parse_and_bind("tab: complete")
- # NB: default delimiters conflict with some command names
- # (eg. query-), clearing everything as it doesn't seem to matter
- readline.set_completer_delims('')
- try:
- readline.read_history_file(self._histfile)
- except FileNotFoundError:
- pass
- except IOError as err:
- msg = f"Failed to read history '{self._histfile}': {err!s}"
- LOG.warning(msg)
-
- def _save_history(self) -> None:
- try:
- readline.write_history_file(self._histfile)
- except IOError as err:
- msg = f"Failed to save history file '{self._histfile}': {err!s}"
- LOG.warning(msg)
-
- @classmethod
- def _parse_value(cls, val: str) -> object:
- try:
- return int(val)
- except ValueError:
- pass
-
- if val.lower() == 'true':
- return True
- if val.lower() == 'false':
- return False
- if val.startswith(('{', '[')):
- # Try first as pure JSON:
- try:
- return json.loads(val)
- except ValueError:
- pass
- # Try once again as FuzzyJSON:
- try:
- tree = ast.parse(val, mode='eval')
- transformed = FuzzyJSON().visit(tree)
- return ast.literal_eval(transformed)
- except (SyntaxError, ValueError):
- pass
- return val
-
- def _cli_expr(self,
- tokens: Sequence[str],
- parent: qmp.QMPObject) -> None:
- for arg in tokens:
- (key, sep, val) = arg.partition('=')
- if sep != '=':
- raise QMPShellError(
- f"Expected a key=value pair, got '{arg!s}'"
- )
-
- value = self._parse_value(val)
- optpath = key.split('.')
- curpath = []
- for path in optpath[:-1]:
- curpath.append(path)
- obj = parent.get(path, {})
- if not isinstance(obj, dict):
- msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
- raise QMPShellError(msg.format('.'.join(curpath)))
- parent[path] = obj
- parent = obj
- if optpath[-1] in parent:
- if isinstance(parent[optpath[-1]], dict):
- msg = 'Cannot use "{:s}" as both leaf and non-leaf key'
- raise QMPShellError(msg.format('.'.join(curpath)))
- raise QMPShellError(f'Cannot set "{key}" multiple times')
- parent[optpath[-1]] = value
-
- def _build_cmd(self, cmdline: str) -> Optional[QMPMessage]:
- """
- Build a QMP input object from a user provided command-line in the
- following format:
-
- < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
- """
- argument_regex = r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+'''
- cmdargs = re.findall(argument_regex, cmdline)
- qmpcmd: QMPMessage
-
- # Transactional CLI entry:
- if cmdargs and cmdargs[0] == 'transaction(':
- self._transmode = True
- self._actions = []
- cmdargs.pop(0)
-
- # Transactional CLI exit:
- if cmdargs and cmdargs[0] == ')' and self._transmode:
- self._transmode = False
- if len(cmdargs) > 1:
- msg = 'Unexpected input after close of Transaction sub-shell'
- raise QMPShellError(msg)
- qmpcmd = {
- 'execute': 'transaction',
- 'arguments': {'actions': self._actions}
- }
- return qmpcmd
-
- # No args, or no args remaining
- if not cmdargs:
- return None
-
- if self._transmode:
- # Parse and cache this Transactional Action
- finalize = False
- action = {'type': cmdargs[0], 'data': {}}
- if cmdargs[-1] == ')':
- cmdargs.pop(-1)
- finalize = True
- self._cli_expr(cmdargs[1:], action['data'])
- self._actions.append(action)
- return self._build_cmd(')') if finalize else None
-
- # Standard command: parse and return it to be executed.
- qmpcmd = {'execute': cmdargs[0], 'arguments': {}}
- self._cli_expr(cmdargs[1:], qmpcmd['arguments'])
- return qmpcmd
-
- def _print(self, qmp_message: object) -> None:
- jsobj = json.dumps(qmp_message,
- indent=4 if self.pretty else None,
- sort_keys=self.pretty)
- print(str(jsobj))
-
- def _execute_cmd(self, cmdline: str) -> bool:
- try:
- qmpcmd = self._build_cmd(cmdline)
- except QMPShellError as err:
- print(
- f"Error while parsing command line: {err!s}\n"
- "command format: <command-name> "
- "[arg-name1=arg1] ... [arg-nameN=argN",
- file=sys.stderr
- )
- return True
- # For transaction mode, we may have just cached the action:
- if qmpcmd is None:
- return True
- if self.verbose:
- self._print(qmpcmd)
- resp = self.cmd_obj(qmpcmd)
- if resp is None:
- print('Disconnected')
- return False
- self._print(resp)
- return True
-
- def connect(self, negotiate: bool = True) -> None:
- self._greeting = super().connect(negotiate)
- self._completer_setup()
-
- def show_banner(self,
- msg: str = 'Welcome to the QMP low-level shell!') -> None:
- """
- Print to stdio a greeting, and the QEMU version if available.
- """
- print(msg)
- if not self._greeting:
- print('Connected')
- return
- version = self._greeting['QMP']['version']['qemu']
- print("Connected to QEMU {major}.{minor}.{micro}\n".format(**version))
-
- @property
- def prompt(self) -> str:
- """
- Return the current shell prompt, including a trailing space.
- """
- if self._transmode:
- return 'TRANS> '
- return '(QEMU) '
-
- def read_exec_command(self) -> bool:
- """
- Read and execute a command.
-
- @return True if execution was ok, return False if disconnected.
- """
- try:
- cmdline = input(self.prompt)
- except EOFError:
- print()
- return False
-
- if cmdline == '':
- for event in self.get_events():
- print(event)
- return True
-
- return self._execute_cmd(cmdline)
-
- def repl(self) -> Iterator[None]:
- """
- Return an iterator that implements the REPL.
- """
- self.show_banner()
- while self.read_exec_command():
- yield
- self.close()
-
-
-class HMPShell(QMPShell):
- """
- HMPShell provides a basic readline-based HMP shell, tunnelled via QMP.
-
- :param address: Address of the QMP server.
- :param pretty: Pretty-print QMP messages.
- :param verbose: Echo outgoing QMP messages to console.
- """
- def __init__(self, address: qmp.SocketAddrT,
- pretty: bool = False, verbose: bool = False):
- super().__init__(address, pretty, verbose)
- self._cpu_index = 0
-
- def _cmd_completion(self) -> None:
- for cmd in self._cmd_passthrough('help')['return'].split('\r\n'):
- if cmd and cmd[0] != '[' and cmd[0] != '\t':
- name = cmd.split()[0] # drop help text
- if name == 'info':
- continue
- if name.find('|') != -1:
- # Command in the form 'foobar|f' or 'f|foobar', take the
- # full name
- opt = name.split('|')
- if len(opt[0]) == 1:
- name = opt[1]
- else:
- name = opt[0]
- self._completer.append(name)
- self._completer.append('help ' + name) # help completion
-
- def _info_completion(self) -> None:
- for cmd in self._cmd_passthrough('info')['return'].split('\r\n'):
- if cmd:
- self._completer.append('info ' + cmd.split()[1])
-
- def _other_completion(self) -> None:
- # special cases
- self._completer.append('help info')
-
- def _fill_completion(self) -> None:
- self._cmd_completion()
- self._info_completion()
- self._other_completion()
-
- def _cmd_passthrough(self, cmdline: str,
- cpu_index: int = 0) -> QMPMessage:
- return self.cmd_obj({
- 'execute': 'human-monitor-command',
- 'arguments': {
- 'command-line': cmdline,
- 'cpu-index': cpu_index
- }
- })
-
- def _execute_cmd(self, cmdline: str) -> bool:
- if cmdline.split()[0] == "cpu":
- # trap the cpu command, it requires special setting
- try:
- idx = int(cmdline.split()[1])
- if 'return' not in self._cmd_passthrough('info version', idx):
- print('bad CPU index')
- return True
- self._cpu_index = idx
- except ValueError:
- print('cpu command takes an integer argument')
- return True
- resp = self._cmd_passthrough(cmdline, self._cpu_index)
- if resp is None:
- print('Disconnected')
- return False
- assert 'return' in resp or 'error' in resp
- if 'return' in resp:
- # Success
- if len(resp['return']) > 0:
- print(resp['return'], end=' ')
- else:
- # Error
- print('%s: %s' % (resp['error']['class'], resp['error']['desc']))
- return True
-
- def show_banner(self, msg: str = 'Welcome to the HMP shell!') -> None:
- QMPShell.show_banner(self, msg)
-
-
-def die(msg: str) -> NoReturn:
- """Write an error to stderr, then exit with a return code of 1."""
- sys.stderr.write('ERROR: %s\n' % msg)
- sys.exit(1)
-
-
-def main() -> None:
- """
- qmp-shell entry point: parse command line arguments and start the REPL.
- """
- parser = argparse.ArgumentParser()
- parser.add_argument('-H', '--hmp', action='store_true',
- help='Use HMP interface')
- parser.add_argument('-N', '--skip-negotiation', action='store_true',
- help='Skip negotiate (for qemu-ga)')
- parser.add_argument('-v', '--verbose', action='store_true',
- help='Verbose (echo commands sent and received)')
- parser.add_argument('-p', '--pretty', action='store_true',
- help='Pretty-print JSON')
-
- default_server = os.environ.get('QMP_SOCKET')
- parser.add_argument('qmp_server', action='store',
- default=default_server,
- help='< UNIX socket path | TCP address:port >')
-
- args = parser.parse_args()
- if args.qmp_server is None:
- parser.error("QMP socket or TCP address must be specified")
-
- shell_class = HMPShell if args.hmp else QMPShell
-
- try:
- address = shell_class.parse_address(args.qmp_server)
- except qmp.QMPBadPortError:
- parser.error(f"Bad port number: {args.qmp_server}")
- return # pycharm doesn't know error() is noreturn
-
- with shell_class(address, args.pretty, args.verbose) as qemu:
- try:
- qemu.connect(negotiate=not args.skip_negotiation)
- except qmp.QMPConnectError:
- die("Didn't get QMP greeting message")
- except qmp.QMPCapabilitiesError:
- die("Couldn't negotiate capabilities")
- except OSError as err:
- die(f"Couldn't connect to {args.qmp_server}: {err!s}")
-
- for _ in qemu.repl():
- pass
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-"""
-QEMU Object Model testing tools.
-
-usage: qom [-h] {set,get,list,tree,fuse} ...
-
-Query and manipulate QOM data
-
-optional arguments:
- -h, --help show this help message and exit
-
-QOM commands:
- {set,get,list,tree,fuse}
- set Set a QOM property value
- get Get a QOM property value
- list List QOM properties at a given path
- tree Show QOM tree from a given path
- fuse Mount a QOM tree as a FUSE filesystem
-"""
-##
-# Copyright John Snow 2020, for Red Hat, Inc.
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-# John Snow <jsnow@redhat.com>
-# Anthony Liguori <aliguori@amazon.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.
-#
-# Based on ./scripts/qmp/qom-[set|get|tree|list]
-##
-
-import argparse
-
-from . import QMPResponseError
-from .qom_common import QOMCommand
-
-
-try:
- from .qom_fuse import QOMFuse
-except ModuleNotFoundError as _err:
- if _err.name != 'fuse':
- raise
-else:
- assert issubclass(QOMFuse, QOMCommand)
-
-
-class QOMSet(QOMCommand):
- """
- QOM Command - Set a property to a given value.
-
- usage: qom-set [-h] [--socket SOCKET] <path>.<property> <value>
-
- Set a QOM property value
-
- positional arguments:
- <path>.<property> QOM path and property, separated by a period '.'
- <value> new QOM property value
-
- optional arguments:
- -h, --help show this help message and exit
- --socket SOCKET, -s SOCKET
- QMP socket path or address (addr:port). May also be
- set via QMP_SOCKET environment variable.
- """
- name = 'set'
- help = 'Set a QOM property value'
-
- @classmethod
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
- super().configure_parser(parser)
- cls.add_path_prop_arg(parser)
- parser.add_argument(
- 'value',
- metavar='<value>',
- action='store',
- help='new QOM property value'
- )
-
- def __init__(self, args: argparse.Namespace):
- super().__init__(args)
- self.path, self.prop = args.path_prop.rsplit('.', 1)
- self.value = args.value
-
- def run(self) -> int:
- rsp = self.qmp.command(
- 'qom-set',
- path=self.path,
- property=self.prop,
- value=self.value
- )
- print(rsp)
- return 0
-
-
-class QOMGet(QOMCommand):
- """
- QOM Command - Get a property's current value.
-
- usage: qom-get [-h] [--socket SOCKET] <path>.<property>
-
- Get a QOM property value
-
- positional arguments:
- <path>.<property> QOM path and property, separated by a period '.'
-
- optional arguments:
- -h, --help show this help message and exit
- --socket SOCKET, -s SOCKET
- QMP socket path or address (addr:port). May also be
- set via QMP_SOCKET environment variable.
- """
- name = 'get'
- help = 'Get a QOM property value'
-
- @classmethod
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
- super().configure_parser(parser)
- cls.add_path_prop_arg(parser)
-
- def __init__(self, args: argparse.Namespace):
- super().__init__(args)
- try:
- tmp = args.path_prop.rsplit('.', 1)
- except ValueError as err:
- raise ValueError('Invalid format for <path>.<property>') from err
- self.path = tmp[0]
- self.prop = tmp[1]
-
- def run(self) -> int:
- rsp = self.qmp.command(
- 'qom-get',
- path=self.path,
- property=self.prop
- )
- if isinstance(rsp, dict):
- for key, value in rsp.items():
- print(f"{key}: {value}")
- else:
- print(rsp)
- return 0
-
-
-class QOMList(QOMCommand):
- """
- QOM Command - List the properties at a given path.
-
- usage: qom-list [-h] [--socket SOCKET] <path>
-
- List QOM properties at a given path
-
- positional arguments:
- <path> QOM path
-
- optional arguments:
- -h, --help show this help message and exit
- --socket SOCKET, -s SOCKET
- QMP socket path or address (addr:port). May also be
- set via QMP_SOCKET environment variable.
- """
- name = 'list'
- help = 'List QOM properties at a given path'
-
- @classmethod
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
- super().configure_parser(parser)
- parser.add_argument(
- 'path',
- metavar='<path>',
- action='store',
- help='QOM path',
- )
-
- def __init__(self, args: argparse.Namespace):
- super().__init__(args)
- self.path = args.path
-
- def run(self) -> int:
- rsp = self.qom_list(self.path)
- for item in rsp:
- if item.child:
- print(f"{item.name}/")
- elif item.link:
- print(f"@{item.name}/")
- else:
- print(item.name)
- return 0
-
-
-class QOMTree(QOMCommand):
- """
- QOM Command - Show the full tree below a given path.
-
- usage: qom-tree [-h] [--socket SOCKET] [<path>]
-
- Show QOM tree from a given path
-
- positional arguments:
- <path> QOM path
-
- optional arguments:
- -h, --help show this help message and exit
- --socket SOCKET, -s SOCKET
- QMP socket path or address (addr:port). May also be
- set via QMP_SOCKET environment variable.
- """
- name = 'tree'
- help = 'Show QOM tree from a given path'
-
- @classmethod
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
- super().configure_parser(parser)
- parser.add_argument(
- 'path',
- metavar='<path>',
- action='store',
- help='QOM path',
- nargs='?',
- default='/'
- )
-
- def __init__(self, args: argparse.Namespace):
- super().__init__(args)
- self.path = args.path
-
- def _list_node(self, path: str) -> None:
- print(path)
- items = self.qom_list(path)
- for item in items:
- if item.child:
- continue
- try:
- rsp = self.qmp.command('qom-get', path=path,
- property=item.name)
- print(f" {item.name}: {rsp} ({item.type})")
- except QMPResponseError as err:
- print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})")
- print('')
- for item in items:
- if not item.child:
- continue
- if path == '/':
- path = ''
- self._list_node(f"{path}/{item.name}")
-
- def run(self) -> int:
- self._list_node(self.path)
- return 0
-
-
-def main() -> int:
- """QOM script main entry point."""
- parser = argparse.ArgumentParser(
- description='Query and manipulate QOM data'
- )
- subparsers = parser.add_subparsers(
- title='QOM commands',
- dest='command'
- )
-
- for command in QOMCommand.__subclasses__():
- command.register(subparsers)
-
- args = parser.parse_args()
-
- if args.command is None:
- parser.error('Command not specified.')
- return 1
-
- cmd_class = args.cmd_class
- assert isinstance(cmd_class, type(QOMCommand))
- return cmd_class.command_runner(args)
+++ /dev/null
-"""
-QOM Command abstractions.
-"""
-##
-# Copyright John Snow 2020, for Red Hat, Inc.
-# Copyright IBM, Corp. 2011
-#
-# Authors:
-# John Snow <jsnow@redhat.com>
-# Anthony Liguori <aliguori@amazon.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.
-#
-# Based on ./scripts/qmp/qom-[set|get|tree|list]
-##
-
-import argparse
-import os
-import sys
-from typing import (
- Any,
- Dict,
- List,
- Optional,
- Type,
- TypeVar,
-)
-
-from . import QEMUMonitorProtocol, QMPError
-
-
-class ObjectPropertyInfo:
- """
- Represents the return type from e.g. qom-list.
- """
- def __init__(self, name: str, type_: str,
- description: Optional[str] = None,
- default_value: Optional[object] = None):
- self.name = name
- self.type = type_
- self.description = description
- self.default_value = default_value
-
- @classmethod
- def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyInfo':
- """
- Build an ObjectPropertyInfo from a Dict with an unknown shape.
- """
- assert value.keys() >= {'name', 'type'}
- assert value.keys() <= {'name', 'type', 'description', 'default-value'}
- return cls(value['name'], value['type'],
- value.get('description'),
- value.get('default-value'))
-
- @property
- def child(self) -> bool:
- """Is this property a child property?"""
- return self.type.startswith('child<')
-
- @property
- def link(self) -> bool:
- """Is this property a link property?"""
- return self.type.startswith('link<')
-
-
-CommandT = TypeVar('CommandT', bound='QOMCommand')
-
-
-class QOMCommand:
- """
- Represents a QOM sub-command.
-
- :param args: Parsed arguments, as returned from parser.parse_args.
- """
- name: str
- help: str
-
- def __init__(self, args: argparse.Namespace):
- if args.socket is None:
- raise QMPError("No QMP socket path or address given")
- self.qmp = QEMUMonitorProtocol(
- QEMUMonitorProtocol.parse_address(args.socket)
- )
- self.qmp.connect()
-
- @classmethod
- def register(cls, subparsers: Any) -> None:
- """
- Register this command with the argument parser.
-
- :param subparsers: argparse subparsers object, from "add_subparsers".
- """
- subparser = subparsers.add_parser(cls.name, help=cls.help,
- description=cls.help)
- cls.configure_parser(subparser)
-
- @classmethod
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
- """
- Configure a parser with this command's arguments.
-
- :param parser: argparse parser or subparser object.
- """
- default_path = os.environ.get('QMP_SOCKET')
- parser.add_argument(
- '--socket', '-s',
- dest='socket',
- action='store',
- help='QMP socket path or address (addr:port).'
- ' May also be set via QMP_SOCKET environment variable.',
- default=default_path
- )
- parser.set_defaults(cmd_class=cls)
-
- @classmethod
- def add_path_prop_arg(cls, parser: argparse.ArgumentParser) -> None:
- """
- Add the <path>.<proptery> positional argument to this command.
-
- :param parser: The parser to add the argument to.
- """
- parser.add_argument(
- 'path_prop',
- metavar='<path>.<property>',
- action='store',
- help="QOM path and property, separated by a period '.'"
- )
-
- def run(self) -> int:
- """
- Run this command.
-
- :return: 0 on success, 1 otherwise.
- """
- raise NotImplementedError
-
- def qom_list(self, path: str) -> List[ObjectPropertyInfo]:
- """
- :return: a strongly typed list from the 'qom-list' command.
- """
- rsp = self.qmp.command('qom-list', path=path)
- # qom-list returns List[ObjectPropertyInfo]
- assert isinstance(rsp, list)
- return [ObjectPropertyInfo.make(x) for x in rsp]
-
- @classmethod
- def command_runner(
- cls: Type[CommandT],
- args: argparse.Namespace
- ) -> int:
- """
- Run a fully-parsed subcommand, with error-handling for the CLI.
-
- :return: The return code from `run()`.
- """
- try:
- cmd = cls(args)
- return cmd.run()
- except QMPError as err:
- print(f"{type(err).__name__}: {err!s}", file=sys.stderr)
- return -1
-
- @classmethod
- def entry_point(cls) -> int:
- """
- Build this command's parser, parse arguments, and run the command.
-
- :return: `run`'s return code.
- """
- parser = argparse.ArgumentParser(description=cls.help)
- cls.configure_parser(parser)
- args = parser.parse_args()
- return cls.command_runner(args)
+++ /dev/null
-"""
-QEMU Object Model FUSE filesystem tool
-
-This script offers a simple FUSE filesystem within which the QOM tree
-may be browsed, queried and edited using traditional shell tooling.
-
-This script requires the 'fusepy' python package.
-
-
-usage: qom-fuse [-h] [--socket SOCKET] <mount>
-
-Mount a QOM tree as a FUSE filesystem
-
-positional arguments:
- <mount> Mount point
-
-optional arguments:
- -h, --help show this help message and exit
- --socket SOCKET, -s SOCKET
- QMP socket path or address (addr:port). May also be
- set via QMP_SOCKET environment variable.
-"""
-##
-# Copyright IBM, Corp. 2012
-# Copyright (C) 2020 Red Hat, Inc.
-#
-# Authors:
-# Anthony Liguori <aliguori@us.ibm.com>
-# Markus Armbruster <armbru@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.
-##
-
-import argparse
-from errno import ENOENT, EPERM
-import stat
-import sys
-from typing import (
- IO,
- Dict,
- Iterator,
- Mapping,
- Optional,
- Union,
-)
-
-import fuse
-from fuse import FUSE, FuseOSError, Operations
-
-from . import QMPResponseError
-from .qom_common import QOMCommand
-
-
-fuse.fuse_python_api = (0, 2)
-
-
-class QOMFuse(QOMCommand, Operations):
- """
- QOMFuse implements both fuse.Operations and QOMCommand.
-
- Operations implements the FS, and QOMCommand implements the CLI command.
- """
- name = 'fuse'
- help = 'Mount a QOM tree as a FUSE filesystem'
- fuse: FUSE
-
- @classmethod
- def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
- super().configure_parser(parser)
- parser.add_argument(
- 'mount',
- metavar='<mount>',
- action='store',
- help="Mount point",
- )
-
- def __init__(self, args: argparse.Namespace):
- super().__init__(args)
- self.mount = args.mount
- self.ino_map: Dict[str, int] = {}
- self.ino_count = 1
-
- def run(self) -> int:
- print(f"Mounting QOMFS to '{self.mount}'", file=sys.stderr)
- self.fuse = FUSE(self, self.mount, foreground=True)
- return 0
-
- def get_ino(self, path: str) -> int:
- """Get an inode number for a given QOM path."""
- if path in self.ino_map:
- return self.ino_map[path]
- self.ino_map[path] = self.ino_count
- self.ino_count += 1
- return self.ino_map[path]
-
- def is_object(self, path: str) -> bool:
- """Is the given QOM path an object?"""
- try:
- self.qom_list(path)
- return True
- except QMPResponseError:
- return False
-
- def is_property(self, path: str) -> bool:
- """Is the given QOM path a property?"""
- path, prop = path.rsplit('/', 1)
- if path == '':
- path = '/'
- try:
- for item in self.qom_list(path):
- if item.name == prop:
- return True
- return False
- except QMPResponseError:
- return False
-
- def is_link(self, path: str) -> bool:
- """Is the given QOM path a link?"""
- path, prop = path.rsplit('/', 1)
- if path == '':
- path = '/'
- try:
- for item in self.qom_list(path):
- if item.name == prop and item.link:
- return True
- return False
- except QMPResponseError:
- return False
-
- def read(self, path: str, size: int, offset: int, fh: IO[bytes]) -> bytes:
- if not self.is_property(path):
- raise FuseOSError(ENOENT)
-
- path, prop = path.rsplit('/', 1)
- if path == '':
- path = '/'
- try:
- data = str(self.qmp.command('qom-get', path=path, property=prop))
- data += '\n' # make values shell friendly
- except QMPResponseError as err:
- raise FuseOSError(EPERM) from err
-
- if offset > len(data):
- return b''
-
- return bytes(data[offset:][:size], encoding='utf-8')
-
- def readlink(self, path: str) -> Union[bool, str]:
- if not self.is_link(path):
- return False
- path, prop = path.rsplit('/', 1)
- prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
- return prefix + str(self.qmp.command('qom-get', path=path,
- property=prop))
-
- def getattr(self, path: str,
- fh: Optional[IO[bytes]] = None) -> Mapping[str, object]:
- if self.is_link(path):
- value = {
- 'st_mode': 0o755 | stat.S_IFLNK,
- 'st_ino': self.get_ino(path),
- 'st_dev': 0,
- 'st_nlink': 2,
- 'st_uid': 1000,
- 'st_gid': 1000,
- 'st_size': 4096,
- 'st_atime': 0,
- 'st_mtime': 0,
- 'st_ctime': 0
- }
- elif self.is_object(path):
- value = {
- 'st_mode': 0o755 | stat.S_IFDIR,
- 'st_ino': self.get_ino(path),
- 'st_dev': 0,
- 'st_nlink': 2,
- 'st_uid': 1000,
- 'st_gid': 1000,
- 'st_size': 4096,
- 'st_atime': 0,
- 'st_mtime': 0,
- 'st_ctime': 0
- }
- elif self.is_property(path):
- value = {
- 'st_mode': 0o644 | stat.S_IFREG,
- 'st_ino': self.get_ino(path),
- 'st_dev': 0,
- 'st_nlink': 1,
- 'st_uid': 1000,
- 'st_gid': 1000,
- 'st_size': 4096,
- 'st_atime': 0,
- 'st_mtime': 0,
- 'st_ctime': 0
- }
- else:
- raise FuseOSError(ENOENT)
- return value
-
- def readdir(self, path: str, fh: IO[bytes]) -> Iterator[str]:
- yield '.'
- yield '..'
- for item in self.qom_list(path):
- yield item.name
--- /dev/null
+"""
+QEMU Guest Agent Client
+
+Usage:
+
+Start QEMU with:
+
+# qemu [...] -chardev socket,path=/tmp/qga.sock,server=on,wait=off,id=qga0 \
+ -device virtio-serial \
+ -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0
+
+Run the script:
+
+$ qemu-ga-client --address=/tmp/qga.sock <command> [args...]
+
+or
+
+$ export QGA_CLIENT_ADDRESS=/tmp/qga.sock
+$ qemu-ga-client <command> [args...]
+
+For example:
+
+$ qemu-ga-client cat /etc/resolv.conf
+# Generated by NetworkManager
+nameserver 10.0.2.3
+$ qemu-ga-client fsfreeze status
+thawed
+$ qemu-ga-client fsfreeze freeze
+2 filesystems frozen
+
+See also: https://wiki.qemu.org/Features/QAPI/GuestAgent
+"""
+
+# Copyright (C) 2012 Ryota Ozaki <ozaki.ryota@gmail.com>
+#
+# This work is licensed under the terms of the GNU GPL, version 2. See
+# the COPYING file in the top-level directory.
+
+import argparse
+import asyncio
+import base64
+import os
+import random
+import sys
+from typing import (
+ Any,
+ Callable,
+ Dict,
+ Optional,
+ Sequence,
+)
+
+from qemu.aqmp import ConnectError, SocketAddrT
+from qemu.aqmp.legacy import QEMUMonitorProtocol
+
+
+# This script has not seen many patches or careful attention in quite
+# some time. If you would like to improve it, please review the design
+# carefully and add docstrings at that point in time. Until then:
+
+# pylint: disable=missing-docstring
+
+
+class QemuGuestAgent(QEMUMonitorProtocol):
+ def __getattr__(self, name: str) -> Callable[..., Any]:
+ def wrapper(**kwds: object) -> object:
+ return self.command('guest-' + name.replace('_', '-'), **kwds)
+ return wrapper
+
+
+class QemuGuestAgentClient:
+ def __init__(self, address: SocketAddrT):
+ self.qga = QemuGuestAgent(address)
+ self.qga.connect(negotiate=False)
+
+ def sync(self, timeout: Optional[float] = 3) -> None:
+ # Avoid being blocked forever
+ if not self.ping(timeout):
+ raise EnvironmentError('Agent seems not alive')
+ uid = random.randint(0, (1 << 32) - 1)
+ while True:
+ ret = self.qga.sync(id=uid)
+ if isinstance(ret, int) and int(ret) == uid:
+ break
+
+ def __file_read_all(self, handle: int) -> bytes:
+ eof = False
+ data = b''
+ while not eof:
+ ret = self.qga.file_read(handle=handle, count=1024)
+ _data = base64.b64decode(ret['buf-b64'])
+ data += _data
+ eof = ret['eof']
+ return data
+
+ def read(self, path: str) -> bytes:
+ handle = self.qga.file_open(path=path)
+ try:
+ data = self.__file_read_all(handle)
+ finally:
+ self.qga.file_close(handle=handle)
+ return data
+
+ def info(self) -> str:
+ info = self.qga.info()
+
+ msgs = []
+ msgs.append('version: ' + info['version'])
+ msgs.append('supported_commands:')
+ enabled = [c['name'] for c in info['supported_commands']
+ if c['enabled']]
+ msgs.append('\tenabled: ' + ', '.join(enabled))
+ disabled = [c['name'] for c in info['supported_commands']
+ if not c['enabled']]
+ msgs.append('\tdisabled: ' + ', '.join(disabled))
+
+ return '\n'.join(msgs)
+
+ @classmethod
+ def __gen_ipv4_netmask(cls, prefixlen: int) -> str:
+ mask = int('1' * prefixlen + '0' * (32 - prefixlen), 2)
+ return '.'.join([str(mask >> 24),
+ str((mask >> 16) & 0xff),
+ str((mask >> 8) & 0xff),
+ str(mask & 0xff)])
+
+ def ifconfig(self) -> str:
+ nifs = self.qga.network_get_interfaces()
+
+ msgs = []
+ for nif in nifs:
+ msgs.append(nif['name'] + ':')
+ if 'ip-addresses' in nif:
+ for ipaddr in nif['ip-addresses']:
+ if ipaddr['ip-address-type'] == 'ipv4':
+ addr = ipaddr['ip-address']
+ mask = self.__gen_ipv4_netmask(int(ipaddr['prefix']))
+ msgs.append(f"\tinet {addr} netmask {mask}")
+ elif ipaddr['ip-address-type'] == 'ipv6':
+ addr = ipaddr['ip-address']
+ prefix = ipaddr['prefix']
+ msgs.append(f"\tinet6 {addr} prefixlen {prefix}")
+ if nif['hardware-address'] != '00:00:00:00:00:00':
+ msgs.append("\tether " + nif['hardware-address'])
+
+ return '\n'.join(msgs)
+
+ def ping(self, timeout: Optional[float]) -> bool:
+ self.qga.settimeout(timeout)
+ try:
+ self.qga.ping()
+ except asyncio.TimeoutError:
+ return False
+ return True
+
+ def fsfreeze(self, cmd: str) -> object:
+ if cmd not in ['status', 'freeze', 'thaw']:
+ raise Exception('Invalid command: ' + cmd)
+ # Can be int (freeze, thaw) or GuestFsfreezeStatus (status)
+ return getattr(self.qga, 'fsfreeze' + '_' + cmd)()
+
+ def fstrim(self, minimum: int) -> Dict[str, object]:
+ # returns GuestFilesystemTrimResponse
+ ret = getattr(self.qga, 'fstrim')(minimum=minimum)
+ assert isinstance(ret, dict)
+ return ret
+
+ def suspend(self, mode: str) -> None:
+ if mode not in ['disk', 'ram', 'hybrid']:
+ raise Exception('Invalid mode: ' + mode)
+
+ try:
+ getattr(self.qga, 'suspend' + '_' + mode)()
+ # On error exception will raise
+ except asyncio.TimeoutError:
+ # On success command will timed out
+ return
+
+ def shutdown(self, mode: str = 'powerdown') -> None:
+ if mode not in ['powerdown', 'halt', 'reboot']:
+ raise Exception('Invalid mode: ' + mode)
+
+ try:
+ self.qga.shutdown(mode=mode)
+ except asyncio.TimeoutError:
+ pass
+
+
+def _cmd_cat(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ if len(args) != 1:
+ print('Invalid argument')
+ print('Usage: cat <file>')
+ sys.exit(1)
+ print(client.read(args[0]))
+
+
+def _cmd_fsfreeze(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ usage = 'Usage: fsfreeze status|freeze|thaw'
+ if len(args) != 1:
+ print('Invalid argument')
+ print(usage)
+ sys.exit(1)
+ if args[0] not in ['status', 'freeze', 'thaw']:
+ print('Invalid command: ' + args[0])
+ print(usage)
+ sys.exit(1)
+ cmd = args[0]
+ ret = client.fsfreeze(cmd)
+ if cmd == 'status':
+ print(ret)
+ return
+
+ assert isinstance(ret, int)
+ verb = 'frozen' if cmd == 'freeze' else 'thawed'
+ print(f"{ret:d} filesystems {verb}")
+
+
+def _cmd_fstrim(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ if len(args) == 0:
+ minimum = 0
+ else:
+ minimum = int(args[0])
+ print(client.fstrim(minimum))
+
+
+def _cmd_ifconfig(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ assert not args
+ print(client.ifconfig())
+
+
+def _cmd_info(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ assert not args
+ print(client.info())
+
+
+def _cmd_ping(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ timeout = 3.0 if len(args) == 0 else float(args[0])
+ alive = client.ping(timeout)
+ if not alive:
+ print("Not responded in %s sec" % args[0])
+ sys.exit(1)
+
+
+def _cmd_suspend(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ usage = 'Usage: suspend disk|ram|hybrid'
+ if len(args) != 1:
+ print('Less argument')
+ print(usage)
+ sys.exit(1)
+ if args[0] not in ['disk', 'ram', 'hybrid']:
+ print('Invalid command: ' + args[0])
+ print(usage)
+ sys.exit(1)
+ client.suspend(args[0])
+
+
+def _cmd_shutdown(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ assert not args
+ client.shutdown()
+
+
+_cmd_powerdown = _cmd_shutdown
+
+
+def _cmd_halt(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ assert not args
+ client.shutdown('halt')
+
+
+def _cmd_reboot(client: QemuGuestAgentClient, args: Sequence[str]) -> None:
+ assert not args
+ client.shutdown('reboot')
+
+
+commands = [m.replace('_cmd_', '') for m in dir() if '_cmd_' in m]
+
+
+def send_command(address: str, cmd: str, args: Sequence[str]) -> None:
+ if not os.path.exists(address):
+ print(f"'{address}' not found. (Is QEMU running?)")
+ sys.exit(1)
+
+ if cmd not in commands:
+ print('Invalid command: ' + cmd)
+ print('Available commands: ' + ', '.join(commands))
+ sys.exit(1)
+
+ try:
+ client = QemuGuestAgentClient(address)
+ except ConnectError as err:
+ print(err)
+ if isinstance(err.exc, ConnectionError):
+ print('(Is QEMU running?)')
+ sys.exit(1)
+
+ if cmd == 'fsfreeze' and args[0] == 'freeze':
+ client.sync(60)
+ elif cmd != 'ping':
+ client.sync()
+
+ globals()['_cmd_' + cmd](client, args)
+
+
+def main() -> None:
+ address = os.environ.get('QGA_CLIENT_ADDRESS')
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--address', action='store',
+ default=address,
+ help='Specify a ip:port pair or a unix socket path')
+ parser.add_argument('command', choices=commands)
+ parser.add_argument('args', nargs='*')
+
+ args = parser.parse_args()
+ if args.address is None:
+ parser.error('address is not specified')
+ sys.exit(1)
+
+ send_command(args.address, args.command, args.args)
+
+
+if __name__ == '__main__':
+ main()
--- /dev/null
+"""
+QEMU Object Model testing tools.
+
+usage: qom [-h] {set,get,list,tree,fuse} ...
+
+Query and manipulate QOM data
+
+optional arguments:
+ -h, --help show this help message and exit
+
+QOM commands:
+ {set,get,list,tree,fuse}
+ set Set a QOM property value
+ get Get a QOM property value
+ list List QOM properties at a given path
+ tree Show QOM tree from a given path
+ fuse Mount a QOM tree as a FUSE filesystem
+"""
+##
+# Copyright John Snow 2020, for Red Hat, Inc.
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# John Snow <jsnow@redhat.com>
+# Anthony Liguori <aliguori@amazon.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.
+#
+# Based on ./scripts/qmp/qom-[set|get|tree|list]
+##
+
+import argparse
+
+from qemu.aqmp import ExecuteError
+
+from .qom_common import QOMCommand
+
+
+try:
+ from .qom_fuse import QOMFuse
+except ModuleNotFoundError as _err:
+ if _err.name != 'fuse':
+ raise
+else:
+ assert issubclass(QOMFuse, QOMCommand)
+
+
+class QOMSet(QOMCommand):
+ """
+ QOM Command - Set a property to a given value.
+
+ usage: qom-set [-h] [--socket SOCKET] <path>.<property> <value>
+
+ Set a QOM property value
+
+ positional arguments:
+ <path>.<property> QOM path and property, separated by a period '.'
+ <value> new QOM property value
+
+ optional arguments:
+ -h, --help show this help message and exit
+ --socket SOCKET, -s SOCKET
+ QMP socket path or address (addr:port). May also be
+ set via QMP_SOCKET environment variable.
+ """
+ name = 'set'
+ help = 'Set a QOM property value'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ cls.add_path_prop_arg(parser)
+ parser.add_argument(
+ 'value',
+ metavar='<value>',
+ action='store',
+ help='new QOM property value'
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.path, self.prop = args.path_prop.rsplit('.', 1)
+ self.value = args.value
+
+ def run(self) -> int:
+ rsp = self.qmp.command(
+ 'qom-set',
+ path=self.path,
+ property=self.prop,
+ value=self.value
+ )
+ print(rsp)
+ return 0
+
+
+class QOMGet(QOMCommand):
+ """
+ QOM Command - Get a property's current value.
+
+ usage: qom-get [-h] [--socket SOCKET] <path>.<property>
+
+ Get a QOM property value
+
+ positional arguments:
+ <path>.<property> QOM path and property, separated by a period '.'
+
+ optional arguments:
+ -h, --help show this help message and exit
+ --socket SOCKET, -s SOCKET
+ QMP socket path or address (addr:port). May also be
+ set via QMP_SOCKET environment variable.
+ """
+ name = 'get'
+ help = 'Get a QOM property value'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ cls.add_path_prop_arg(parser)
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ try:
+ tmp = args.path_prop.rsplit('.', 1)
+ except ValueError as err:
+ raise ValueError('Invalid format for <path>.<property>') from err
+ self.path = tmp[0]
+ self.prop = tmp[1]
+
+ def run(self) -> int:
+ rsp = self.qmp.command(
+ 'qom-get',
+ path=self.path,
+ property=self.prop
+ )
+ if isinstance(rsp, dict):
+ for key, value in rsp.items():
+ print(f"{key}: {value}")
+ else:
+ print(rsp)
+ return 0
+
+
+class QOMList(QOMCommand):
+ """
+ QOM Command - List the properties at a given path.
+
+ usage: qom-list [-h] [--socket SOCKET] <path>
+
+ List QOM properties at a given path
+
+ positional arguments:
+ <path> QOM path
+
+ optional arguments:
+ -h, --help show this help message and exit
+ --socket SOCKET, -s SOCKET
+ QMP socket path or address (addr:port). May also be
+ set via QMP_SOCKET environment variable.
+ """
+ name = 'list'
+ help = 'List QOM properties at a given path'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ parser.add_argument(
+ 'path',
+ metavar='<path>',
+ action='store',
+ help='QOM path',
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.path = args.path
+
+ def run(self) -> int:
+ rsp = self.qom_list(self.path)
+ for item in rsp:
+ if item.child:
+ print(f"{item.name}/")
+ elif item.link:
+ print(f"@{item.name}/")
+ else:
+ print(item.name)
+ return 0
+
+
+class QOMTree(QOMCommand):
+ """
+ QOM Command - Show the full tree below a given path.
+
+ usage: qom-tree [-h] [--socket SOCKET] [<path>]
+
+ Show QOM tree from a given path
+
+ positional arguments:
+ <path> QOM path
+
+ optional arguments:
+ -h, --help show this help message and exit
+ --socket SOCKET, -s SOCKET
+ QMP socket path or address (addr:port). May also be
+ set via QMP_SOCKET environment variable.
+ """
+ name = 'tree'
+ help = 'Show QOM tree from a given path'
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ parser.add_argument(
+ 'path',
+ metavar='<path>',
+ action='store',
+ help='QOM path',
+ nargs='?',
+ default='/'
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.path = args.path
+
+ def _list_node(self, path: str) -> None:
+ print(path)
+ items = self.qom_list(path)
+ for item in items:
+ if item.child:
+ continue
+ try:
+ rsp = self.qmp.command('qom-get', path=path,
+ property=item.name)
+ print(f" {item.name}: {rsp} ({item.type})")
+ except ExecuteError as err:
+ print(f" {item.name}: <EXCEPTION: {err!s}> ({item.type})")
+ print('')
+ for item in items:
+ if not item.child:
+ continue
+ if path == '/':
+ path = ''
+ self._list_node(f"{path}/{item.name}")
+
+ def run(self) -> int:
+ self._list_node(self.path)
+ return 0
+
+
+def main() -> int:
+ """QOM script main entry point."""
+ parser = argparse.ArgumentParser(
+ description='Query and manipulate QOM data'
+ )
+ subparsers = parser.add_subparsers(
+ title='QOM commands',
+ dest='command'
+ )
+
+ for command in QOMCommand.__subclasses__():
+ command.register(subparsers)
+
+ args = parser.parse_args()
+
+ if args.command is None:
+ parser.error('Command not specified.')
+ return 1
+
+ cmd_class = args.cmd_class
+ assert isinstance(cmd_class, type(QOMCommand))
+ return cmd_class.command_runner(args)
--- /dev/null
+"""
+QOM Command abstractions.
+"""
+##
+# Copyright John Snow 2020, for Red Hat, Inc.
+# Copyright IBM, Corp. 2011
+#
+# Authors:
+# John Snow <jsnow@redhat.com>
+# Anthony Liguori <aliguori@amazon.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.
+#
+# Based on ./scripts/qmp/qom-[set|get|tree|list]
+##
+
+import argparse
+import os
+import sys
+from typing import (
+ Any,
+ Dict,
+ List,
+ Optional,
+ Type,
+ TypeVar,
+)
+
+from qemu.aqmp import QMPError
+from qemu.aqmp.legacy import QEMUMonitorProtocol
+
+
+class ObjectPropertyInfo:
+ """
+ Represents the return type from e.g. qom-list.
+ """
+ def __init__(self, name: str, type_: str,
+ description: Optional[str] = None,
+ default_value: Optional[object] = None):
+ self.name = name
+ self.type = type_
+ self.description = description
+ self.default_value = default_value
+
+ @classmethod
+ def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyInfo':
+ """
+ Build an ObjectPropertyInfo from a Dict with an unknown shape.
+ """
+ assert value.keys() >= {'name', 'type'}
+ assert value.keys() <= {'name', 'type', 'description', 'default-value'}
+ return cls(value['name'], value['type'],
+ value.get('description'),
+ value.get('default-value'))
+
+ @property
+ def child(self) -> bool:
+ """Is this property a child property?"""
+ return self.type.startswith('child<')
+
+ @property
+ def link(self) -> bool:
+ """Is this property a link property?"""
+ return self.type.startswith('link<')
+
+
+CommandT = TypeVar('CommandT', bound='QOMCommand')
+
+
+class QOMCommand:
+ """
+ Represents a QOM sub-command.
+
+ :param args: Parsed arguments, as returned from parser.parse_args.
+ """
+ name: str
+ help: str
+
+ def __init__(self, args: argparse.Namespace):
+ if args.socket is None:
+ raise QMPError("No QMP socket path or address given")
+ self.qmp = QEMUMonitorProtocol(
+ QEMUMonitorProtocol.parse_address(args.socket)
+ )
+ self.qmp.connect()
+
+ @classmethod
+ def register(cls, subparsers: Any) -> None:
+ """
+ Register this command with the argument parser.
+
+ :param subparsers: argparse subparsers object, from "add_subparsers".
+ """
+ subparser = subparsers.add_parser(cls.name, help=cls.help,
+ description=cls.help)
+ cls.configure_parser(subparser)
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ """
+ Configure a parser with this command's arguments.
+
+ :param parser: argparse parser or subparser object.
+ """
+ default_path = os.environ.get('QMP_SOCKET')
+ parser.add_argument(
+ '--socket', '-s',
+ dest='socket',
+ action='store',
+ help='QMP socket path or address (addr:port).'
+ ' May also be set via QMP_SOCKET environment variable.',
+ default=default_path
+ )
+ parser.set_defaults(cmd_class=cls)
+
+ @classmethod
+ def add_path_prop_arg(cls, parser: argparse.ArgumentParser) -> None:
+ """
+ Add the <path>.<proptery> positional argument to this command.
+
+ :param parser: The parser to add the argument to.
+ """
+ parser.add_argument(
+ 'path_prop',
+ metavar='<path>.<property>',
+ action='store',
+ help="QOM path and property, separated by a period '.'"
+ )
+
+ def run(self) -> int:
+ """
+ Run this command.
+
+ :return: 0 on success, 1 otherwise.
+ """
+ raise NotImplementedError
+
+ def qom_list(self, path: str) -> List[ObjectPropertyInfo]:
+ """
+ :return: a strongly typed list from the 'qom-list' command.
+ """
+ rsp = self.qmp.command('qom-list', path=path)
+ # qom-list returns List[ObjectPropertyInfo]
+ assert isinstance(rsp, list)
+ return [ObjectPropertyInfo.make(x) for x in rsp]
+
+ @classmethod
+ def command_runner(
+ cls: Type[CommandT],
+ args: argparse.Namespace
+ ) -> int:
+ """
+ Run a fully-parsed subcommand, with error-handling for the CLI.
+
+ :return: The return code from `run()`.
+ """
+ try:
+ cmd = cls(args)
+ return cmd.run()
+ except QMPError as err:
+ print(f"{type(err).__name__}: {err!s}", file=sys.stderr)
+ return -1
+
+ @classmethod
+ def entry_point(cls) -> int:
+ """
+ Build this command's parser, parse arguments, and run the command.
+
+ :return: `run`'s return code.
+ """
+ parser = argparse.ArgumentParser(description=cls.help)
+ cls.configure_parser(parser)
+ args = parser.parse_args()
+ return cls.command_runner(args)
--- /dev/null
+"""
+QEMU Object Model FUSE filesystem tool
+
+This script offers a simple FUSE filesystem within which the QOM tree
+may be browsed, queried and edited using traditional shell tooling.
+
+This script requires the 'fusepy' python package.
+
+
+usage: qom-fuse [-h] [--socket SOCKET] <mount>
+
+Mount a QOM tree as a FUSE filesystem
+
+positional arguments:
+ <mount> Mount point
+
+optional arguments:
+ -h, --help show this help message and exit
+ --socket SOCKET, -s SOCKET
+ QMP socket path or address (addr:port). May also be
+ set via QMP_SOCKET environment variable.
+"""
+##
+# Copyright IBM, Corp. 2012
+# Copyright (C) 2020 Red Hat, Inc.
+#
+# Authors:
+# Anthony Liguori <aliguori@us.ibm.com>
+# Markus Armbruster <armbru@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.
+##
+
+import argparse
+from errno import ENOENT, EPERM
+import stat
+import sys
+from typing import (
+ IO,
+ Dict,
+ Iterator,
+ Mapping,
+ Optional,
+ Union,
+)
+
+import fuse
+from fuse import FUSE, FuseOSError, Operations
+
+from qemu.aqmp import ExecuteError
+
+from .qom_common import QOMCommand
+
+
+fuse.fuse_python_api = (0, 2)
+
+
+class QOMFuse(QOMCommand, Operations):
+ """
+ QOMFuse implements both fuse.Operations and QOMCommand.
+
+ Operations implements the FS, and QOMCommand implements the CLI command.
+ """
+ name = 'fuse'
+ help = 'Mount a QOM tree as a FUSE filesystem'
+ fuse: FUSE
+
+ @classmethod
+ def configure_parser(cls, parser: argparse.ArgumentParser) -> None:
+ super().configure_parser(parser)
+ parser.add_argument(
+ 'mount',
+ metavar='<mount>',
+ action='store',
+ help="Mount point",
+ )
+
+ def __init__(self, args: argparse.Namespace):
+ super().__init__(args)
+ self.mount = args.mount
+ self.ino_map: Dict[str, int] = {}
+ self.ino_count = 1
+
+ def run(self) -> int:
+ print(f"Mounting QOMFS to '{self.mount}'", file=sys.stderr)
+ self.fuse = FUSE(self, self.mount, foreground=True)
+ return 0
+
+ def get_ino(self, path: str) -> int:
+ """Get an inode number for a given QOM path."""
+ if path in self.ino_map:
+ return self.ino_map[path]
+ self.ino_map[path] = self.ino_count
+ self.ino_count += 1
+ return self.ino_map[path]
+
+ def is_object(self, path: str) -> bool:
+ """Is the given QOM path an object?"""
+ try:
+ self.qom_list(path)
+ return True
+ except ExecuteError:
+ return False
+
+ def is_property(self, path: str) -> bool:
+ """Is the given QOM path a property?"""
+ path, prop = path.rsplit('/', 1)
+ if path == '':
+ path = '/'
+ try:
+ for item in self.qom_list(path):
+ if item.name == prop:
+ return True
+ return False
+ except ExecuteError:
+ return False
+
+ def is_link(self, path: str) -> bool:
+ """Is the given QOM path a link?"""
+ path, prop = path.rsplit('/', 1)
+ if path == '':
+ path = '/'
+ try:
+ for item in self.qom_list(path):
+ if item.name == prop and item.link:
+ return True
+ return False
+ except ExecuteError:
+ return False
+
+ def read(self, path: str, size: int, offset: int, fh: IO[bytes]) -> bytes:
+ if not self.is_property(path):
+ raise FuseOSError(ENOENT)
+
+ path, prop = path.rsplit('/', 1)
+ if path == '':
+ path = '/'
+ try:
+ data = str(self.qmp.command('qom-get', path=path, property=prop))
+ data += '\n' # make values shell friendly
+ except ExecuteError as err:
+ raise FuseOSError(EPERM) from err
+
+ if offset > len(data):
+ return b''
+
+ return bytes(data[offset:][:size], encoding='utf-8')
+
+ def readlink(self, path: str) -> Union[bool, str]:
+ if not self.is_link(path):
+ return False
+ path, prop = path.rsplit('/', 1)
+ prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
+ return prefix + str(self.qmp.command('qom-get', path=path,
+ property=prop))
+
+ def getattr(self, path: str,
+ fh: Optional[IO[bytes]] = None) -> Mapping[str, object]:
+ if self.is_link(path):
+ value = {
+ 'st_mode': 0o755 | stat.S_IFLNK,
+ 'st_ino': self.get_ino(path),
+ 'st_dev': 0,
+ 'st_nlink': 2,
+ 'st_uid': 1000,
+ 'st_gid': 1000,
+ 'st_size': 4096,
+ 'st_atime': 0,
+ 'st_mtime': 0,
+ 'st_ctime': 0
+ }
+ elif self.is_object(path):
+ value = {
+ 'st_mode': 0o755 | stat.S_IFDIR,
+ 'st_ino': self.get_ino(path),
+ 'st_dev': 0,
+ 'st_nlink': 2,
+ 'st_uid': 1000,
+ 'st_gid': 1000,
+ 'st_size': 4096,
+ 'st_atime': 0,
+ 'st_mtime': 0,
+ 'st_ctime': 0
+ }
+ elif self.is_property(path):
+ value = {
+ 'st_mode': 0o644 | stat.S_IFREG,
+ 'st_ino': self.get_ino(path),
+ 'st_dev': 0,
+ 'st_nlink': 1,
+ 'st_uid': 1000,
+ 'st_gid': 1000,
+ 'st_size': 4096,
+ 'st_atime': 0,
+ 'st_mtime': 0,
+ 'st_ctime': 0
+ }
+ else:
+ raise FuseOSError(ENOENT)
+ return value
+
+ def readdir(self, path: str, fh: IO[bytes]) -> Iterator[str]:
+ yield '.'
+ yield '..'
+ for item in self.qom_list(path):
+ yield item.name
[options.entry_points]
console_scripts =
- qom = qemu.qmp.qom:main
- qom-set = qemu.qmp.qom:QOMSet.entry_point
- qom-get = qemu.qmp.qom:QOMGet.entry_point
- qom-list = qemu.qmp.qom:QOMList.entry_point
- qom-tree = qemu.qmp.qom:QOMTree.entry_point
- qom-fuse = qemu.qmp.qom_fuse:QOMFuse.entry_point [fuse]
- qemu-ga-client = qemu.qmp.qemu_ga_client:main
- qmp-shell = qemu.qmp.qmp_shell:main
+ qom = qemu.utils.qom:main
+ qom-set = qemu.utils.qom:QOMSet.entry_point
+ qom-get = qemu.utils.qom:QOMGet.entry_point
+ qom-list = qemu.utils.qom:QOMList.entry_point
+ qom-tree = qemu.utils.qom:QOMTree.entry_point
+ qom-fuse = qemu.utils.qom_fuse:QOMFuse.entry_point [fuse]
+ qemu-ga-client = qemu.utils.qemu_ga_client:main
+ qmp-shell = qemu.aqmp.qmp_shell:main
aqmp-tui = qemu.aqmp.aqmp_tui:main [tui]
[flake8]
warn_unused_configs = True
namespace_packages = True
-[mypy-qemu.qmp.qom_fuse]
+[mypy-qemu.utils.qom_fuse]
# fusepy has no type stubs:
allow_subclassing_any = True
.[devel]
.[fuse] # Workaround to trigger tox venv rebuild
.[tui] # Workaround to trigger tox venv rebuild
+ setuptools < 60 # Workaround, please see commit msg.
commands =
make check
# -*- mode: python -*-
+# vim: filetype=python
#
# Copyright (C) 2015-2019 Zoltán Kővágó <DirtY.iCE.hu@gmail.com>
#
#
# @resize: This permission is required to change the size of a block node.
#
-# @graph-mod: This permission is required to change the node that this
-# BdrvChild points to.
-#
# Since: 4.0
##
{ 'enum': 'BlockPermission',
- 'data': [ 'consistent-read', 'write', 'write-unchanged', 'resize',
- 'graph-mod' ] }
+ 'data': [ 'consistent-read', 'write', 'write-unchanged', 'resize' ] }
+
##
# @XDbgBlockGraphEdge:
#
# Since: 4.2
##
{ 'enum': 'BlockExportType',
- 'data': [ 'nbd', 'vhost-user-blk',
+ 'data': [ 'nbd',
+ { 'name': 'vhost-user-blk',
+ 'if': 'CONFIG_VHOST_USER_BLK_SERVER' },
{ 'name': 'fuse', 'if': 'CONFIG_FUSE' } ] }
##
'discriminator': 'type',
'data': {
'nbd': 'BlockExportOptionsNbd',
- 'vhost-user-blk': 'BlockExportOptionsVhostUserBlk',
+ 'vhost-user-blk': { 'type': 'BlockExportOptionsVhostUserBlk',
+ 'if': 'CONFIG_VHOST_USER_BLK_SERVER' },
'fuse': { 'type': 'BlockExportOptionsFuse',
'if': 'CONFIG_FUSE' }
} }
# -*- Mode: Python -*-
+# vim: filetype=python
##
# = Compatibility policy
#
# @memdev: memory backend linked with device
#
-# @node: the numa node
+# @node: the numa node (Since: 7.0)
#
# Since: 6.2
##
#
# @memdev: memory backend linked with device
#
-# @node: the numa node
+# @node: the numa node (Since: 7.0)
#
# Since: 6.2
##
##
{ 'command': 'x-query-profile',
'returns': 'HumanReadableText',
+ 'if': 'CONFIG_TCG',
'features': [ 'unstable' ] }
##
'qapi-events-@0@.h'.format(module),
'qapi-commands-@0@.c'.format(module),
'qapi-commands-@0@.h'.format(module),
+ 'qapi-commands-@0@.trace-events'.format(module),
]
endif
if module.endswith('-target')
if output.endswith('.h')
genh += qapi_files[i]
endif
+ if output.endswith('.trace-events')
+ qapi_trace_events += qapi_files[i]
+ endif
util_ss.add(qapi_files[i])
i = i + 1
endforeach
if output.endswith('.h')
genh += qapi_files[i]
endif
+ if output.endswith('.trace-events')
+ qapi_trace_events += qapi_files[i]
+ endif
specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: qapi_files[i])
i = i + 1
endforeach
# @pages-per-second: the number of memory pages transferred per second
# (Since 4.0)
#
+# @precopy-bytes: The number of bytes sent in the pre-copy phase
+# (since 7.0).
+#
+# @downtime-bytes: The number of bytes sent while the guest is paused
+# (since 7.0).
+#
+# @postcopy-bytes: The number of bytes sent during the post-copy phase
+# (since 7.0).
+#
# Since: 0.14
##
{ 'struct': 'MigrationStats',
'normal-bytes': 'int', 'dirty-pages-rate' : 'int',
'mbps' : 'number', 'dirty-sync-count' : 'int',
'postcopy-requests' : 'int', 'page-size' : 'int',
- 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64' } }
+ 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64',
+ 'precopy-bytes' : 'uint64', 'downtime-bytes' : 'uint64',
+ 'postcopy-bytes' : 'uint64' } }
##
# @XBZRLECacheStats:
#
# @node: the numa node
#
-# @size: the size of epc section
+# @size: the size of EPC section
#
-# Since: 6.2
+# Since: 7.0
##
{ 'struct': 'SGXEPCSection',
'data': { 'node': 'int',
#
# @flc: true if FLC is supported
#
-# @sections: The EPC sections info for guest
+# @section-size: The EPC section size for guest
+# Redundant with @sections. Just for backward compatibility.
+#
+# @sections: The EPC sections info for guest (Since: 7.0)
+#
+# Features:
+# @deprecated: Member @section-size is deprecated. Use @sections instead.
#
# Since: 6.2
##
'sgx1': 'bool',
'sgx2': 'bool',
'flc': 'bool',
+ 'section-size': { 'type': 'uint64',
+ 'features': [ 'deprecated' ] },
'sections': ['SGXEPCSection']},
'if': 'TARGET_I386' }
#
# -> { "execute": "query-sgx" }
# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true,
-# "flc": true, "section-size" : 0 } }
+# "flc": true, "section-size" : 96468992,
+# "sections": [{"node": 0, "size": 67108864},
+# {"node": 1, "size": 29360128}]} }
#
##
{ 'command': 'query-sgx', 'returns': 'SGXInfo', 'if': 'TARGET_I386' }
#
# -> { "execute": "query-sgx-capabilities" }
# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true,
-# "flc": true, "section-size" : 0 } }
+# "flc": true, "section-size" : 96468992,
+# "section" : [{"node": 0, "size": 67108864},
+# {"node": 1, "size": 29360128}]} }
#
##
{ 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' }
# @json-cli: If present, the "-device" command line option supports JSON
# syntax with a structure identical to the arguments of this
# command.
+# @json-cli-hotplug: If present, the "-device" command line option supports JSON
+# syntax without the reference counting leak that broke
+# hot-unplug
#
# Notes:
#
{ 'command': 'device_add',
'data': {'driver': 'str', '*bus': 'str', '*id': 'str'},
'gen': false, # so we can get the additional arguments
- 'features': ['json-cli'] }
+ 'features': ['json-cli', 'json-cli-hotplug'] }
##
# @device_del:
# -*- Mode: Python -*-
+# vim: filetype=python
#
##
# -*- mode: python -*-
+# vim: filetype=python
#
# Copyright (C) 2011-2016 LluÃs Vilanova <vilanova@ac.upc.edu>
#
##
{ 'enum' : 'InputButton',
'data' : [ 'left', 'middle', 'right', 'wheel-up', 'wheel-down', 'side',
- 'extra' ] }
+ 'extra', 'wheel-left', 'wheel-right' ] }
##
# @InputAxis:
#include "hw/display/edid.h"
static qemu_edid_info info = {
- .prefx = 1024,
- .prefy = 768,
+ .prefx = 1280,
+ .prefy = 800,
};
static void usage(FILE *out)
ERST
DEF("create", img_create,
- "create [--object objectdef] [-q] [-f fmt] [-b backing_file] [-F backing_fmt] [-u] [-o options] filename [size]")
+ "create [--object objectdef] [-q] [-f fmt] [-b backing_file [-F backing_fmt]] [-u] [-o options] filename [size]")
SRST
-.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE] [-F BACKING_FMT] [-u] [-o OPTIONS] FILENAME [SIZE]
+.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE [-F BACKING_FMT]] [-u] [-o OPTIONS] FILENAME [SIZE]
ERST
DEF("dd", img_dd,
}
}
+ if (i == n) {
+ /*
+ * The whole buf is the same.
+ * No reason to split it into chunks, so return now.
+ */
+ *pnum = i;
+ return !is_zero;
+ }
+
tail = (sector_num + i) & (alignment - 1);
if (tail) {
if (is_zero && i <= tail) {
- /* treat unallocated areas which only consist
- * of a small tail as allocated. */
+ /*
+ * For sure next sector after i is data, and it will rewrite this
+ * tail anyway due to RMW. So, let's just write data now.
+ */
is_zero = false;
}
if (!is_zero) {
- /* align up end offset of allocated areas. */
+ /* If possible, align up end offset of allocated areas. */
i += alignment - tail;
i = MIN(i, n);
} else {
- /* align down end offset of zero areas. */
+ /*
+ * For sure next sector after i is data, and it will rewrite this
+ * tail anyway due to RMW. Better is avoid RMW and write zeroes up
+ * to aligned bound.
+ */
i -= tail;
}
}
-smp 16,sockets=2,dies=2,cores=2,threads=2,maxcpus=16
+ The following sub-option defines a CPU topology hierarchy (2 sockets
+ totally on the machine, 2 clusters per socket, 2 cores per cluster,
+ 2 threads per core) for ARM virt machines which support sockets/clusters
+ /cores/threads. Some members of the option can be omitted but their values
+ will be automatically computed:
+
+ ::
+
+ -smp 16,sockets=2,clusters=2,cores=2,threads=2,maxcpus=16
+
Historically preference was given to the coarsest topology parameters
when computing missing values (ie sockets preferred over cores, which
were preferred over threads), however, this behaviour is considered
0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82,
0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b);
+static void get_pci_address_for_device(GuestPCIAddress *pci,
+ HDEVINFO dev_info)
+{
+ SP_DEVINFO_DATA dev_info_data;
+ DWORD j;
+ DWORD size;
+ bool partial_pci = false;
+
+ dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
+
+ for (j = 0;
+ SetupDiEnumDeviceInfo(dev_info, j, &dev_info_data);
+ j++) {
+ DWORD addr, bus, ui_slot, type;
+ int func, slot;
+ size = sizeof(DWORD);
+
+ /*
+ * There is no need to allocate buffer in the next functions. The
+ * size is known and ULONG according to
+ * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ dev_info, &dev_info_data, SPDRP_BUSNUMBER,
+ &type, (PBYTE)&bus, size, NULL)) {
+ debug_error("failed to get PCI bus");
+ bus = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * The function retrieves the device's address. This value will be
+ * transformed into device function and number
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ dev_info, &dev_info_data, SPDRP_ADDRESS,
+ &type, (PBYTE)&addr, size, NULL)) {
+ debug_error("failed to get PCI address");
+ addr = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * This call returns UINumber of DEVICE_CAPABILITIES structure.
+ * This number is typically a user-perceived slot number.
+ */
+ if (!SetupDiGetDeviceRegistryProperty(
+ dev_info, &dev_info_data, SPDRP_UI_NUMBER,
+ &type, (PBYTE)&ui_slot, size, NULL)) {
+ debug_error("failed to get PCI slot");
+ ui_slot = -1;
+ partial_pci = true;
+ }
+
+ /*
+ * SetupApi gives us the same information as driver with
+ * IoGetDeviceProperty. According to Microsoft:
+ *
+ * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF)
+ * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF)
+ * SPDRP_ADDRESS is propertyAddress, so we do the same.
+ *
+ * https://docs.microsoft.com/en-us/windows/desktop/api/setupapi/nf-setupapi-setupdigetdeviceregistrypropertya
+ */
+ if (partial_pci) {
+ pci->domain = -1;
+ pci->slot = -1;
+ pci->function = -1;
+ pci->bus = -1;
+ continue;
+ } else {
+ func = ((int)addr == -1) ? -1 : addr & 0x0000FFFF;
+ slot = ((int)addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
+ if ((int)ui_slot != slot) {
+ g_debug("mismatch with reported slot values: %d vs %d",
+ (int)ui_slot, slot);
+ }
+ pci->domain = 0;
+ pci->slot = (int)ui_slot;
+ pci->function = func;
+ pci->bus = (int)bus;
+ return;
+ }
+ }
+}
+
static GuestPCIAddress *get_pci_info(int number, Error **errp)
{
- HDEVINFO dev_info;
+ HDEVINFO dev_info = INVALID_HANDLE_VALUE;
+ HDEVINFO parent_dev_info = INVALID_HANDLE_VALUE;
+
SP_DEVINFO_DATA dev_info_data;
SP_DEVICE_INTERFACE_DATA dev_iface_data;
HANDLE dev_file;
int i;
GuestPCIAddress *pci = NULL;
- bool partial_pci = false;
pci = g_malloc0(sizeof(*pci));
pci->domain = -1;
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (dev_info == INVALID_HANDLE_VALUE) {
error_setg_win32(errp, GetLastError(), "failed to get devices tree");
- goto out;
+ goto end;
}
g_debug("enumerating devices");
dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
dev_iface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) {
- PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL;
+ g_autofree PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL;
STORAGE_DEVICE_NUMBER sdn;
- char *parent_dev_id = NULL;
- HDEVINFO parent_dev_info;
+ g_autofree char *parent_dev_id = NULL;
SP_DEVINFO_DATA parent_dev_info_data;
- DWORD j;
DWORD size = 0;
g_debug("getting device path");
if (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data,
&GUID_DEVINTERFACE_DISK, 0,
&dev_iface_data)) {
- while (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
- pdev_iface_detail_data,
- size, &size,
- &dev_info_data)) {
+ if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
+ pdev_iface_detail_data,
+ size, &size,
+ &dev_info_data)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
pdev_iface_detail_data = g_malloc(size);
pdev_iface_detail_data->cbSize =
} else {
error_setg_win32(errp, GetLastError(),
"failed to get device interfaces");
- goto free_dev_info;
+ goto end;
}
}
+ if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data,
+ pdev_iface_detail_data,
+ size, &size,
+ &dev_info_data)) {
+ // pdev_iface_detail_data already is allocated
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device interfaces");
+ goto end;
+ }
+
dev_file = CreateFile(pdev_iface_detail_data->DevicePath, 0,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0,
NULL);
- g_free(pdev_iface_detail_data);
if (!DeviceIoControl(dev_file, IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof(sdn), &size, NULL)) {
CloseHandle(dev_file);
error_setg_win32(errp, GetLastError(),
"failed to get device slot number");
- goto free_dev_info;
+ goto end;
}
CloseHandle(dev_file);
} else {
error_setg_win32(errp, GetLastError(),
"failed to get device interfaces");
- goto free_dev_info;
+ goto end;
}
g_debug("found device slot %d. Getting storage controller", number);
ULONG dev_id_size = 0;
size = 0;
- while (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
- parent_dev_id, size, &size)) {
+ if (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
+ parent_dev_id, size, &size)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
parent_dev_id = g_malloc(size);
} else {
error_setg_win32(errp, GetLastError(),
"failed to get device instance ID");
- goto out;
+ goto end;
}
}
+ if (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data,
+ parent_dev_id, size, &size)) {
+ // parent_dev_id already is allocated
+ error_setg_win32(errp, GetLastError(),
+ "failed to get device instance ID");
+ goto end;
+ }
+
/*
* CM API used here as opposed to
* SetupDiGetDeviceProperty(..., DEVPKEY_Device_Parent, ...)
g_error("CM_Locate_DevInst failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get device instance");
- goto out;
+ goto end;
}
cr = CM_Get_Parent(&parent_dev_inst, dev_inst, 0);
if (cr != CR_SUCCESS) {
g_error("CM_Get_Parent failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get parent device instance");
- goto out;
+ goto end;
}
cr = CM_Get_Device_ID_Size(&dev_id_size, parent_dev_inst, 0);
g_error("CM_Get_Device_ID_Size failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get parent device ID length");
- goto out;
+ goto end;
}
++dev_id_size;
g_error("CM_Get_Device_ID failed with code %lx", cr);
error_setg_win32(errp, GetLastError(),
"failed to get parent device ID");
- goto out;
+ goto end;
}
}
parent_dev_info =
SetupDiGetClassDevs(&GUID_DEVINTERFACE_STORAGEPORT, parent_dev_id,
NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
- g_free(parent_dev_id);
if (parent_dev_info == INVALID_HANDLE_VALUE) {
error_setg_win32(errp, GetLastError(),
"failed to get parent device");
- goto out;
+ goto end;
}
parent_dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(parent_dev_info, 0, &parent_dev_info_data)) {
error_setg_win32(errp, GetLastError(),
"failed to get parent device data");
- goto out;
+ goto end;
}
- for (j = 0;
- SetupDiEnumDeviceInfo(parent_dev_info, j, &parent_dev_info_data);
- j++) {
- DWORD addr, bus, ui_slot, type;
- int func, slot;
+ get_pci_address_for_device(pci, parent_dev_info);
- /*
- * There is no need to allocate buffer in the next functions. The
- * size is known and ULONG according to
- * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx
- */
- if (!SetupDiGetDeviceRegistryProperty(
- parent_dev_info, &parent_dev_info_data, SPDRP_BUSNUMBER,
- &type, (PBYTE)&bus, size, NULL)) {
- debug_error("failed to get PCI bus");
- bus = -1;
- partial_pci = true;
- }
-
- /*
- * The function retrieves the device's address. This value will be
- * transformed into device function and number
- */
- if (!SetupDiGetDeviceRegistryProperty(
- parent_dev_info, &parent_dev_info_data, SPDRP_ADDRESS,
- &type, (PBYTE)&addr, size, NULL)) {
- debug_error("failed to get PCI address");
- addr = -1;
- partial_pci = true;
- }
-
- /*
- * This call returns UINumber of DEVICE_CAPABILITIES structure.
- * This number is typically a user-perceived slot number.
- */
- if (!SetupDiGetDeviceRegistryProperty(
- parent_dev_info, &parent_dev_info_data, SPDRP_UI_NUMBER,
- &type, (PBYTE)&ui_slot, size, NULL)) {
- debug_error("failed to get PCI slot");
- ui_slot = -1;
- partial_pci = true;
- }
-
- /*
- * SetupApi gives us the same information as driver with
- * IoGetDeviceProperty. According to Microsoft:
- *
- * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF)
- * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF)
- * SPDRP_ADDRESS is propertyAddress, so we do the same.
- *
- * https://docs.microsoft.com/en-us/windows/desktop/api/setupapi/nf-setupapi-setupdigetdeviceregistrypropertya
- */
- if (partial_pci) {
- pci->domain = -1;
- pci->slot = -1;
- pci->function = -1;
- pci->bus = -1;
- continue;
- } else {
- func = ((int)addr == -1) ? -1 : addr & 0x0000FFFF;
- slot = ((int)addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF;
- if ((int)ui_slot != slot) {
- g_debug("mismatch with reported slot values: %d vs %d",
- (int)ui_slot, slot);
- }
- pci->domain = 0;
- pci->slot = (int)ui_slot;
- pci->function = func;
- pci->bus = (int)bus;
- break;
- }
- }
- SetupDiDestroyDeviceInfoList(parent_dev_info);
break;
}
-free_dev_info:
- SetupDiDestroyDeviceInfoList(dev_info);
-out:
+end:
+ if (parent_dev_info != INVALID_HANDLE_VALUE) {
+ SetupDiDestroyDeviceInfoList(parent_dev_info);
+ }
+ if (dev_info != INVALID_HANDLE_VALUE) {
+ SetupDiDestroyDeviceInfoList(dev_info);
+ }
return pci;
}
char const *version_id;
} ga_matrix_lookup_t;
-static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
+static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][7] = {
{
/* Desktop editions */
{ 5, 0, "Microsoft Windows 2000", "2000"},
{ 6, 1, "Microsoft Windows 7" "7"},
{ 6, 2, "Microsoft Windows 8", "8"},
{ 6, 3, "Microsoft Windows 8.1", "8.1"},
- {10, 0, "Microsoft Windows 10", "10"},
{ 0, 0, 0}
},{
/* Server editions */
{ 6, 2, "Microsoft Windows Server 2012", "2012"},
{ 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
{ 0, 0, 0},
- { 0, 0, 0},
{ 0, 0, 0}
}
};
-typedef struct _ga_win_10_0_server_t {
- int final_build;
+typedef struct _ga_win_10_0_t {
+ int first_build;
char const *version;
char const *version_id;
-} ga_win_10_0_server_t;
+} ga_win_10_0_t;
-static ga_win_10_0_server_t const WIN_10_0_SERVER_VERSION_MATRIX[4] = {
+static ga_win_10_0_t const WIN_10_0_SERVER_VERSION_MATRIX[4] = {
{14393, "Microsoft Windows Server 2016", "2016"},
{17763, "Microsoft Windows Server 2019", "2019"},
{20344, "Microsoft Windows Server 2022", "2022"},
{0, 0}
};
+static ga_win_10_0_t const WIN_10_0_CLIENT_VERSION_MATRIX[3] = {
+ {10240, "Microsoft Windows 10", "10"},
+ {22000, "Microsoft Windows 11", "11"},
+ {0, 0}
+};
+
static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
{
typedef NTSTATUS(WINAPI *rtl_get_version_t)(
DWORD build = os_version->dwBuildNumber;
int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
- ga_win_10_0_server_t const *win_10_0_table = WIN_10_0_SERVER_VERSION_MATRIX;
+ ga_win_10_0_t const *win_10_0_table = tbl_idx ?
+ WIN_10_0_SERVER_VERSION_MATRIX : WIN_10_0_CLIENT_VERSION_MATRIX;
+ ga_win_10_0_t const *win_10_0_version = NULL;
while (table->version != NULL) {
- if (major == 10 && minor == 0 && tbl_idx) {
+ if (major == 10 && minor == 0) {
while (win_10_0_table->version != NULL) {
- if (build <= win_10_0_table->final_build) {
- if (id) {
- return g_strdup(win_10_0_table->version_id);
- } else {
- return g_strdup(win_10_0_table->version);
- }
+ if (build >= win_10_0_table->first_build) {
+ win_10_0_version = win_10_0_table;
}
win_10_0_table++;
}
+ if (win_10_0_table) {
+ if (id) {
+ return g_strdup(win_10_0_version->version_id);
+ } else {
+ return g_strdup(win_10_0_version->version);
+ }
+ }
} else if (major == table->major && minor == table->minor) {
if (id) {
return g_strdup(table->version_id);
'qga-qapi-visit.h',
]
+# Problem: to generate trace events, we'd have to add the .trace-events
+# file to qapi_trace_events like we do in qapi/meson.build. Since
+# qapi_trace_events is used by trace/meson.build, we'd have to move
+# subdir('qga') above subdir('trace') in the top-level meson.build.
+# Can't, because it would break the dependency of qga on qemuutil (which
+# depends on trace_ss). Not worth solving now; simply suppress trace
+# event generation instead.
qga_qapi_files = custom_target('QGA QAPI files',
output: qga_qapi_outputs,
input: 'qapi-schema.json',
- command: [ qapi_gen, '-o', 'qga', '-p', 'qga-', '@INPUT0@' ],
+ command: [ qapi_gen, '-o', 'qga', '-p', 'qga-', '@INPUT0@',
+ '--suppress-tracing' ],
depend_files: qapi_gen_depends)
qga_ss = ss.source_set()
endif
qga_msi = custom_target('QGA MSI',
input: files('installer/qemu-ga.wxs'),
- output: 'qemu-ga-@0@.msi'.format(config_host['ARCH']),
+ output: 'qemu-ga-@0@.msi'.format(host_arch),
depends: deps,
command: [
find_program('env'),
CROSS_COMPILE=$(riscv32_cross_prefix) \
PLATFORM="generic"
cp opensbi/build/platform/generic/firmware/fw_dynamic.bin ../pc-bios/opensbi-riscv32-generic-fw_dynamic.bin
- cp opensbi/build/platform/generic/firmware/fw_dynamic.elf ../pc-bios/opensbi-riscv32-generic-fw_dynamic.elf
opensbi64-generic:
$(MAKE) -C opensbi \
CROSS_COMPILE=$(riscv64_cross_prefix) \
PLATFORM="generic"
cp opensbi/build/platform/generic/firmware/fw_dynamic.bin ../pc-bios/opensbi-riscv64-generic-fw_dynamic.bin
- cp opensbi/build/platform/generic/firmware/fw_dynamic.elf ../pc-bios/opensbi-riscv64-generic-fw_dynamic.elf
MESON = meson
NINJA = ninja
-Subproject commit a6906b024c6cca5a86496f51eb4bfee3a0c36148
+Subproject commit 5b4c5acdcd552a4e1796aeca6bb700f6cbb0282d
-Subproject commit b9062deaaea7269369eaa46260d75edcaf276afa
+Subproject commit 04dfc984ac0ad70e2d1f58d1121e3b7670901124
-Subproject commit 2dd4b9b3f84019668719344b40dba79d681be41c
+Subproject commit 6a62e0cb0dfe9cd28b70547dbea5caf76847c3a9
--disable-whpx \
--disable-xen \
--disable-xen-pci-passthrough \
---disable-xfsctl \
--disable-xkbcommon \
--disable-zstd \
--enable-attr \
--enable-xen --enable-brlapi \
--enable-linux-aio --enable-attr \
--enable-cap-ng --enable-trace-backends=log --enable-spice --enable-rbd \
- --enable-xfsctl --enable-libusb --enable-usb-redir \
+ --enable-libusb --enable-usb-redir \
--enable-libiscsi --enable-libnfs --enable-seccomp \
--enable-tpm --enable-libssh --enable-lzo --enable-snappy --enable-bzip2 \
--enable-numa --enable-rdma --enable-smartcard --enable-virglrenderer \
# compatibility levels for each CPU model.
#
-from qemu import qmp
+from qemu.aqmp.legacy import QEMUMonitorProtocol
import sys
-if len(sys.argv) != 1:
+if len(sys.argv) != 2:
print("syntax: %s QMP-SOCK\n\n" % __file__ +
"Where QMP-SOCK points to a QEMU process such as\n\n" +
" # qemu-system-x86_64 -qmp unix:/tmp/qmp,server,nowait " +
sock = sys.argv[1]
-cmd = sys.argv[2]
-shell = qmp.QEMUMonitorProtocol(sock)
+shell = QEMUMonitorProtocol(sock)
shell.connect()
models = shell.cmd("query-cpu-definitions")
--- /dev/null
+#! /bin/sh
+
+if test $# = 0; then
+ exit 0
+fi
+
+# Create list of config switches that should be poisoned in common code...
+# but filter out CONFIG_TCG and CONFIG_USER_ONLY which are special.
+exec sed -n \
+ -e' /CONFIG_TCG/d' \
+ -e '/CONFIG_USER_ONLY/d' \
+ -e '/^#define / {' \
+ -e 's///' \
+ -e 's/ .*//' \
+ -e 's/^/#pragma GCC poison /p' \
+ -e '}' "$@"
"trace_file",
}
+BUILTIN_OPTIONS = {
+ "strip",
+}
+
LINE_WIDTH = 76
return not (set(opt["choices"]) <= {"auto", "disabled", "enabled"})
+def filter_options(json):
+ if ":" in json["name"]:
+ return False
+ if json["section"] == "user":
+ return json["name"] not in SKIP_OPTIONS
+ else:
+ return json["name"] in BUILTIN_OPTIONS
+
+
def load_options(json):
- json = [
- x
- for x in json
- if x["section"] == "user"
- and ":" not in x["name"]
- and x["name"] not in SKIP_OPTIONS
- ]
+ json = [x for x in json if filter_options(x)]
return sorted(json, key=lambda x: x["name"])
printf "%s\n" ' jemalloc/system/tcmalloc)'
printf "%s\n" ' --enable-slirp[=CHOICE] Whether and how to find the slirp library'
printf "%s\n" ' (choices: auto/disabled/enabled/internal/system)'
+ printf "%s\n" ' --enable-strip Strip targets on install'
printf "%s\n" ' --enable-tcg-interpreter TCG with bytecode interpreter (slow)'
printf "%s\n" ' --enable-trace-backends=CHOICE'
printf "%s\n" ' Set available tracing backends [log] (choices:'
--disable-spice) printf "%s" -Dspice=disabled ;;
--enable-spice-protocol) printf "%s" -Dspice_protocol=enabled ;;
--disable-spice-protocol) printf "%s" -Dspice_protocol=disabled ;;
+ --enable-strip) printf "%s" -Dstrip=true ;;
+ --disable-strip) printf "%s" -Dstrip=false ;;
--enable-tcg) printf "%s" -Dtcg=enabled ;;
--disable-tcg) printf "%s" -Dtcg=disabled ;;
--enable-tcg-interpreter) printf "%s" -Dtcg_interpreter=true ;;
print('''
SPEED = quick
-.speed.quick = $(foreach s,$(sort $(filter-out %-slow, $1)), --suite $s)
-.speed.slow = $(foreach s,$(sort $1), --suite $s)
+.speed.quick = $(foreach s,$(sort $(filter-out %-slow %-thorough, $1)), --suite $s)
+.speed.slow = $(foreach s,$(sort $(filter-out %-thorough, $1)), --suite $s)
+.speed.thorough = $(foreach s,$(sort $1), --suite $s)
.mtestargs = --no-rebuild -t 0
ifneq ($(SPEED), quick)
for s in test_suites:
# The suite name in the introspection info is "PROJECT:SUITE"
s = s.split(':')[1]
- if s == 'slow':
+ if s == 'slow' or s == 'thorough':
continue
if s.endswith('-slow'):
s = s[:-5]
suites[s].speeds.append('slow')
+ if s.endswith('-thorough'):
+ s = s[:-9]
+ suites[s].speeds.append('thorough')
suites[s].deps.update(deps)
def emit_prolog(suites, prefix):
print(f'{prefix}-report.junit.xml $(all-{prefix}-xml): {prefix}-report%.junit.xml: run-ninja')
print(f'\t$(MAKE) {prefix}$* MTESTARGS="$(MTESTARGS) --logbase {prefix}-report$*" && ln -f meson-logs/$@ .')
-def emit_suite(name, suite, prefix):
+def emit_suite_deps(name, suite, prefix):
deps = ' '.join(suite.deps)
targets = f'{prefix}-{name} {prefix}-report-{name}.junit.xml {prefix} {prefix}-report.junit.xml'
print()
print(f'ifneq ($(filter {prefix}-build {targets}, $(MAKECMDGOALS)),)')
print(f'.{prefix}.build-suites += {name}')
print(f'endif')
+
+def emit_suite(name, suite, prefix):
+ emit_suite_deps(name, suite, prefix)
+ targets = f'{prefix}-{name} {prefix}-report-{name}.junit.xml {prefix} {prefix}-report.junit.xml'
print(f'ifneq ($(filter {targets}, $(MAKECMDGOALS)),)')
print(f'.{prefix}.mtest-suites += ' + ' '.join(suite.names(name)))
print(f'endif')
testsuites = defaultdict(Suite)
for test in introspect['tests']:
process_tests(test, targets, testsuites)
+# HACK: check-block is a separate target so that it runs with --verbose;
+# only write the dependencies
+emit_suite_deps('block', testsuites['block'], 'check')
+del testsuites['block']
emit_prolog(testsuites, 'check')
for name, suite in testsuites.items():
emit_suite(name, suite, 'check')
def gen_call(name: str,
arg_type: Optional[QAPISchemaObjectType],
boxed: bool,
- ret_type: Optional[QAPISchemaType]) -> str:
+ ret_type: Optional[QAPISchemaType],
+ gen_tracing: bool) -> str:
ret = ''
argstr = ''
if ret_type:
lhs = 'retval = '
- ret = mcgen('''
+ name = c_name(name)
+ upper = name.upper()
- %(lhs)sqmp_%(c_name)s(%(args)s&err);
- error_propagate(errp, err);
-''',
- c_name=c_name(name), args=argstr, lhs=lhs)
- if ret_type:
+ if gen_tracing:
ret += mcgen('''
+
+ if (trace_event_get_state_backends(TRACE_QMP_ENTER_%(upper)s)) {
+ g_autoptr(GString) req_json = qobject_to_json(QOBJECT(args));
+
+ trace_qmp_enter_%(name)s(req_json->str);
+ }
+ ''',
+ upper=upper, name=name)
+
+ ret += mcgen('''
+
+ %(lhs)sqmp_%(name)s(%(args)s&err);
+''',
+ name=name, args=argstr, lhs=lhs)
+
+ ret += mcgen('''
if (err) {
+''')
+
+ if gen_tracing:
+ ret += mcgen('''
+ trace_qmp_exit_%(name)s(error_get_pretty(err), false);
+''',
+ name=name)
+
+ ret += mcgen('''
+ error_propagate(errp, err);
goto out;
}
+''')
+
+ if ret_type:
+ ret += mcgen('''
qmp_marshal_output_%(c_name)s(retval, ret, errp);
''',
c_name=ret_type.c_name())
+
+ if gen_tracing:
+ if ret_type:
+ ret += mcgen('''
+
+ if (trace_event_get_state_backends(TRACE_QMP_EXIT_%(upper)s)) {
+ g_autoptr(GString) ret_json = qobject_to_json(*ret);
+
+ trace_qmp_exit_%(name)s(ret_json->str, true);
+ }
+ ''',
+ upper=upper, name=name)
+ else:
+ ret += mcgen('''
+
+ trace_qmp_exit_%(name)s("{}", true);
+ ''',
+ name=name)
+
return ret
proto=build_marshal_proto(name))
+def gen_trace(name: str) -> str:
+ return mcgen('''
+qmp_enter_%(name)s(const char *json) "%%s"
+qmp_exit_%(name)s(const char *result, bool succeeded) "%%s %%d"
+''',
+ name=c_name(name))
+
+
def gen_marshal(name: str,
arg_type: Optional[QAPISchemaObjectType],
boxed: bool,
- ret_type: Optional[QAPISchemaType]) -> str:
+ ret_type: Optional[QAPISchemaType],
+ gen_tracing: bool) -> str:
have_args = boxed or (arg_type and not arg_type.is_empty())
if have_args:
assert arg_type is not None
}
''')
- ret += gen_call(name, arg_type, boxed, ret_type)
+ ret += gen_call(name, arg_type, boxed, ret_type, gen_tracing)
ret += mcgen('''
class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor):
- def __init__(self, prefix: str):
+ def __init__(self, prefix: str, gen_tracing: bool):
super().__init__(
prefix, 'qapi-commands',
- ' * Schema-defined QAPI/QMP commands', None, __doc__)
+ ' * Schema-defined QAPI/QMP commands', None, __doc__,
+ gen_tracing=gen_tracing)
self._visited_ret_types: Dict[QAPIGenC, Set[QAPISchemaType]] = {}
+ self._gen_tracing = gen_tracing
def _begin_user_module(self, name: str) -> None:
self._visited_ret_types[self._genc] = set()
''',
commands=commands, visit=visit))
+
+ if self._gen_tracing and commands != 'qapi-commands':
+ self._genc.add(mcgen('''
+#include "qapi/qmp/qjson.h"
+#include "trace/trace-%(nm)s_trace_events.h"
+''',
+ nm=c_name(commands, protect=False)))
+ # We use c_name(commands, protect=False) to turn '-' into '_', to
+ # match .underscorify() in trace/meson.build
+
self._genh.add(mcgen('''
#include "%(types)s.h"
with ifcontext(ifcond, self._genh, self._genc):
self._genh.add(gen_command_decl(name, arg_type, boxed, ret_type))
self._genh.add(gen_marshal_decl(name))
- self._genc.add(gen_marshal(name, arg_type, boxed, ret_type))
+ self._genc.add(gen_marshal(name, arg_type, boxed, ret_type,
+ self._gen_tracing))
+ if self._gen_tracing:
+ self._gen_trace_events.add(gen_trace(name))
with self._temp_module('./init'):
with ifcontext(ifcond, self._genh, self._genc):
self._genc.add(gen_register_command(
def gen_commands(schema: QAPISchema,
output_dir: str,
- prefix: str) -> None:
- vis = QAPISchemaGenCommandVisitor(prefix)
+ prefix: str,
+ gen_tracing: bool) -> None:
+ vis = QAPISchemaGenCommandVisitor(prefix, gen_tracing)
schema.visit(vis)
vis.write(output_dir)
return guardend(self.fname)
+class QAPIGenTrace(QAPIGen):
+ def _top(self) -> str:
+ return super()._top() + '# AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n'
+
+
@contextmanager
def ifcontext(ifcond: QAPISchemaIfCond, *args: QAPIGenCCode) -> Iterator[None]:
"""
what: str,
user_blurb: str,
builtin_blurb: Optional[str],
- pydoc: str):
+ pydoc: str,
+ gen_tracing: bool = False):
self._prefix = prefix
self._what = what
self._user_blurb = user_blurb
self._builtin_blurb = builtin_blurb
self._pydoc = pydoc
self._current_module: Optional[str] = None
- self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH]] = {}
+ self._module: Dict[str, Tuple[QAPIGenC, QAPIGenH,
+ Optional[QAPIGenTrace]]] = {}
self._main_module: Optional[str] = None
+ self._gen_tracing = gen_tracing
@property
def _genc(self) -> QAPIGenC:
assert self._current_module is not None
return self._module[self._current_module][1]
+ @property
+ def _gen_trace_events(self) -> QAPIGenTrace:
+ assert self._gen_tracing
+ assert self._current_module is not None
+ gent = self._module[self._current_module][2]
+ assert gent is not None
+ return gent
+
@staticmethod
def _module_dirname(name: str) -> str:
if QAPISchemaModule.is_user_module(name):
basename = self._module_filename(self._what, name)
genc = QAPIGenC(basename + '.c', blurb, self._pydoc)
genh = QAPIGenH(basename + '.h', blurb, self._pydoc)
- self._module[name] = (genc, genh)
+
+ gent: Optional[QAPIGenTrace] = None
+ if self._gen_tracing:
+ gent = QAPIGenTrace(basename + '.trace-events')
+
+ self._module[name] = (genc, genh, gent)
self._current_module = name
@contextmanager
self._current_module = old_module
def write(self, output_dir: str, opt_builtins: bool = False) -> None:
- for name, (genc, genh) in self._module.items():
+ for name, (genc, genh, gent) in self._module.items():
if QAPISchemaModule.is_builtin_module(name) and not opt_builtins:
continue
genc.write(output_dir)
genh.write(output_dir)
+ if gent is not None:
+ gent.write(output_dir)
def _begin_builtin_module(self) -> None:
pass
output_dir: str,
prefix: str,
unmask: bool = False,
- builtins: bool = False) -> None:
+ builtins: bool = False,
+ gen_tracing: bool = False) -> None:
"""
Generate C code for the given schema into the target directory.
schema = QAPISchema(schema_file)
gen_types(schema, output_dir, prefix, builtins)
gen_visit(schema, output_dir, prefix, builtins)
- gen_commands(schema, output_dir, prefix)
+ gen_commands(schema, output_dir, prefix, gen_tracing)
gen_events(schema, output_dir, prefix)
gen_introspect(schema, output_dir, prefix, unmask)
parser.add_argument('-u', '--unmask-non-abi-names', action='store_true',
dest='unmask',
help="expose non-ABI names in introspection")
+
+ # Option --suppress-tracing exists so we can avoid solving build system
+ # problems. TODO Drop it when we no longer need it.
+ parser.add_argument('--suppress-tracing', action='store_true',
+ help="suppress adding trace events to qmp marshals")
+
parser.add_argument('schema', action='store')
args = parser.parse_args()
output_dir=args.output_dir,
prefix=args.prefix,
unmask=args.unmask,
- builtins=args.builtins)
+ builtins=args.builtins,
+ gen_tracing=not args.suppress_tracing)
except QAPIError as err:
print(f"{sys.argv[0]}: {str(err)}", file=sys.stderr)
return 1
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import qemu_ga_client
+from qemu.utils import qemu_ga_client
if __name__ == '__main__':
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp import qmp_shell
+from qemu.aqmp import qmp_shell
if __name__ == '__main__':
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom_fuse import QOMFuse
+from qemu.utils.qom_fuse import QOMFuse
if __name__ == '__main__':
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom import QOMGet
+from qemu.utils.qom import QOMGet
if __name__ == '__main__':
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom import QOMList
+from qemu.utils.qom import QOMList
if __name__ == '__main__':
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom import QOMSet
+from qemu.utils.qom import QOMSet
if __name__ == '__main__':
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
-from qemu.qmp.qom import QOMTree
+from qemu.utils.qom import QOMTree
if __name__ == '__main__':
from graphviz import Digraph
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python'))
-from qemu.qmp import (
- QEMUMonitorProtocol,
- QMPResponseError,
-)
+from qemu.aqmp import QMPError
+from qemu.aqmp.legacy import QEMUMonitorProtocol
def perm(arr):
s = 'w' if 'write' in arr else '_'
s += 'r' if 'consistent-read' in arr else '_'
s += 'u' if 'write-unchanged' in arr else '_'
- s += 'g' if 'graph-mod' in arr else '_'
s += 's' if 'resize' in arr else '_'
return s
reply = json.loads(subprocess.check_output(ar))
if 'error' in reply:
- raise QMPResponseError(reply)
+ raise QMPError(reply)
return reply['return']
#
# This work is licensed under the terms of the GNU GPL version 2.
# See the COPYING file in the top-level directory.
+#
+# The script will copy the headers into two target folders:
+#
+# - linux-headers/ for files that are required for compiling for a
+# Linux host. Generally we have these so we can use kernel structs
+# and defines that are more recent than the headers that might be
+# installed on the host system. Usually this script can do simple
+# file copies for these headers.
+#
+# - include/standard-headers/ for files that are used for guest
+# device emulation and are required on all hosts. For instance, we
+# get our definitions of the virtio structures from the Linux
+# kernel headers, but we need those definitions regardless of which
+# host OS we are building for. This script has to be careful to
+# sanitize the headers to remove any use of Linux-specifics such as
+# types like "__u64". This work is done in the cp_portable function.
tmpdir=$(mktemp -d)
linux="$1"
}
ret = fdt_open_into(fdt, fdt, *sizep);
if (ret) {
- error_report("Unable to copy device tree in memory");
+ error_report("%s: Unable to copy device tree into memory: %s",
+ __func__, fdt_strerror(ret));
exit(1);
}
ret = fdt_open_into(fdt, fdt, dt_size);
if (ret) {
- error_report("Unable to copy device tree in memory");
+ error_report("%s: Unable to copy device tree into memory: %s",
+ __func__, fdt_strerror(ret));
goto fail;
}
int qemu_fdt_add_path(void *fdt, const char *path)
{
const char *name;
- const char *p = path;
int namelen, retval;
int parent = 0;
return -1;
}
- while (p) {
- name = p + 1;
- p = strchr(name, '/');
- namelen = p != NULL ? p - name : strlen(name);
+ do {
+ name = path + 1;
+ path = strchr(name, '/');
+ namelen = path != NULL ? path - name : strlen(name);
retval = fdt_subnode_offset_namelen(fdt, parent, name, namelen);
if (retval < 0 && retval != -FDT_ERR_NOTFOUND) {
}
parent = retval;
- }
+ } while (path);
return retval;
}
{
dma_barrier(as, DMA_DIRECTION_FROM_DEVICE);
-#define FILLBUF_SIZE 512
- uint8_t fillbuf[FILLBUF_SIZE];
- int l;
- MemTxResult error = MEMTX_OK;
-
- memset(fillbuf, c, FILLBUF_SIZE);
- while (len > 0) {
- l = len < FILLBUF_SIZE ? len : FILLBUF_SIZE;
- error |= address_space_write(as, addr, attrs, fillbuf, l);
- len -= l;
- addr += l;
- }
-
- return error;
+ return address_space_set(as, addr, c, len, attrs);
}
void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint,
}
-static MemTxResult dma_buf_rw(void *buf, int32_t len, uint64_t *residp,
+static MemTxResult dma_buf_rw(void *buf, dma_addr_t len, dma_addr_t *residual,
QEMUSGList *sg, DMADirection dir,
MemTxAttrs attrs)
{
uint8_t *ptr = buf;
- uint64_t resid;
+ dma_addr_t xresidual;
int sg_cur_index;
MemTxResult res = MEMTX_OK;
- resid = sg->size;
+ xresidual = sg->size;
sg_cur_index = 0;
- len = MIN(len, resid);
+ len = MIN(len, xresidual);
while (len > 0) {
ScatterGatherEntry entry = sg->sg[sg_cur_index++];
- int32_t xfer = MIN(len, entry.len);
+ dma_addr_t xfer = MIN(len, entry.len);
res |= dma_memory_rw(sg->as, entry.base, ptr, xfer, dir, attrs);
ptr += xfer;
len -= xfer;
- resid -= xfer;
+ xresidual -= xfer;
}
- if (residp) {
- *residp = resid;
+ if (residual) {
+ *residual = xresidual;
}
return res;
}
-uint64_t dma_buf_read(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs)
+MemTxResult dma_buf_read(void *ptr, dma_addr_t len, dma_addr_t *residual,
+ QEMUSGList *sg, MemTxAttrs attrs)
{
- uint64_t resid;
-
- dma_buf_rw(ptr, len, &resid, sg, DMA_DIRECTION_FROM_DEVICE, attrs);
-
- return resid;
+ return dma_buf_rw(ptr, len, residual, sg, DMA_DIRECTION_FROM_DEVICE, attrs);
}
-uint64_t dma_buf_write(void *ptr, int32_t len, QEMUSGList *sg, MemTxAttrs attrs)
+MemTxResult dma_buf_write(void *ptr, dma_addr_t len, dma_addr_t *residual,
+ QEMUSGList *sg, MemTxAttrs attrs)
{
- uint64_t resid;
-
- dma_buf_rw(ptr, len, &resid, sg, DMA_DIRECTION_TO_DEVICE, attrs);
-
- return resid;
+ return dma_buf_rw(ptr, len, residual, sg, DMA_DIRECTION_TO_DEVICE, attrs);
}
void dma_acct_start(BlockBackend *blk, BlockAcctCookie *cookie,
unsigned size = memop_size(op);
MemTxResult r;
+ if (mr->alias) {
+ return memory_region_dispatch_read(mr->alias,
+ mr->alias_offset + addr,
+ pval, op, attrs);
+ }
if (!memory_region_access_valid(mr, addr, size, false, attrs)) {
*pval = unassigned_mem_read(mr, addr, size);
return MEMTX_DECODE_ERROR;
{
unsigned size = memop_size(op);
+ if (mr->alias) {
+ return memory_region_dispatch_write(mr->alias,
+ mr->alias_offset + addr,
+ data, op, attrs);
+ }
if (!memory_region_access_valid(mr, addr, size, true, attrs)) {
unassigned_mem_write(mr, addr, data, size);
return MEMTX_DECODE_ERROR;
hwaddr offset,
MemoryRegion *subregion)
{
+ MemoryRegion *alias;
+
assert(!subregion->container);
subregion->container = mr;
+ for (alias = subregion->alias; alias; alias = alias->alias) {
+ alias->mapped_via_alias++;
+ }
subregion->addr = offset;
memory_region_update_container_subregions(subregion);
}
void memory_region_del_subregion(MemoryRegion *mr,
MemoryRegion *subregion)
{
+ MemoryRegion *alias;
+
memory_region_transaction_begin();
assert(subregion->container == mr);
subregion->container = NULL;
+ for (alias = subregion->alias; alias; alias = alias->alias) {
+ alias->mapped_via_alias--;
+ assert(alias->mapped_via_alias >= 0);
+ }
QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link);
memory_region_unref(subregion);
memory_region_update_pending |= mr->enabled && subregion->enabled;
bool memory_region_is_mapped(MemoryRegion *mr)
{
- return mr->container ? true : false;
+ return !!mr->container || mr->mapped_via_alias;
}
/* Same as memory_region_find, but it does not add a reference to the
void memory_global_dirty_log_start(unsigned int flags)
{
+ unsigned int old_flags = global_dirty_tracking;
+
if (vmstate_change) {
qemu_del_vm_change_state_handler(vmstate_change);
vmstate_change = NULL;
assert(flags && !(flags & (~GLOBAL_DIRTY_MASK)));
assert(!(global_dirty_tracking & flags));
global_dirty_tracking |= flags;
-
trace_global_dirty_changed(global_dirty_tracking);
- MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
-
- /* Refresh DIRTY_MEMORY_MIGRATION bit. */
- memory_region_transaction_begin();
- memory_region_update_pending = true;
- memory_region_transaction_commit();
+ if (!old_flags) {
+ MEMORY_LISTENER_CALL_GLOBAL(log_global_start, Forward);
+ memory_region_transaction_begin();
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
+ }
}
static void memory_global_dirty_log_do_stop(unsigned int flags)
trace_global_dirty_changed(global_dirty_tracking);
- /* Refresh DIRTY_MEMORY_MIGRATION bit. */
- memory_region_transaction_begin();
- memory_region_update_pending = true;
- memory_region_transaction_commit();
-
- MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
+ if (!global_dirty_tracking) {
+ memory_region_transaction_begin();
+ memory_region_update_pending = true;
+ memory_region_transaction_commit();
+ MEMORY_LISTENER_CALL_GLOBAL(log_global_stop, Reverse);
+ }
}
static void memory_vm_change_state_handler(void *opaque, bool running,
return true;
}
-void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
+static void mtree_info_flatview(bool dispatch_tree, bool owner)
{
- MemoryRegionListHead ml_head;
- MemoryRegionList *ml, *ml2;
+ struct FlatViewInfo fvi = {
+ .counter = 0,
+ .dispatch_tree = dispatch_tree,
+ .owner = owner,
+ };
AddressSpace *as;
+ FlatView *view;
+ GArray *fv_address_spaces;
+ GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
+ AccelClass *ac = ACCEL_GET_CLASS(current_accel());
- if (flatview) {
- FlatView *view;
- struct FlatViewInfo fvi = {
- .counter = 0,
- .dispatch_tree = dispatch_tree,
- .owner = owner,
- };
- GArray *fv_address_spaces;
- GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
- AccelClass *ac = ACCEL_GET_CLASS(current_accel());
-
- if (ac->has_memory) {
- fvi.ac = ac;
+ if (ac->has_memory) {
+ fvi.ac = ac;
+ }
+
+ /* Gather all FVs in one table */
+ QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
+ view = address_space_get_flatview(as);
+
+ fv_address_spaces = g_hash_table_lookup(views, view);
+ if (!fv_address_spaces) {
+ fv_address_spaces = g_array_new(false, false, sizeof(as));
+ g_hash_table_insert(views, view, fv_address_spaces);
}
- /* Gather all FVs in one table */
- QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- view = address_space_get_flatview(as);
+ g_array_append_val(fv_address_spaces, as);
+ }
- fv_address_spaces = g_hash_table_lookup(views, view);
- if (!fv_address_spaces) {
- fv_address_spaces = g_array_new(false, false, sizeof(as));
- g_hash_table_insert(views, view, fv_address_spaces);
- }
+ /* Print */
+ g_hash_table_foreach(views, mtree_print_flatview, &fvi);
- g_array_append_val(fv_address_spaces, as);
- }
+ /* Free */
+ g_hash_table_foreach_remove(views, mtree_info_flatview_free, 0);
+ g_hash_table_unref(views);
+}
+
+struct AddressSpaceInfo {
+ MemoryRegionListHead *ml_head;
+ bool owner;
+ bool disabled;
+};
- /* Print */
- g_hash_table_foreach(views, mtree_print_flatview, &fvi);
+/* Returns negative value if a < b; zero if a = b; positive value if a > b. */
+static gint address_space_compare_name(gconstpointer a, gconstpointer b)
+{
+ const AddressSpace *as_a = a;
+ const AddressSpace *as_b = b;
- /* Free */
- g_hash_table_foreach_remove(views, mtree_info_flatview_free, 0);
- g_hash_table_unref(views);
+ return g_strcmp0(as_a->name, as_b->name);
+}
- return;
- }
+static void mtree_print_as_name(gpointer data, gpointer user_data)
+{
+ AddressSpace *as = data;
+
+ qemu_printf("address-space: %s\n", as->name);
+}
+
+static void mtree_print_as(gpointer key, gpointer value, gpointer user_data)
+{
+ MemoryRegion *mr = key;
+ GSList *as_same_root_mr_list = value;
+ struct AddressSpaceInfo *asi = user_data;
+
+ g_slist_foreach(as_same_root_mr_list, mtree_print_as_name, NULL);
+ mtree_print_mr(mr, 1, 0, asi->ml_head, asi->owner, asi->disabled);
+ qemu_printf("\n");
+}
+
+static gboolean mtree_info_as_free(gpointer key, gpointer value,
+ gpointer user_data)
+{
+ GSList *as_same_root_mr_list = value;
+
+ g_slist_free(as_same_root_mr_list);
+
+ return true;
+}
+
+static void mtree_info_as(bool dispatch_tree, bool owner, bool disabled)
+{
+ MemoryRegionListHead ml_head;
+ MemoryRegionList *ml, *ml2;
+ AddressSpace *as;
+ GHashTable *views = g_hash_table_new(g_direct_hash, g_direct_equal);
+ GSList *as_same_root_mr_list;
+ struct AddressSpaceInfo asi = {
+ .ml_head = &ml_head,
+ .owner = owner,
+ .disabled = disabled,
+ };
QTAILQ_INIT(&ml_head);
QTAILQ_FOREACH(as, &address_spaces, address_spaces_link) {
- qemu_printf("address-space: %s\n", as->name);
- mtree_print_mr(as->root, 1, 0, &ml_head, owner, disabled);
- qemu_printf("\n");
+ /* Create hashtable, key=AS root MR, value = list of AS */
+ as_same_root_mr_list = g_hash_table_lookup(views, as->root);
+ as_same_root_mr_list = g_slist_insert_sorted(as_same_root_mr_list, as,
+ address_space_compare_name);
+ g_hash_table_insert(views, as->root, as_same_root_mr_list);
}
+ /* print address spaces */
+ g_hash_table_foreach(views, mtree_print_as, &asi);
+ g_hash_table_foreach_remove(views, mtree_info_as_free, 0);
+ g_hash_table_unref(views);
+
/* print aliased regions */
QTAILQ_FOREACH(ml, &ml_head, mrqueue) {
qemu_printf("memory-region: %s\n", memory_region_name(ml->mr));
}
}
+void mtree_info(bool flatview, bool dispatch_tree, bool owner, bool disabled)
+{
+ if (flatview) {
+ mtree_info_flatview(dispatch_tree, owner);
+ } else {
+ mtree_info_as(dispatch_tree, owner, disabled);
+ }
+}
+
void memory_region_init_ram(MemoryRegion *mr,
Object *owner,
const char *name,
}
}
+MemTxResult address_space_set(AddressSpace *as, hwaddr addr,
+ uint8_t c, hwaddr len, MemTxAttrs attrs)
+{
+#define FILLBUF_SIZE 512
+ uint8_t fillbuf[FILLBUF_SIZE];
+ int l;
+ MemTxResult error = MEMTX_OK;
+
+ memset(fillbuf, c, FILLBUF_SIZE);
+ while (len > 0) {
+ l = len < FILLBUF_SIZE ? len : FILLBUF_SIZE;
+ error |= address_space_write(as, addr, attrs, fillbuf, l);
+ len -= l;
+ addr += l;
+ }
+
+ return error;
+}
+
void cpu_physical_memory_rw(hwaddr addr, void *buf,
hwaddr len, bool is_write)
{
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qom/object.h"
#include "sysemu/replay.h"
#include "sysemu/sysemu.h"
+#include "sysemu/rtc.h"
static enum {
RTC_BASE_UTC,
qemu_opts_foreach(qemu_find_opts("device"),
device_init_func, NULL, &error_fatal);
QTAILQ_FOREACH(opt, &device_opts, next) {
+ DeviceState *dev;
loc_push_restore(&opt->loc);
/*
* TODO Eventually we should call qmp_device_add() here to make sure it
* from the start, so call qdev_device_add_from_qdict() directly for
* now.
*/
- qdev_device_add_from_qdict(opt->opts, true, &error_fatal);
+ dev = qdev_device_add_from_qdict(opt->opts, true, &error_fatal);
+ object_unref(OBJECT(dev));
loc_pop(&opt->loc);
}
rom_reset_order_override();
break;
}
case QEMU_OPTION_drive:
- if (drive_def(optarg) == NULL) {
+ opts = qemu_opts_parse_noisily(qemu_find_opts("drive"),
+ optarg, false);
+ if (opts == NULL) {
exit(1);
}
break;
"\n"
#ifdef CONFIG_FUSE
" --export [type=]fuse,id=<id>,node-name=<node-name>,mountpoint=<file>\n"
-" [,growable=on|off][,writable=on|off]\n"
+" [,growable=on|off][,writable=on|off][,allow-other=on|off|auto]\n"
" export the specified block node over FUSE\n"
"\n"
#endif /* CONFIG_FUSE */
+#ifdef CONFIG_VHOST_USER_BLK_SERVER
+" --export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,\n"
+" addr.type=unix,addr.path=<socket-path>[,writable=on|off]\n"
+" [,logical-block-size=<block-size>][,num-queues=<num-queues>]\n"
+" export the specified block node as a\n"
+" vhost-user-blk device over UNIX domain socket\n"
+" --export [type=]vhost-user-blk,id=<id>,node-name=<node-name>,\n"
+" addr.type=fd,addr.str=<fd>[,writable=on|off]\n"
+" [,logical-block-size=<block-size>][,num-queues=<num-queues>]\n"
+" export the specified block node as a\n"
+" vhost-user-blk device over file descriptor\n"
+"\n"
+#endif /* CONFIG_VHOST_USER_BLK_SERVER */
" --monitor [chardev=]name[,mode=control][,pretty[=on|off]]\n"
" configure a QMP monitor\n"
"\n"
stub_ss.add(files('dump.c'))
stub_ss.add(files('error-printf.c'))
stub_ss.add(files('fdset.c'))
-stub_ss.add(files('fw_cfg.c'))
stub_ss.add(files('gdbstub.c'))
stub_ss.add(files('get-vm-name.c'))
if linux_io_uring.found()
stub_ss.add(files('module-opts.c'))
stub_ss.add(files('monitor.c'))
stub_ss.add(files('monitor-core.c'))
-stub_ss.add(files('pci-bus.c'))
stub_ss.add(files('qemu-timer-notify-cb.c'))
stub_ss.add(files('qmp_memory_device.c'))
stub_ss.add(files('qmp-command-available.c'))
stub_ss.add(files('replay-tools.c'))
endif
if have_system
+ stub_ss.add(files('fw_cfg.c'))
+ stub_ss.add(files('pci-bus.c'))
stub_ss.add(files('semihost.c'))
stub_ss.add(files('usb-dev-stub.c'))
stub_ss.add(files('xen-hw-stub.c'))
return;
}
- /*
- * KVM does not support modifications to this feature.
- * We have not registered the cpu properties when KVM
- * is in use, so the user will not be able to set them.
- */
- if (!kvm_enabled()) {
- arm_cpu_pauth_finalize(cpu, &local_err);
- if (local_err != NULL) {
- error_propagate(errp, local_err);
- return;
- }
+ arm_cpu_pauth_finalize(cpu, &local_err);
+ if (local_err != NULL) {
+ error_propagate(errp, local_err);
+ return;
}
}
kvm_arm_set_cpu_features_from_host(cpu);
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
aarch64_add_sve_properties(obj);
+ aarch64_add_pauth_properties(obj);
}
#else
hvf_arm_set_cpu_features_from_host(cpu);
void aarch64_sve_change_el(CPUARMState *env, int old_el,
int new_el, bool el0_a64);
void aarch64_add_sve_properties(Object *obj);
+void aarch64_add_pauth_properties(Object *obj);
/*
* SVE registers are encoded in KVM's memory in an endianness-invariant format.
int arch_val = 0, impdef_val = 0;
uint64_t t;
+ /* Exit early if PAuth is enabled, and fall through to disable it */
+ if (kvm_enabled() && cpu->prop_pauth) {
+ if (!cpu_isar_feature(aa64_pauth, cpu)) {
+ error_setg(errp, "'pauth' feature not supported by KVM on this host");
+ }
+
+ return;
+ }
+
/* TODO: Handle HaveEnhancedPAC, HaveEnhancedPAC2, HaveFPAC. */
if (cpu->prop_pauth) {
if (cpu->prop_pauth_impdef) {
static Property arm_cpu_pauth_impdef_property =
DEFINE_PROP_BOOL("pauth-impdef", ARMCPU, prop_pauth_impdef, false);
+void aarch64_add_pauth_properties(Object *obj)
+{
+ ARMCPU *cpu = ARM_CPU(obj);
+
+ /* Default to PAUTH on, with the architected algorithm on TCG. */
+ qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_property);
+ if (kvm_enabled()) {
+ /*
+ * Mirror PAuth support from the probed sysregs back into the
+ * property for KVM. Is it just a bit backward? Yes it is!
+ */
+ cpu->prop_pauth = cpu_isar_feature(aa64_pauth, cpu);
+ } else {
+ qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property);
+ }
+}
+
/* -cpu max: if KVM is enabled, like -cpu host (best possible with this host);
* otherwise, a CPU with as many features enabled as our emulation supports.
* The version of '-cpu max' for qemu-system-arm is defined in cpu.c;
cpu->dcz_blocksize = 7; /* 512 bytes */
#endif
- /* Default to PAUTH on, with the architected algorithm. */
- qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_property);
- qdev_property_add_static(DEVICE(obj), &arm_cpu_pauth_impdef_property);
-
bitmap_fill(cpu->sve_vq_supported, ARM_MAX_VQ);
}
+ aarch64_add_pauth_properties(obj);
aarch64_add_sve_properties(obj);
object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_max_vq,
cpu_max_set_sve_max_vq, NULL, NULL);
return target_el;
}
-void arm_log_exception(int idx)
+void arm_log_exception(CPUState *cs)
{
+ int idx = cs->exception_index;
+
if (qemu_loglevel_mask(CPU_LOG_INT)) {
const char *exc = NULL;
static const char * const excnames[] = {
if (!exc) {
exc = "unknown";
}
- qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s]\n", idx, exc);
+ qemu_log_mask(CPU_LOG_INT, "Taking exception %d [%s] on CPU %d\n",
+ idx, exc, cs->cpu_index);
}
}
* separately here.
*
* The vector table entry used is always the 0x14 Hyp mode entry point,
- * unless this is an UNDEF/HVC/abort taken from Hyp to Hyp.
+ * unless this is an UNDEF/SVC/HVC/abort taken from Hyp to Hyp.
* The offset applied to the preferred return address is always zero
* (see DDI0487C.a section G1.12.3).
* PSTATE A/I/F masks are set based only on the SCR.EA/IRQ/FIQ values.
addr = 0x04;
break;
case EXCP_SWI:
- addr = 0x14;
+ addr = 0x08;
break;
case EXCP_BKPT:
/* Fall through to prefetch abort. */
assert(!arm_feature(env, ARM_FEATURE_M));
- arm_log_exception(cs->exception_index);
+ arm_log_exception(cs);
qemu_log_mask(CPU_LOG_INT, "...from EL%d to EL%d\n", arm_current_el(env),
new_el);
if (qemu_loglevel_mask(CPU_LOG_INT)
ARMMMUFaultInfo *fi, ARMCacheAttrs *cacheattrs)
__attribute__((nonnull));
-void arm_log_exception(int idx);
+void arm_log_exception(CPUState *cs);
#endif /* !CONFIG_USER_ONLY */
return ioctl(fd, KVM_GET_ONE_REG, &idreg);
}
+static bool kvm_arm_pauth_supported(void)
+{
+ return (kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_ADDRESS) &&
+ kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_GENERIC));
+}
+
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
{
/* Identify the feature bits corresponding to the host CPU, and
*/
struct kvm_vcpu_init init = { .target = -1, };
+ /*
+ * Ask for Pointer Authentication if supported. We can't play the
+ * SVE trick of synthesising the ID reg as KVM won't tell us
+ * whether we have the architected or IMPDEF version of PAuth, so
+ * we have to use the actual ID regs.
+ */
+ if (kvm_arm_pauth_supported()) {
+ init.features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS |
+ 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC);
+ }
+
if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) {
return false;
}
assert(kvm_arm_sve_supported());
cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_SVE;
}
+ if (cpu_isar_feature(aa64_pauth, cpu)) {
+ cpu->kvm_init_features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS |
+ 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC);
+ }
/* Do KVM_ARM_VCPU_INIT ioctl */
ret = kvm_arm_vcpu_init(cs);
uint32_t lr;
bool ignore_stackfaults;
- arm_log_exception(cs->exception_index);
+ arm_log_exception(cs);
/*
* For exceptions we just mark as pending on the NVIC, and let that
SegmentCache idt; /* only base and limit are used */
target_ulong cr[5]; /* NOTE: cr1 is unused */
+
+ bool pdptrs_valid;
+ uint64_t pdptrs[4];
int32_t a20_mask;
BNDReg bnd_regs[4];
static int has_xsave;
static int has_xcrs;
static int has_pit_state2;
+static int has_sregs2;
static int has_exception_payload;
static bool has_msr_mcg_ext_ctl;
has_xsave = kvm_check_extension(s, KVM_CAP_XSAVE);
has_xcrs = kvm_check_extension(s, KVM_CAP_XCRS);
has_pit_state2 = kvm_check_extension(s, KVM_CAP_PIT_STATE2);
+ has_sregs2 = kvm_check_extension(s, KVM_CAP_SREGS2) > 0;
hv_vpindex_settable = kvm_check_extension(s, KVM_CAP_HYPERV_VP_INDEX);
CPUX86State *env = &cpu->env;
struct kvm_sregs sregs;
+ /*
+ * The interrupt_bitmap is ignored because KVM_SET_SREGS is
+ * always followed by KVM_SET_VCPU_EVENTS.
+ */
memset(sregs.interrupt_bitmap, 0, sizeof(sregs.interrupt_bitmap));
- if (env->interrupt_injected >= 0) {
- sregs.interrupt_bitmap[env->interrupt_injected / 64] |=
- (uint64_t)1 << (env->interrupt_injected % 64);
- }
if ((env->eflags & VM_MASK)) {
set_v8086_seg(&sregs.cs, &env->segs[R_CS]);
return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS, &sregs);
}
+static int kvm_put_sregs2(X86CPU *cpu)
+{
+ CPUX86State *env = &cpu->env;
+ struct kvm_sregs2 sregs;
+ int i;
+
+ sregs.flags = 0;
+
+ if ((env->eflags & VM_MASK)) {
+ set_v8086_seg(&sregs.cs, &env->segs[R_CS]);
+ set_v8086_seg(&sregs.ds, &env->segs[R_DS]);
+ set_v8086_seg(&sregs.es, &env->segs[R_ES]);
+ set_v8086_seg(&sregs.fs, &env->segs[R_FS]);
+ set_v8086_seg(&sregs.gs, &env->segs[R_GS]);
+ set_v8086_seg(&sregs.ss, &env->segs[R_SS]);
+ } else {
+ set_seg(&sregs.cs, &env->segs[R_CS]);
+ set_seg(&sregs.ds, &env->segs[R_DS]);
+ set_seg(&sregs.es, &env->segs[R_ES]);
+ set_seg(&sregs.fs, &env->segs[R_FS]);
+ set_seg(&sregs.gs, &env->segs[R_GS]);
+ set_seg(&sregs.ss, &env->segs[R_SS]);
+ }
+
+ set_seg(&sregs.tr, &env->tr);
+ set_seg(&sregs.ldt, &env->ldt);
+
+ sregs.idt.limit = env->idt.limit;
+ sregs.idt.base = env->idt.base;
+ memset(sregs.idt.padding, 0, sizeof sregs.idt.padding);
+ sregs.gdt.limit = env->gdt.limit;
+ sregs.gdt.base = env->gdt.base;
+ memset(sregs.gdt.padding, 0, sizeof sregs.gdt.padding);
+
+ sregs.cr0 = env->cr[0];
+ sregs.cr2 = env->cr[2];
+ sregs.cr3 = env->cr[3];
+ sregs.cr4 = env->cr[4];
+
+ sregs.cr8 = cpu_get_apic_tpr(cpu->apic_state);
+ sregs.apic_base = cpu_get_apic_base(cpu->apic_state);
+
+ sregs.efer = env->efer;
+
+ if (env->pdptrs_valid) {
+ for (i = 0; i < 4; i++) {
+ sregs.pdptrs[i] = env->pdptrs[i];
+ }
+ sregs.flags |= KVM_SREGS2_FLAGS_PDPTRS_VALID;
+ }
+
+ return kvm_vcpu_ioctl(CPU(cpu), KVM_SET_SREGS2, &sregs);
+}
+
+
static void kvm_msr_buf_reset(X86CPU *cpu)
{
memset(cpu->kvm_msr_buf, 0, MSR_BUF_SIZE);
{
CPUX86State *env = &cpu->env;
struct kvm_sregs sregs;
- int bit, i, ret;
+ int ret;
ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS, &sregs);
if (ret < 0) {
return ret;
}
- /* There can only be one pending IRQ set in the bitmap at a time, so try
- to find it and save its number instead (-1 for none). */
- env->interrupt_injected = -1;
- for (i = 0; i < ARRAY_SIZE(sregs.interrupt_bitmap); i++) {
- if (sregs.interrupt_bitmap[i]) {
- bit = ctz64(sregs.interrupt_bitmap[i]);
- env->interrupt_injected = i * 64 + bit;
- break;
- }
+ /*
+ * The interrupt_bitmap is ignored because KVM_GET_SREGS is
+ * always preceded by KVM_GET_VCPU_EVENTS.
+ */
+
+ get_seg(&env->segs[R_CS], &sregs.cs);
+ get_seg(&env->segs[R_DS], &sregs.ds);
+ get_seg(&env->segs[R_ES], &sregs.es);
+ get_seg(&env->segs[R_FS], &sregs.fs);
+ get_seg(&env->segs[R_GS], &sregs.gs);
+ get_seg(&env->segs[R_SS], &sregs.ss);
+
+ get_seg(&env->tr, &sregs.tr);
+ get_seg(&env->ldt, &sregs.ldt);
+
+ env->idt.limit = sregs.idt.limit;
+ env->idt.base = sregs.idt.base;
+ env->gdt.limit = sregs.gdt.limit;
+ env->gdt.base = sregs.gdt.base;
+
+ env->cr[0] = sregs.cr0;
+ env->cr[2] = sregs.cr2;
+ env->cr[3] = sregs.cr3;
+ env->cr[4] = sregs.cr4;
+
+ env->efer = sregs.efer;
+
+ /* changes to apic base and cr8/tpr are read back via kvm_arch_post_run */
+ x86_update_hflags(env);
+
+ return 0;
+}
+
+static int kvm_get_sregs2(X86CPU *cpu)
+{
+ CPUX86State *env = &cpu->env;
+ struct kvm_sregs2 sregs;
+ int i, ret;
+
+ ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_SREGS2, &sregs);
+ if (ret < 0) {
+ return ret;
}
get_seg(&env->segs[R_CS], &sregs.cs);
env->efer = sregs.efer;
+ env->pdptrs_valid = sregs.flags & KVM_SREGS2_FLAGS_PDPTRS_VALID;
+
+ if (env->pdptrs_valid) {
+ for (i = 0; i < 4; i++) {
+ env->pdptrs[i] = sregs.pdptrs[i];
+ }
+ }
+
/* changes to apic base and cr8/tpr are read back via kvm_arch_post_run */
x86_update_hflags(env);
assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
/* must be before kvm_put_nested_state so that EFER.SVME is set */
- ret = kvm_put_sregs(x86_cpu);
+ ret = has_sregs2 ? kvm_put_sregs2(x86_cpu) : kvm_put_sregs(x86_cpu);
if (ret < 0) {
return ret;
}
if (ret < 0) {
goto out;
}
- ret = kvm_get_sregs(cpu);
+ ret = has_sregs2 ? kvm_get_sregs2(cpu) : kvm_get_sregs(cpu);
if (ret < 0) {
goto out;
}
.needed = intel_sgx_msrs_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.msr_ia32_sgxlepubkeyhash, X86CPU, 4),
+ VMSTATE_END_OF_LIST()
+ }
+ };
+
+static bool pdptrs_needed(void *opaque)
+{
+ X86CPU *cpu = opaque;
+ CPUX86State *env = &cpu->env;
+ return env->pdptrs_valid;
+}
+
+static int pdptrs_post_load(void *opaque, int version_id)
+{
+ X86CPU *cpu = opaque;
+ CPUX86State *env = &cpu->env;
+ env->pdptrs_valid = true;
+ return 0;
+}
+
+
+static const VMStateDescription vmstate_pdptrs = {
+ .name = "cpu/pdptrs",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = pdptrs_needed,
+ .post_load = pdptrs_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64_ARRAY(env.pdptrs, X86CPU, 4),
VMSTATE_END_OF_LIST()
}
};
#endif
&vmstate_msr_tsx_ctrl,
&vmstate_msr_intel_sgx,
+ &vmstate_pdptrs,
NULL
}
};
.name = "tlb_entry",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINTTL(mr, OpenRISCTLBEntry),
VMSTATE_UINTTL(tr, OpenRISCTLBEntry),
info->d_machine = PPC_ELF_MACHINE;
info->d_class = ELFCLASS;
- if (ppc_interrupts_little_endian(cpu)) {
+ if (ppc_interrupts_little_endian(cpu, cpu->env.has_hv_mode)) {
info->d_endian = ELFDATA2LSB;
} else {
info->d_endian = ELFDATA2MSB;
"PowerPC 601v1")
POWERPC_DEF("601_v2", CPU_POWERPC_601_v2, 601v,
"PowerPC 601v2")
- POWERPC_DEF("602", CPU_POWERPC_602, 602,
- "PowerPC 602")
POWERPC_DEF("603", CPU_POWERPC_603, 603,
"PowerPC 603")
POWERPC_DEF("603e_v1.1", CPU_POWERPC_603E_v11, 603E,
"PowerPC 7410 v1.3 (G4)")
POWERPC_DEF("7410_v1.4", CPU_POWERPC_7410_v14, 7410,
"PowerPC 7410 v1.4 (G4)")
- POWERPC_DEF("7448_v1.0", CPU_POWERPC_7448_v10, 7400,
+ POWERPC_DEF("7448_v1.0", CPU_POWERPC_7448_v10, 7445,
"PowerPC 7448 v1.0 (G4)")
- POWERPC_DEF("7448_v1.1", CPU_POWERPC_7448_v11, 7400,
+ POWERPC_DEF("7448_v1.1", CPU_POWERPC_7448_v11, 7445,
"PowerPC 7448 v1.1 (G4)")
- POWERPC_DEF("7448_v2.0", CPU_POWERPC_7448_v20, 7400,
+ POWERPC_DEF("7448_v2.0", CPU_POWERPC_7448_v20, 7445,
"PowerPC 7448 v2.0 (G4)")
- POWERPC_DEF("7448_v2.1", CPU_POWERPC_7448_v21, 7400,
+ POWERPC_DEF("7448_v2.1", CPU_POWERPC_7448_v21, 7445,
"PowerPC 7448 v2.1 (G4)")
POWERPC_DEF("7450_v1.0", CPU_POWERPC_7450_v10, 7450,
"PowerPC 7450 v1.0 (G4)")
/* PowerPC CPU aliases */
PowerPCCPUAlias ppc_cpu_aliases[] = {
- { "403", "403gc" },
{ "405", "405d4" },
{ "405cr", "405crc" },
{ "405gp", "405gpd" },
CPU_POWERPC_601_v0 = 0x00010001,
CPU_POWERPC_601_v1 = 0x00010001,
CPU_POWERPC_601_v2 = 0x00010002,
- CPU_POWERPC_602 = 0x00050100,
CPU_POWERPC_603 = 0x00030100,
CPU_POWERPC_603E_v11 = 0x00060101,
CPU_POWERPC_603E_v12 = 0x00060102,
#define MSR_UCLE 26 /* User-mode cache lock enable for BookE */
#define MSR_VR 25 /* altivec available x hflags */
#define MSR_SPE 25 /* SPE enable for BookE x hflags */
-#define MSR_AP 23 /* Access privilege state on 602 hflags */
#define MSR_VSX 23 /* Vector Scalar Extension (ISA 2.06 and later) x hflags */
-#define MSR_SA 22 /* Supervisor access mode on 602 hflags */
#define MSR_S 22 /* Secure state */
#define MSR_KEY 19 /* key bit on 603e */
#define MSR_POW 18 /* Power management */
+#define MSR_WE 18 /* Wait State Enable on 405 */
#define MSR_TGPR 17 /* TGPR usage on 602/603 x */
#define MSR_CE 17 /* Critical interrupt enable on embedded PowerPC x */
#define MSR_ILE 16 /* Interrupt little-endian mode */
#define msr_ucle ((env->msr >> MSR_UCLE) & 1)
#define msr_vr ((env->msr >> MSR_VR) & 1)
#define msr_spe ((env->msr >> MSR_SPE) & 1)
-#define msr_ap ((env->msr >> MSR_AP) & 1)
#define msr_vsx ((env->msr >> MSR_VSX) & 1)
-#define msr_sa ((env->msr >> MSR_SA) & 1)
#define msr_key ((env->msr >> MSR_KEY) & 1)
#define msr_pow ((env->msr >> MSR_POW) & 1)
#define msr_tgpr ((env->msr >> MSR_TGPR) & 1)
int nb_pids; /* Number of available PID registers */
int tlb_type; /* Type of TLB we're dealing with */
ppc_tlb_t tlb; /* TLB is optional. Allocate them only if needed */
- target_ulong pb[4]; /* 403 dedicated access protection registers */
bool tlb_dirty; /* Set to non-zero when modifying TLB */
bool kvm_sw_tlb; /* non-zero if KVM SW TLB API is active */
uint32_t tlb_need_flush; /* Delayed flush needed */
PPC_MFTB = 0x0000000000000200ULL,
/* Fixed-point unit extensions */
- /* PowerPC 602 specific */
- PPC_602_SPEC = 0x0000000000000400ULL,
/* isel instruction */
PPC_ISEL = 0x0000000000000800ULL,
/* popcntb instruction */
#define PPC_TCG_INSNS (PPC_INSNS_BASE | PPC_POWER | PPC_POWER2 \
| PPC_POWER_RTC | PPC_POWER_BR | PPC_64B \
| PPC_64BX | PPC_64H | PPC_WAIT | PPC_MFTB \
- | PPC_602_SPEC | PPC_ISEL | PPC_POPCNTB \
+ | PPC_ISEL | PPC_POPCNTB \
| PPC_STRING | PPC_FLOAT | PPC_FLOAT_EXT \
| PPC_FLOAT_FSQRT | PPC_FLOAT_FRES \
| PPC_FLOAT_FRSQRTE | PPC_FLOAT_FRSQRTES \
return cpu->env.spr_cb[spr].name != NULL;
}
-static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu)
+#if !defined(CONFIG_USER_ONLY)
+static inline bool ppc_interrupts_little_endian(PowerPCCPU *cpu, bool hv)
{
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu);
+ CPUPPCState *env = &cpu->env;
+ bool ile;
+
+ if (hv && env->has_hv_mode) {
+ if (is_isa300(pcc)) {
+ ile = !!(env->spr[SPR_HID0] & HID0_POWER9_HILE);
+ } else {
+ ile = !!(env->spr[SPR_HID0] & HID0_HILE);
+ }
- /*
- * Only models that have an LPCR and know about LPCR_ILE can do little
- * endian.
- */
- if (pcc->lpcr_mask & LPCR_ILE) {
- return !!(cpu->env.spr[SPR_LPCR] & LPCR_ILE);
+ } else if (pcc->lpcr_mask & LPCR_ILE) {
+ ile = !!(env->spr[SPR_LPCR] & LPCR_ILE);
+ } else {
+ ile = !!(msr_ile);
}
- return false;
+ return ile;
}
+#endif
void dump_mmu(CPUPPCState *env);
0x00000000);
}
-/* SPR specific to PowerPC 602 implementation */
-static void register_602_sprs(CPUPPCState *env)
-{
- /* ESA registers */
- /* XXX : not implemented */
- spr_register(env, SPR_SER, "SER",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* XXX : not implemented */
- spr_register(env, SPR_SEBR, "SEBR",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* XXX : not implemented */
- spr_register(env, SPR_ESASRR, "ESASRR",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* Floating point status */
- /* XXX : not implemented */
- spr_register(env, SPR_SP, "SP",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* XXX : not implemented */
- spr_register(env, SPR_LT, "LT",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* Watchdog timer */
- /* XXX : not implemented */
- spr_register(env, SPR_TCR, "TCR",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* Interrupt base */
- spr_register(env, SPR_IBR, "IBR",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* XXX : not implemented */
- spr_register(env, SPR_IABR, "IABR",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
-}
-
/* SPR specific to PowerPC 601 implementation */
static void register_601_sprs(CPUPPCState *env)
{
#endif
}
-static void init_excp_602(CPUPPCState *env)
-{
-#if !defined(CONFIG_USER_ONLY)
- /* XXX: exception prefix has a special behavior on 602 */
- env->excp_vectors[POWERPC_EXCP_RESET] = 0x00000100;
- env->excp_vectors[POWERPC_EXCP_MCHECK] = 0x00000200;
- env->excp_vectors[POWERPC_EXCP_DSI] = 0x00000300;
- env->excp_vectors[POWERPC_EXCP_ISI] = 0x00000400;
- env->excp_vectors[POWERPC_EXCP_EXTERNAL] = 0x00000500;
- env->excp_vectors[POWERPC_EXCP_ALIGN] = 0x00000600;
- env->excp_vectors[POWERPC_EXCP_PROGRAM] = 0x00000700;
- env->excp_vectors[POWERPC_EXCP_FPU] = 0x00000800;
- env->excp_vectors[POWERPC_EXCP_DECR] = 0x00000900;
- env->excp_vectors[POWERPC_EXCP_SYSCALL] = 0x00000C00;
- env->excp_vectors[POWERPC_EXCP_TRACE] = 0x00000D00;
- env->excp_vectors[POWERPC_EXCP_IFTLB] = 0x00001000;
- env->excp_vectors[POWERPC_EXCP_DLTLB] = 0x00001100;
- env->excp_vectors[POWERPC_EXCP_DSTLB] = 0x00001200;
- env->excp_vectors[POWERPC_EXCP_IABR] = 0x00001300;
- env->excp_vectors[POWERPC_EXCP_SMI] = 0x00001400;
- env->excp_vectors[POWERPC_EXCP_WDT] = 0x00001500;
- env->excp_vectors[POWERPC_EXCP_EMUL] = 0x00001600;
- /* Hardware reset vector */
- env->hreset_vector = 0x00000100UL;
-#endif
-}
-
static void init_excp_603(CPUPPCState *env)
{
#if !defined(CONFIG_USER_ONLY)
PPC_MEM_SYNC | PPC_MEM_EIEIO |
PPC_40x_TLB | PPC_MEM_TLBIA | PPC_MEM_TLBSYNC |
PPC_4xx_COMMON | PPC_405_MAC | PPC_40x_EXCP;
- pcc->msr_mask = (1ull << MSR_POW) |
+ pcc->msr_mask = (1ull << MSR_WE) |
(1ull << MSR_CE) |
(1ull << MSR_EE) |
(1ull << MSR_PR) |
(1ull << MSR_FP) |
+ (1ull << MSR_ME) |
(1ull << MSR_DWE) |
(1ull << MSR_DE) |
(1ull << MSR_IR) |
pcc->flags = POWERPC_FLAG_SE | POWERPC_FLAG_RTC_CLK | POWERPC_FLAG_HID0_LE;
}
-static void init_proc_602(CPUPPCState *env)
-{
- register_ne_601_sprs(env);
- register_sdr1_sprs(env);
- register_602_sprs(env);
- /* Time base */
- register_tbl(env);
- /* hardware implementation registers */
- /* XXX : not implemented */
- spr_register(env, SPR_HID0, "HID0",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* XXX : not implemented */
- spr_register(env, SPR_HID1, "HID1",
- SPR_NOACCESS, SPR_NOACCESS,
- &spr_read_generic, &spr_write_generic,
- 0x00000000);
- /* Memory management */
- register_low_BATs(env);
- register_6xx_7xx_soft_tlb(env, 64, 2);
- init_excp_602(env);
- env->dcache_line_size = 32;
- env->icache_line_size = 32;
- /* Allocate hardware IRQ controller */
- ppc6xx_irq_init(env_archcpu(env));
-}
-
-POWERPC_FAMILY(602)(ObjectClass *oc, void *data)
-{
- DeviceClass *dc = DEVICE_CLASS(oc);
- PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
-
- dc->desc = "PowerPC 602";
- pcc->init_proc = init_proc_602;
- pcc->check_pow = check_pow_hid0;
- pcc->insns_flags = PPC_INSNS_BASE | PPC_STRING | PPC_MFTB |
- PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
- PPC_FLOAT_FRSQRTE | PPC_FLOAT_STFIWX |
- PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
- PPC_MEM_SYNC | PPC_MEM_EIEIO |
- PPC_MEM_TLBIE | PPC_6xx_TLB | PPC_MEM_TLBSYNC |
- PPC_SEGMENT | PPC_602_SPEC;
- pcc->msr_mask = (1ull << MSR_VSX) |
- (1ull << MSR_SA) |
- (1ull << MSR_POW) |
- (1ull << MSR_TGPR) |
- (1ull << MSR_ILE) |
- (1ull << MSR_EE) |
- (1ull << MSR_PR) |
- (1ull << MSR_FP) |
- (1ull << MSR_ME) |
- (1ull << MSR_FE0) |
- (1ull << MSR_SE) |
- (1ull << MSR_DE) |
- (1ull << MSR_FE1) |
- (1ull << MSR_EP) |
- (1ull << MSR_IR) |
- (1ull << MSR_DR) |
- (1ull << MSR_RI) |
- (1ull << MSR_LE);
- /* XXX: 602 MMU is quite specific. Should add a special case */
- pcc->mmu_model = POWERPC_MMU_SOFT_6xx;
- pcc->excp_model = POWERPC_EXCP_602;
- pcc->bus_model = PPC_FLAGS_INPUT_6xx;
- pcc->bfd_mach = bfd_mach_ppc_602;
- pcc->flags = POWERPC_FLAG_TGPR | POWERPC_FLAG_SE |
- POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK;
-}
-
static void init_proc_603(CPUPPCState *env)
{
register_ne_601_sprs(env);
PPC_FLOAT | PPC_FLOAT_FSEL | PPC_FLOAT_FRES |
PPC_FLOAT_FSQRT | PPC_FLOAT_FRSQRTE |
PPC_FLOAT_STFIWX |
+ PPC_FLOAT_EXT |
PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ |
PPC_MEM_SYNC | PPC_MEM_EIEIO |
PPC_MEM_TLBIE | PPC_MEM_TLBSYNC |
PPC_64B |
+ PPC_POPCNTB |
PPC_SEGMENT_64B | PPC_SLBI;
pcc->insns_flags2 = PPC2_FP_CVT_S64;
pcc->msr_mask = (1ull << MSR_SF) |
msr = (target_ulong)0;
msr |= (target_ulong)MSR_HVB;
- msr |= (target_ulong)0 << MSR_AP; /* TO BE CHECKED */
- msr |= (target_ulong)0 << MSR_SA; /* TO BE CHECKED */
msr |= (target_ulong)1 << MSR_EP;
#if defined(DO_SINGLE_STEP) && 0
/* Single step trace mode */
#include "exec/cpu_ldst.h"
#endif
-/* #define DEBUG_SOFTWARE_TLB */
-
/*****************************************************************************/
/* Exception processing */
#if !defined(CONFIG_USER_ONLY)
env->nip);
}
+static void ppc_excp_debug_sw_tlb(CPUPPCState *env, int excp)
+{
+ const char *es;
+ target_ulong *miss, *cmp;
+ int en;
+
+ if (!qemu_loglevel_mask(CPU_LOG_MMU)) {
+ return;
+ }
+
+ if (excp == POWERPC_EXCP_IFTLB) {
+ es = "I";
+ en = 'I';
+ miss = &env->spr[SPR_IMISS];
+ cmp = &env->spr[SPR_ICMP];
+ } else {
+ if (excp == POWERPC_EXCP_DLTLB) {
+ es = "DL";
+ } else {
+ es = "DS";
+ }
+ en = 'D';
+ miss = &env->spr[SPR_DMISS];
+ cmp = &env->spr[SPR_DCMP];
+ }
+ qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
+ TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 "
+ TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
+ env->spr[SPR_HASH1], env->spr[SPR_HASH2],
+ env->error_code);
+}
+
+
static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp,
target_ulong *msr)
{
return;
}
- ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
- if (ail == 0) {
- return;
+ ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+ if (ail == 0) {
+ return;
+ }
+ if (ail == 1) {
+ /* AIL=1 is reserved, treat it like AIL=0 */
+ return;
+ }
+
+ } else if (excp_model == POWERPC_EXCP_POWER10) {
+ if (!mmu_all_on && !hv_escalation) {
+ /*
+ * AIL works for HV interrupts even with guest MSR[IR/DR] disabled.
+ * Guest->guest and HV->HV interrupts do require MMU on.
+ */
+ return;
+ }
+
+ if (*new_msr & MSR_HVB) {
+ if (!(env->spr[SPR_LPCR] & LPCR_HAIL)) {
+ /* HV interrupts depend on LPCR[HAIL] */
+ return;
+ }
+ ail = 3; /* HAIL=1 gives AIL=3 behaviour for HV interrupts */
+ } else {
+ ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+ }
+ if (ail == 0) {
+ return;
+ }
+ if (ail == 1 || ail == 2) {
+ /* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */
+ return;
+ }
+ } else {
+ /* Other processors do not support AIL */
+ return;
+ }
+
+ /*
+ * AIL applies, so the new MSR gets IR and DR set, and an offset applied
+ * to the new IP.
+ */
+ *new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
+
+ if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
+ if (ail == 2) {
+ *vector |= 0x0000000000018000ull;
+ } else if (ail == 3) {
+ *vector |= 0xc000000000004000ull;
+ }
+ } else {
+ /*
+ * scv AIL is a little different. AIL=2 does not change the address,
+ * only the MSR. AIL=3 replaces the 0x17000 base with 0xc...3000.
+ */
+ if (ail == 3) {
+ *vector &= ~0x0000000000017000ull; /* Un-apply the base offset */
+ *vector |= 0xc000000000003000ull; /* Apply scv's AIL=3 offset */
+ }
+ }
+#endif
+}
+
+static void powerpc_set_excp_state(PowerPCCPU *cpu,
+ target_ulong vector, target_ulong msr)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+
+ /*
+ * We don't use hreg_store_msr here as already have treated any
+ * special case that could occur. Just store MSR and update hflags
+ *
+ * Note: We *MUST* not use hreg_store_msr() as-is anyway because it
+ * will prevent setting of the HV bit which some exceptions might need
+ * to do.
+ */
+ env->msr = msr & env->msr_mask;
+ hreg_compute_hflags(env);
+ env->nip = vector;
+ /* Reset exception state */
+ cs->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+
+ /* Reset the reservation */
+ env->reserve_addr = -1;
+
+ /*
+ * Any interrupt is context synchronizing, check if TCG TLB needs
+ * a delayed flush on ppc64
+ */
+ check_tlb_flush(env, false);
+}
+
+static void powerpc_excp_40x(PowerPCCPU *cpu, int excp)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+ target_ulong msr, new_msr, vector;
+ int srr0, srr1;
+
+ if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) {
+ cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ }
+
+ qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
+ " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp),
+ excp, env->error_code);
+
+ /* new srr1 value excluding must-be-zero bits */
+ msr = env->msr & ~0x783f0000ULL;
+
+ /*
+ * new interrupt handler msr preserves existing ME unless
+ * explicitly overriden.
+ */
+ new_msr = env->msr & (((target_ulong)1 << MSR_ME));
+
+ /* target registers */
+ srr0 = SPR_SRR0;
+ srr1 = SPR_SRR1;
+
+ /*
+ * Hypervisor emulation assistance interrupt only exists on server
+ * arch 2.05 server or later.
+ */
+ if (excp == POWERPC_EXCP_HV_EMU) {
+ excp = POWERPC_EXCP_PROGRAM;
+ }
+
+ vector = env->excp_vectors[excp];
+ if (vector == (target_ulong)-1ULL) {
+ cpu_abort(cs, "Raised an exception without defined vector %d\n",
+ excp);
+ }
+
+ vector |= env->excp_prefix;
+
+ switch (excp) {
+ case POWERPC_EXCP_CRITICAL: /* Critical input */
+ srr0 = SPR_40x_SRR2;
+ srr1 = SPR_40x_SRR3;
+ break;
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ if (msr_me == 0) {
+ /*
+ * Machine check exception is not enabled. Enter
+ * checkstop state.
+ */
+ fprintf(stderr, "Machine check while not allowed. "
+ "Entering checkstop state\n");
+ if (qemu_log_separate()) {
+ qemu_log("Machine check while not allowed. "
+ "Entering checkstop state\n");
+ }
+ cs->halted = 1;
+ cpu_interrupt_exittb(cs);
+ }
+
+ /* machine check exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+
+ srr0 = SPR_40x_SRR2;
+ srr1 = SPR_40x_SRR3;
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ trace_ppc_excp_dsi(env->spr[SPR_40x_ESR], env->spr[SPR_40x_DEAR]);
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ trace_ppc_excp_isi(msr, env->nip);
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ break;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ break;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_FP:
+ if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
+ trace_ppc_excp_fp_ignore();
+ cs->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+ return;
+ }
+ env->spr[SPR_40x_ESR] = ESR_FP;
+ break;
+ case POWERPC_EXCP_INVAL:
+ trace_ppc_excp_inval(env->nip);
+ env->spr[SPR_40x_ESR] = ESR_PIL;
+ break;
+ case POWERPC_EXCP_PRIV:
+ env->spr[SPR_40x_ESR] = ESR_PPR;
+ break;
+ case POWERPC_EXCP_TRAP:
+ env->spr[SPR_40x_ESR] = ESR_PTR;
+ break;
+ default:
+ cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ env->error_code);
+ break;
+ }
+ break;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ dump_syscall(env);
+
+ /*
+ * We need to correct the NIP which in this case is supposed
+ * to point to the next instruction
+ */
+ env->nip += 4;
+ break;
+ case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
+ trace_ppc_excp_print("FIT");
+ break;
+ case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
+ trace_ppc_excp_print("WDT");
+ break;
+ case POWERPC_EXCP_DTLB: /* Data TLB error */
+ case POWERPC_EXCP_ITLB: /* Instruction TLB error */
+ break;
+ case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
+ trace_ppc_excp_print("PIT");
+ break;
+ case POWERPC_EXCP_DEBUG: /* Debug interrupt */
+ cpu_abort(cs, "%s exception not implemented\n",
+ powerpc_excp_name(excp));
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ break;
+ }
+
+ /* Sanity check */
+ if (!(env->msr_mask & MSR_HVB)) {
+ if (new_msr & MSR_HVB) {
+ cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with "
+ "no HV support\n", excp);
+ }
+ if (srr0 == SPR_HSRR0) {
+ cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with "
+ "no HV support\n", excp);
+ }
+ }
+
+ /* Save PC */
+ env->spr[srr0] = env->nip;
+
+ /* Save MSR */
+ env->spr[srr1] = msr;
+
+ powerpc_set_excp_state(cpu, vector, new_msr);
+}
+
+static void powerpc_excp_74xx(PowerPCCPU *cpu, int excp)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+ target_ulong msr, new_msr, vector;
+
+ if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) {
+ cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ }
+
+ qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
+ " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp),
+ excp, env->error_code);
+
+ /* new srr1 value excluding must-be-zero bits */
+ msr = env->msr & ~0x783f0000ULL;
+
+ /*
+ * new interrupt handler msr preserves existing ME unless
+ * explicitly overriden
+ */
+ new_msr = env->msr & ((target_ulong)1 << MSR_ME);
+
+ /*
+ * Hypervisor emulation assistance interrupt only exists on server
+ * arch 2.05 server or later.
+ */
+ if (excp == POWERPC_EXCP_HV_EMU) {
+ excp = POWERPC_EXCP_PROGRAM;
+ }
+
+ vector = env->excp_vectors[excp];
+ if (vector == (target_ulong)-1ULL) {
+ cpu_abort(cs, "Raised an exception without defined vector %d\n",
+ excp);
+ }
+
+ vector |= env->excp_prefix;
+
+ switch (excp) {
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ if (msr_me == 0) {
+ /*
+ * Machine check exception is not enabled. Enter
+ * checkstop state.
+ */
+ fprintf(stderr, "Machine check while not allowed. "
+ "Entering checkstop state\n");
+ if (qemu_log_separate()) {
+ qemu_log("Machine check while not allowed. "
+ "Entering checkstop state\n");
+ }
+ cs->halted = 1;
+ cpu_interrupt_exittb(cs);
+ }
+
+ /* machine check exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ trace_ppc_excp_isi(msr, env->nip);
+ msr |= env->error_code;
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ break;
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ /* Get rS/rD and rA from faulting opcode */
+ /*
+ * Note: the opcode fields will not be set properly for a
+ * direct store load/store, but nobody cares as nobody
+ * actually uses direct store segments.
+ */
+ env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16;
+ break;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_FP:
+ if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
+ trace_ppc_excp_fp_ignore();
+ cs->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+ return;
+ }
+
+ /*
+ * FP exceptions always have NIP pointing to the faulting
+ * instruction, so always use store_next and claim we are
+ * precise in the MSR.
+ */
+ msr |= 0x00100000;
+ break;
+ case POWERPC_EXCP_INVAL:
+ trace_ppc_excp_inval(env->nip);
+ msr |= 0x00080000;
+ break;
+ case POWERPC_EXCP_PRIV:
+ msr |= 0x00040000;
+ break;
+ case POWERPC_EXCP_TRAP:
+ msr |= 0x00020000;
+ break;
+ default:
+ /* Should never occur */
+ cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ env->error_code);
+ break;
+ }
+ break;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ {
+ int lev = env->error_code;
+
+ if ((lev == 1) && cpu->vhyp) {
+ dump_hcall(env);
+ } else {
+ dump_syscall(env);
+ }
+
+ /*
+ * We need to correct the NIP which in this case is supposed
+ * to point to the next instruction
+ */
+ env->nip += 4;
+
+ /*
+ * The Virtual Open Firmware (VOF) relies on the 'sc 1'
+ * instruction to communicate with QEMU. The pegasos2 machine
+ * uses VOF and the 74xx CPUs, so although the 74xx don't have
+ * HV mode, we need to keep hypercall support.
+ */
+ if ((lev == 1) && cpu->vhyp) {
+ PPCVirtualHypervisorClass *vhc =
+ PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+ vhc->hypercall(cpu->vhyp, cpu);
+ return;
+ }
+
+ break;
+ }
+ case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
+ case POWERPC_EXCP_DECR: /* Decrementer exception */
+ break;
+ case POWERPC_EXCP_RESET: /* System reset exception */
+ if (msr_pow) {
+ cpu_abort(cs, "Trying to deliver power-saving system reset "
+ "exception %d with no HV support\n", excp);
+ }
+ break;
+ case POWERPC_EXCP_TRACE: /* Trace exception */
+ break;
+ case POWERPC_EXCP_VPU: /* Vector unavailable exception */
+ break;
+ case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
+ case POWERPC_EXCP_SMI: /* System management interrupt */
+ case POWERPC_EXCP_THERM: /* Thermal interrupt */
+ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
+ case POWERPC_EXCP_VPUA: /* Vector assist exception */
+ cpu_abort(cs, "%s exception not implemented\n",
+ powerpc_excp_name(excp));
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ break;
+ }
+
+ /* Sanity check */
+ if (!(env->msr_mask & MSR_HVB)) {
+ if (new_msr & MSR_HVB) {
+ cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with "
+ "no HV support\n", excp);
+ }
+ }
+
+ /*
+ * Sort out endianness of interrupt, this differs depending on the
+ * CPU, the HV mode, etc...
+ */
+ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
+ new_msr |= (target_ulong)1 << MSR_LE;
+ }
+
+ /* Save PC */
+ env->spr[SPR_SRR0] = env->nip;
+
+ /* Save MSR */
+ env->spr[SPR_SRR1] = msr;
+
+ powerpc_set_excp_state(cpu, vector, new_msr);
+}
+
+#ifdef TARGET_PPC64
+static void powerpc_excp_books(PowerPCCPU *cpu, int excp)
+{
+ CPUState *cs = CPU(cpu);
+ CPUPPCState *env = &cpu->env;
+ int excp_model = env->excp_model;
+ target_ulong msr, new_msr, vector;
+ int srr0, srr1, lev = -1;
+
+ if (excp <= POWERPC_EXCP_NONE || excp >= POWERPC_EXCP_NB) {
+ cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ }
+
+ qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
+ " => %s (%d) error=%02x\n", env->nip, powerpc_excp_name(excp),
+ excp, env->error_code);
+
+ /* new srr1 value excluding must-be-zero bits */
+ msr = env->msr & ~0x783f0000ULL;
+
+ /*
+ * new interrupt handler msr preserves existing HV and ME unless
+ * explicitly overriden
+ */
+ new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
+
+ /* target registers */
+ srr0 = SPR_SRR0;
+ srr1 = SPR_SRR1;
+
+ /*
+ * check for special resume at 0x100 from doze/nap/sleep/winkle on
+ * P7/P8/P9
+ */
+ if (env->resume_as_sreset) {
+ excp = powerpc_reset_wakeup(cs, env, excp, &msr);
+ }
+
+ /*
+ * We don't want to generate a Hypervisor Emulation Assistance
+ * Interrupt if we don't have HVB in msr_mask (PAPR mode).
+ */
+ if (excp == POWERPC_EXCP_HV_EMU && !(env->msr_mask & MSR_HVB)) {
+ excp = POWERPC_EXCP_PROGRAM;
+ }
+
+ vector = env->excp_vectors[excp];
+ if (vector == (target_ulong)-1ULL) {
+ cpu_abort(cs, "Raised an exception without defined vector %d\n",
+ excp);
+ }
+
+ vector |= env->excp_prefix;
+
+ switch (excp) {
+ case POWERPC_EXCP_MCHECK: /* Machine check exception */
+ if (msr_me == 0) {
+ /*
+ * Machine check exception is not enabled. Enter
+ * checkstop state.
+ */
+ fprintf(stderr, "Machine check while not allowed. "
+ "Entering checkstop state\n");
+ if (qemu_log_separate()) {
+ qemu_log("Machine check while not allowed. "
+ "Entering checkstop state\n");
+ }
+ cs->halted = 1;
+ cpu_interrupt_exittb(cs);
+ }
+ if (env->msr_mask & MSR_HVB) {
+ /*
+ * ISA specifies HV, but can be delivered to guest with HV
+ * clear (e.g., see FWNMI in PAPR).
+ */
+ new_msr |= (target_ulong)MSR_HVB;
+ }
+
+ /* machine check exceptions don't have ME set */
+ new_msr &= ~((target_ulong)1 << MSR_ME);
+
+ break;
+ case POWERPC_EXCP_DSI: /* Data storage exception */
+ trace_ppc_excp_dsi(env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ break;
+ case POWERPC_EXCP_ISI: /* Instruction storage exception */
+ trace_ppc_excp_isi(msr, env->nip);
+ msr |= env->error_code;
+ break;
+ case POWERPC_EXCP_EXTERNAL: /* External input */
+ {
+ bool lpes0;
+
+ /*
+ * LPES0 is only taken into consideration if we support HV
+ * mode for this CPU.
+ */
+ if (!env->has_hv_mode) {
+ break;
+ }
+
+ lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
+
+ if (!lpes0) {
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ }
+
+ break;
+ }
+ case POWERPC_EXCP_ALIGN: /* Alignment exception */
+ /* Get rS/rD and rA from faulting opcode */
+ /*
+ * Note: the opcode fields will not be set properly for a
+ * direct store load/store, but nobody cares as nobody
+ * actually uses direct store segments.
+ */
+ env->spr[SPR_DSISR] |= (env->error_code & 0x03FF0000) >> 16;
+ break;
+ case POWERPC_EXCP_PROGRAM: /* Program exception */
+ switch (env->error_code & ~0xF) {
+ case POWERPC_EXCP_FP:
+ if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
+ trace_ppc_excp_fp_ignore();
+ cs->exception_index = POWERPC_EXCP_NONE;
+ env->error_code = 0;
+ return;
+ }
+
+ /*
+ * FP exceptions always have NIP pointing to the faulting
+ * instruction, so always use store_next and claim we are
+ * precise in the MSR.
+ */
+ msr |= 0x00100000;
+ break;
+ case POWERPC_EXCP_INVAL:
+ trace_ppc_excp_inval(env->nip);
+ msr |= 0x00080000;
+ break;
+ case POWERPC_EXCP_PRIV:
+ msr |= 0x00040000;
+ break;
+ case POWERPC_EXCP_TRAP:
+ msr |= 0x00020000;
+ break;
+ default:
+ /* Should never occur */
+ cpu_abort(cs, "Invalid program exception %d. Aborting\n",
+ env->error_code);
+ break;
+ }
+ break;
+ case POWERPC_EXCP_SYSCALL: /* System call exception */
+ lev = env->error_code;
+
+ if ((lev == 1) && cpu->vhyp) {
+ dump_hcall(env);
+ } else {
+ dump_syscall(env);
}
- if (ail == 1) {
- /* AIL=1 is reserved, treat it like AIL=0 */
+
+ /*
+ * We need to correct the NIP which in this case is supposed
+ * to point to the next instruction
+ */
+ env->nip += 4;
+
+ /* "PAPR mode" built-in hypercall emulation */
+ if ((lev == 1) && cpu->vhyp) {
+ PPCVirtualHypervisorClass *vhc =
+ PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp);
+ vhc->hypercall(cpu->vhyp, cpu);
return;
}
+ if (lev == 1) {
+ new_msr |= (target_ulong)MSR_HVB;
+ }
+ break;
+ case POWERPC_EXCP_SYSCALL_VECTORED: /* scv exception */
+ lev = env->error_code;
+ dump_syscall(env);
+ env->nip += 4;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_EE);
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
- } else if (excp_model == POWERPC_EXCP_POWER10) {
- if (!mmu_all_on && !hv_escalation) {
+ vector += lev * 0x20;
+
+ env->lr = env->nip;
+ env->ctr = msr;
+ break;
+ case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
+ case POWERPC_EXCP_DECR: /* Decrementer exception */
+ break;
+ case POWERPC_EXCP_RESET: /* System reset exception */
+ /* A power-saving exception sets ME, otherwise it is unchanged */
+ if (msr_pow) {
+ /* indicate that we resumed from power save mode */
+ msr |= 0x10000;
+ new_msr |= ((target_ulong)1 << MSR_ME);
+ }
+ if (env->msr_mask & MSR_HVB) {
/*
- * AIL works for HV interrupts even with guest MSR[IR/DR] disabled.
- * Guest->guest and HV->HV interrupts do require MMU on.
+ * ISA specifies HV, but can be delivered to guest with HV
+ * clear (e.g., see FWNMI in PAPR, NMI injection in QEMU).
*/
- return;
- }
-
- if (*new_msr & MSR_HVB) {
- if (!(env->spr[SPR_LPCR] & LPCR_HAIL)) {
- /* HV interrupts depend on LPCR[HAIL] */
- return;
- }
- ail = 3; /* HAIL=1 gives AIL=3 behaviour for HV interrupts */
+ new_msr |= (target_ulong)MSR_HVB;
} else {
- ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
+ if (msr_pow) {
+ cpu_abort(cs, "Trying to deliver power-saving system reset "
+ "exception %d with no HV support\n", excp);
+ }
}
- if (ail == 0) {
- return;
+ break;
+ case POWERPC_EXCP_DSEG: /* Data segment exception */
+ case POWERPC_EXCP_ISEG: /* Instruction segment exception */
+ case POWERPC_EXCP_TRACE: /* Trace exception */
+ break;
+ case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */
+ msr |= env->error_code;
+ /* fall through */
+ case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
+ case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
+ case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */
+ case POWERPC_EXCP_HV_EMU:
+ case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ break;
+ case POWERPC_EXCP_VPU: /* Vector unavailable exception */
+ case POWERPC_EXCP_VSXU: /* VSX unavailable exception */
+ case POWERPC_EXCP_FU: /* Facility unavailable exception */
+ env->spr[SPR_FSCR] |= ((target_ulong)env->error_code << 56);
+ break;
+ case POWERPC_EXCP_HV_FU: /* Hypervisor Facility Unavailable Exception */
+ env->spr[SPR_HFSCR] |= ((target_ulong)env->error_code << FSCR_IC_POS);
+ srr0 = SPR_HSRR0;
+ srr1 = SPR_HSRR1;
+ new_msr |= (target_ulong)MSR_HVB;
+ new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
+ break;
+ case POWERPC_EXCP_THERM: /* Thermal interrupt */
+ case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
+ case POWERPC_EXCP_VPUA: /* Vector assist exception */
+ case POWERPC_EXCP_MAINT: /* Maintenance exception */
+ case POWERPC_EXCP_SDOOR: /* Doorbell interrupt */
+ case POWERPC_EXCP_HV_MAINT: /* Hypervisor Maintenance exception */
+ cpu_abort(cs, "%s exception not implemented\n",
+ powerpc_excp_name(excp));
+ break;
+ default:
+ cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
+ break;
+ }
+
+ /* Sanity check */
+ if (!(env->msr_mask & MSR_HVB)) {
+ if (new_msr & MSR_HVB) {
+ cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with "
+ "no HV support\n", excp);
}
- if (ail == 1 || ail == 2) {
- /* AIL=1 and AIL=2 are reserved, treat them like AIL=0 */
- return;
+ if (srr0 == SPR_HSRR0) {
+ cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with "
+ "no HV support\n", excp);
}
- } else {
- /* Other processors do not support AIL */
- return;
}
/*
- * AIL applies, so the new MSR gets IR and DR set, and an offset applied
- * to the new IP.
+ * Sort out endianness of interrupt, this differs depending on the
+ * CPU, the HV mode, etc...
*/
- *new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
-
- if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
- if (ail == 2) {
- *vector |= 0x0000000000018000ull;
- } else if (ail == 3) {
- *vector |= 0xc000000000004000ull;
- }
- } else {
- /*
- * scv AIL is a little different. AIL=2 does not change the address,
- * only the MSR. AIL=3 replaces the 0x17000 base with 0xc...3000.
- */
- if (ail == 3) {
- *vector &= ~0x0000000000017000ull; /* Un-apply the base offset */
- *vector |= 0xc000000000003000ull; /* Apply scv's AIL=3 offset */
- }
+ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
+ new_msr |= (target_ulong)1 << MSR_LE;
}
-#endif
-}
-static void powerpc_set_excp_state(PowerPCCPU *cpu,
- target_ulong vector, target_ulong msr)
-{
- CPUState *cs = CPU(cpu);
- CPUPPCState *env = &cpu->env;
+ new_msr |= (target_ulong)1 << MSR_SF;
- /*
- * We don't use hreg_store_msr here as already have treated any
- * special case that could occur. Just store MSR and update hflags
- *
- * Note: We *MUST* not use hreg_store_msr() as-is anyway because it
- * will prevent setting of the HV bit which some exceptions might need
- * to do.
- */
- env->msr = msr & env->msr_mask;
- hreg_compute_hflags(env);
- env->nip = vector;
- /* Reset exception state */
- cs->exception_index = POWERPC_EXCP_NONE;
- env->error_code = 0;
+ if (excp != POWERPC_EXCP_SYSCALL_VECTORED) {
+ /* Save PC */
+ env->spr[srr0] = env->nip;
- /* Reset the reservation */
- env->reserve_addr = -1;
+ /* Save MSR */
+ env->spr[srr1] = msr;
+ }
- /*
- * Any interrupt is context synchronizing, check if TCG TLB needs
- * a delayed flush on ppc64
- */
- check_tlb_flush(env, false);
+ /* This can update new_msr and vector if AIL applies */
+ ppc_excp_apply_ail(cpu, excp_model, excp, msr, &new_msr, &vector);
+
+ powerpc_set_excp_state(cpu, vector, new_msr);
}
+#else
+static inline void powerpc_excp_books(PowerPCCPU *cpu, int excp)
+{
+ g_assert_not_reached();
+}
+#endif
/*
* Note that this function should be greatly optimized when called
* with a constant excp, from ppc_hw_interrupt
*/
-static void powerpc_excp(PowerPCCPU *cpu, int excp)
+static inline void powerpc_excp_legacy(PowerPCCPU *cpu, int excp)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable/VPU */
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
break;
- case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
- /* XXX: TODO */
- cpu_abort(cs, "Embedded floating point data exception "
- "is not implemented yet !\n");
- env->spr[SPR_BOOKE_ESR] = ESR_SPV;
- break;
- case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
- /* XXX: TODO */
- cpu_abort(cs, "Embedded floating point round exception "
- "is not implemented yet !\n");
- env->spr[SPR_BOOKE_ESR] = ESR_SPV;
- break;
- case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
- /* XXX: TODO */
- cpu_abort(cs,
- "Performance counter exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
break;
case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
trace_ppc_excp_print("PIT");
break;
- case POWERPC_EXCP_IO: /* IO error exception */
- /* XXX: TODO */
- cpu_abort(cs, "601 IO error exception is not implemented yet !\n");
- break;
- case POWERPC_EXCP_RUNM: /* Run mode exception */
- /* XXX: TODO */
- cpu_abort(cs, "601 run mode exception is not implemented yet !\n");
- break;
- case POWERPC_EXCP_EMUL: /* Emulation trap exception */
- /* XXX: TODO */
- cpu_abort(cs, "602 emulation trap exception "
- "is not implemented yet !\n");
- break;
case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
switch (excp_model) {
- case POWERPC_EXCP_602:
case POWERPC_EXCP_603:
case POWERPC_EXCP_G2:
/* Swap temporary saved registers with GPRs */
}
/* fall through */
case POWERPC_EXCP_7x5:
-#if defined(DEBUG_SOFTWARE_TLB)
- if (qemu_log_enabled()) {
- const char *es;
- target_ulong *miss, *cmp;
- int en;
-
- if (excp == POWERPC_EXCP_IFTLB) {
- es = "I";
- en = 'I';
- miss = &env->spr[SPR_IMISS];
- cmp = &env->spr[SPR_ICMP];
- } else {
- if (excp == POWERPC_EXCP_DLTLB) {
- es = "DL";
- } else {
- es = "DS";
- }
- en = 'D';
- miss = &env->spr[SPR_DMISS];
- cmp = &env->spr[SPR_DCMP];
- }
- qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
- TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 "
- TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
- env->spr[SPR_HASH1], env->spr[SPR_HASH2],
- env->error_code);
- }
-#endif
+ ppc_excp_debug_sw_tlb(env, excp);
+
msr |= env->crf[0] << 28;
msr |= env->error_code; /* key, D/I, S/L bits */
/* Set way using a LRU mechanism */
break;
}
break;
+ case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
+ case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
+ case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
+ case POWERPC_EXCP_IO: /* IO error exception */
+ case POWERPC_EXCP_RUNM: /* Run mode exception */
+ case POWERPC_EXCP_EMUL: /* Emulation trap exception */
case POWERPC_EXCP_FPA: /* Floating-point assist exception */
- /* XXX: TODO */
- cpu_abort(cs, "Floating point assist exception "
- "is not implemented yet !\n");
- break;
case POWERPC_EXCP_DABR: /* Data address breakpoint */
- /* XXX: TODO */
- cpu_abort(cs, "DABR exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
- /* XXX: TODO */
- cpu_abort(cs, "IABR exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_SMI: /* System management interrupt */
- /* XXX: TODO */
- cpu_abort(cs, "SMI exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_THERM: /* Thermal interrupt */
- /* XXX: TODO */
- cpu_abort(cs, "Thermal management exception "
- "is not implemented yet !\n");
- break;
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
- /* XXX: TODO */
- cpu_abort(cs,
- "Performance counter exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_VPUA: /* Vector assist exception */
- /* XXX: TODO */
- cpu_abort(cs, "VPU assist exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_SOFTP: /* Soft patch exception */
- /* XXX: TODO */
- cpu_abort(cs,
- "970 soft-patch exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_MAINT: /* Maintenance exception */
- /* XXX: TODO */
- cpu_abort(cs,
- "970 maintenance exception is not implemented yet !\n");
- break;
case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */
- /* XXX: TODO */
- cpu_abort(cs, "Maskable external exception "
- "is not implemented yet !\n");
- break;
case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */
- /* XXX: TODO */
- cpu_abort(cs, "Non maskable external exception "
- "is not implemented yet !\n");
+ cpu_abort(cs, "%s exception not implemented\n",
+ powerpc_excp_name(excp));
break;
default:
excp_invalid:
* Sort out endianness of interrupt, this differs depending on the
* CPU, the HV mode, etc...
*/
-#ifdef TARGET_PPC64
- if (excp_model == POWERPC_EXCP_POWER7) {
- if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) {
- new_msr |= (target_ulong)1 << MSR_LE;
- }
- } else if (excp_model == POWERPC_EXCP_POWER8) {
- if (new_msr & MSR_HVB) {
- if (env->spr[SPR_HID0] & HID0_HILE) {
- new_msr |= (target_ulong)1 << MSR_LE;
- }
- } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
- new_msr |= (target_ulong)1 << MSR_LE;
- }
- } else if (excp_model == POWERPC_EXCP_POWER9 ||
- excp_model == POWERPC_EXCP_POWER10) {
- if (new_msr & MSR_HVB) {
- if (env->spr[SPR_HID0] & HID0_POWER9_HILE) {
- new_msr |= (target_ulong)1 << MSR_LE;
- }
- } else if (env->spr[SPR_LPCR] & LPCR_ILE) {
- new_msr |= (target_ulong)1 << MSR_LE;
- }
- } else if (msr_ile) {
- new_msr |= (target_ulong)1 << MSR_LE;
- }
-#else
- if (msr_ile) {
+ if (ppc_interrupts_little_endian(cpu, !!(new_msr & MSR_HVB))) {
new_msr |= (target_ulong)1 << MSR_LE;
}
-#endif
#if defined(TARGET_PPC64)
if (excp_model == POWERPC_EXCP_BOOKE) {
powerpc_set_excp_state(cpu, vector, new_msr);
}
+static void powerpc_excp(PowerPCCPU *cpu, int excp)
+{
+ CPUPPCState *env = &cpu->env;
+
+ switch (env->excp_model) {
+ case POWERPC_EXCP_40x:
+ powerpc_excp_40x(cpu, excp);
+ break;
+ case POWERPC_EXCP_74xx:
+ powerpc_excp_74xx(cpu, excp);
+ break;
+ case POWERPC_EXCP_970:
+ case POWERPC_EXCP_POWER7:
+ case POWERPC_EXCP_POWER8:
+ case POWERPC_EXCP_POWER9:
+ case POWERPC_EXCP_POWER10:
+ powerpc_excp_books(cpu, excp);
+ break;
+ default:
+ powerpc_excp_legacy(cpu, excp);
+ }
+}
+
void ppc_cpu_do_interrupt(CPUState *cs)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
*/
msr = (1ULL << MSR_ME);
msr |= env->msr & (1ULL << MSR_SF);
- if (ppc_interrupts_little_endian(cpu)) {
+ if (ppc_interrupts_little_endian(cpu, false)) {
msr |= (1ULL << MSR_LE);
}
(env->spr[SPR_PSSCR] & PSSCR_EC);
}
#endif /* defined(TARGET_PPC64) */
-#endif /* CONFIG_TCG */
static void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
{
/* MSR:POW cannot be set by any form of rfi */
msr &= ~(1ULL << MSR_POW);
+ /* MSR:TGPR cannot be set by any form of rfi */
+ if (env->flags & POWERPC_FLAG_TGPR)
+ msr &= ~(1ULL << MSR_TGPR);
+
#if defined(TARGET_PPC64)
/* Switching to 32-bit ? Crop the nip */
if (!msr_is_64bit(env, msr)) {
check_tlb_flush(env, false);
}
-#ifdef CONFIG_TCG
void helper_rfi(CPUPPCState *env)
{
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful);
DEF_HELPER_FLAGS_2(load_sr, TCG_CALL_NO_RWG, tl, env, tl)
DEF_HELPER_FLAGS_3(store_sr, TCG_CALL_NO_RWG, void, env, tl, tl)
-DEF_HELPER_FLAGS_1(602_mfrom, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_1(msgsnd, void, tl)
DEF_HELPER_2(msgclr, void, env, tl)
DEF_HELPER_1(book3s_msgsnd, void, tl)
DEF_HELPER_FLAGS_2(store_vtb, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(store_tbu40, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_2(store_hid0_601, void, env, tl)
-DEF_HELPER_3(store_403_pbr, void, env, i32, tl)
DEF_HELPER_FLAGS_1(load_40x_pit, TCG_CALL_NO_RWG, tl, env)
DEF_HELPER_FLAGS_2(store_40x_pit, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(store_40x_tcr, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(store_40x_tsr, TCG_CALL_NO_RWG, void, env, tl)
+DEF_HELPER_2(store_40x_pid, void, env, tl)
DEF_HELPER_2(store_40x_dbcr0, void, env, tl)
DEF_HELPER_2(store_40x_sler, void, env, tl)
DEF_HELPER_FLAGS_2(store_booke_tcr, TCG_CALL_NO_RWG, void, env, tl)
*/
unsigned immu_idx, dmmu_idx;
dmmu_idx = msr & (1 << MSR_PR) ? 0 : 1;
- if (env->mmu_model & POWERPC_MMU_BOOKE) {
+ if (env->mmu_model == POWERPC_MMU_BOOKE ||
+ env->mmu_model == POWERPC_MMU_BOOKE206) {
dmmu_idx |= msr & (1 << MSR_GS) ? 4 : 0;
immu_idx = dmmu_idx;
immu_idx |= msr & (1 << MSR_IS) ? 2 : 0;
void cpu_interrupt_exittb(CPUState *cs)
{
- if (!kvm_enabled()) {
+ /*
+ * We don't need to worry about translation blocks
+ * when running with KVM.
+ */
+ if (kvm_enabled()) {
return;
}
((value >> MSR_DR) & 1) != msr_dr) {
cpu_interrupt_exittb(cs);
}
- if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
+ if ((env->mmu_model == POWERPC_MMU_BOOKE ||
+ env->mmu_model == POWERPC_MMU_BOOKE206) &&
((value >> MSR_GS) & 1) != msr_gs) {
cpu_interrupt_exittb(cs);
}
}
}
-/*****************************************************************************/
-/* 602 specific instructions */
-/* mfrom is the most crazy instruction ever seen, imho ! */
-/* Real implementation uses a ROM table. Do the same */
-/*
- * Extremely decomposed:
- * -arg / 256
- * return 256 * log10(10 + 1.0) + 0.5
- */
-#if !defined(CONFIG_USER_ONLY)
-target_ulong helper_602_mfrom(target_ulong arg)
-{
- if (likely(arg < 602)) {
-#include "mfrom_table.c.inc"
- return mfrom_ROM_table[arg];
- } else {
- return 0;
- }
-}
-#endif
-
/*****************************************************************************/
/* Altivec extension helpers */
#if defined(HOST_WORDS_BIGENDIAN)
pmu_update_summaries(env);
}
-static int cpu_load_old(QEMUFile *f, void *opaque, int version_id)
-{
- PowerPCCPU *cpu = opaque;
- CPUPPCState *env = &cpu->env;
- unsigned int i, j;
- target_ulong sdr1;
- uint32_t fpscr, vscr;
-#if defined(TARGET_PPC64)
- int32_t slb_nr;
-#endif
- target_ulong xer;
-
- for (i = 0; i < 32; i++) {
- qemu_get_betls(f, &env->gpr[i]);
- }
-#if !defined(TARGET_PPC64)
- for (i = 0; i < 32; i++) {
- qemu_get_betls(f, &env->gprh[i]);
- }
-#endif
- qemu_get_betls(f, &env->lr);
- qemu_get_betls(f, &env->ctr);
- for (i = 0; i < 8; i++) {
- qemu_get_be32s(f, &env->crf[i]);
- }
- qemu_get_betls(f, &xer);
- cpu_write_xer(env, xer);
- qemu_get_betls(f, &env->reserve_addr);
- qemu_get_betls(f, &env->msr);
- for (i = 0; i < 4; i++) {
- qemu_get_betls(f, &env->tgpr[i]);
- }
- for (i = 0; i < 32; i++) {
- union {
- float64 d;
- uint64_t l;
- } u;
- u.l = qemu_get_be64(f);
- *cpu_fpr_ptr(env, i) = u.d;
- }
- qemu_get_be32s(f, &fpscr);
- env->fpscr = fpscr;
- qemu_get_sbe32s(f, &env->access_type);
-#if defined(TARGET_PPC64)
- qemu_get_betls(f, &env->spr[SPR_ASR]);
- qemu_get_sbe32s(f, &slb_nr);
-#endif
- qemu_get_betls(f, &sdr1);
- for (i = 0; i < 32; i++) {
- qemu_get_betls(f, &env->sr[i]);
- }
- for (i = 0; i < 2; i++) {
- for (j = 0; j < 8; j++) {
- qemu_get_betls(f, &env->DBAT[i][j]);
- }
- }
- for (i = 0; i < 2; i++) {
- for (j = 0; j < 8; j++) {
- qemu_get_betls(f, &env->IBAT[i][j]);
- }
- }
- qemu_get_sbe32s(f, &env->nb_tlb);
- qemu_get_sbe32s(f, &env->tlb_per_way);
- qemu_get_sbe32s(f, &env->nb_ways);
- qemu_get_sbe32s(f, &env->last_way);
- qemu_get_sbe32s(f, &env->id_tlbs);
- qemu_get_sbe32s(f, &env->nb_pids);
- if (env->tlb.tlb6) {
- /* XXX assumes 6xx */
- for (i = 0; i < env->nb_tlb; i++) {
- qemu_get_betls(f, &env->tlb.tlb6[i].pte0);
- qemu_get_betls(f, &env->tlb.tlb6[i].pte1);
- qemu_get_betls(f, &env->tlb.tlb6[i].EPN);
- }
- }
- for (i = 0; i < 4; i++) {
- qemu_get_betls(f, &env->pb[i]);
- }
- for (i = 0; i < 1024; i++) {
- qemu_get_betls(f, &env->spr[i]);
- }
- if (!cpu->vhyp) {
- ppc_store_sdr1(env, sdr1);
- }
- qemu_get_be32s(f, &vscr);
- ppc_store_vscr(env, vscr);
- qemu_get_be64s(f, &env->spe_acc);
- qemu_get_be32s(f, &env->spe_fscr);
- qemu_get_betls(f, &env->msr_mask);
- qemu_get_be32s(f, &env->flags);
- qemu_get_sbe32s(f, &env->error_code);
- qemu_get_be32s(f, &env->pending_interrupts);
- qemu_get_be32s(f, &env->irq_input_state);
- for (i = 0; i < POWERPC_EXCP_NB; i++) {
- qemu_get_betls(f, &env->excp_vectors[i]);
- }
- qemu_get_betls(f, &env->excp_prefix);
- qemu_get_betls(f, &env->ivor_mask);
- qemu_get_betls(f, &env->ivpr_mask);
- qemu_get_betls(f, &env->hreset_vector);
- qemu_get_betls(f, &env->nip);
- qemu_get_sbetl(f); /* Discard unused hflags */
- qemu_get_sbetl(f); /* Discard unused hflags_nmsr */
- qemu_get_sbe32(f); /* Discard unused mmu_idx */
- qemu_get_sbe32(f); /* Discard unused power_mode */
-
- post_load_update_msr(env);
-
- return 0;
-}
-
static int get_avr(QEMUFile *f, void *pv, size_t size,
const VMStateField *field)
{
.name = "cpu/tm",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.needed = tm_needed,
.fields = (VMStateField []) {
VMSTATE_UINTTL_ARRAY(env.tm_gpr, PowerPCCPU, 32),
return env->nb_tlb && (env->tlb_type == TLB_EMB);
}
-static bool pbr403_needed(void *opaque)
-{
- PowerPCCPU *cpu = opaque;
- uint32_t pvr = cpu->env.spr[SPR_PVR];
-
- return (pvr & 0xffff0000) == 0x00200000;
-}
-
-static const VMStateDescription vmstate_pbr403 = {
- .name = "cpu/pbr403",
- .version_id = 1,
- .minimum_version_id = 1,
- .needed = pbr403_needed,
- .fields = (VMStateField[]) {
- VMSTATE_UINTTL_ARRAY(env.pb, PowerPCCPU, 4),
- VMSTATE_END_OF_LIST()
- },
-};
-
static const VMStateDescription vmstate_tlbemb = {
.name = "cpu/tlb6xx",
.version_id = 1,
env.nb_tlb,
vmstate_tlbemb_entry,
ppcemb_tlb_t),
- /* 403 protection registers */
VMSTATE_END_OF_LIST()
},
- .subsections = (const VMStateDescription*[]) {
- &vmstate_pbr403,
- NULL
- }
};
static const VMStateDescription vmstate_tlbmas_entry = {
.name = "cpu",
.version_id = 5,
.minimum_version_id = 5,
- .minimum_version_id_old = 4,
- .load_state_old = cpu_load_old,
.pre_save = cpu_pre_save,
.post_load = cpu_post_load,
.fields = (VMStateField[]) {
+++ /dev/null
-static const uint8_t mfrom_ROM_table[602] = {
- 77, 77, 76, 76, 75, 75, 74, 74,
- 73, 73, 72, 72, 71, 71, 70, 70,
- 69, 69, 68, 68, 68, 67, 67, 66,
- 66, 65, 65, 64, 64, 64, 63, 63,
- 62, 62, 61, 61, 61, 60, 60, 59,
- 59, 58, 58, 58, 57, 57, 56, 56,
- 56, 55, 55, 54, 54, 54, 53, 53,
- 53, 52, 52, 51, 51, 51, 50, 50,
- 50, 49, 49, 49, 48, 48, 47, 47,
- 47, 46, 46, 46, 45, 45, 45, 44,
- 44, 44, 43, 43, 43, 42, 42, 42,
- 42, 41, 41, 41, 40, 40, 40, 39,
- 39, 39, 39, 38, 38, 38, 37, 37,
- 37, 37, 36, 36, 36, 35, 35, 35,
- 35, 34, 34, 34, 34, 33, 33, 33,
- 33, 32, 32, 32, 32, 31, 31, 31,
- 31, 30, 30, 30, 30, 29, 29, 29,
- 29, 28, 28, 28, 28, 28, 27, 27,
- 27, 27, 26, 26, 26, 26, 26, 25,
- 25, 25, 25, 25, 24, 24, 24, 24,
- 24, 23, 23, 23, 23, 23, 23, 22,
- 22, 22, 22, 22, 21, 21, 21, 21,
- 21, 21, 20, 20, 20, 20, 20, 20,
- 19, 19, 19, 19, 19, 19, 19, 18,
- 18, 18, 18, 18, 18, 17, 17, 17,
- 17, 17, 17, 17, 16, 16, 16, 16,
- 16, 16, 16, 16, 15, 15, 15, 15,
- 15, 15, 15, 15, 14, 14, 14, 14,
- 14, 14, 14, 14, 13, 13, 13, 13,
- 13, 13, 13, 13, 13, 12, 12, 12,
- 12, 12, 12, 12, 12, 12, 12, 11,
- 11, 11, 11, 11, 11, 11, 11, 11,
- 11, 11, 10, 10, 10, 10, 10, 10,
- 10, 10, 10, 10, 10, 9, 9, 9,
- 9, 9, 9, 9, 9, 9, 9, 9,
- 9, 9, 8, 8, 8, 8, 8, 8,
- 8, 8, 8, 8, 8, 8, 8, 8,
- 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 7, 7, 7, 7, 7, 7, 7,
- 7, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 6, 6, 6, 6,
- 6, 6, 6, 6, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 5, 5, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 3, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 0,
-};
+++ /dev/null
-#define _GNU_SOURCE
-#include "qemu/osdep.h"
-#include <math.h>
-
-int main(void)
-{
- double d;
- uint8_t n;
- int i;
-
- printf("static const uint8_t mfrom_ROM_table[602] =\n{\n ");
- for (i = 0; i < 602; i++) {
- /*
- * Extremely decomposed:
- * -T0 / 256
- * T0 = 256 * log10(10 + 1.0) + 0.5
- */
- d = -i;
- d /= 256.0;
- d = exp10(d);
- d += 1.0;
- d = log10(d);
- d *= 256;
- d += 0.5;
- n = d;
- printf("%3d, ", n);
- if ((i & 7) == 7) {
- printf("\n ");
- }
- }
- printf("\n};\n");
-
- return 0;
-}
}
}
-void helper_store_403_pbr(CPUPPCState *env, uint32_t num, target_ulong value)
-{
- if (likely(env->pb[num] != value)) {
- env->pb[num] = value;
- /* Should be optimized */
- tlb_flush(env_cpu(env));
- }
-}
-
void helper_store_40x_dbcr0(CPUPPCState *env, target_ulong val)
{
/* Bits 26 & 27 affect single-stepping. */
case -2:
/* Access rights violation */
cs->exception_index = POWERPC_EXCP_ISI;
- env->error_code = 0x08000000;
+ if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+ (env->mmu_model == POWERPC_MMU_BOOKE206)) {
+ env->error_code = 0;
+ } else {
+ env->error_code = 0x08000000;
+ }
break;
case -3:
/* No execute protection violation */
if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
(env->mmu_model == POWERPC_MMU_BOOKE206)) {
env->spr[SPR_BOOKE_ESR] = 0x00000000;
+ env->error_code = 0;
+ } else {
+ env->error_code = 0x10000000;
}
cs->exception_index = POWERPC_EXCP_ISI;
- env->error_code = 0x10000000;
break;
case -4:
/* Direct store exception */
/* No code fetch is allowed in direct-store areas */
cs->exception_index = POWERPC_EXCP_ISI;
- env->error_code = 0x10000000;
+ if ((env->mmu_model == POWERPC_MMU_BOOKE) ||
+ (env->mmu_model == POWERPC_MMU_BOOKE206)) {
+ env->error_code = 0;
+ } else {
+ env->error_code = 0x10000000;
+ }
break;
}
} else {
#define PPC4XX_TLBLO_ATTR_MASK 0x000000FF
#define PPC4XX_TLBLO_RPN_MASK 0xFFFFFC00
+void helper_store_40x_pid(CPUPPCState *env, target_ulong val)
+{
+ if (env->spr[SPR_40x_PID] != val) {
+ env->spr[SPR_40x_PID] = val;
+ env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH;
+ }
+}
+
target_ulong helper_4xx_tlbre_hi(CPUPPCState *env, target_ulong entry)
{
ppcemb_tlb_t *tlb;
size = PPC4XX_TLBHI_SIZE_DEFAULT;
}
ret |= size << PPC4XX_TLBHI_SIZE_SHIFT;
- env->spr[SPR_40x_PID] = tlb->PID;
+ helper_store_40x_pid(env, tlb->PID);
return ret;
}
tlb->prot & PAGE_WRITE ? 'w' : '-',
tlb->prot & PAGE_EXEC ? 'x' : '-',
tlb->prot & PAGE_VALID ? 'v' : '-', (int)tlb->PID);
+
+ env->tlb_need_flush |= TLB_NEED_LOCAL_FLUSH;
}
target_ulong helper_4xx_tlbsx(CPUPPCState *env, target_ulong address)
{
TCGv t0 = tcg_temp_new();
tcg_gen_andi_tl(t0, cpu_gpr[gprn], 0xFF);
- gen_store_spr(SPR_40x_PID, t0);
+ gen_helper_store_40x_pid(cpu_env, t0);
tcg_temp_free(t0);
}
}
#endif
-/* PowerPC 403 specific registers */
-/* PBL1 / PBU1 / PBL2 / PBU2 */
+/* PIR */
#if !defined(CONFIG_USER_ONLY)
-void spr_read_403_pbr(DisasContext *ctx, int gprn, int sprn)
-{
- tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env,
- offsetof(CPUPPCState, pb[sprn - SPR_403_PBL1]));
-}
-
-void spr_write_403_pbr(DisasContext *ctx, int sprn, int gprn)
-{
- TCGv_i32 t0 = tcg_const_i32(sprn - SPR_403_PBL1);
- gen_helper_store_403_pbr(cpu_env, t0, cpu_gpr[gprn]);
- tcg_temp_free_i32(t0);
-}
-
void spr_write_pir(DisasContext *ctx, int sprn, int gprn)
{
TCGv t0 = tcg_temp_new();
}
}
-/* PowerPC 602 specific instructions */
-
-/* dsa */
-static void gen_dsa(DisasContext *ctx)
-{
- /* XXX: TODO */
- gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
-}
-
-/* esa */
-static void gen_esa(DisasContext *ctx)
-{
- /* XXX: TODO */
- gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
-}
-
-/* mfrom */
-static void gen_mfrom(DisasContext *ctx)
-{
-#if defined(CONFIG_USER_ONLY)
- GEN_PRIV;
-#else
- CHK_SV;
- gen_helper_602_mfrom(cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)]);
-#endif /* defined(CONFIG_USER_ONLY) */
-}
-
/* 602 - 603 - G2 TLB management */
/* tlbld */
GEN_HANDLER(srliq, 0x1F, 0x18, 0x17, 0x00000000, PPC_POWER_BR),
GEN_HANDLER(srlq, 0x1F, 0x18, 0x16, 0x00000000, PPC_POWER_BR),
GEN_HANDLER(srq, 0x1F, 0x18, 0x14, 0x00000000, PPC_POWER_BR),
-GEN_HANDLER(dsa, 0x1F, 0x14, 0x13, 0x03FFF801, PPC_602_SPEC),
-GEN_HANDLER(esa, 0x1F, 0x14, 0x12, 0x03FFF801, PPC_602_SPEC),
-GEN_HANDLER(mfrom, 0x1F, 0x09, 0x08, 0x03E0F801, PPC_602_SPEC),
GEN_HANDLER2(tlbld_6xx, "tlbld", 0x1F, 0x12, 0x1E, 0x03FF0001, PPC_6xx_TLB),
GEN_HANDLER2(tlbli_6xx, "tlbli", 0x1F, 0x12, 0x1F, 0x03FF0001, PPC_6xx_TLB),
GEN_HANDLER(clf, 0x1F, 0x16, 0x03, 0x03E00000, PPC_POWER),
#include "hw/qdev-properties.h"
#include "migration/vmstate.h"
#include "fpu/softfloat-helpers.h"
+#include "sysemu/kvm.h"
+#include "kvm_riscv.h"
/* RISC-V CPU definitions */
}
#endif
+#if defined(CONFIG_KVM)
+static void riscv_host_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+#if defined(TARGET_RISCV32)
+ set_misa(env, MXL_RV32, 0);
+#elif defined(TARGET_RISCV64)
+ set_misa(env, MXL_RV64, 0);
+#endif
+}
+#endif
+
static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model)
{
ObjectClass *oc;
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
- env->pc = value;
+
+ if (env->xl == MXL_RV32) {
+ env->pc = (int32_t)value;
+ } else {
+ env->pc = value;
+ }
}
static void riscv_cpu_synchronize_from_tb(CPUState *cs,
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
- env->pc = tb->pc;
+ RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
+
+ if (xl == MXL_RV32) {
+ env->pc = (int32_t)tb->pc;
+ } else {
+ env->pc = tb->pc;
+ }
}
static bool riscv_cpu_has_work(CPUState *cs)
void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb,
target_ulong *data)
{
- env->pc = data[0];
+ RISCVMXL xl = FIELD_EX32(tb->flags, TB_FLAGS, XL);
+ if (xl == MXL_RV32) {
+ env->pc = (int32_t)data[0];
+ } else {
+ env->pc = data[0];
+ }
}
static void riscv_cpu_reset(DeviceState *dev)
*/
env->mstatus = set_field(env->mstatus, MSTATUS64_SXL, env->misa_mxl);
env->mstatus = set_field(env->mstatus, MSTATUS64_UXL, env->misa_mxl);
+ if (riscv_has_ext(env, RVH)) {
+ env->vsstatus = set_field(env->vsstatus,
+ MSTATUS64_SXL, env->misa_mxl);
+ env->vsstatus = set_field(env->vsstatus,
+ MSTATUS64_UXL, env->misa_mxl);
+ env->mstatus_hs = set_field(env->mstatus_hs,
+ MSTATUS64_SXL, env->misa_mxl);
+ env->mstatus_hs = set_field(env->mstatus_hs,
+ MSTATUS64_UXL, env->misa_mxl);
+ }
}
env->mcause = 0;
env->pc = env->resetvec;
/* mmte is supposed to have pm.current hardwired to 1 */
env->mmte |= (PM_EXT_INITIAL | MMTE_M_PM_CURRENT);
#endif
+ env->xl = riscv_cpu_mxl(env);
+ riscv_cpu_update_mask(env);
cs->exception_index = RISCV_EXCP_NONE;
env->load_res = -1;
set_default_nan_mode(1, &env->fp_status);
+
+#ifndef CONFIG_USER_ONLY
+ if (kvm_enabled()) {
+ kvm_riscv_reset_vcpu(cpu);
+ }
+#endif
}
static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
RISCVCPU *cpu = RISCV_CPU(dev);
CPURISCVState *env = &cpu->env;
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
+ CPUClass *cc = CPU_CLASS(mcc);
int priv_version = 0;
Error *local_err = NULL;
switch (env->misa_mxl_max) {
#ifdef TARGET_RISCV64
case MXL_RV64:
+ cc->gdb_core_xml_file = "riscv-64bit-cpu.xml";
break;
case MXL_RV128:
break;
#endif
case MXL_RV32:
+ cc->gdb_core_xml_file = "riscv-32bit-cpu.xml";
break;
default:
g_assert_not_reached();
}
set_vext_version(env, vext_version);
}
+ if ((cpu->cfg.ext_zve32f || cpu->cfg.ext_zve64f) && !cpu->cfg.ext_f) {
+ error_setg(errp, "Zve32f/Zve64f extension depends upon RVF.");
+ return;
+ }
if (cpu->cfg.ext_j) {
ext |= RVJ;
}
case IRQ_S_EXT:
case IRQ_VS_EXT:
case IRQ_M_EXT:
- riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level));
+ if (kvm_enabled()) {
+ kvm_riscv_set_irq(cpu, irq, level);
+ } else {
+ riscv_cpu_update_mip(cpu, 1 << irq, BOOL_TO_MASK(level));
+ }
break;
default:
g_assert_not_reached();
DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true),
DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false),
DEFINE_PROP_BOOL("Zfhmin", RISCVCPU, cfg.ext_zfhmin, false),
+ DEFINE_PROP_BOOL("Zve32f", RISCVCPU, cfg.ext_zve32f, false),
+ DEFINE_PROP_BOOL("Zve64f", RISCVCPU, cfg.ext_zve64f, false),
DEFINE_PROP_BOOL("mmu", RISCVCPU, cfg.mmu, true),
DEFINE_PROP_BOOL("pmp", RISCVCPU, cfg.pmp, true),
cc->gdb_read_register = riscv_cpu_gdb_read_register;
cc->gdb_write_register = riscv_cpu_gdb_write_register;
cc->gdb_num_core_regs = 33;
-#if defined(TARGET_RISCV32)
- cc->gdb_core_xml_file = "riscv-32bit-cpu.xml";
-#elif defined(TARGET_RISCV64)
- cc->gdb_core_xml_file = "riscv-64bit-cpu.xml";
-#endif
cc->gdb_stop_before_watchpoint = true;
cc->disas_set_info = riscv_cpu_disas_set_info;
#ifndef CONFIG_USER_ONLY
.class_init = riscv_cpu_class_init,
},
DEFINE_CPU(TYPE_RISCV_CPU_ANY, riscv_any_cpu_init),
+#if defined(CONFIG_KVM)
+ DEFINE_CPU(TYPE_RISCV_CPU_HOST, riscv_host_cpu_init),
+#endif
#if defined(TARGET_RISCV32)
DEFINE_CPU(TYPE_RISCV_CPU_BASE32, rv32_base_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_IBEX, rv32_ibex_cpu_init),
#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51")
#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34")
#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54")
+#define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host")
#if defined(TARGET_RISCV32)
# define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE32
FIELD(VTYPE, VMA, 7, 1)
FIELD(VTYPE, VEDIV, 8, 2)
FIELD(VTYPE, RESERVED, 10, sizeof(target_ulong) * 8 - 11)
-FIELD(VTYPE, VILL, sizeof(target_ulong) * 8 - 1, 1)
struct CPURISCVState {
target_ulong gpr[32];
target_ulong vl;
target_ulong vstart;
target_ulong vtype;
+ bool vill;
target_ulong pc;
target_ulong load_res;
uint32_t misa_mxl_max; /* max mxl for this cpu */
uint32_t misa_ext; /* current extensions */
uint32_t misa_ext_mask; /* max ext for this cpu */
+ uint32_t xl; /* current xlen */
/* 128-bit helpers upper part return value */
target_ulong retxh;
target_ulong upmmask;
target_ulong upmbase;
#endif
+ target_ulong cur_pmmask;
+ target_ulong cur_pmbase;
float_status fp_status;
/* Fields from here on are preserved across CPU reset. */
QEMUTimer *timer; /* Internal timer */
+
+ hwaddr kernel_addr;
+ hwaddr fdt_addr;
+
+ /* kvm timer */
+ bool kvm_timer_dirty;
+ uint64_t kvm_timer_time;
+ uint64_t kvm_timer_compare;
+ uint64_t kvm_timer_state;
+ uint64_t kvm_timer_frequency;
};
OBJECT_DECLARE_TYPE(RISCVCPU, RISCVCPUClass,
bool ext_icsr;
bool ext_zfh;
bool ext_zfhmin;
+ bool ext_zve32f;
+ bool ext_zve64f;
char *priv_spec;
char *user_spec;
/* The combination of MXL/SXL/UXL that applies to the current cpu mode. */
FIELD(TB_FLAGS, XL, 20, 2)
/* If PointerMasking should be applied */
-FIELD(TB_FLAGS, PM_ENABLED, 22, 1)
+FIELD(TB_FLAGS, PM_MASK_ENABLED, 22, 1)
+FIELD(TB_FLAGS, PM_BASE_ENABLED, 23, 1)
#ifdef TARGET_RISCV32
#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32)
}
#endif
+#if defined(TARGET_RISCV32)
+#define cpu_recompute_xl(env) ((void)(env), MXL_RV32)
+#else
+static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
+{
+ RISCVMXL xl = env->misa_mxl;
+#if !defined(CONFIG_USER_ONLY)
+ /*
+ * When emulating a 32-bit-only cpu, use RV32.
+ * When emulating a 64-bit cpu, and MXL has been reduced to RV32,
+ * MSTATUSH doesn't have UXL/SXL, therefore XLEN cannot be widened
+ * back to RV64 for lower privs.
+ */
+ if (xl != MXL_RV32) {
+ switch (env->priv) {
+ case PRV_M:
+ break;
+ case PRV_U:
+ xl = get_field(env->mstatus, MSTATUS64_UXL);
+ break;
+ default: /* PRV_S | PRV_H */
+ xl = get_field(env->mstatus, MSTATUS64_SXL);
+ break;
+ }
+ }
+#endif
+ return xl;
+}
+#endif
+
+static inline int riscv_cpu_xlen(CPURISCVState *env)
+{
+ return 16 << env->xl;
+}
+
/*
* Encode LMUL to lmul as follows:
* LMUL vlmul lmul
void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *pflags);
+void riscv_cpu_update_mask(CPURISCVState *env);
+
RISCVException riscv_csrrw(CPURISCVState *env, int csrno,
target_ulong *ret_value,
target_ulong new_value, target_ulong write_mask);
#define COUNTEREN_IR (1 << 2)
#define COUNTEREN_HPM3 (1 << 3)
+/* vsstatus CSR bits */
+#define VSSTATUS64_UXL 0x0000000300000000ULL
+
/* Privilege modes */
#define PRV_U 0
#define PRV_S 1
#endif
}
-static RISCVMXL cpu_get_xl(CPURISCVState *env)
-{
-#if defined(TARGET_RISCV32)
- return MXL_RV32;
-#elif defined(CONFIG_USER_ONLY)
- return MXL_RV64;
-#else
- RISCVMXL xl = riscv_cpu_mxl(env);
-
- /*
- * When emulating a 32-bit-only cpu, use RV32.
- * When emulating a 64-bit cpu, and MXL has been reduced to RV32,
- * MSTATUSH doesn't have UXL/SXL, therefore XLEN cannot be widened
- * back to RV64 for lower privs.
- */
- if (xl != MXL_RV32) {
- switch (env->priv) {
- case PRV_M:
- break;
- case PRV_U:
- xl = get_field(env->mstatus, MSTATUS64_UXL);
- break;
- default: /* PRV_S | PRV_H */
- xl = get_field(env->mstatus, MSTATUS64_SXL);
- break;
- }
- }
- return xl;
-#endif
-}
-
void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *pflags)
{
+ CPUState *cs = env_cpu(env);
+ RISCVCPU *cpu = RISCV_CPU(cs);
+
uint32_t flags = 0;
- *pc = env->pc;
+ *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc;
*cs_base = 0;
- if (riscv_has_ext(env, RVV)) {
+ if (riscv_has_ext(env, RVV) || cpu->cfg.ext_zve32f || cpu->cfg.ext_zve64f) {
/*
* If env->vl equals to VLMAX, we can use generic vector operation
* expanders (GVEC) to accerlate the vector operations.
uint32_t maxsz = vlmax << sew;
bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl) &&
(maxsz >= 8);
- flags = FIELD_DP32(flags, TB_FLAGS, VILL,
- FIELD_EX64(env->vtype, VTYPE, VILL));
+ flags = FIELD_DP32(flags, TB_FLAGS, VILL, env->vill);
flags = FIELD_DP32(flags, TB_FLAGS, SEW, sew);
flags = FIELD_DP32(flags, TB_FLAGS, LMUL,
FIELD_EX64(env->vtype, VTYPE, VLMUL));
flags = FIELD_DP32(flags, TB_FLAGS, MSTATUS_HS_VS,
get_field(env->mstatus_hs, MSTATUS_VS));
}
+#endif
+
+ flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl);
+ if (env->cur_pmmask < (env->xl == MXL_RV32 ? UINT32_MAX : UINT64_MAX)) {
+ flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1);
+ }
+ if (env->cur_pmbase != 0) {
+ flags = FIELD_DP32(flags, TB_FLAGS, PM_BASE_ENABLED, 1);
+ }
+
+ *pflags = flags;
+}
+
+void riscv_cpu_update_mask(CPURISCVState *env)
+{
+ target_ulong mask = -1, base = 0;
+ /*
+ * TODO: Current RVJ spec does not specify
+ * how the extension interacts with XLEN.
+ */
+#ifndef CONFIG_USER_ONLY
if (riscv_has_ext(env, RVJ)) {
- int priv = flags & TB_FLAGS_PRIV_MMU_MASK;
- bool pm_enabled = false;
- switch (priv) {
- case PRV_U:
- pm_enabled = env->mmte & U_PM_ENABLE;
+ switch (env->priv) {
+ case PRV_M:
+ if (env->mmte & M_PM_ENABLE) {
+ mask = env->mpmmask;
+ base = env->mpmbase;
+ }
break;
case PRV_S:
- pm_enabled = env->mmte & S_PM_ENABLE;
+ if (env->mmte & S_PM_ENABLE) {
+ mask = env->spmmask;
+ base = env->spmbase;
+ }
break;
- case PRV_M:
- pm_enabled = env->mmte & M_PM_ENABLE;
+ case PRV_U:
+ if (env->mmte & U_PM_ENABLE) {
+ mask = env->upmmask;
+ base = env->upmbase;
+ }
break;
default:
g_assert_not_reached();
}
- flags = FIELD_DP32(flags, TB_FLAGS, PM_ENABLED, pm_enabled);
}
#endif
-
- flags = FIELD_DP32(flags, TB_FLAGS, XL, cpu_get_xl(env));
-
- *pflags = flags;
+ if (env->xl == MXL_RV32) {
+ env->cur_pmmask = mask & UINT32_MAX;
+ env->cur_pmbase = base & UINT32_MAX;
+ } else {
+ env->cur_pmmask = mask;
+ env->cur_pmbase = base;
+ }
}
#ifndef CONFIG_USER_ONLY
}
/* tlb_flush is unnecessary as mode is contained in mmu_idx */
env->priv = newpriv;
+ env->xl = cpu_recompute_xl(env);
+ riscv_cpu_update_mask(env);
/*
* Clear the load reservation - otherwise a reservation placed in one
static RISCVException vs(CPURISCVState *env, int csrno)
{
- if (env->misa_ext & RVV) {
+ CPUState *cs = env_cpu(env);
+ RISCVCPU *cpu = RISCV_CPU(cs);
+
+ if (env->misa_ext & RVV ||
+ cpu->cfg.ext_zve32f || cpu->cfg.ext_zve64f) {
#if !defined(CONFIG_USER_ONLY)
if (!env->debugger && !riscv_cpu_vector_enabled(env)) {
return RISCV_EXCP_ILLEGAL_INST;
static RISCVException read_vtype(CPURISCVState *env, int csrno,
target_ulong *val)
{
- *val = env->vtype;
+ uint64_t vill;
+ switch (env->xl) {
+ case MXL_RV32:
+ vill = (uint32_t)env->vill << 31;
+ break;
+ case MXL_RV64:
+ vill = (uint64_t)env->vill << 63;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ *val = (target_ulong)vill | env->vtype;
return RISCV_EXCP_NONE;
}
(1ULL << (RISCV_EXCP_STORE_GUEST_AMO_ACCESS_FAULT)));
static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE |
SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS |
- SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS | (target_ulong)SSTATUS64_UXL;
+ SSTATUS_SUM | SSTATUS_MXR | SSTATUS_VS;
static const target_ulong sip_writable_mask = SIP_SSIP | MIP_USIP | MIP_UEIP;
static const target_ulong hip_writable_mask = MIP_VSSIP;
static const target_ulong hvip_writable_mask = MIP_VSSIP | MIP_VSTIP | MIP_VSEIP;
{
uint64_t mstatus = env->mstatus;
uint64_t mask = 0;
+ RISCVMXL xl = riscv_cpu_mxl(env);
/* flush tlb on mstatus fields that affect VM */
if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | MSTATUS_MPV |
MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR |
MSTATUS_TW | MSTATUS_VS;
- if (riscv_cpu_mxl(env) != MXL_RV32) {
+ if (xl != MXL_RV32 || env->debugger) {
/*
* RV32: MPV and GVA are not in mstatus. The current plan is to
* add them to mstatush. For now, we just don't support it.
*/
mask |= MSTATUS_MPV | MSTATUS_GVA;
+ if ((val & MSTATUS64_UXL) != 0) {
+ mask |= MSTATUS64_UXL;
+ }
}
mstatus = (mstatus & ~mask) | (val & mask);
- RISCVMXL xl = riscv_cpu_mxl(env);
if (xl > MXL_RV32) {
- /* SXL and UXL fields are for now read only */
+ /* SXL field is for now read only */
mstatus = set_field(mstatus, MSTATUS64_SXL, xl);
- mstatus = set_field(mstatus, MSTATUS64_UXL, xl);
}
env->mstatus = mstatus;
+ env->xl = cpu_recompute_xl(env);
return RISCV_EXCP_NONE;
}
/* flush translation cache */
tb_flush(env_cpu(env));
env->misa_ext = val;
+ env->xl = riscv_cpu_mxl(env);
return RISCV_EXCP_NONE;
}
{
uint64_t mask = sstatus_v1_10_mask;
uint64_t sstatus = env->mstatus & mask;
+ if (env->xl != MXL_RV32 || env->debugger) {
+ mask |= SSTATUS64_UXL;
+ }
*val = int128_make128(sstatus, add_status_sd(MXL_RV128, sstatus));
return RISCV_EXCP_NONE;
target_ulong *val)
{
target_ulong mask = (sstatus_v1_10_mask);
-
+ if (env->xl != MXL_RV32 || env->debugger) {
+ mask |= SSTATUS64_UXL;
+ }
/* TODO: Use SXL not MXL. */
*val = add_status_sd(riscv_cpu_mxl(env), env->mstatus & mask);
return RISCV_EXCP_NONE;
target_ulong val)
{
target_ulong mask = (sstatus_v1_10_mask);
+
+ if (env->xl != MXL_RV32 || env->debugger) {
+ if ((val & SSTATUS64_UXL) != 0) {
+ mask |= SSTATUS64_UXL;
+ }
+ }
target_ulong newval = (env->mstatus & ~mask) | (val & mask);
return write_mstatus(env, CSR_MSTATUS, newval);
}
target_ulong val)
{
uint64_t mask = (target_ulong)-1;
+ if ((val & VSSTATUS64_UXL) == 0) {
+ mask &= ~VSSTATUS64_UXL;
+ }
env->vsstatus = (env->vsstatus & ~mask) | (uint64_t)val;
return RISCV_EXCP_NONE;
}
return RISCV_EXCP_NONE;
}
+static bool check_pmp_reg_index(CPURISCVState *env, uint32_t reg_index)
+{
+ /* TODO: RV128 restriction check */
+ if ((reg_index & 1) && (riscv_cpu_mxl(env) == MXL_RV64)) {
+ return false;
+ }
+ return true;
+}
+
static RISCVException read_pmpcfg(CPURISCVState *env, int csrno,
target_ulong *val)
{
+ uint32_t reg_index = csrno - CSR_PMPCFG0;
+
+ if (!check_pmp_reg_index(env, reg_index)) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
*val = pmpcfg_csr_read(env, csrno - CSR_PMPCFG0);
return RISCV_EXCP_NONE;
}
static RISCVException write_pmpcfg(CPURISCVState *env, int csrno,
target_ulong val)
{
+ uint32_t reg_index = csrno - CSR_PMPCFG0;
+
+ if (!check_pmp_reg_index(env, reg_index)) {
+ return RISCV_EXCP_ILLEGAL_INST;
+ }
pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val);
return RISCV_EXCP_NONE;
}
int csr_priv = get_field(csrno, 0x300);
int pm_current;
+ if (env->debugger) {
+ return false;
+ }
/*
* If priv lvls differ that means we're accessing csr from higher priv lvl,
* so allow the access
/* hardwiring pm.instruction bit to 0, since it's not supported yet */
wpri_val &= ~(MMTE_M_PM_INSN | MMTE_S_PM_INSN | MMTE_U_PM_INSN);
env->mmte = wpri_val | PM_EXT_DIRTY;
+ riscv_cpu_update_mask(env);
/* Set XS and SD bits, since PM CSRs are dirty */
mstatus = env->mstatus | MSTATUS_XS;
uint64_t mstatus;
env->mpmmask = val;
+ if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) {
+ env->cur_pmmask = val;
+ }
env->mmte |= PM_EXT_DIRTY;
/* Set XS and SD bits, since PM CSRs are dirty */
return RISCV_EXCP_NONE;
}
env->spmmask = val;
+ if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) {
+ env->cur_pmmask = val;
+ }
env->mmte |= PM_EXT_DIRTY;
/* Set XS and SD bits, since PM CSRs are dirty */
return RISCV_EXCP_NONE;
}
env->upmmask = val;
+ if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) {
+ env->cur_pmmask = val;
+ }
env->mmte |= PM_EXT_DIRTY;
/* Set XS and SD bits, since PM CSRs are dirty */
uint64_t mstatus;
env->mpmbase = val;
+ if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) {
+ env->cur_pmbase = val;
+ }
env->mmte |= PM_EXT_DIRTY;
/* Set XS and SD bits, since PM CSRs are dirty */
return RISCV_EXCP_NONE;
}
env->spmbase = val;
+ if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) {
+ env->cur_pmbase = val;
+ }
env->mmte |= PM_EXT_DIRTY;
/* Set XS and SD bits, since PM CSRs are dirty */
return RISCV_EXCP_NONE;
}
env->upmbase = val;
+ if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) {
+ env->cur_pmbase = val;
+ }
env->mmte |= PM_EXT_DIRTY;
/* Set XS and SD bits, since PM CSRs are dirty */
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
+ target_ulong tmp;
if (n < 32) {
- return gdb_get_regl(mem_buf, env->gpr[n]);
+ tmp = env->gpr[n];
} else if (n == 32) {
- return gdb_get_regl(mem_buf, env->pc);
+ tmp = env->pc;
+ } else {
+ return 0;
+ }
+
+ switch (env->misa_mxl_max) {
+ case MXL_RV32:
+ return gdb_get_reg32(mem_buf, tmp);
+ case MXL_RV64:
+ return gdb_get_reg64(mem_buf, tmp);
+ default:
+ g_assert_not_reached();
}
return 0;
}
{
RISCVCPU *cpu = RISCV_CPU(cs);
CPURISCVState *env = &cpu->env;
-
- if (n == 0) {
- /* discard writes to x0 */
- return sizeof(target_ulong);
- } else if (n < 32) {
- env->gpr[n] = ldtul_p(mem_buf);
- return sizeof(target_ulong);
+ int length = 0;
+ target_ulong tmp;
+
+ switch (env->misa_mxl_max) {
+ case MXL_RV32:
+ tmp = (int32_t)ldl_p(mem_buf);
+ length = 4;
+ break;
+ case MXL_RV64:
+ if (env->xl < MXL_RV64) {
+ tmp = (int32_t)ldq_p(mem_buf);
+ } else {
+ tmp = ldq_p(mem_buf);
+ }
+ length = 8;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ if (n > 0 && n < 32) {
+ env->gpr[n] = tmp;
} else if (n == 32) {
- env->pc = ldtul_p(mem_buf);
- return sizeof(target_ulong);
+ env->pc = tmp;
}
- return 0;
+
+ return length;
}
static int riscv_gdb_get_fpu(CPURISCVState *env, GByteArray *buf, int n)
cs->gdb_num_regs),
"riscv-vector.xml", 0);
}
-#if defined(TARGET_RISCV32)
- gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual,
- 1, "riscv-32bit-virtual.xml", 0);
-#elif defined(TARGET_RISCV64)
- gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual,
- 1, "riscv-64bit-virtual.xml", 0);
-#endif
+ switch (env->misa_mxl_max) {
+ case MXL_RV32:
+ gdb_register_coprocessor(cs, riscv_gdb_get_virtual,
+ riscv_gdb_set_virtual,
+ 1, "riscv-32bit-virtual.xml", 0);
+ break;
+ case MXL_RV64:
+ gdb_register_coprocessor(cs, riscv_gdb_get_virtual,
+ riscv_gdb_set_virtual,
+ 1, "riscv-64bit-virtual.xml", 0);
+ break;
+ default:
+ g_assert_not_reached();
+ }
gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr,
riscv_gen_dynamic_csr_xml(cs, cs->gdb_num_regs),
DEF_HELPER_4(csrw_i128, void, env, int, tl, tl)
DEF_HELPER_6(csrrw_i128, tl, env, int, tl, tl, tl, tl)
#ifndef CONFIG_USER_ONLY
-DEF_HELPER_2(sret, tl, env, tl)
-DEF_HELPER_2(mret, tl, env, tl)
+DEF_HELPER_1(sret, tl, env)
+DEF_HELPER_1(mret, tl, env)
DEF_HELPER_1(wfi, void, env)
DEF_HELPER_1(tlb_flush, void, env)
#endif
static bool trans_sret(DisasContext *ctx, arg_sret *a)
{
#ifndef CONFIG_USER_ONLY
- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
-
if (has_ext(ctx, RVS)) {
- gen_helper_sret(cpu_pc, cpu_env, cpu_pc);
+ gen_helper_sret(cpu_pc, cpu_env);
tcg_gen_exit_tb(NULL, 0); /* no chaining */
ctx->base.is_jmp = DISAS_NORETURN;
} else {
static bool trans_mret(DisasContext *ctx, arg_mret *a)
{
#ifndef CONFIG_USER_ONLY
- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
- gen_helper_mret(cpu_pc, cpu_env, cpu_pc);
+ gen_helper_mret(cpu_pc, cpu_env);
tcg_gen_exit_tb(NULL, 0); /* no chaining */
ctx->base.is_jmp = DISAS_NORETURN;
return true;
static bool trans_wfi(DisasContext *ctx, arg_wfi *a)
{
#ifndef CONFIG_USER_ONLY
- tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn);
+ gen_set_pc_imm(ctx, ctx->pc_succ_insn);
gen_helper_wfi(cpu_env);
return true;
#else
static bool gen_lr(DisasContext *ctx, arg_atomic *a, MemOp mop)
{
- TCGv src1 = get_gpr(ctx, a->rs1, EXT_ZERO);
+ TCGv src1 = get_address(ctx, a->rs1, 0);
if (a->rl) {
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL);
}
- src1 = gen_pm_adjust_address(ctx, src1);
tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop);
if (a->aq) {
tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ);
TCGLabel *l1 = gen_new_label();
TCGLabel *l2 = gen_new_label();
- src1 = get_gpr(ctx, a->rs1, EXT_ZERO);
- src1 = gen_pm_adjust_address(ctx, src1);
+ src1 = get_address(ctx, a->rs1, 0);
tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1);
/*
MemOp mop)
{
TCGv dest = dest_gpr(ctx, a->rd);
- TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE);
+ TCGv src1 = get_address(ctx, a->rs1, 0);
TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE);
- src1 = gen_pm_adjust_address(ctx, src1);
func(dest, src1, src2, ctx->mem_idx, mop);
gen_set_gpr(ctx, a->rd, dest);
REQUIRE_FPU;
REQUIRE_EXT(ctx, RVD);
- addr = get_gpr(ctx, a->rs1, EXT_NONE);
- if (a->imm) {
- TCGv temp = temp_new(ctx);
- tcg_gen_addi_tl(temp, addr, a->imm);
- addr = temp;
- }
- addr = gen_pm_adjust_address(ctx, addr);
-
+ addr = get_address(ctx, a->rs1, a->imm);
tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], addr, ctx->mem_idx, MO_TEUQ);
mark_fs_dirty(ctx);
REQUIRE_FPU;
REQUIRE_EXT(ctx, RVD);
- addr = get_gpr(ctx, a->rs1, EXT_NONE);
- if (a->imm) {
- TCGv temp = temp_new(ctx);
- tcg_gen_addi_tl(temp, addr, a->imm);
- addr = temp;
- }
- addr = gen_pm_adjust_address(ctx, addr);
-
+ addr = get_address(ctx, a->rs1, a->imm);
tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUQ);
-
return true;
}
REQUIRE_FPU;
REQUIRE_EXT(ctx, RVF);
- addr = get_gpr(ctx, a->rs1, EXT_NONE);
- if (a->imm) {
- TCGv temp = temp_new(ctx);
- tcg_gen_addi_tl(temp, addr, a->imm);
- addr = temp;
- }
- addr = gen_pm_adjust_address(ctx, addr);
-
+ addr = get_address(ctx, a->rs1, a->imm);
dest = cpu_fpr[a->rd];
tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, MO_TEUL);
gen_nanbox_s(dest, dest);
REQUIRE_FPU;
REQUIRE_EXT(ctx, RVF);
- addr = get_gpr(ctx, a->rs1, EXT_NONE);
- if (a->imm) {
- TCGv temp = tcg_temp_new();
- tcg_gen_addi_tl(temp, addr, a->imm);
- addr = temp;
- }
- addr = gen_pm_adjust_address(ctx, addr);
-
+ addr = get_address(ctx, a->rs1, a->imm);
tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, MO_TEUL);
-
return true;
}
tcg_gen_addi_tl(cpu_pc, get_gpr(ctx, a->rs1, EXT_NONE), a->imm);
tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2);
+ gen_set_pc(ctx, cpu_pc);
if (!has_ext(ctx, RVC)) {
TCGv t0 = tcg_temp_new();
tcg_temp_free(t0);
}
- if (a->rd != 0) {
- tcg_gen_movi_tl(cpu_gpr[a->rd], ctx->pc_succ_insn);
- }
+ gen_set_gpri(ctx, a->rd, ctx->pc_succ_insn);
tcg_gen_lookup_and_goto_ptr();
if (misaligned) {
static bool gen_load_tl(DisasContext *ctx, arg_lb *a, MemOp memop)
{
TCGv dest = dest_gpr(ctx, a->rd);
- TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE);
-
- if (a->imm) {
- TCGv temp = temp_new(ctx);
- tcg_gen_addi_tl(temp, addr, a->imm);
- addr = temp;
- }
- addr = gen_pm_adjust_address(ctx, addr);
+ TCGv addr = get_address(ctx, a->rs1, a->imm);
tcg_gen_qemu_ld_tl(dest, addr, ctx->mem_idx, memop);
gen_set_gpr(ctx, a->rd, dest);
static bool gen_store_tl(DisasContext *ctx, arg_sb *a, MemOp memop)
{
- TCGv addr = get_gpr(ctx, a->rs1, EXT_NONE);
+ TCGv addr = get_address(ctx, a->rs1, a->imm);
TCGv data = get_gpr(ctx, a->rs2, EXT_NONE);
- if (a->imm) {
- TCGv temp = temp_new(ctx);
- tcg_gen_addi_tl(temp, addr, a->imm);
- addr = temp;
- }
- addr = gen_pm_adjust_address(ctx, addr);
-
tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop);
return true;
}
* FENCE_I is a no-op in QEMU,
* however we need to end the translation block
*/
- tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn);
+ gen_set_pc_imm(ctx, ctx->pc_succ_insn);
tcg_gen_exit_tb(NULL, 0);
ctx->base.is_jmp = DISAS_NORETURN;
return true;
static bool do_csr_post(DisasContext *ctx)
{
/* We may have changed important cpu state -- exit to main loop. */
- tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn);
+ gen_set_pc_imm(ctx, ctx->pc_succ_insn);
tcg_gen_exit_tb(NULL, 0);
ctx->base.is_jmp = DISAS_NORETURN;
return true;
static bool trans_csrrw(DisasContext *ctx, arg_csrrw *a)
{
- if (get_xl(ctx) < MXL_RV128) {
+ RISCVMXL xl = get_xl(ctx);
+ if (xl < MXL_RV128) {
TCGv src = get_gpr(ctx, a->rs1, EXT_NONE);
/*
return do_csrw(ctx, a->csr, src);
}
- TCGv mask = tcg_constant_tl(-1);
+ TCGv mask = tcg_constant_tl(xl == MXL_RV32 ? UINT32_MAX :
+ (target_ulong)-1);
return do_csrrw(ctx, a->rd, a->csr, src, mask);
} else {
TCGv srcl = get_gpr(ctx, a->rs1, EXT_NONE);
static bool trans_csrrwi(DisasContext *ctx, arg_csrrwi *a)
{
- if (get_xl(ctx) < MXL_RV128) {
+ RISCVMXL xl = get_xl(ctx);
+ if (xl < MXL_RV128) {
TCGv src = tcg_constant_tl(a->rs1);
/*
return do_csrw(ctx, a->csr, src);
}
- TCGv mask = tcg_constant_tl(-1);
+ TCGv mask = tcg_constant_tl(xl == MXL_RV32 ? UINT32_MAX :
+ (target_ulong)-1);
return do_csrrw(ctx, a->rd, a->csr, src, mask);
} else {
TCGv src = tcg_constant_tl(a->rs1);
}
}
+static bool require_zve32f(DisasContext *s)
+{
+ /* RVV + Zve32f = RVV. */
+ if (has_ext(s, RVV)) {
+ return true;
+ }
+
+ /* Zve32f doesn't support FP64. (Section 18.2) */
+ return s->ext_zve32f ? s->sew <= MO_32 : true;
+}
+
+static bool require_scale_zve32f(DisasContext *s)
+{
+ /* RVV + Zve32f = RVV. */
+ if (has_ext(s, RVV)) {
+ return true;
+ }
+
+ /* Zve32f doesn't support FP64. (Section 18.2) */
+ return s->ext_zve64f ? s->sew <= MO_16 : true;
+}
+
+static bool require_zve64f(DisasContext *s)
+{
+ /* RVV + Zve64f = RVV. */
+ if (has_ext(s, RVV)) {
+ return true;
+ }
+
+ /* Zve64f doesn't support FP64. (Section 18.2) */
+ return s->ext_zve64f ? s->sew <= MO_32 : true;
+}
+
+static bool require_scale_zve64f(DisasContext *s)
+{
+ /* RVV + Zve64f = RVV. */
+ if (has_ext(s, RVV)) {
+ return true;
+ }
+
+ /* Zve64f doesn't support FP64. (Section 18.2) */
+ return s->ext_zve64f ? s->sew <= MO_16 : true;
+}
+
/* Destination vector register group cannot overlap source mask register. */
static bool require_vm(int vm, int vd)
{
{
TCGv s1, dst;
- if (!require_rvv(s) || !has_ext(s, RVV)) {
+ if (!require_rvv(s) ||
+ !(has_ext(s, RVV) || s->ext_zve32f || s->ext_zve64f)) {
return false;
}
gen_set_gpr(s, rd, dst);
mark_vs_dirty(s);
- tcg_gen_movi_tl(cpu_pc, s->pc_succ_insn);
+ gen_set_pc_imm(s, s->pc_succ_insn);
tcg_gen_lookup_and_goto_ptr();
s->base.is_jmp = DISAS_NORETURN;
{
TCGv dst;
- if (!require_rvv(s) || !has_ext(s, RVV)) {
+ if (!require_rvv(s) ||
+ !(has_ext(s, RVV) || s->ext_zve32f || s->ext_zve64f)) {
return false;
}
gen_helper_vsetvl(dst, cpu_env, s1, s2);
gen_set_gpr(s, rd, dst);
mark_vs_dirty(s);
- tcg_gen_movi_tl(cpu_pc, s->pc_succ_insn);
+ gen_set_pc_imm(s, s->pc_succ_insn);
tcg_gen_lookup_and_goto_ptr();
s->base.is_jmp = DISAS_NORETURN;
uint8_t eew)
{
int8_t emul = eew - s->sew + s->lmul;
- return (emul >= -3 && emul <= 3) &&
- require_align(vs2, emul) &&
- require_align(vd, s->lmul) &&
- require_nf(vd, nf, s->lmul);
+ bool ret = (emul >= -3 && emul <= 3) &&
+ require_align(vs2, emul) &&
+ require_align(vd, s->lmul) &&
+ require_nf(vd, nf, s->lmul);
+
+ /*
+ * All Zve* extensions support all vector load and store instructions,
+ * except Zve64* extensions do not support EEW=64 for index values
+ * when XLEN=32. (Section 18.2)
+ */
+ if (get_xl(s) == MXL_RV32) {
+ ret &= (!has_ext(s, RVV) && s->ext_zve64f ? eew != MO_64 : true);
+ }
+
+ return ret;
}
/*
dest = tcg_temp_new_ptr();
mask = tcg_temp_new_ptr();
src2 = tcg_temp_new_ptr();
- src1 = get_gpr(s, rs1, EXT_NONE);
+ src1 = get_gpr(s, rs1, EXT_SIGN);
data = FIELD_DP32(data, VDATA, VM, vm);
data = FIELD_DP32(data, VDATA, LMUL, s->lmul);
GEN_OPIVX_TRANS(vmax_vx, opivx_check)
/* Vector Single-Width Integer Multiply Instructions */
+
+static bool vmulh_vv_check(DisasContext *s, arg_rmrr *a)
+{
+ /*
+ * All Zve* extensions support all vector integer instructions,
+ * except that the vmulh integer multiply variants
+ * that return the high word of the product
+ * (vmulh.vv, vmulh.vx, vmulhu.vv, vmulhu.vx, vmulhsu.vv, vmulhsu.vx)
+ * are not included for EEW=64 in Zve64*. (Section 18.2)
+ */
+ return opivv_check(s, a) &&
+ (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true);
+}
+
+static bool vmulh_vx_check(DisasContext *s, arg_rmrr *a)
+{
+ /*
+ * All Zve* extensions support all vector integer instructions,
+ * except that the vmulh integer multiply variants
+ * that return the high word of the product
+ * (vmulh.vv, vmulh.vx, vmulhu.vv, vmulhu.vx, vmulhsu.vv, vmulhsu.vx)
+ * are not included for EEW=64 in Zve64*. (Section 18.2)
+ */
+ return opivx_check(s, a) &&
+ (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true);
+}
+
GEN_OPIVV_GVEC_TRANS(vmul_vv, mul)
-GEN_OPIVV_TRANS(vmulh_vv, opivv_check)
-GEN_OPIVV_TRANS(vmulhu_vv, opivv_check)
-GEN_OPIVV_TRANS(vmulhsu_vv, opivv_check)
+GEN_OPIVV_TRANS(vmulh_vv, vmulh_vv_check)
+GEN_OPIVV_TRANS(vmulhu_vv, vmulh_vv_check)
+GEN_OPIVV_TRANS(vmulhsu_vv, vmulh_vv_check)
GEN_OPIVX_GVEC_TRANS(vmul_vx, muls)
-GEN_OPIVX_TRANS(vmulh_vx, opivx_check)
-GEN_OPIVX_TRANS(vmulhu_vx, opivx_check)
-GEN_OPIVX_TRANS(vmulhsu_vx, opivx_check)
+GEN_OPIVX_TRANS(vmulh_vx, vmulh_vx_check)
+GEN_OPIVX_TRANS(vmulhu_vx, vmulh_vx_check)
+GEN_OPIVX_TRANS(vmulhsu_vx, vmulh_vx_check)
/* Vector Integer Divide Instructions */
GEN_OPIVV_TRANS(vdivu_vv, opivv_check)
GEN_OPIVX_TRANS(vasubu_vx, opivx_check)
/* Vector Single-Width Fractional Multiply with Rounding and Saturation */
-GEN_OPIVV_TRANS(vsmul_vv, opivv_check)
-GEN_OPIVX_TRANS(vsmul_vx, opivx_check)
+
+static bool vsmul_vv_check(DisasContext *s, arg_rmrr *a)
+{
+ /*
+ * All Zve* extensions support all vector fixed-point arithmetic
+ * instructions, except that vsmul.vv and vsmul.vx are not supported
+ * for EEW=64 in Zve64*. (Section 18.2)
+ */
+ return opivv_check(s, a) &&
+ (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true);
+}
+
+static bool vsmul_vx_check(DisasContext *s, arg_rmrr *a)
+{
+ /*
+ * All Zve* extensions support all vector fixed-point arithmetic
+ * instructions, except that vsmul.vv and vsmul.vx are not supported
+ * for EEW=64 in Zve64*. (Section 18.2)
+ */
+ return opivx_check(s, a) &&
+ (!has_ext(s, RVV) && s->ext_zve64f ? s->sew != MO_64 : true);
+}
+
+GEN_OPIVV_TRANS(vsmul_vv, vsmul_vv_check)
+GEN_OPIVX_TRANS(vsmul_vx, vsmul_vx_check)
/* Vector Single-Width Scaling Shift Instructions */
GEN_OPIVV_TRANS(vssrl_vv, opivv_check)
return require_rvv(s) &&
require_rvf(s) &&
vext_check_isa_ill(s) &&
- vext_check_sss(s, a->rd, a->rs1, a->rs2, a->vm);
+ vext_check_sss(s, a->rd, a->rs1, a->rs2, a->vm) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
/* OPFVV without GVEC IR */
return require_rvv(s) &&
require_rvf(s) &&
vext_check_isa_ill(s) &&
- vext_check_ss(s, a->rd, a->rs2, a->vm);
+ vext_check_ss(s, a->rd, a->rs2, a->vm) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
/* OPFVF without GVEC IR */
require_scale_rvf(s) &&
(s->sew != MO_8) &&
vext_check_isa_ill(s) &&
- vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm);
+ vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
/* OPFVV with WIDEN */
require_scale_rvf(s) &&
(s->sew != MO_8) &&
vext_check_isa_ill(s) &&
- vext_check_ds(s, a->rd, a->rs2, a->vm);
+ vext_check_ds(s, a->rd, a->rs2, a->vm) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
/* OPFVF with WIDEN */
require_scale_rvf(s) &&
(s->sew != MO_8) &&
vext_check_isa_ill(s) &&
- vext_check_dds(s, a->rd, a->rs1, a->rs2, a->vm);
+ vext_check_dds(s, a->rd, a->rs1, a->rs2, a->vm) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
/* WIDEN OPFVV with WIDEN */
require_scale_rvf(s) &&
(s->sew != MO_8) &&
vext_check_isa_ill(s) &&
- vext_check_dd(s, a->rd, a->rs2, a->vm);
+ vext_check_dd(s, a->rd, a->rs2, a->vm) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
/* WIDEN OPFVF with WIDEN */
require_rvf(s) &&
vext_check_isa_ill(s) &&
/* OPFV instructions ignore vs1 check */
- vext_check_ss(s, a->rd, a->rs2, a->vm);
+ vext_check_ss(s, a->rd, a->rs2, a->vm) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
static bool do_opfv(DisasContext *s, arg_rmr *a,
return require_rvv(s) &&
require_rvf(s) &&
vext_check_isa_ill(s) &&
- vext_check_mss(s, a->rd, a->rs1, a->rs2);
+ vext_check_mss(s, a->rd, a->rs1, a->rs2) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
GEN_OPFVV_TRANS(vmfeq_vv, opfvv_cmp_check)
return require_rvv(s) &&
require_rvf(s) &&
vext_check_isa_ill(s) &&
- vext_check_ms(s, a->rd, a->rs2);
+ vext_check_ms(s, a->rd, a->rs2) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
GEN_OPFVF_TRANS(vmfeq_vf, opfvf_cmp_check)
if (require_rvv(s) &&
require_rvf(s) &&
vext_check_isa_ill(s) &&
- require_align(a->rd, s->lmul)) {
+ require_align(a->rd, s->lmul) &&
+ require_zve32f(s) &&
+ require_zve64f(s)) {
gen_set_rm(s, RISCV_FRM_DYN);
TCGv_i64 t1;
static bool opxfv_widen_check(DisasContext *s, arg_rmr *a)
{
return opfv_widen_check(s, a) &&
- require_rvf(s);
+ require_rvf(s) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
static bool opffv_widen_check(DisasContext *s, arg_rmr *a)
{
return opfv_widen_check(s, a) &&
require_scale_rvf(s) &&
- (s->sew != MO_8);
+ (s->sew != MO_8) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
#define GEN_OPFV_WIDEN_TRANS(NAME, CHECK, HELPER, FRM) \
require_scale_rvf(s) &&
vext_check_isa_ill(s) &&
/* OPFV widening instructions ignore vs1 check */
- vext_check_ds(s, a->rd, a->rs2, a->vm);
+ vext_check_ds(s, a->rd, a->rs2, a->vm) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
#define GEN_OPFXV_WIDEN_TRANS(NAME) \
{
return opfv_narrow_check(s, a) &&
require_rvf(s) &&
- (s->sew != MO_64);
+ (s->sew != MO_64) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
static bool opffv_narrow_check(DisasContext *s, arg_rmr *a)
{
return opfv_narrow_check(s, a) &&
require_scale_rvf(s) &&
- (s->sew != MO_8);
+ (s->sew != MO_8) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
#define GEN_OPFV_NARROW_TRANS(NAME, CHECK, HELPER, FRM) \
require_scale_rvf(s) &&
vext_check_isa_ill(s) &&
/* OPFV narrowing instructions ignore vs1 check */
- vext_check_sd(s, a->rd, a->rs2, a->vm);
+ vext_check_sd(s, a->rd, a->rs2, a->vm) &&
+ require_scale_zve32f(s) &&
+ require_scale_zve64f(s);
}
#define GEN_OPXFV_NARROW_TRANS(NAME, HELPER, FRM) \
static bool freduction_check(DisasContext *s, arg_rmrr *a)
{
return reduction_check(s, a) &&
- require_rvf(s);
+ require_rvf(s) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
GEN_OPFVV_TRANS(vfredsum_vs, freduction_check)
{
if (require_rvv(s) &&
require_rvf(s) &&
- vext_check_isa_ill(s)) {
+ vext_check_isa_ill(s) &&
+ require_zve32f(s) &&
+ require_zve64f(s)) {
gen_set_rm(s, RISCV_FRM_DYN);
unsigned int ofs = (8 << s->sew);
{
if (require_rvv(s) &&
require_rvf(s) &&
- vext_check_isa_ill(s)) {
+ vext_check_isa_ill(s) &&
+ require_zve32f(s) &&
+ require_zve64f(s)) {
gen_set_rm(s, RISCV_FRM_DYN);
/* The instructions ignore LMUL and vector register group. */
static bool fslideup_check(DisasContext *s, arg_rmrr *a)
{
return slideup_check(s, a) &&
- require_rvf(s);
+ require_rvf(s) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
static bool fslidedown_check(DisasContext *s, arg_rmrr *a)
{
return slidedown_check(s, a) &&
- require_rvf(s);
+ require_rvf(s) &&
+ require_zve32f(s) &&
+ require_zve64f(s);
}
GEN_OPFVF_TRANS(vfslide1up_vf, fslideup_check)
--- /dev/null
+/*
+ * QEMU KVM RISC-V specific function stubs
+ *
+ * Copyright (c) 2020 Huawei Technologies Co., Ltd
+ *
+ * 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 "cpu.h"
+#include "kvm_riscv.h"
+
+void kvm_riscv_reset_vcpu(RISCVCPU *cpu)
+{
+ abort();
+}
+
+void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level)
+{
+ abort();
+}
--- /dev/null
+/*
+ * RISC-V implementation of KVM hooks
+ *
+ * Copyright (c) 2020 Huawei Technologies Co., Ltd
+ *
+ * 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 <sys/ioctl.h>
+
+#include <linux/kvm.h>
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "qemu/error-report.h"
+#include "qemu/main-loop.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/kvm.h"
+#include "sysemu/kvm_int.h"
+#include "cpu.h"
+#include "trace.h"
+#include "hw/pci/pci.h"
+#include "exec/memattrs.h"
+#include "exec/address-spaces.h"
+#include "hw/boards.h"
+#include "hw/irq.h"
+#include "qemu/log.h"
+#include "hw/loader.h"
+#include "kvm_riscv.h"
+#include "sbi_ecall_interface.h"
+#include "chardev/char-fe.h"
+#include "migration/migration.h"
+#include "sysemu/runstate.h"
+
+static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type,
+ uint64_t idx)
+{
+ uint64_t id = KVM_REG_RISCV | type | idx;
+
+ switch (riscv_cpu_mxl(env)) {
+ case MXL_RV32:
+ id |= KVM_REG_SIZE_U32;
+ break;
+ case MXL_RV64:
+ id |= KVM_REG_SIZE_U64;
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return id;
+}
+
+#define RISCV_CORE_REG(env, name) kvm_riscv_reg_id(env, KVM_REG_RISCV_CORE, \
+ KVM_REG_RISCV_CORE_REG(name))
+
+#define RISCV_CSR_REG(env, name) kvm_riscv_reg_id(env, KVM_REG_RISCV_CSR, \
+ KVM_REG_RISCV_CSR_REG(name))
+
+#define RISCV_TIMER_REG(env, name) kvm_riscv_reg_id(env, KVM_REG_RISCV_TIMER, \
+ KVM_REG_RISCV_TIMER_REG(name))
+
+#define RISCV_FP_F_REG(env, idx) kvm_riscv_reg_id(env, KVM_REG_RISCV_FP_F, idx)
+
+#define RISCV_FP_D_REG(env, idx) kvm_riscv_reg_id(env, KVM_REG_RISCV_FP_D, idx)
+
+#define KVM_RISCV_GET_CSR(cs, env, csr, reg) \
+ do { \
+ int ret = kvm_get_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \
+ if (ret) { \
+ return ret; \
+ } \
+ } while (0)
+
+#define KVM_RISCV_SET_CSR(cs, env, csr, reg) \
+ do { \
+ int ret = kvm_set_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \
+ if (ret) { \
+ return ret; \
+ } \
+ } while (0)
+
+#define KVM_RISCV_GET_TIMER(cs, env, name, reg) \
+ do { \
+ int ret = kvm_get_one_reg(cs, RISCV_TIMER_REG(env, name), ®); \
+ if (ret) { \
+ abort(); \
+ } \
+ } while (0)
+
+#define KVM_RISCV_SET_TIMER(cs, env, name, reg) \
+ do { \
+ int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, time), ®); \
+ if (ret) { \
+ abort(); \
+ } \
+ } while (0)
+
+static int kvm_riscv_get_regs_core(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ target_ulong reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ ret = kvm_get_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®);
+ if (ret) {
+ return ret;
+ }
+ env->pc = reg;
+
+ for (i = 1; i < 32; i++) {
+ uint64_t id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CORE, i);
+ ret = kvm_get_one_reg(cs, id, ®);
+ if (ret) {
+ return ret;
+ }
+ env->gpr[i] = reg;
+ }
+
+ return ret;
+}
+
+static int kvm_riscv_put_regs_core(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ target_ulong reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ reg = env->pc;
+ ret = kvm_set_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®);
+ if (ret) {
+ return ret;
+ }
+
+ for (i = 1; i < 32; i++) {
+ uint64_t id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CORE, i);
+ reg = env->gpr[i];
+ ret = kvm_set_one_reg(cs, id, ®);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int kvm_riscv_get_regs_csr(CPUState *cs)
+{
+ int ret = 0;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ KVM_RISCV_GET_CSR(cs, env, sstatus, env->mstatus);
+ KVM_RISCV_GET_CSR(cs, env, sie, env->mie);
+ KVM_RISCV_GET_CSR(cs, env, stvec, env->stvec);
+ KVM_RISCV_GET_CSR(cs, env, sscratch, env->sscratch);
+ KVM_RISCV_GET_CSR(cs, env, sepc, env->sepc);
+ KVM_RISCV_GET_CSR(cs, env, scause, env->scause);
+ KVM_RISCV_GET_CSR(cs, env, stval, env->stval);
+ KVM_RISCV_GET_CSR(cs, env, sip, env->mip);
+ KVM_RISCV_GET_CSR(cs, env, satp, env->satp);
+ return ret;
+}
+
+static int kvm_riscv_put_regs_csr(CPUState *cs)
+{
+ int ret = 0;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ KVM_RISCV_SET_CSR(cs, env, sstatus, env->mstatus);
+ KVM_RISCV_SET_CSR(cs, env, sie, env->mie);
+ KVM_RISCV_SET_CSR(cs, env, stvec, env->stvec);
+ KVM_RISCV_SET_CSR(cs, env, sscratch, env->sscratch);
+ KVM_RISCV_SET_CSR(cs, env, sepc, env->sepc);
+ KVM_RISCV_SET_CSR(cs, env, scause, env->scause);
+ KVM_RISCV_SET_CSR(cs, env, stval, env->stval);
+ KVM_RISCV_SET_CSR(cs, env, sip, env->mip);
+ KVM_RISCV_SET_CSR(cs, env, satp, env->satp);
+
+ return ret;
+}
+
+static int kvm_riscv_get_regs_fp(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (riscv_has_ext(env, RVD)) {
+ uint64_t reg;
+ for (i = 0; i < 32; i++) {
+ ret = kvm_get_one_reg(cs, RISCV_FP_D_REG(env, i), ®);
+ if (ret) {
+ return ret;
+ }
+ env->fpr[i] = reg;
+ }
+ return ret;
+ }
+
+ if (riscv_has_ext(env, RVF)) {
+ uint32_t reg;
+ for (i = 0; i < 32; i++) {
+ ret = kvm_get_one_reg(cs, RISCV_FP_F_REG(env, i), ®);
+ if (ret) {
+ return ret;
+ }
+ env->fpr[i] = reg;
+ }
+ return ret;
+ }
+
+ return ret;
+}
+
+static int kvm_riscv_put_regs_fp(CPUState *cs)
+{
+ int ret = 0;
+ int i;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (riscv_has_ext(env, RVD)) {
+ uint64_t reg;
+ for (i = 0; i < 32; i++) {
+ reg = env->fpr[i];
+ ret = kvm_set_one_reg(cs, RISCV_FP_D_REG(env, i), ®);
+ if (ret) {
+ return ret;
+ }
+ }
+ return ret;
+ }
+
+ if (riscv_has_ext(env, RVF)) {
+ uint32_t reg;
+ for (i = 0; i < 32; i++) {
+ reg = env->fpr[i];
+ ret = kvm_set_one_reg(cs, RISCV_FP_F_REG(env, i), ®);
+ if (ret) {
+ return ret;
+ }
+ }
+ return ret;
+ }
+
+ return ret;
+}
+
+static void kvm_riscv_get_regs_timer(CPUState *cs)
+{
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (env->kvm_timer_dirty) {
+ return;
+ }
+
+ KVM_RISCV_GET_TIMER(cs, env, time, env->kvm_timer_time);
+ KVM_RISCV_GET_TIMER(cs, env, compare, env->kvm_timer_compare);
+ KVM_RISCV_GET_TIMER(cs, env, state, env->kvm_timer_state);
+ KVM_RISCV_GET_TIMER(cs, env, frequency, env->kvm_timer_frequency);
+
+ env->kvm_timer_dirty = true;
+}
+
+static void kvm_riscv_put_regs_timer(CPUState *cs)
+{
+ uint64_t reg;
+ CPURISCVState *env = &RISCV_CPU(cs)->env;
+
+ if (!env->kvm_timer_dirty) {
+ return;
+ }
+
+ KVM_RISCV_SET_TIMER(cs, env, time, env->kvm_timer_time);
+ KVM_RISCV_SET_TIMER(cs, env, compare, env->kvm_timer_compare);
+
+ /*
+ * To set register of RISCV_TIMER_REG(state) will occur a error from KVM
+ * on env->kvm_timer_state == 0, It's better to adapt in KVM, but it
+ * doesn't matter that adaping in QEMU now.
+ * TODO If KVM changes, adapt here.
+ */
+ if (env->kvm_timer_state) {
+ KVM_RISCV_SET_TIMER(cs, env, state, env->kvm_timer_state);
+ }
+
+ /*
+ * For now, migration will not work between Hosts with different timer
+ * frequency. Therefore, we should check whether they are the same here
+ * during the migration.
+ */
+ if (migration_is_running(migrate_get_current()->state)) {
+ KVM_RISCV_GET_TIMER(cs, env, frequency, reg);
+ if (reg != env->kvm_timer_frequency) {
+ error_report("Dst Hosts timer frequency != Src Hosts");
+ }
+ }
+
+ env->kvm_timer_dirty = false;
+}
+
+const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
+ KVM_CAP_LAST_INFO
+};
+
+int kvm_arch_get_registers(CPUState *cs)
+{
+ int ret = 0;
+
+ ret = kvm_riscv_get_regs_core(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_get_regs_csr(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_get_regs_fp(cs);
+ if (ret) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int kvm_arch_put_registers(CPUState *cs, int level)
+{
+ int ret = 0;
+
+ ret = kvm_riscv_put_regs_core(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_put_regs_csr(cs);
+ if (ret) {
+ return ret;
+ }
+
+ ret = kvm_riscv_put_regs_fp(cs);
+ if (ret) {
+ return ret;
+ }
+
+ return ret;
+}
+
+int kvm_arch_release_virq_post(int virq)
+{
+ return 0;
+}
+
+int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
+ uint64_t address, uint32_t data, PCIDevice *dev)
+{
+ return 0;
+}
+
+int kvm_arch_destroy_vcpu(CPUState *cs)
+{
+ return 0;
+}
+
+unsigned long kvm_arch_vcpu_id(CPUState *cpu)
+{
+ return cpu->cpu_index;
+}
+
+static void kvm_riscv_vm_state_change(void *opaque, bool running,
+ RunState state)
+{
+ CPUState *cs = opaque;
+
+ if (running) {
+ kvm_riscv_put_regs_timer(cs);
+ } else {
+ kvm_riscv_get_regs_timer(cs);
+ }
+}
+
+void kvm_arch_init_irq_routing(KVMState *s)
+{
+}
+
+int kvm_arch_init_vcpu(CPUState *cs)
+{
+ int ret = 0;
+ target_ulong isa;
+ RISCVCPU *cpu = RISCV_CPU(cs);
+ CPURISCVState *env = &cpu->env;
+ uint64_t id;
+
+ qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs);
+
+ id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
+ KVM_REG_RISCV_CONFIG_REG(isa));
+ ret = kvm_get_one_reg(cs, id, &isa);
+ if (ret) {
+ return ret;
+ }
+ env->misa_ext = isa;
+
+ return ret;
+}
+
+int kvm_arch_msi_data_to_gsi(uint32_t data)
+{
+ abort();
+}
+
+int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route,
+ int vector, PCIDevice *dev)
+{
+ return 0;
+}
+
+int kvm_arch_init(MachineState *ms, KVMState *s)
+{
+ return 0;
+}
+
+int kvm_arch_irqchip_create(KVMState *s)
+{
+ return 0;
+}
+
+int kvm_arch_process_async_events(CPUState *cs)
+{
+ return 0;
+}
+
+void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run)
+{
+}
+
+MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
+{
+ return MEMTXATTRS_UNSPECIFIED;
+}
+
+bool kvm_arch_stop_on_emulation_error(CPUState *cs)
+{
+ return true;
+}
+
+static int kvm_riscv_handle_sbi(CPUState *cs, struct kvm_run *run)
+{
+ int ret = 0;
+ unsigned char ch;
+ switch (run->riscv_sbi.extension_id) {
+ case SBI_EXT_0_1_CONSOLE_PUTCHAR:
+ ch = run->riscv_sbi.args[0];
+ qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch));
+ break;
+ case SBI_EXT_0_1_CONSOLE_GETCHAR:
+ ret = qemu_chr_fe_read_all(serial_hd(0)->be, &ch, sizeof(ch));
+ if (ret == sizeof(ch)) {
+ run->riscv_sbi.args[0] = ch;
+ } else {
+ run->riscv_sbi.args[0] = -1;
+ }
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: un-handled SBI EXIT, specific reasons is %lu\n",
+ __func__, run->riscv_sbi.extension_id);
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
+{
+ int ret = 0;
+ switch (run->exit_reason) {
+ case KVM_EXIT_RISCV_SBI:
+ ret = kvm_riscv_handle_sbi(cs, run);
+ break;
+ default:
+ qemu_log_mask(LOG_UNIMP, "%s: un-handled exit reason %d\n",
+ __func__, run->exit_reason);
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+void kvm_riscv_reset_vcpu(RISCVCPU *cpu)
+{
+ CPURISCVState *env = &cpu->env;
+
+ if (!kvm_enabled()) {
+ return;
+ }
+ env->pc = cpu->env.kernel_addr;
+ env->gpr[10] = kvm_arch_vcpu_id(CPU(cpu)); /* a0 */
+ env->gpr[11] = cpu->env.fdt_addr; /* a1 */
+ env->satp = 0;
+}
+
+void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level)
+{
+ int ret;
+ unsigned virq = level ? KVM_INTERRUPT_SET : KVM_INTERRUPT_UNSET;
+
+ if (irq != IRQ_S_EXT) {
+ perror("kvm riscv set irq != IRQ_S_EXT\n");
+ abort();
+ }
+
+ ret = kvm_vcpu_ioctl(CPU(cpu), KVM_INTERRUPT, &virq);
+ if (ret < 0) {
+ perror("Set irq failed");
+ abort();
+ }
+}
+
+bool kvm_arch_cpu_check_are_resettable(void)
+{
+ return true;
+}
--- /dev/null
+/*
+ * QEMU KVM support -- RISC-V specific functions.
+ *
+ * Copyright (c) 2020 Huawei Technologies Co., Ltd
+ *
+ * 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 QEMU_KVM_RISCV_H
+#define QEMU_KVM_RISCV_H
+
+void kvm_riscv_reset_vcpu(RISCVCPU *cpu);
+void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level);
+
+#endif
static const VMStateDescription vmstate_vector = {
.name = "cpu/vector",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.needed = vector_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64_ARRAY(env.vreg, RISCVCPU, 32 * RV_VLEN_MAX / 64),
VMSTATE_UINTTL(env.vl, RISCVCPU),
VMSTATE_UINTTL(env.vstart, RISCVCPU),
VMSTATE_UINTTL(env.vtype, RISCVCPU),
+ VMSTATE_BOOL(env.vill, RISCVCPU),
VMSTATE_END_OF_LIST()
}
};
}
};
+static bool kvmtimer_needed(void *opaque)
+{
+ return kvm_enabled();
+}
+
+static int cpu_post_load(void *opaque, int version_id)
+{
+ RISCVCPU *cpu = opaque;
+ CPURISCVState *env = &cpu->env;
+
+ env->kvm_timer_dirty = true;
+ return 0;
+}
+
+static const VMStateDescription vmstate_kvmtimer = {
+ .name = "cpu/kvmtimer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = kvmtimer_needed,
+ .post_load = cpu_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(env.kvm_timer_time, RISCVCPU),
+ VMSTATE_UINT64(env.kvm_timer_compare, RISCVCPU),
+ VMSTATE_UINT64(env.kvm_timer_state, RISCVCPU),
+
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int riscv_cpu_post_load(void *opaque, int version_id)
+{
+ RISCVCPU *cpu = opaque;
+ CPURISCVState *env = &cpu->env;
+
+ env->xl = cpu_recompute_xl(env);
+ riscv_cpu_update_mask(env);
+ return 0;
+}
+
const VMStateDescription vmstate_riscv_cpu = {
.name = "cpu",
.version_id = 3,
.minimum_version_id = 3,
+ .post_load = riscv_cpu_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.gpr, RISCVCPU, 32),
VMSTATE_UINT64_ARRAY(env.fpr, RISCVCPU, 32),
&vmstate_vector,
&vmstate_pointermasking,
&vmstate_rv128,
+ &vmstate_kvmtimer,
NULL
}
};
'translate.c',
'm128_helper.c'
))
+riscv_ss.add(when: 'CONFIG_KVM', if_true: files('kvm.c'), if_false: files('kvm-stub.c'))
riscv_softmmu_ss = ss.source_set()
riscv_softmmu_ss.add(files(
void helper_csrw(CPURISCVState *env, int csr, target_ulong src)
{
- RISCVException ret = riscv_csrrw(env, csr, NULL, src, -1);
+ target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1;
+ RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask);
if (ret != RISCV_EXCP_NONE) {
riscv_raise_exception(env, ret, GETPC());
#ifndef CONFIG_USER_ONLY
-target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb)
+target_ulong helper_sret(CPURISCVState *env)
{
uint64_t mstatus;
target_ulong prev_priv, prev_virt;
return retpc;
}
-target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb)
+target_ulong helper_mret(CPURISCVState *env)
{
if (!(env->priv >= PRV_M)) {
riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC());
{
int i;
uint8_t cfg_val;
+ int pmpcfg_nums = 2 << riscv_cpu_mxl(env);
trace_pmpcfg_csr_write(env->mhartid, reg_index, val);
- if ((reg_index & 1) && (sizeof(target_ulong) == 8)) {
- qemu_log_mask(LOG_GUEST_ERROR,
- "ignoring pmpcfg write - incorrect address\n");
- return;
- }
-
- for (i = 0; i < sizeof(target_ulong); i++) {
+ for (i = 0; i < pmpcfg_nums; i++) {
cfg_val = (val >> 8 * i) & 0xff;
pmp_write_cfg(env, (reg_index * 4) + i, cfg_val);
}
int i;
target_ulong cfg_val = 0;
target_ulong val = 0;
+ int pmpcfg_nums = 2 << riscv_cpu_mxl(env);
- for (i = 0; i < sizeof(target_ulong); i++) {
+ for (i = 0; i < pmpcfg_nums; i++) {
val = pmp_read_cfg(env, (reg_index * 4) + i);
cfg_val |= (val << (i * 8));
}
--- /dev/null
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Western Digital Corporation or its affiliates.
+ *
+ * Authors:
+ * Anup Patel <anup.patel@wdc.com>
+ */
+
+#ifndef __SBI_ECALL_INTERFACE_H__
+#define __SBI_ECALL_INTERFACE_H__
+
+/* clang-format off */
+
+/* SBI Extension IDs */
+#define SBI_EXT_0_1_SET_TIMER 0x0
+#define SBI_EXT_0_1_CONSOLE_PUTCHAR 0x1
+#define SBI_EXT_0_1_CONSOLE_GETCHAR 0x2
+#define SBI_EXT_0_1_CLEAR_IPI 0x3
+#define SBI_EXT_0_1_SEND_IPI 0x4
+#define SBI_EXT_0_1_REMOTE_FENCE_I 0x5
+#define SBI_EXT_0_1_REMOTE_SFENCE_VMA 0x6
+#define SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID 0x7
+#define SBI_EXT_0_1_SHUTDOWN 0x8
+#define SBI_EXT_BASE 0x10
+#define SBI_EXT_TIME 0x54494D45
+#define SBI_EXT_IPI 0x735049
+#define SBI_EXT_RFENCE 0x52464E43
+#define SBI_EXT_HSM 0x48534D
+
+/* SBI function IDs for BASE extension*/
+#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0
+#define SBI_EXT_BASE_GET_IMP_ID 0x1
+#define SBI_EXT_BASE_GET_IMP_VERSION 0x2
+#define SBI_EXT_BASE_PROBE_EXT 0x3
+#define SBI_EXT_BASE_GET_MVENDORID 0x4
+#define SBI_EXT_BASE_GET_MARCHID 0x5
+#define SBI_EXT_BASE_GET_MIMPID 0x6
+
+/* SBI function IDs for TIME extension*/
+#define SBI_EXT_TIME_SET_TIMER 0x0
+
+/* SBI function IDs for IPI extension*/
+#define SBI_EXT_IPI_SEND_IPI 0x0
+
+/* SBI function IDs for RFENCE extension*/
+#define SBI_EXT_RFENCE_REMOTE_FENCE_I 0x0
+#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA 0x1
+#define SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID 0x2
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA 0x3
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID 0x4
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA 0x5
+#define SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID 0x6
+
+/* SBI function IDs for HSM extension */
+#define SBI_EXT_HSM_HART_START 0x0
+#define SBI_EXT_HSM_HART_STOP 0x1
+#define SBI_EXT_HSM_HART_GET_STATUS 0x2
+
+#define SBI_HSM_HART_STATUS_STARTED 0x0
+#define SBI_HSM_HART_STATUS_STOPPED 0x1
+#define SBI_HSM_HART_STATUS_START_PENDING 0x2
+#define SBI_HSM_HART_STATUS_STOP_PENDING 0x3
+
+#define SBI_SPEC_VERSION_MAJOR_OFFSET 24
+#define SBI_SPEC_VERSION_MAJOR_MASK 0x7f
+#define SBI_SPEC_VERSION_MINOR_MASK 0xffffff
+#define SBI_EXT_VENDOR_START 0x09000000
+#define SBI_EXT_VENDOR_END 0x09FFFFFF
+/* clang-format on */
+
+#endif
static TCGv load_res;
static TCGv load_val;
/* globals for PM CSRs */
-static TCGv pm_mask[4];
-static TCGv pm_base[4];
+static TCGv pm_mask;
+static TCGv pm_base;
#include "exec/gen-icount.h"
bool ext_ifencei;
bool ext_zfh;
bool ext_zfhmin;
+ bool ext_zve32f;
+ bool ext_zve64f;
bool hlsx;
/* vector extension */
bool vill;
/* Space for 3 operands plus 1 extra for address computation. */
TCGv temp[4];
/* PointerMasking extension */
- bool pm_enabled;
- TCGv pm_mask;
- TCGv pm_base;
+ bool pm_mask_enabled;
+ bool pm_base_enabled;
} DisasContext;
static inline bool has_ext(DisasContext *ctx, uint32_t ext)
tcg_gen_movcond_i64(TCG_COND_GEU, out, in, t_max, in, t_nan);
}
+static void gen_set_pc_imm(DisasContext *ctx, target_ulong dest)
+{
+ if (get_xl(ctx) == MXL_RV32) {
+ dest = (int32_t)dest;
+ }
+ tcg_gen_movi_tl(cpu_pc, dest);
+}
+
+static void gen_set_pc(DisasContext *ctx, TCGv dest)
+{
+ if (get_xl(ctx) == MXL_RV32) {
+ tcg_gen_ext32s_tl(cpu_pc, dest);
+ } else {
+ tcg_gen_mov_tl(cpu_pc, dest);
+ }
+}
+
static void generate_exception(DisasContext *ctx, int excp)
{
- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
+ gen_set_pc_imm(ctx, ctx->base.pc_next);
gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp));
ctx->base.is_jmp = DISAS_NORETURN;
}
static void generate_exception_mtval(DisasContext *ctx, int excp)
{
- tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next);
+ gen_set_pc_imm(ctx, ctx->base.pc_next);
tcg_gen_st_tl(cpu_pc, cpu_env, offsetof(CPURISCVState, badaddr));
gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp));
ctx->base.is_jmp = DISAS_NORETURN;
{
if (translator_use_goto_tb(&ctx->base, dest)) {
tcg_gen_goto_tb(n);
- tcg_gen_movi_tl(cpu_pc, dest);
+ gen_set_pc_imm(ctx, dest);
tcg_gen_exit_tb(ctx->base.tb, n);
} else {
- tcg_gen_movi_tl(cpu_pc, dest);
+ gen_set_pc_imm(ctx, dest);
tcg_gen_lookup_and_goto_ptr();
}
}
return;
}
}
- if (rd != 0) {
- tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn);
- }
+ gen_set_gpri(ctx, rd, ctx->pc_succ_insn);
gen_goto_tb(ctx, 0, ctx->base.pc_next + imm); /* must use this for safety */
ctx->base.is_jmp = DISAS_NORETURN;
}
-/*
- * Generates address adjustment for PointerMasking
- */
-static TCGv gen_pm_adjust_address(DisasContext *s, TCGv src)
+/* Compute a canonical address from a register plus offset. */
+static TCGv get_address(DisasContext *ctx, int rs1, int imm)
{
- TCGv temp;
- if (!s->pm_enabled) {
- /* Load unmodified address */
- return src;
- } else {
- temp = temp_new(s);
- tcg_gen_andc_tl(temp, src, s->pm_mask);
- tcg_gen_or_tl(temp, temp, s->pm_base);
- return temp;
+ TCGv addr = temp_new(ctx);
+ TCGv src1 = get_gpr(ctx, rs1, EXT_NONE);
+
+ tcg_gen_addi_tl(addr, src1, imm);
+ if (ctx->pm_mask_enabled) {
+ tcg_gen_and_tl(addr, addr, pm_mask);
+ } else if (get_xl(ctx) == MXL_RV32) {
+ tcg_gen_ext32u_tl(addr, addr);
}
+ if (ctx->pm_base_enabled) {
+ tcg_gen_or_tl(addr, addr, pm_base);
+ }
+ return addr;
}
#ifndef CONFIG_USER_ONLY
ctx->ext_ifencei = cpu->cfg.ext_ifencei;
ctx->ext_zfh = cpu->cfg.ext_zfh;
ctx->ext_zfhmin = cpu->cfg.ext_zfhmin;
+ ctx->ext_zve32f = cpu->cfg.ext_zve32f;
+ ctx->ext_zve64f = cpu->cfg.ext_zve64f;
ctx->vlen = cpu->cfg.vlen;
ctx->elen = cpu->cfg.elen;
ctx->mstatus_hs_fs = FIELD_EX32(tb_flags, TB_FLAGS, MSTATUS_HS_FS);
ctx->cs = cs;
ctx->ntemp = 0;
memset(ctx->temp, 0, sizeof(ctx->temp));
- ctx->pm_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_ENABLED);
- int priv = tb_flags & TB_FLAGS_PRIV_MMU_MASK;
- ctx->pm_mask = pm_mask[priv];
- ctx->pm_base = pm_base[priv];
-
+ ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
+ ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
ctx->zero = tcg_constant_tl(0);
}
"load_res");
load_val = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, load_val),
"load_val");
-#ifndef CONFIG_USER_ONLY
/* Assign PM CSRs to tcg globals */
- pm_mask[PRV_U] =
- tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, upmmask), "upmmask");
- pm_base[PRV_U] =
- tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, upmbase), "upmbase");
- pm_mask[PRV_S] =
- tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, spmmask), "spmmask");
- pm_base[PRV_S] =
- tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, spmbase), "spmbase");
- pm_mask[PRV_M] =
- tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, mpmmask), "mpmmask");
- pm_base[PRV_M] =
- tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, mpmbase), "mpmbase");
-#endif
+ pm_mask = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, cur_pmmask),
+ "pmmask");
+ pm_base = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, cur_pmbase),
+ "pmbase");
}
uint64_t lmul = FIELD_EX64(s2, VTYPE, VLMUL);
uint16_t sew = 8 << FIELD_EX64(s2, VTYPE, VSEW);
uint8_t ediv = FIELD_EX64(s2, VTYPE, VEDIV);
- bool vill = FIELD_EX64(s2, VTYPE, VILL);
- target_ulong reserved = FIELD_EX64(s2, VTYPE, RESERVED);
+ int xlen = riscv_cpu_xlen(env);
+ bool vill = (s2 >> (xlen - 1)) & 0x1;
+ target_ulong reserved = s2 &
+ MAKE_64BIT_MASK(R_VTYPE_RESERVED_SHIFT,
+ xlen - 1 - R_VTYPE_RESERVED_SHIFT);
if (lmul & 4) {
/* Fractional LMUL. */
|| (ediv != 0)
|| (reserved != 0)) {
/* only set vill bit. */
- env->vtype = FIELD_DP64(0, VTYPE, VILL, 1);
+ env->vill = 1;
+ env->vtype = 0;
env->vl = 0;
env->vstart = 0;
return 0;
return scale < 0 ? vlenb >> -scale : vlenb << scale;
}
+static inline target_ulong adjust_addr(CPURISCVState *env, target_ulong addr)
+{
+ return (addr & env->cur_pmmask) | env->cur_pmbase;
+}
+
/*
* This function checks watchpoint before real load operation.
*
target_ulong pagelen = -(addr | TARGET_PAGE_MASK);
target_ulong curlen = MIN(pagelen, len);
- probe_access(env, addr, curlen, access_type,
+ probe_access(env, adjust_addr(env, addr), curlen, access_type,
cpu_mmu_index(env, false), ra);
if (len > curlen) {
addr += curlen;
curlen = len - curlen;
- probe_access(env, addr, curlen, access_type,
+ probe_access(env, adjust_addr(env, addr), curlen, access_type,
cpu_mmu_index(env, false), ra);
}
}
k = 0;
while (k < nf) {
target_ulong addr = base + stride * i + (k << esz);
- ldst_elem(env, addr, i + k * max_elems, vd, ra);
+ ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra);
k++;
}
}
k = 0;
while (k < nf) {
target_ulong addr = base + ((i * nf + k) << esz);
- ldst_elem(env, addr, i + k * max_elems, vd, ra);
+ ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra);
k++;
}
}
k = 0;
while (k < nf) {
abi_ptr addr = get_index_addr(base, i, vs2) + (k << esz);
- ldst_elem(env, addr, i + k * max_elems, vd, ra);
+ ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra);
k++;
}
}
if (!vm && !vext_elem_mask(v0, i)) {
continue;
}
- addr = base + i * (nf << esz);
+ addr = adjust_addr(env, base + i * (nf << esz));
if (i == 0) {
probe_pages(env, addr, nf << esz, ra, MMU_DATA_LOAD);
} else {
cpu_mmu_index(env, false));
if (host) {
#ifdef CONFIG_USER_ONLY
- if (page_check_range(addr, nf << esz, PAGE_READ) < 0) {
+ if (page_check_range(addr, offset, PAGE_READ) < 0) {
vl = i;
goto ProbeSuccess;
}
#else
- probe_pages(env, addr, nf << esz, ra, MMU_DATA_LOAD);
+ probe_pages(env, addr, offset, ra, MMU_DATA_LOAD);
#endif
} else {
vl = i;
break;
}
remain -= offset;
- addr += offset;
+ addr = adjust_addr(env, addr + offset);
}
}
}
}
while (k < nf) {
target_ulong addr = base + ((i * nf + k) << esz);
- ldst_elem(env, addr, i + k * max_elems, vd, ra);
+ ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra);
k++;
}
}
/* load/store rest of elements of current segment pointed by vstart */
for (pos = off; pos < max_elems; pos++, env->vstart++) {
target_ulong addr = base + ((pos + k * max_elems) << esz);
- ldst_elem(env, addr, pos + k * max_elems, vd, ra);
+ ldst_elem(env, adjust_addr(env, addr), pos + k * max_elems, vd, ra);
}
k++;
}
for (; k < nf; k++) {
for (i = 0; i < max_elems; i++, env->vstart++) {
target_ulong addr = base + ((i + k * max_elems) << esz);
- ldst_elem(env, addr, i + k * max_elems, vd, ra);
+ ldst_elem(env, adjust_addr(env, addr), i + k * max_elems, vd, ra);
}
}
[CC_OP_NZ_F64] = "CC_OP_NZ_F64",
[CC_OP_NZ_F128] = "CC_OP_NZ_F128",
[CC_OP_ICM] = "CC_OP_ICM",
- [CC_OP_SLA_32] = "CC_OP_SLA_32",
- [CC_OP_SLA_64] = "CC_OP_SLA_64",
+ [CC_OP_SLA] = "CC_OP_SLA",
[CC_OP_FLOGR] = "CC_OP_FLOGR",
[CC_OP_LCBB] = "CC_OP_LCBB",
[CC_OP_VC] = "CC_OP_VC",
CC_OP_NZ_F128, /* FP dst != 0 (128bit) */
CC_OP_ICM, /* insert characters under mask */
- CC_OP_SLA_32, /* Calculate shift left signed (32bit) */
- CC_OP_SLA_64, /* Calculate shift left signed (64bit) */
+ CC_OP_SLA, /* Calculate shift left signed */
CC_OP_FLOGR, /* find leftmost one */
CC_OP_LCBB, /* load count to block boundary */
CC_OP_VC, /* vector compare result */
case S390_CPU_STATE_OPERATING:
cpu->env.sigp_order = SIGP_STOP_STORE_STATUS;
cpu_inject_stop(cpu);
- /* store will be performed in do_stop_interrup() */
+ /* store will be performed in do_stop_interrupt() */
break;
case S390_CPU_STATE_STOPPED:
/* already stopped, just store the status */
{
S390CPU *cpu = env_archcpu(env);
- if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
- qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
- }
+ /*
+ * Complete the STOP operation before exposing the CPU as
+ * STOPPED to the system.
+ */
if (cpu->env.sigp_order == SIGP_STOP_STORE_STATUS) {
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
}
env->sigp_order = 0;
+ if (s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu) == 0) {
+ qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+ }
env->pending_int &= ~INTERRUPT_STOP;
}
}
}
-static uint32_t cc_calc_sla_32(uint32_t src, int shift)
+static uint32_t cc_calc_sla(uint64_t src, int shift)
{
- uint32_t mask = ((1U << shift) - 1U) << (32 - shift);
- uint32_t sign = 1U << 31;
- uint32_t match;
- int32_t r;
-
- /* Check if the sign bit stays the same. */
- if (src & sign) {
- match = mask;
- } else {
- match = 0;
- }
- if ((src & mask) != match) {
- /* Overflow. */
- return 3;
- }
-
- r = ((src << shift) & ~sign) | (src & sign);
- if (r == 0) {
- return 0;
- } else if (r < 0) {
- return 1;
- }
- return 2;
-}
-
-static uint32_t cc_calc_sla_64(uint64_t src, int shift)
-{
- uint64_t mask = ((1ULL << shift) - 1ULL) << (64 - shift);
+ uint64_t mask = -1ULL << (63 - shift);
uint64_t sign = 1ULL << 63;
uint64_t match;
int64_t r;
case CC_OP_ICM:
r = cc_calc_icm(src, dst);
break;
- case CC_OP_SLA_32:
- r = cc_calc_sla_32(src, dst);
- break;
- case CC_OP_SLA_64:
- r = cc_calc_sla_64(src, dst);
+ case CC_OP_SLA:
+ r = cc_calc_sla(src, dst);
break;
case CC_OP_FLOGR:
r = cc_calc_flogr(dst);
C(0xb9e1, POPCNT, RRE, PC, 0, r2_o, r1, 0, popcnt, nz64)
/* ROTATE LEFT SINGLE LOGICAL */
- C(0xeb1d, RLL, RSY_a, Z, r3_o, sh32, new, r1_32, rll32, 0)
- C(0xeb1c, RLLG, RSY_a, Z, r3_o, sh64, r1, 0, rll64, 0)
+ C(0xeb1d, RLL, RSY_a, Z, r3_o, sh, new, r1_32, rll32, 0)
+ C(0xeb1c, RLLG, RSY_a, Z, r3_o, sh, r1, 0, rll64, 0)
/* ROTATE THEN INSERT SELECTED BITS */
C(0xec55, RISBG, RIE_f, GIE, 0, r2, r1, 0, risbg, s64)
C(0x0400, SPM, RR_a, Z, r1, 0, 0, 0, spm, 0)
/* SHIFT LEFT SINGLE */
- D(0x8b00, SLA, RS_a, Z, r1, sh32, new, r1_32, sla, 0, 31)
- D(0xebdd, SLAK, RSY_a, DO, r3, sh32, new, r1_32, sla, 0, 31)
- D(0xeb0b, SLAG, RSY_a, Z, r3, sh64, r1, 0, sla, 0, 63)
+ D(0x8b00, SLA, RS_a, Z, r1, sh, new, r1_32, sla, 0, 31)
+ D(0xebdd, SLAK, RSY_a, DO, r3, sh, new, r1_32, sla, 0, 31)
+ D(0xeb0b, SLAG, RSY_a, Z, r3, sh, r1, 0, sla, 0, 63)
/* SHIFT LEFT SINGLE LOGICAL */
- C(0x8900, SLL, RS_a, Z, r1_o, sh32, new, r1_32, sll, 0)
- C(0xebdf, SLLK, RSY_a, DO, r3_o, sh32, new, r1_32, sll, 0)
- C(0xeb0d, SLLG, RSY_a, Z, r3_o, sh64, r1, 0, sll, 0)
+ C(0x8900, SLL, RS_a, Z, r1_o, sh, new, r1_32, sll, 0)
+ C(0xebdf, SLLK, RSY_a, DO, r3_o, sh, new, r1_32, sll, 0)
+ C(0xeb0d, SLLG, RSY_a, Z, r3_o, sh, r1, 0, sll, 0)
/* SHIFT RIGHT SINGLE */
- C(0x8a00, SRA, RS_a, Z, r1_32s, sh32, new, r1_32, sra, s32)
- C(0xebdc, SRAK, RSY_a, DO, r3_32s, sh32, new, r1_32, sra, s32)
- C(0xeb0a, SRAG, RSY_a, Z, r3_o, sh64, r1, 0, sra, s64)
+ C(0x8a00, SRA, RS_a, Z, r1_32s, sh, new, r1_32, sra, s32)
+ C(0xebdc, SRAK, RSY_a, DO, r3_32s, sh, new, r1_32, sra, s32)
+ C(0xeb0a, SRAG, RSY_a, Z, r3_o, sh, r1, 0, sra, s64)
/* SHIFT RIGHT SINGLE LOGICAL */
- C(0x8800, SRL, RS_a, Z, r1_32u, sh32, new, r1_32, srl, 0)
- C(0xebde, SRLK, RSY_a, DO, r3_32u, sh32, new, r1_32, srl, 0)
- C(0xeb0c, SRLG, RSY_a, Z, r3_o, sh64, r1, 0, srl, 0)
+ C(0x8800, SRL, RS_a, Z, r1_32u, sh, new, r1_32, srl, 0)
+ C(0xebde, SRLK, RSY_a, DO, r3_32u, sh, new, r1_32, srl, 0)
+ C(0xeb0c, SRLG, RSY_a, Z, r3_o, sh, r1, 0, srl, 0)
/* SHIFT LEFT DOUBLE */
- D(0x8f00, SLDA, RS_a, Z, r1_D32, sh64, new, r1_D32, sla, 0, 31)
+ D(0x8f00, SLDA, RS_a, Z, r1_D32, sh, new, r1_D32, sla, 0, 63)
/* SHIFT LEFT DOUBLE LOGICAL */
- C(0x8d00, SLDL, RS_a, Z, r1_D32, sh64, new, r1_D32, sll, 0)
+ C(0x8d00, SLDL, RS_a, Z, r1_D32, sh, new, r1_D32, sll, 0)
/* SHIFT RIGHT DOUBLE */
- C(0x8e00, SRDA, RS_a, Z, r1_D32, sh64, new, r1_D32, sra, s64)
+ C(0x8e00, SRDA, RS_a, Z, r1_D32, sh, new, r1_D32, sra, s64)
/* SHIFT RIGHT DOUBLE LOGICAL */
- C(0x8c00, SRDL, RS_a, Z, r1_D32, sh64, new, r1_D32, srl, 0)
+ C(0x8c00, SRDL, RS_a, Z, r1_D32, sh, new, r1_D32, srl, 0)
/* SQUARE ROOT */
F(0xb314, SQEBR, RRE, Z, 0, e2, new, e1, sqeb, 0, IF_BFP)
case CC_OP_LTUGTU_64:
case CC_OP_TM_32:
case CC_OP_TM_64:
- case CC_OP_SLA_32:
- case CC_OP_SLA_64:
+ case CC_OP_SLA:
case CC_OP_SUBU:
case CC_OP_NZ_F128:
case CC_OP_VC:
/* ====================================================================== */
/* Miscellaneous helpers, used by several operations. */
-static void help_l2_shift(DisasContext *s, DisasOps *o, int mask)
-{
- int b2 = get_field(s, b2);
- int d2 = get_field(s, d2);
-
- if (b2 == 0) {
- o->in2 = tcg_const_i64(d2 & mask);
- } else {
- o->in2 = get_address(s, 0, b2, d2);
- tcg_gen_andi_i64(o->in2, o->in2, mask);
- }
-}
-
static DisasJumpType help_goto_direct(DisasContext *s, uint64_t dest)
{
if (dest == s->pc_tmp) {
static DisasJumpType op_sla(DisasContext *s, DisasOps *o)
{
+ TCGv_i64 t;
uint64_t sign = 1ull << s->insn->data;
- enum cc_op cco = s->insn->data == 31 ? CC_OP_SLA_32 : CC_OP_SLA_64;
- gen_op_update2_cc_i64(s, cco, o->in1, o->in2);
+ if (s->insn->data == 31) {
+ t = tcg_temp_new_i64();
+ tcg_gen_shli_i64(t, o->in1, 32);
+ } else {
+ t = o->in1;
+ }
+ gen_op_update2_cc_i64(s, CC_OP_SLA, t, o->in2);
+ if (s->insn->data == 31) {
+ tcg_temp_free_i64(t);
+ }
tcg_gen_shl_i64(o->out, o->in1, o->in2);
/* The arithmetic left shift is curious in that it does not affect
the sign bit. Copy that over from the source unchanged. */
static void wout_r1_D32(DisasContext *s, DisasOps *o)
{
int r1 = get_field(s, r1);
+ TCGv_i64 t = tcg_temp_new_i64();
store_reg32_i64(r1 + 1, o->out);
- tcg_gen_shri_i64(o->out, o->out, 32);
- store_reg32_i64(r1, o->out);
+ tcg_gen_shri_i64(t, o->out, 32);
+ store_reg32_i64(r1, t);
+ tcg_temp_free_i64(t);
}
#define SPEC_wout_r1_D32 SPEC_r1_even
}
#define SPEC_in2_ri2 0
-static void in2_sh32(DisasContext *s, DisasOps *o)
+static void in2_sh(DisasContext *s, DisasOps *o)
{
- help_l2_shift(s, o, 31);
-}
-#define SPEC_in2_sh32 0
+ int b2 = get_field(s, b2);
+ int d2 = get_field(s, d2);
-static void in2_sh64(DisasContext *s, DisasOps *o)
-{
- help_l2_shift(s, o, 63);
+ if (b2 == 0) {
+ o->in2 = tcg_const_i64(d2 & 0x3f);
+ } else {
+ o->in2 = get_address(s, 0, b2, d2);
+ tcg_gen_andi_i64(o->in2, o->in2, 0x3f);
+ }
}
-#define SPEC_in2_sh64 0
+#define SPEC_in2_sh 0
static void in2_m2_8u(DisasContext *s, DisasOps *o)
{
.name = "cpu_timer",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(frequency, CPUTimer),
VMSTATE_UINT32(disabled, CPUTimer),
.name = "trap_state",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64(tpc, trap_state),
VMSTATE_UINT64(tnpc, trap_state),
.name = "tlb_entry",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64(tag, SparcTLBEntry),
VMSTATE_UINT64(tte, SparcTLBEntry),
.name = "cpu",
.version_id = SPARC_VMSTATE_VER,
.minimum_version_id = SPARC_VMSTATE_VER,
- .minimum_version_id_old = SPARC_VMSTATE_VER,
.pre_save = cpu_pre_save,
.fields = (VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.gregs, SPARCCPU, 8),
@echo " $(MAKE) check-clean Clean the tests and related data"
@echo
@echo "The following are useful for CI builds"
- @echo " $(MAKE) check-build Build most test binaris"
+ @echo " $(MAKE) check-build Build most test binaries"
@echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)"
@echo
@echo
ifeq ($(CONFIG_TOOLS)$(CONFIG_POSIX),yy)
check: check-block
-export PYTHON
-
-ifneq ($(filter check check-block check-build, $(MAKECMDGOALS)),)
-ninja-cmd-goals += \
- qemu-img$(EXESUF) \
- qemu-io$(EXESUF) \
- qemu-nbd$(EXESUF) \
- storage-daemon/qemu-storage-daemon$(EXESUF) \
- $(filter qemu-system-%, $(ninja-targets))
-endif
-
-check-block: $(SRC_PATH)/tests/check-block.sh run-ninja
- @$<
+check-block: run-ninja
+ $(if $(MAKE.n),,+)$(MESON) test $(MTESTARGS) $(.mtestargs) --verbose \
+ --logbase iotestslog $(call .speed.$(SPEED), block block-slow block-thorough)
endif
check-build: run-ninja
--- /dev/null
+# Smoke tests for 74xx cpus (aka G4).
+#
+# Copyright (c) 2021, IBM Corp.
+#
+# 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 QemuSystemTest
+from avocado_qemu import wait_for_console_pattern
+
+class ppc74xxCpu(QemuSystemTest):
+ """
+ :avocado: tags=arch:ppc
+ """
+ timeout = 5
+
+ def test_ppc_7400(self):
+ """
+ :avocado: tags=cpu:7400
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7410(self):
+ """
+ :avocado: tags=cpu:7410
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,74xx')
+
+ def test_ppc_7441(self):
+ """
+ :avocado: tags=cpu:7441
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7445(self):
+ """
+ :avocado: tags=cpu:7445
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7447(self):
+ """
+ :avocado: tags=cpu:7447
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7447a(self):
+ """
+ :avocado: tags=cpu:7447a
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7448(self):
+ """
+ :avocado: tags=cpu:7448
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,MPC86xx')
+
+ def test_ppc_7450(self):
+ """
+ :avocado: tags=cpu:7450
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7451(self):
+ """
+ :avocado: tags=cpu:7451
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7455(self):
+ """
+ :avocado: tags=cpu:7455
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7457(self):
+ """
+ :avocado: tags=cpu:7457
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
+
+ def test_ppc_7457a(self):
+ """
+ :avocado: tags=cpu:7457a
+ """
+ self.vm.set_console()
+ self.vm.launch()
+ wait_for_console_pattern(self, '>> OpenBIOS')
+ wait_for_console_pattern(self, '>> CPU type PowerPC,G4')
#!/bin/sh
-# Honor the SPEED environment variable, just like we do it for the qtests.
-if [ "$SPEED" = "slow" ]; then
- format_list="raw qcow2"
- group=
-elif [ "$SPEED" = "thorough" ]; then
- format_list="raw qcow2 qed vmdk vpc"
+if [ "$#" -eq 0 ]; then
+ echo "Usage: $0 fmt..." >&2
+ exit 99
+fi
+
+# Honor the SPEED environment variable, just like we do it for "meson test"
+format_list="$@"
+if [ "$SPEED" = "slow" ] || [ "$SPEED" = "thorough" ]; then
group=
else
- format_list=qcow2
group="-g auto"
fi
-if [ "$#" -ne 0 ]; then
- format_list="$@"
-fi
+skip() {
+ echo "1..0 #SKIP $*"
+ exit 0
+}
if grep -q "CONFIG_GPROF=y" config-host.mak 2>/dev/null ; then
- echo "GPROF is enabled ==> Not running the qemu-iotests."
- exit 0
+ skip "GPROF is enabled ==> Not running the qemu-iotests."
fi
# Disable tests with any sanitizer except for specific ones
done
if echo ${SANITIZE_FLAGS} | grep -q "\-fsanitize" 2>/dev/null; then
# Have a sanitize flag that is not allowed, stop
- echo "Sanitizers are enabled ==> Not running the qemu-iotests."
- exit 0
+ skip "Sanitizers are enabled ==> Not running the qemu-iotests."
fi
if [ -z "$(find . -name 'qemu-system-*' -print)" ]; then
- echo "No qemu-system binary available ==> Not running the qemu-iotests."
- exit 0
+ skip "No qemu-system binary available ==> Not running the qemu-iotests."
fi
if ! command -v bash >/dev/null 2>&1 ; then
- echo "bash not available ==> Not running the qemu-iotests."
- exit 0
+ skip "bash not available ==> Not running the qemu-iotests."
fi
if LANG=C bash --version | grep -q 'GNU bash, version [123]' ; then
- echo "bash version too old ==> Not running the qemu-iotests."
- exit 0
+ skip "bash version too old ==> Not running the qemu-iotests."
fi
if ! (sed --version | grep 'GNU sed') > /dev/null 2>&1 ; then
if ! command -v gsed >/dev/null 2>&1; then
- echo "GNU sed not available ==> Not running the qemu-iotests."
- exit 0
+ skip "GNU sed not available ==> Not running the qemu-iotests."
fi
else
# Double-check that we're not using BusyBox' sed which says
# that "This is not GNU sed version 4.0" ...
if sed --version | grep -q 'not GNU sed' ; then
- echo "BusyBox sed not supported ==> Not running the qemu-iotests."
- exit 0
+ skip "BusyBox sed not supported ==> Not running the qemu-iotests."
fi
fi
# QEMU_CHECK_BLOCK_AUTO is used to disable some unstable sub-tests
export QEMU_CHECK_BLOCK_AUTO=1
export PYTHONUTF8=1
+# If make was called with -jN we want to call ./check with -j N. Extract the
+# flag from MAKEFLAGS, so that if it absent (or MAKEFLAGS is not defined), JOBS
+# would be an empty line otherwise JOBS is prepared string of flag with value:
+# "-j N"
+# Note, that the following works even if make was called with "-j N" or even
+# "--jobs N", as all these variants becomes simply "-jN" in MAKEFLAGS variable.
+JOBS=$(echo "$MAKEFLAGS" | sed -n 's/\(^\|.* \)-j\([0-9]\+\)\( .*\|$\)/-j \2/p')
ret=0
for fmt in $format_list ; do
- ${PYTHON} ./check -makecheck -$fmt $group || ret=1
+ ${PYTHON} ./check $JOBS -tap -$fmt $group || ret=1
done
exit $ret
+# THIS FILE WAS AUTO-GENERATED
+#
+# $ lcitool dockerfile alpine-edge qemu
+#
+# https://gitlab.com/libvirt/libvirt-ci
-FROM alpine:edge
+FROM docker.io/library/alpine:edge
-RUN apk update
-RUN apk upgrade
+RUN apk update && \
+ apk upgrade && \
+ apk add \
+ alsa-lib-dev \
+ attr-dev \
+ bash \
+ bc \
+ bzip2 \
+ bzip2-dev \
+ ca-certificates \
+ capstone-dev \
+ ccache \
+ cdrkit \
+ ceph-dev \
+ clang \
+ ctags \
+ curl-dev \
+ cyrus-sasl-dev \
+ dbus \
+ diffutils \
+ dtc-dev \
+ eudev-dev \
+ findutils \
+ fuse3-dev \
+ g++ \
+ gcc \
+ gcovr \
+ gettext \
+ git \
+ glib-dev \
+ glib-static \
+ gnutls-dev \
+ gtk+3.0-dev \
+ libaio-dev \
+ libbpf-dev \
+ libcap-ng-dev \
+ libdrm-dev \
+ libepoxy-dev \
+ libffi-dev \
+ libgcrypt-dev \
+ libjpeg-turbo-dev \
+ libnfs-dev \
+ libpng-dev \
+ libseccomp-dev \
+ libselinux-dev \
+ libslirp-dev \
+ libssh-dev \
+ libtasn1-dev \
+ liburing-dev \
+ libusb-dev \
+ libxml2-dev \
+ linux-pam-dev \
+ llvm11 \
+ lttng-ust-dev \
+ lzo-dev \
+ make \
+ mesa-dev \
+ meson \
+ multipath-tools \
+ ncurses-dev \
+ ndctl-dev \
+ net-tools \
+ nettle-dev \
+ nmap-ncat \
+ numactl-dev \
+ openssh-client \
+ pcre-dev \
+ perl \
+ perl-test-harness \
+ pixman-dev \
+ pkgconf \
+ pulseaudio-dev \
+ py3-numpy \
+ py3-pillow \
+ py3-pip \
+ py3-sphinx \
+ py3-sphinx_rtd_theme \
+ py3-virtualenv \
+ py3-yaml \
+ python3 \
+ rpm2cpio \
+ samurai \
+ sdl2-dev \
+ sdl2_image-dev \
+ sed \
+ snappy-dev \
+ sparse \
+ spice-dev \
+ spice-protocol \
+ tar \
+ tesseract-ocr \
+ texinfo \
+ usbredir-dev \
+ util-linux \
+ vde2-dev \
+ virglrenderer-dev \
+ vte3-dev \
+ which \
+ xen-dev \
+ xfsprogs-dev \
+ zlib-dev \
+ zlib-static \
+ zstd-dev && \
+ mkdir -p /usr/libexec/ccache-wrappers && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
-# Please keep this list sorted alphabetically
-ENV PACKAGES \
- alsa-lib-dev \
- bash \
- binutils \
- ccache \
- coreutils \
- curl-dev \
- g++ \
- gcc \
- git \
- glib-dev \
- glib-static \
- gnutls-dev \
- gtk+3.0-dev \
- libaio-dev \
- libbpf-dev \
- libcap-ng-dev \
- libffi-dev \
- libjpeg-turbo-dev \
- libnfs-dev \
- libpng-dev \
- libseccomp-dev \
- libssh-dev \
- libusb-dev \
- libxml2-dev \
- lzo-dev \
- make \
- mesa-dev \
- mesa-egl \
- mesa-gbm \
- meson \
- ncurses-dev \
- ninja \
- perl \
- pulseaudio-dev \
- python3 \
- py3-sphinx \
- py3-sphinx_rtd_theme \
- shadow \
- snappy-dev \
- spice-dev \
- texinfo \
- usbredir-dev \
- util-linux-dev \
- vde2-dev \
- virglrenderer-dev \
- vte3-dev \
- xfsprogs-dev \
- zlib-dev \
- zlib-static
-
-RUN apk add $PACKAGES
+ENV LANG "en_US.UTF-8"
+ENV MAKE "/usr/bin/make"
+ENV NINJA "/usr/bin/ninja"
+ENV PYTHON "/usr/bin/python3"
+ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
-FROM docker.io/centos:8
+# THIS FILE WAS AUTO-GENERATED
+#
+# $ lcitool dockerfile centos-8 qemu
+#
+# https://gitlab.com/libvirt/libvirt-ci
-RUN dnf -y update
-ENV PACKAGES \
- SDL2-devel \
- alsa-lib-devel \
- bc \
- brlapi-devel \
- bzip2 \
- bzip2-devel \
- ca-certificates \
- capstone-devel \
- ccache \
- clang \
- ctags \
- cyrus-sasl-devel \
- daxctl-devel \
- dbus-daemon \
- device-mapper-multipath-devel \
- diffutils \
- findutils \
- gcc \
- gcc-c++ \
- genisoimage \
- gettext \
- git \
- glib2-devel \
- glibc-langpack-en \
- glibc-static \
- glusterfs-api-devel \
- gnutls-devel \
- gtk3-devel \
- hostname \
- jemalloc-devel \
- libaio-devel \
- libasan \
- libattr-devel \
- libbpf-devel \
- libcacard-devel \
- libcap-ng-devel \
- libcurl-devel \
- libdrm-devel \
- libepoxy-devel \
- libfdt-devel \
- libffi-devel \
- libgcrypt-devel \
- libiscsi-devel \
- libjpeg-devel \
- libnfs-devel \
- libpmem-devel \
- libpng-devel \
- librbd-devel \
- libseccomp-devel \
- libselinux-devel \
- libslirp-devel \
- libssh-devel \
- libtasn1-devel \
- libubsan \
- libudev-devel \
- libusbx-devel \
- libxml2-devel \
- libzstd-devel \
- llvm \
- lzo-devel \
- make \
- mesa-libgbm-devel \
- ncurses-devel \
- nettle-devel \
- ninja-build \
- nmap-ncat \
- numactl-devel \
- openssh-clients \
- pam-devel \
- perl \
- perl-Test-Harness \
- pixman-devel \
- pkgconfig \
- pulseaudio-libs-devel \
- python3 \
- python3-PyYAML \
- python3-numpy \
- python3-pillow \
- python3-pip \
- python3-setuptools \
- python3-sphinx \
- python3-sphinx_rtd_theme \
- python3-virtualenv \
- python3-wheel \
- rdma-core-devel \
- rpm \
- sed \
- snappy-devel \
- spice-protocol \
- spice-server-devel \
- systemd-devel \
- systemtap-sdt-devel \
- tar \
- texinfo \
- usbredir-devel \
- util-linux \
- virglrenderer-devel \
- vte291-devel \
- which \
- xfsprogs-devel \
- zlib-devel
+FROM docker.io/library/centos:8
-RUN dnf install -y dnf-plugins-core && \
- dnf config-manager --set-enabled powertools && \
- dnf install -y centos-release-advanced-virtualization && \
- dnf install -y epel-release && \
- dnf install -y $PACKAGES
-RUN rpm -q $PACKAGES | sort > /packages.txt
+RUN dnf update -y && \
+ dnf install 'dnf-command(config-manager)' -y && \
+ dnf config-manager --set-enabled -y powertools && \
+ dnf install -y centos-release-advanced-virtualization && \
+ dnf install -y epel-release && \
+ dnf install -y \
+ SDL2-devel \
+ alsa-lib-devel \
+ bash \
+ bc \
+ brlapi-devel \
+ bzip2 \
+ bzip2-devel \
+ ca-certificates \
+ capstone-devel \
+ ccache \
+ clang \
+ ctags \
+ cyrus-sasl-devel \
+ daxctl-devel \
+ dbus-daemon \
+ device-mapper-multipath-devel \
+ diffutils \
+ findutils \
+ fuse3-devel \
+ gcc \
+ gcc-c++ \
+ genisoimage \
+ gettext \
+ git \
+ glib2-devel \
+ glib2-static \
+ glibc-langpack-en \
+ glibc-static \
+ glusterfs-api-devel \
+ gnutls-devel \
+ gtk3-devel \
+ hostname \
+ jemalloc-devel \
+ libaio-devel \
+ libasan \
+ libattr-devel \
+ libbpf-devel \
+ libcacard-devel \
+ libcap-ng-devel \
+ libcurl-devel \
+ libdrm-devel \
+ libepoxy-devel \
+ libfdt-devel \
+ libffi-devel \
+ libgcrypt-devel \
+ libiscsi-devel \
+ libjpeg-devel \
+ libnfs-devel \
+ libpmem-devel \
+ libpng-devel \
+ librbd-devel \
+ libseccomp-devel \
+ libselinux-devel \
+ libslirp-devel \
+ libssh-devel \
+ libtasn1-devel \
+ libubsan \
+ libudev-devel \
+ liburing-devel \
+ libusbx-devel \
+ libxml2-devel \
+ libzstd-devel \
+ llvm \
+ lttng-ust-devel \
+ lzo-devel \
+ make \
+ mesa-libgbm-devel \
+ meson \
+ ncurses-devel \
+ nettle-devel \
+ ninja-build \
+ nmap-ncat \
+ numactl-devel \
+ openssh-clients \
+ pam-devel \
+ pcre-static \
+ perl \
+ perl-Test-Harness \
+ pixman-devel \
+ pkgconfig \
+ pulseaudio-libs-devel \
+ python3 \
+ python3-PyYAML \
+ python3-numpy \
+ python3-pillow \
+ python3-pip \
+ python3-sphinx \
+ python3-sphinx_rtd_theme \
+ python3-virtualenv \
+ rdma-core-devel \
+ rpm \
+ sed \
+ snappy-devel \
+ spice-protocol \
+ spice-server-devel \
+ systemd-devel \
+ systemtap-sdt-devel \
+ tar \
+ texinfo \
+ usbredir-devel \
+ util-linux \
+ virglrenderer-devel \
+ vte291-devel \
+ which \
+ xfsprogs-devel \
+ zlib-devel \
+ zlib-static && \
+ dnf autoremove -y && \
+ dnf clean all -y && \
+ rpm -qa | sort > /packages.txt && \
+ mkdir -p /usr/libexec/ccache-wrappers && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
+
+ENV LANG "en_US.UTF-8"
+ENV MAKE "/usr/bin/make"
+ENV NINJA "/usr/bin/ninja"
+ENV PYTHON "/usr/bin/python3"
+ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
RUN apt update && \
DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \
DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \
+ bison \
bzip2 \
ca-certificates \
ccache \
mingw32-pixman \
mingw32-pkg-config \
mingw32-SDL2 \
+ msitools \
perl \
perl-Test-Harness \
python3 \
mingw64-libusbx \
mingw64-pixman \
mingw64-pkg-config \
+ msitools \
perl \
perl-Test-Harness \
python3 \
-FROM registry.fedoraproject.org/fedora:33
+# THIS FILE WAS AUTO-GENERATED
+#
+# $ lcitool dockerfile fedora-35 qemu
+#
+# https://gitlab.com/libvirt/libvirt-ci
-# Please keep this list sorted alphabetically
-ENV PACKAGES \
- SDL2-devel \
- SDL2_image-devel \
- alsa-lib-devel \
- bc \
- brlapi-devel \
- bzip2 \
- bzip2-devel \
- ca-certificates \
- capstone-devel \
- ccache \
- clang \
- ctags \
- cyrus-sasl-devel \
- daxctl-devel \
- dbus-daemon \
- device-mapper-multipath-devel \
- diffutils \
- findutils \
- gcc \
- gcc-c++ \
- gcovr \
- genisoimage \
- gettext \
- git \
- glib2-devel \
- glibc-langpack-en \
- glibc-static \
- glusterfs-api-devel \
- gnutls-devel \
- gtk3-devel \
- hostname \
- jemalloc-devel \
- libaio-devel \
- libasan \
- libattr-devel \
- libbpf-devel \
- libcacard-devel \
- libcap-ng-devel \
- libcurl-devel \
- libdrm-devel \
- libepoxy-devel \
- libfdt-devel \
- libffi-devel \
- libgcrypt-devel \
- libiscsi-devel \
- libjpeg-devel \
- libnfs-devel \
- libpmem-devel \
- libpng-devel \
- librbd-devel \
- libseccomp-devel \
- libselinux-devel \
- libslirp-devel \
- libssh-devel \
- libtasn1-devel \
- libubsan \
- libudev-devel \
- liburing-devel \
- libusbx-devel \
- libxml2-devel \
- libzstd-devel \
- llvm \
- lttng-ust-devel \
- lzo-devel \
- make \
- mesa-libgbm-devel \
- meson \
- ncurses-devel \
- nettle-devel \
- ninja-build \
- nmap-ncat \
- numactl-devel \
- openssh-clients \
- pam-devel \
- perl-Test-Harness \
- perl-base \
- pixman-devel \
- pkgconfig \
- pulseaudio-libs-devel \
- python3 \
- python3-PyYAML \
- python3-numpy \
- python3-opencv \
- python3-pillow \
- python3-pip \
- python3-sphinx \
- python3-sphinx_rtd_theme \
- python3-virtualenv \
- rdma-core-devel \
- rpm \
- sed \
- snappy-devel \
- sparse \
- spice-protocol \
- spice-server-devel \
- systemd-devel \
- systemtap-sdt-devel \
- tar \
- tesseract \
- tesseract-langpack-eng \
- texinfo \
- usbredir-devel \
- util-linux \
- virglrenderer-devel \
- vte291-devel \
- which \
- xen-devel \
- xfsprogs-devel \
- zlib-devel
-ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3
+FROM registry.fedoraproject.org/fedora:35
-RUN dnf install -y $PACKAGES
-RUN rpm -q $PACKAGES | sort > /packages.txt
-ENV PATH $PATH:/usr/libexec/python3-sphinx/
+RUN dnf install -y nosync && \
+ echo -e '#!/bin/sh\n\
+if test -d /usr/lib64\n\
+then\n\
+ export LD_PRELOAD=/usr/lib64/nosync/nosync.so\n\
+else\n\
+ export LD_PRELOAD=/usr/lib/nosync/nosync.so\n\
+fi\n\
+exec "$@"' > /usr/bin/nosync && \
+ chmod +x /usr/bin/nosync && \
+ nosync dnf update -y && \
+ nosync dnf install -y \
+ SDL2-devel \
+ SDL2_image-devel \
+ alsa-lib-devel \
+ bash \
+ bc \
+ brlapi-devel \
+ bzip2 \
+ bzip2-devel \
+ ca-certificates \
+ capstone-devel \
+ ccache \
+ clang \
+ ctags \
+ cyrus-sasl-devel \
+ daxctl-devel \
+ dbus-daemon \
+ device-mapper-multipath-devel \
+ diffutils \
+ findutils \
+ fuse3-devel \
+ gcc \
+ gcc-c++ \
+ gcovr \
+ genisoimage \
+ gettext \
+ git \
+ glib2-devel \
+ glib2-static \
+ glibc-langpack-en \
+ glibc-static \
+ glusterfs-api-devel \
+ gnutls-devel \
+ gtk3-devel \
+ hostname \
+ jemalloc-devel \
+ libaio-devel \
+ libasan \
+ libattr-devel \
+ libbpf-devel \
+ libcacard-devel \
+ libcap-ng-devel \
+ libcurl-devel \
+ libdrm-devel \
+ libepoxy-devel \
+ libfdt-devel \
+ libffi-devel \
+ libgcrypt-devel \
+ libiscsi-devel \
+ libjpeg-devel \
+ libnfs-devel \
+ libpmem-devel \
+ libpng-devel \
+ librbd-devel \
+ libseccomp-devel \
+ libselinux-devel \
+ libslirp-devel \
+ libssh-devel \
+ libtasn1-devel \
+ libubsan \
+ libudev-devel \
+ liburing-devel \
+ libusbx-devel \
+ libxml2-devel \
+ libzstd-devel \
+ llvm \
+ lttng-ust-devel \
+ lzo-devel \
+ make \
+ mesa-libgbm-devel \
+ meson \
+ ncurses-devel \
+ nettle-devel \
+ ninja-build \
+ nmap-ncat \
+ numactl-devel \
+ openssh-clients \
+ pam-devel \
+ pcre-static \
+ perl-Test-Harness \
+ perl-base \
+ pixman-devel \
+ pkgconfig \
+ pulseaudio-libs-devel \
+ python3 \
+ python3-PyYAML \
+ python3-numpy \
+ python3-opencv \
+ python3-pillow \
+ python3-pip \
+ python3-sphinx \
+ python3-sphinx_rtd_theme \
+ python3-virtualenv \
+ rdma-core-devel \
+ rpm \
+ sed \
+ snappy-devel \
+ sparse \
+ spice-protocol \
+ spice-server-devel \
+ systemd-devel \
+ systemtap-sdt-devel \
+ tar \
+ tesseract \
+ tesseract-langpack-eng \
+ texinfo \
+ usbredir-devel \
+ util-linux \
+ virglrenderer-devel \
+ vte291-devel \
+ which \
+ xen-devel \
+ xfsprogs-devel \
+ zlib-devel \
+ zlib-static && \
+ nosync dnf autoremove -y && \
+ nosync dnf clean all -y && \
+ rpm -qa | sort > /packages.txt && \
+ mkdir -p /usr/libexec/ccache-wrappers && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
+
+ENV LANG "en_US.UTF-8"
+ENV MAKE "/usr/bin/make"
+ENV NINJA "/usr/bin/ninja"
+ENV PYTHON "/usr/bin/python3"
+ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
+# THIS FILE WAS AUTO-GENERATED
+#
+# $ lcitool dockerfile opensuse-leap-152 qemu
+#
+# https://gitlab.com/libvirt/libvirt-ci
+
FROM registry.opensuse.org/opensuse/leap:15.2
-# Please keep this list sorted alphabetically
-ENV PACKAGES \
- Mesa-devel \
- alsa-lib-devel \
- bc \
- brlapi-devel \
- bzip2 \
- ca-certificates \
- ccache \
- clang \
- ctags \
- cyrus-sasl-devel \
- dbus-1 \
- diffutils \
- findutils \
- gcc \
- gcc-c++ \
- gcovr \
- gettext-runtime \
- git \
- glib2-devel \
- glibc-locale \
- glibc-static \
- glusterfs-devel \
- gtk3-devel \
- hostname \
- jemalloc-devel \
- libSDL2-devel \
- libSDL2_image-devel \
- libaio-devel \
- libasan6 \
- libattr-devel \
- libbpf-devel \
- libbz2-devel \
- libcacard-devel \
- libcap-ng-devel \
- libcurl-devel \
- libdrm-devel \
- libepoxy-devel \
- libfdt-devel \
- libffi-devel \
- libgcrypt-devel \
- libgnutls-devel \
- libiscsi-devel \
- libjpeg8-devel \
- libndctl-devel \
- libnettle-devel \
- libnfs-devel \
- libnuma-devel \
- libpixman-1-0-devel \
- libpmem-devel \
- libpng16-devel \
- libpulse-devel \
- librbd-devel \
- libseccomp-devel \
- libselinux-devel \
- libspice-server-devel \
- libssh-devel \
- libtasn1-devel \
- libubsan1 \
- libudev-devel \
- libusb-1_0-devel \
- libxml2-devel \
- libzstd-devel \
- llvm \
- lttng-ust-devel \
- lzo-devel \
- make \
- mkisofs \
- ncat \
- ncurses-devel \
- ninja \
- openssh \
- pam-devel \
- perl-Test-Harness \
- perl-base \
- pkgconfig \
- python3-Pillow \
- python3-PyYAML \
- python3-Sphinx \
- python3-base \
- python3-numpy \
- python3-opencv \
- python3-pip \
- python3-setuptools \
- python3-sphinx_rtd_theme \
- python3-virtualenv \
- python3-wheel \
- rdma-core-devel \
- rpm \
- sed \
- snappy-devel \
- sparse \
- spice-protocol-devel \
- systemd-devel \
- systemtap-sdt-devel \
- tar \
- tesseract-ocr \
- tesseract-ocr-traineddata-english \
- texinfo \
- usbredir-devel \
- util-linux \
- virglrenderer-devel \
- vte-devel \
- which \
- xen-devel \
- xfsprogs-devel \
- zlib-devel
-ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3.6
+RUN zypper update -y && \
+ zypper install -y \
+ Mesa-devel \
+ alsa-lib-devel \
+ bash \
+ bc \
+ brlapi-devel \
+ bzip2 \
+ ca-certificates \
+ ccache \
+ clang \
+ ctags \
+ cyrus-sasl-devel \
+ dbus-1 \
+ diffutils \
+ findutils \
+ fuse3-devel \
+ gcc \
+ gcc-c++ \
+ gcovr \
+ gettext-runtime \
+ git \
+ glib2-devel \
+ glibc-locale \
+ glibc-static \
+ glusterfs-devel \
+ gtk3-devel \
+ hostname \
+ jemalloc-devel \
+ libSDL2-devel \
+ libSDL2_image-devel \
+ libaio-devel \
+ libasan6 \
+ libattr-devel \
+ libbpf-devel \
+ libbz2-devel \
+ libcacard-devel \
+ libcap-ng-devel \
+ libcurl-devel \
+ libdrm-devel \
+ libepoxy-devel \
+ libfdt-devel \
+ libffi-devel \
+ libgcrypt-devel \
+ libgnutls-devel \
+ libiscsi-devel \
+ libjpeg8-devel \
+ libndctl-devel \
+ libnettle-devel \
+ libnfs-devel \
+ libnuma-devel \
+ libpixman-1-0-devel \
+ libpmem-devel \
+ libpng16-devel \
+ libpulse-devel \
+ librbd-devel \
+ libseccomp-devel \
+ libselinux-devel \
+ libspice-server-devel \
+ libssh-devel \
+ libtasn1-devel \
+ libubsan1 \
+ libudev-devel \
+ liburing-devel \
+ libusb-1_0-devel \
+ libxml2-devel \
+ libzstd-devel \
+ llvm \
+ lttng-ust-devel \
+ lzo-devel \
+ make \
+ mkisofs \
+ ncat \
+ ncurses-devel \
+ ninja \
+ openssh \
+ pam-devel \
+ pcre-devel-static \
+ perl-Test-Harness \
+ perl-base \
+ pkgconfig \
+ python3-Pillow \
+ python3-PyYAML \
+ python3-Sphinx \
+ python3-base \
+ python3-numpy \
+ python3-opencv \
+ python3-pip \
+ python3-setuptools \
+ python3-sphinx_rtd_theme \
+ python3-virtualenv \
+ python3-wheel \
+ rdma-core-devel \
+ rpm \
+ sed \
+ snappy-devel \
+ sparse \
+ spice-protocol-devel \
+ systemd-devel \
+ systemtap-sdt-devel \
+ tar \
+ tesseract-ocr \
+ tesseract-ocr-traineddata-english \
+ texinfo \
+ usbredir-devel \
+ util-linux \
+ virglrenderer-devel \
+ vte-devel \
+ which \
+ xen-devel \
+ xfsprogs-devel \
+ zlib-devel \
+ zlib-devel-static && \
+ zypper clean --all && \
+ rpm -qa | sort > /packages.txt && \
+ mkdir -p /usr/libexec/ccache-wrappers && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
+
+RUN pip3 install \
+ meson==0.56.0
-RUN zypper update -y && zypper --non-interactive install -y $PACKAGES
-RUN rpm -q $PACKAGES | sort > /packages.txt
+ENV LANG "en_US.UTF-8"
+ENV MAKE "/usr/bin/make"
+ENV NINJA "/usr/bin/ninja"
+ENV PYTHON "/usr/bin/python3"
+ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
+++ /dev/null
-#
-# Latest Ubuntu Release
-#
-# Useful for testing against relatively bleeding edge libraries and
-# compilers. We also have seperate recipe for the most recent LTS
-# release.
-#
-# When updating use the full tag not :latest otherwise the build
-# system won't pick up that it has changed.
-#
-
-FROM docker.io/library/ubuntu:20.04
-ENV PACKAGES \
- ccache \
- clang \
- dbus \
- gcc \
- gettext \
- git \
- glusterfs-common \
- libaio-dev \
- libattr1-dev \
- libbrlapi-dev \
- libbz2-dev \
- libcacard-dev \
- libcap-ng-dev \
- libcurl4-gnutls-dev \
- libdrm-dev \
- libepoxy-dev \
- libfdt-dev \
- libffi-dev \
- libgbm-dev \
- libgnutls28-dev \
- libgtk-3-dev \
- libibverbs-dev \
- libiscsi-dev \
- libjemalloc-dev \
- libjpeg-turbo8-dev \
- liblzo2-dev \
- libncurses5-dev \
- libncursesw5-dev \
- libnfs-dev \
- libnuma-dev \
- libpixman-1-dev \
- libpng-dev \
- librados-dev \
- librbd-dev \
- librdmacm-dev \
- libsasl2-dev \
- libsdl2-dev \
- libseccomp-dev \
- libsnappy-dev \
- libspice-protocol-dev \
- libspice-server-dev \
- libssh-dev \
- libusb-1.0-0-dev \
- libusbredirhost-dev \
- libvdeplug-dev \
- libvte-2.91-dev \
- libxen-dev \
- libzstd-dev \
- make \
- ninja-build \
- python3-yaml \
- python3-sphinx \
- python3-sphinx-rtd-theme \
- sparse \
- xfslibs-dev
-RUN apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES
-RUN dpkg -l $PACKAGES | sort > /packages.txt
+# THIS FILE WAS AUTO-GENERATED
+#
+# $ lcitool dockerfile ubuntu-1804 qemu
+#
+# https://gitlab.com/libvirt/libvirt-ci
+
FROM docker.io/library/ubuntu:18.04
-ENV PACKAGES \
- bc \
- bsdmainutils \
- bzip2 \
- ca-certificates \
- ccache \
- clang \
- dbus \
- debianutils \
- diffutils \
- exuberant-ctags \
- findutils \
- g++ \
- gcc \
- gcovr \
- genisoimage \
- gettext \
- git \
- glusterfs-common \
- hostname \
- libaio-dev \
- libasan5 \
- libasound2-dev \
- libattr1-dev \
- libbrlapi-dev \
- libbz2-dev \
- libc6-dev \
- libcacard-dev \
- libcap-ng-dev \
- libcapstone-dev \
- libcurl4-gnutls-dev \
- libdaxctl-dev \
- libdrm-dev \
- libepoxy-dev \
- libfdt-dev \
- libffi-dev \
- libgbm-dev \
- libgcrypt20-dev \
- libglib2.0-dev \
- libgnutls28-dev \
- libgtk-3-dev \
- libibverbs-dev \
- libiscsi-dev \
- libjemalloc-dev \
- libjpeg-turbo8-dev \
- liblttng-ust-dev \
- liblzo2-dev \
- libncursesw5-dev \
- libnfs-dev \
- libnuma-dev \
- libpam0g-dev \
- libpixman-1-dev \
- libpmem-dev \
- libpng-dev \
- libpulse-dev \
- librbd-dev \
- librdmacm-dev \
- libsasl2-dev \
- libsdl2-dev \
- libsdl2-image-dev \
- libseccomp-dev \
- libselinux-dev \
- libsnappy-dev \
- libspice-protocol-dev \
- libspice-server-dev \
- libssh-dev \
- libsystemd-dev \
- libtasn1-6-dev \
- libtest-harness-perl \
- libubsan1 \
- libudev-dev \
- libusb-1.0-0-dev \
- libusbredirhost-dev \
- libvdeplug-dev \
- libvirglrenderer-dev \
- libvte-2.91-dev \
- libxen-dev \
- libxml2-dev \
- libzstd-dev \
- llvm \
- locales \
- make \
- multipath-tools \
- netcat-openbsd \
- nettle-dev \
- ninja-build \
- openssh-client \
- perl-base \
- pkgconf \
- python3 \
- python3-numpy \
- python3-opencv \
- python3-pillow \
- python3-pip \
- python3-setuptools \
- python3-sphinx \
- python3-sphinx-rtd-theme \
- python3-venv \
- python3-wheel \
- python3-yaml \
- rpm2cpio \
- sed \
- sparse \
- systemtap-sdt-dev \
- tar \
- tesseract-ocr \
- tesseract-ocr-eng \
- texinfo \
- xfslibs-dev \
- zlib1g-dev
-RUN apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES
-RUN dpkg -l $PACKAGES | sort > /packages.txt
+RUN export DEBIAN_FRONTEND=noninteractive && \
+ apt-get update && \
+ apt-get install -y eatmydata && \
+ eatmydata apt-get dist-upgrade -y && \
+ eatmydata apt-get install --no-install-recommends -y \
+ bash \
+ bc \
+ bsdmainutils \
+ bzip2 \
+ ca-certificates \
+ ccache \
+ clang \
+ dbus \
+ debianutils \
+ diffutils \
+ exuberant-ctags \
+ findutils \
+ g++ \
+ gcc \
+ gcovr \
+ genisoimage \
+ gettext \
+ git \
+ glusterfs-common \
+ hostname \
+ libaio-dev \
+ libasan5 \
+ libasound2-dev \
+ libattr1-dev \
+ libbrlapi-dev \
+ libbz2-dev \
+ libc6-dev \
+ libcacard-dev \
+ libcap-ng-dev \
+ libcapstone-dev \
+ libcurl4-gnutls-dev \
+ libdaxctl-dev \
+ libdrm-dev \
+ libepoxy-dev \
+ libfdt-dev \
+ libffi-dev \
+ libgbm-dev \
+ libgcrypt20-dev \
+ libglib2.0-dev \
+ libgnutls28-dev \
+ libgtk-3-dev \
+ libibverbs-dev \
+ libiscsi-dev \
+ libjemalloc-dev \
+ libjpeg-turbo8-dev \
+ liblttng-ust-dev \
+ liblzo2-dev \
+ libncursesw5-dev \
+ libnfs-dev \
+ libnuma-dev \
+ libpam0g-dev \
+ libpcre2-dev \
+ libpixman-1-dev \
+ libpmem-dev \
+ libpng-dev \
+ libpulse-dev \
+ librbd-dev \
+ librdmacm-dev \
+ libsasl2-dev \
+ libsdl2-dev \
+ libsdl2-image-dev \
+ libseccomp-dev \
+ libselinux1-dev \
+ libsnappy-dev \
+ libspice-protocol-dev \
+ libspice-server-dev \
+ libssh-dev \
+ libsystemd-dev \
+ libtasn1-6-dev \
+ libtest-harness-perl \
+ libubsan1 \
+ libudev-dev \
+ libusb-1.0-0-dev \
+ libusbredirhost-dev \
+ libvdeplug-dev \
+ libvirglrenderer-dev \
+ libvte-2.91-dev \
+ libxen-dev \
+ libxml2-dev \
+ libzstd-dev \
+ llvm \
+ locales \
+ make \
+ multipath-tools \
+ netcat-openbsd \
+ nettle-dev \
+ ninja-build \
+ openssh-client \
+ perl-base \
+ pkgconf \
+ python3 \
+ python3-numpy \
+ python3-opencv \
+ python3-pillow \
+ python3-pip \
+ python3-setuptools \
+ python3-sphinx \
+ python3-sphinx-rtd-theme \
+ python3-venv \
+ python3-wheel \
+ python3-yaml \
+ rpm2cpio \
+ sed \
+ sparse \
+ systemtap-sdt-dev \
+ tar \
+ tesseract-ocr \
+ tesseract-ocr-eng \
+ texinfo \
+ xfslibs-dev \
+ zlib1g-dev && \
+ eatmydata apt-get autoremove -y && \
+ eatmydata apt-get autoclean -y && \
+ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \
+ dpkg-reconfigure locales && \
+ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
+ mkdir -p /usr/libexec/ccache-wrappers && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
+
+RUN pip3 install \
+ meson==0.56.0
+
+ENV LANG "en_US.UTF-8"
+ENV MAKE "/usr/bin/make"
+ENV NINJA "/usr/bin/ninja"
+ENV PYTHON "/usr/bin/python3"
+ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
# https://bugs.launchpad.net/qemu/+bug/1838763
ENV QEMU_CONFIGURE_OPTS --disable-libssh
+# THIS FILE WAS AUTO-GENERATED
+#
+# $ lcitool dockerfile ubuntu-2004 qemu
+#
+# https://gitlab.com/libvirt/libvirt-ci
+
FROM docker.io/library/ubuntu:20.04
-ENV PACKAGES \
- bc \
- bsdmainutils \
- bzip2 \
- ca-certificates \
- ccache \
- clang \
- dbus \
- debianutils \
- diffutils \
- exuberant-ctags \
- findutils \
- g++ \
- gcc \
- gcovr \
- genisoimage \
- gettext \
- git \
- hostname \
- libaio-dev \
- libasan5 \
- libasound2-dev \
- libattr1-dev \
- libbrlapi-dev \
- libbz2-dev \
- libc6-dev \
- libcacard-dev \
- libcap-ng-dev \
- libcapstone-dev \
- libcurl4-gnutls-dev \
- libdaxctl-dev \
- libdrm-dev \
- libepoxy-dev \
- libfdt-dev \
- libffi-dev \
- libgbm-dev \
- libgcrypt20-dev \
- libglib2.0-dev \
- libglusterfs-dev \
- libgnutls28-dev \
- libgtk-3-dev \
- libibverbs-dev \
- libiscsi-dev \
- libjemalloc-dev \
- libjpeg-turbo8-dev \
- liblttng-ust-dev \
- liblzo2-dev \
- libncursesw5-dev \
- libnfs-dev \
- libnuma-dev \
- libpam0g-dev \
- libpixman-1-dev \
- libpmem-dev \
- libpng-dev \
- libpulse-dev \
- librbd-dev \
- librdmacm-dev \
- libsasl2-dev \
- libsdl2-dev \
- libsdl2-image-dev \
- libseccomp-dev \
- libselinux-dev \
- libslirp-dev \
- libsnappy-dev \
- libspice-protocol-dev \
- libspice-server-dev \
- libssh-dev \
- libsystemd-dev \
- libtasn1-6-dev \
- libtest-harness-perl \
- libubsan1 \
- libudev-dev \
- libusb-1.0-0-dev \
- libusbredirhost-dev \
- libvdeplug-dev \
- libvirglrenderer-dev \
- libvte-2.91-dev \
- libxen-dev \
- libxml2-dev \
- libzstd-dev \
- llvm \
- locales \
- make \
- multipath-tools \
- ncat \
- nettle-dev \
- ninja-build \
- openssh-client \
- perl-base \
- pkgconf \
- python3 \
- python3-numpy \
- python3-opencv \
- python3-pillow \
- python3-pip \
- python3-setuptools \
- python3-sphinx \
- python3-sphinx-rtd-theme \
- python3-venv \
- python3-wheel \
- python3-yaml \
- rpm2cpio \
- sed \
- sparse \
- systemtap-sdt-dev \
- tar \
- tesseract-ocr \
- tesseract-ocr-eng \
- texinfo \
- xfslibs-dev \
- zlib1g-dev
-RUN apt-get update && \
- DEBIAN_FRONTEND=noninteractive apt-get -y install $PACKAGES
-RUN dpkg -l $PACKAGES | sort > /packages.txt
+RUN export DEBIAN_FRONTEND=noninteractive && \
+ apt-get update && \
+ apt-get install -y eatmydata && \
+ eatmydata apt-get dist-upgrade -y && \
+ eatmydata apt-get install --no-install-recommends -y \
+ bash \
+ bc \
+ bsdmainutils \
+ bzip2 \
+ ca-certificates \
+ ccache \
+ clang \
+ dbus \
+ debianutils \
+ diffutils \
+ exuberant-ctags \
+ findutils \
+ g++ \
+ gcc \
+ gcovr \
+ genisoimage \
+ gettext \
+ git \
+ hostname \
+ libaio-dev \
+ libasan5 \
+ libasound2-dev \
+ libattr1-dev \
+ libbrlapi-dev \
+ libbz2-dev \
+ libc6-dev \
+ libcacard-dev \
+ libcap-ng-dev \
+ libcapstone-dev \
+ libcurl4-gnutls-dev \
+ libdaxctl-dev \
+ libdrm-dev \
+ libepoxy-dev \
+ libfdt-dev \
+ libffi-dev \
+ libfuse3-dev \
+ libgbm-dev \
+ libgcrypt20-dev \
+ libglib2.0-dev \
+ libglusterfs-dev \
+ libgnutls28-dev \
+ libgtk-3-dev \
+ libibverbs-dev \
+ libiscsi-dev \
+ libjemalloc-dev \
+ libjpeg-turbo8-dev \
+ liblttng-ust-dev \
+ liblzo2-dev \
+ libncursesw5-dev \
+ libnfs-dev \
+ libnuma-dev \
+ libpam0g-dev \
+ libpcre2-dev \
+ libpixman-1-dev \
+ libpmem-dev \
+ libpng-dev \
+ libpulse-dev \
+ librbd-dev \
+ librdmacm-dev \
+ libsasl2-dev \
+ libsdl2-dev \
+ libsdl2-image-dev \
+ libseccomp-dev \
+ libselinux1-dev \
+ libslirp-dev \
+ libsnappy-dev \
+ libspice-protocol-dev \
+ libspice-server-dev \
+ libssh-dev \
+ libsystemd-dev \
+ libtasn1-6-dev \
+ libtest-harness-perl \
+ libubsan1 \
+ libudev-dev \
+ libusb-1.0-0-dev \
+ libusbredirhost-dev \
+ libvdeplug-dev \
+ libvirglrenderer-dev \
+ libvte-2.91-dev \
+ libxen-dev \
+ libxml2-dev \
+ libzstd-dev \
+ llvm \
+ locales \
+ make \
+ multipath-tools \
+ ncat \
+ nettle-dev \
+ ninja-build \
+ openssh-client \
+ perl-base \
+ pkgconf \
+ python3 \
+ python3-numpy \
+ python3-opencv \
+ python3-pillow \
+ python3-pip \
+ python3-setuptools \
+ python3-sphinx \
+ python3-sphinx-rtd-theme \
+ python3-venv \
+ python3-wheel \
+ python3-yaml \
+ rpm2cpio \
+ sed \
+ sparse \
+ systemtap-sdt-dev \
+ tar \
+ tesseract-ocr \
+ tesseract-ocr-eng \
+ texinfo \
+ xfslibs-dev \
+ zlib1g-dev && \
+ eatmydata apt-get autoremove -y && \
+ eatmydata apt-get autoclean -y && \
+ sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \
+ dpkg-reconfigure locales && \
+ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \
+ mkdir -p /usr/libexec/ccache-wrappers && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/c++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/g++ && \
+ ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc
+
+RUN pip3 install \
+ meson==0.56.0
+
+ENV LANG "en_US.UTF-8"
+ENV MAKE "/usr/bin/make"
+ENV NINJA "/usr/bin/ninja"
+ENV PYTHON "/usr/bin/python3"
+ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers"
# Apply patch https://reviews.llvm.org/D75820
# This is required for TSan in clang-10 to compile with QEMU.
RUN sed -i 's/^const/static const/g' /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h
--- /dev/null
+
+LCITOOL_REFRESH = $(SRC_PATH)/tests/lcitool/refresh
+
+lcitool:
+ @echo 'Manage build environment manifests'
+ @echo
+ @echo 'Available targets:'
+ @echo
+ @echo ' lcitool: Print this help.'
+ @echo ' lcitool-refresh: Re-generate all build environment manifests.'
+ @echo
+
+lcitool-help: lcitool
+
+lcitool-refresh:
+ $(call quiet-command, git submodule update --init $(SRC_PATH)/tests/lcitool/libvirt-ci)
+ $(call quiet-command, $(LCITOOL_REFRESH))
--- /dev/null
+Subproject commit 29cec2153b9a4dbb2e66f1cbc9866a4eff519cfd
--- /dev/null
+---
+packages:
+ - alsa
+ - bash
+ - bc
+ - brlapi
+ - bzip2
+ - bzip2-libs
+ - capstone
+ - ccache
+ - clang
+ - column
+ - ctags
+ - cyrus-sasl
+ - daxctl
+ - dbus-daemon
+ - device-mapper-multipath
+ - diffutils
+ - dtrace
+ - findutils
+ - fuse3
+ - g++
+ - gcc
+ - gcovr
+ - gettext
+ - genisoimage
+ - glib2
+ - glib2-static
+ - glibc-static
+ - glusterfs
+ - gnutls
+ - gtk3
+ - hostname
+ - libaio
+ - libattr
+ - libasan
+ - libbpf
+ - libcacard
+ - libcap-ng
+ - libcurl
+ - libdrm
+ - libepoxy
+ - libfdt
+ - libffi
+ - libgcrypt
+ - libibverbs
+ - libiscsi
+ - libjemalloc
+ - libjpeg
+ - libnfs
+ - libnuma
+ - libpmem
+ - libpng
+ - librbd
+ - librdmacm
+ - libseccomp
+ - libselinux
+ - libslirp
+ - libssh
+ - libtasn1
+ - libubsan
+ - libudev
+ - liburing
+ - libusbx
+ - libvdeplug
+ - libxml2
+ - libzstd
+ - llvm
+ - lttng-ust
+ - lzo
+ - netcat
+ - nettle
+ - ninja
+ - nsis
+ - make
+ - mesa-libgbm
+ - meson
+ - ncursesw
+ - pam
+ - pcre-static
+ - perl
+ - perl-Test-Harness
+ - pixman
+ - pkg-config
+ - pulseaudio
+ - python3
+ - python3-PyYAML
+ - python3-numpy
+ - python3-opencv
+ - python3-pillow
+ - python3-pip
+ - python3-sphinx
+ - python3-sphinx-rtd-theme
+ - python3-virtualenv
+ - rpm2cpio
+ - sdl2
+ - sdl2-image
+ - sed
+ - snappy
+ - sparse
+ - spice-protocol
+ - spice-server
+ - ssh-client
+ - systemd
+ - tar
+ - tesseract
+ - tesseract-eng
+ - texinfo
+ - usbredir
+ - virglrenderer
+ - vte
+ - which
+ - xen
+ - xfsprogs
+ - zlib
+ - zlib-static
--- /dev/null
+#!/usr/bin/python3
+#
+# Re-generate container recipes
+#
+# This script uses the "lcitool" available from
+#
+# https://gitlab.com/libvirt/libvirt-ci
+#
+# Copyright (c) 2020 Red Hat Inc.
+#
+# This work is licensed under the terms of the GNU GPL, version 2
+# or (at your option) any later version. See the COPYING file in
+# the top-level directory.
+
+import sys
+import os
+import subprocess
+
+from pathlib import Path
+
+if len(sys.argv) != 1:
+ print("syntax: %s" % sys.argv[0], file=sys.stderr)
+ sys.exit(1)
+
+self_dir = Path(__file__).parent
+src_dir = self_dir.parent.parent
+dockerfiles_dir = Path(src_dir, "tests", "docker", "dockerfiles")
+
+lcitool_path = Path(self_dir, "libvirt-ci", "lcitool")
+
+lcitool_cmd = [lcitool_path, "--data-dir", self_dir]
+
+def atomic_write(filename, content):
+ tmp = filename.with_suffix(filename.suffix + ".tmp")
+ try:
+ with tmp.open("w") as fp:
+ print(content, file=fp, end="")
+ tmp.rename(filename)
+ except Exception as ex:
+ tmp.unlink()
+ raise
+
+def generate(filename, cmd, trailer):
+ print("Generate %s" % filename)
+ lcitool=subprocess.run(cmd, capture_output=True)
+
+ if lcitool.returncode != 0:
+ raise Exception("Failed to generate %s: %s" % (filename, lcitool.stderr))
+
+ content = lcitool.stdout.decode("utf8")
+ if trailer is not None:
+ content += trailer
+ atomic_write(filename, content)
+
+def generate_dockerfile(host, target, cross=None, trailer=None):
+ filename = Path(src_dir, "tests", "docker", "dockerfiles", host + ".docker")
+ cmd = lcitool_cmd + ["dockerfile"]
+ if cross is not None:
+ cmd.extend(["--cross", cross])
+ cmd.extend([target, "qemu"])
+ generate(filename, cmd, trailer)
+
+def generate_cirrus(target, trailer=None):
+ filename = Path(src_dir, ".gitlab-ci.d", "cirrus", target + ".vars")
+ cmd = [lcitool_path, "variables", target, "qemu"]
+ generate(filename, cmd, trailer)
+
+ubuntu1804_skipssh = [
+ "# https://bugs.launchpad.net/qemu/+bug/1838763\n",
+ "ENV QEMU_CONFIGURE_OPTS --disable-libssh\n"
+]
+
+ubuntu2004_tsanhack = [
+ "# Apply patch https://reviews.llvm.org/D75820\n",
+ "# This is required for TSan in clang-10 to compile with QEMU.\n",
+ "RUN sed -i 's/^const/static const/g' /usr/lib/llvm-10/lib/clang/10.0.0/include/sanitizer/tsan_interface.h\n"
+]
+
+try:
+ generate_dockerfile("centos8", "centos-8")
+ generate_dockerfile("fedora", "fedora-35")
+ generate_dockerfile("ubuntu1804", "ubuntu-1804",
+ trailer="".join(ubuntu1804_skipssh))
+ generate_dockerfile("ubuntu2004", "ubuntu-2004",
+ trailer="".join(ubuntu2004_tsanhack))
+ generate_dockerfile("opensuse-leap", "opensuse-leap-152")
+ generate_dockerfile("alpine", "alpine-edge")
+
+ generate_cirrus("freebsd-12")
+ generate_cirrus("freebsd-13")
+ generate_cirrus("macos-11")
+
+ sys.exit(0)
+except Exception as ex:
+ print(str(ex), file=sys.stderr)
+ sys.exit(1)
py3 = import('python').find_installation()
subdir('bench')
+subdir('qemu-iotests')
test_qapi_outputs = [
'qapi-builtin-types.c',
'test-qapi-visit.h',
]
+# Problem: to generate trace events, we'd have to add the .trace-events
+# file to qapi_trace_events like we do in qapi/meson.build. Since
+# qapi_trace_events is used by trace/meson.build, we'd have to move
+# subdir('tests') above subdir('trace') in the top-level meson.build.
+# Can't, because it would break the dependency of qga on qemuutil (which
+# depends on trace_ss). Not worth solving now; simply suppress trace
+# event generation instead.
test_qapi_files = custom_target('Test QAPI files',
output: test_qapi_outputs,
input: files('qapi-schema/qapi-schema-test.json',
'qapi-schema/include/sub-module.json',
'qapi-schema/sub-sub-module.json'),
command: [ qapi_gen, '-o', meson.current_build_dir(),
- '-b', '-p', 'test-', '@INPUT0@' ],
+ '-b', '-p', 'test-', '@INPUT0@',
+ '--suppress-tracing' ],
depend_files: qapi_gen_depends)
# meson doesn't like generated output in other directories
_supported_fmt qcow2
_supported_proto file fuse
# We want to test compat=0.10, which does not support external data
-# files or refcount widths other than 16
-_unsupported_imgopts data_file 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
+# files or refcount widths other than 16 or compression type
+_unsupported_imgopts data_file compression_type \
+ 'refcount_bits=\([^1]\|.\([^6]\|$\)\)'
CLUSTER_SIZE=65536
echo
_make_test_img -o $compat 64M
$PYTHON qcow2.py "$TEST_IMG" add-header-ext 0x12345678 "This is a test header extension"
- $PYTHON qcow2.py "$TEST_IMG" dump-header
+ _qcow2_dump_header
_check_test_img
echo
echo === Rewrite header with no backing file ===
echo
$QEMU_IMG rebase -u -b "" "$TEST_IMG"
- $PYTHON qcow2.py "$TEST_IMG" dump-header
+ _qcow2_dump_header
_check_test_img
echo
echo === Add a backing file and format ===
echo
$QEMU_IMG rebase -u -b "/some/backing/file/path" -F host_device "$TEST_IMG"
- $PYTHON qcow2.py "$TEST_IMG" dump-header
+ _qcow2_dump_header
done
# success, all done
# Without feature table
$PYTHON qcow2.py "$TEST_IMG" del-header-ext 0x6803f857
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features
+_qcow2_dump_header | grep features
$PYTHON qcow2.py "$TEST_IMG" dump-header-exts
_img_info
echo
_make_test_img 64M
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 63
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features
+_qcow2_dump_header | grep features
$PYTHON qcow2.py "$TEST_IMG" dump-header-exts
echo
echo
_check_test_img -r all
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep features
+_qcow2_dump_header | grep features
$PYTHON qcow2.py "$TEST_IMG" dump-header-exts
# success, all done
$QEMU_IO -c "write -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io
# The dirty bit must not be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
_check_test_img
echo
| _filter_qemu_io
# The dirty bit must be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
_check_test_img
echo
$QEMU_IO -r -c "read -P 0x5a 0 512" "$TEST_IMG" | _filter_qemu_io
# The dirty bit must be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
echo
echo "== Repairing the image file must succeed =="
_check_test_img -r all
# The dirty bit must not be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
echo
echo "== Data should still be accessible after repair =="
| _filter_qemu_io
# The dirty bit must be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
$QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io
# The dirty bit must not be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
echo
echo "== Creating an image file with lazy_refcounts=off =="
| _filter_qemu_io
# The dirty bit must not be set since lazy_refcounts=off
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
_check_test_img
echo
$QEMU_IMG commit "$TEST_IMG"
# The dirty bit must not be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
-$PYTHON qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
+_qcow2_dump_header "$TEST_IMG".base | grep incompatible_features
_check_test_img
TEST_IMG="$TEST_IMG".base _check_test_img
| _filter_qemu_io
# The dirty bit must be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
_check_test_img
_make_test_img -o "compat=1.1,lazy_refcounts=on" $size
| _filter_qemu_io
# The dirty bit must not be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
_check_test_img
import qcow2
from qcow2 import QcowHeader
import iotests
-from iotests import qemu_img, qemu_img_verbose, qemu_io
+from iotests import qemu_img, qemu_img_log, qemu_io
import struct
import subprocess
import sys
def test_grow_refcount_table(self):
qemu_io('-c', 'write 3800M 1M', test_img)
- qemu_img_verbose('check' , test_img)
+ qemu_img_log('check' , test_img)
pass
if __name__ == '__main__':
+ iotests.activate_logging()
iotests.main(supported_fmts=['qcow2'],
- supported_protocols=['file'])
+ supported_protocols=['file'],
+ unsupported_imgopts=['refcount_bits'])
No errors were found on the image.
7292415/33554432 = 21.73% allocated, 0.00% fragmented, 0.00% compressed clusters
Image end offset: 4296217088
+
.
----------------------------------------------------------------------
Ran 1 tests
_supported_proto file
# A compat=0.10 image is created in this test which does not support anything
# other than refcount_bits=16;
-# it also will not support an external data file
-_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file
+# it also will not support an external data file and compression type
+_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file \
+ compression_type
_require_drivers nbd
+if [ "$QEMU_DEFAULT_MACHINE" = "pc" ]; then
+ _require_devices lsi53c895a
+fi
+
do_run_qemu()
{
echo Testing: "$@"
_check_test_img
# The corrupt bit should not be set anyway
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
# Try to write something, thereby forcing the corrupt bit to be set
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
# The corrupt bit must now be set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
# This information should be available through qemu-img info
_img_info --format-specific
# Redirect new data cluster onto refcount block
poke_file "$TEST_IMG" "$l2_offset" "\x80\x00\x00\x00\x00\x02\x00\x00"
_check_test_img
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
# Try to fix it
_check_test_img -r all
# The corrupt bit should be cleared
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
# Look if it's really really fixed
$QEMU_IO -c "$OPEN_RW" -c "write -P 0x2a 0 512" | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
echo
echo "=== Testing cluster data reference into inactive L2 table ==="
poke_file "$TEST_IMG" "$l2_offset_after_snapshot" \
"\x80\x00\x00\x00\x00\x04\x00\x00"
_check_test_img
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
$QEMU_IO -c "$OPEN_RW" -c "write -P 3 0 512" | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
_check_test_img -r all
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
$QEMU_IO -c "$OPEN_RW" -c "write -P 4 0 512" | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header | grep incompatible_features
# Check data
$QEMU_IO -c "$OPEN_RO" -c "read -P 4 0 512" | _filter_qemu_io
# Let the refblock appear unaligned
poke_file "$TEST_IMG" "$rt_offset" "\x00\x00\x00\x00\xff\xff\x2a\x00"
# Mark the image dirty, thus forcing an automatic check when opening it
-poke_file "$TEST_IMG" 72 "\x00\x00\x00\x00\x00\x00\x00\x01"
+$PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 0
# Open the image (qemu should refuse to do so)
$QEMU_IO -c close "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: true
# not work with it;
# we have explicit tests for various cluster sizes, the remaining tests
# require the default 64k cluster
-_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file cluster_size
+# we don't have explicit tests for zstd qcow2 compression type, as zstd may be
+# not compiled in. And we can't create compat images with comression type
+# extension
+_unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' data_file \
+ cluster_size compression_type
echo
echo "=== Testing version downgrade with zero expansion ==="
echo
_make_test_img -o "compat=1.1,lazy_refcounts=on" 64M
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
$QEMU_IO -c "write -z 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "write -z 32M 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IMG amend -o "compat=0.10" --image-opts \
driver=qcow2,file.filename=$TEST_IMG,l2-cache-entry-size=4096
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IO -c "read -P 0 0 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c "read -P 0 32M 128k" "$TEST_IMG" | _filter_qemu_io
$QEMU_IO -c map "$TEST_IMG" | _filter_qemu_io
_NO_VALGRIND \
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush \
-c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
_make_test_img -o "compat=1.1" 64M
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit compatible 42
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit autoclear 42
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG"
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
_check_test_img
echo
echo
_make_test_img -o "compat=0.10" 64M
$QEMU_IO -c "write -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IMG amend -o "compat=1.1,lazy_refcounts=on,size=128M" "$TEST_IMG"
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
$QEMU_IMG snapshot -c foo "$TEST_IMG"
$QEMU_IMG resize "$TEST_IMG" 64M &&
echo "unexpected pass"
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
+_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)'
$QEMU_IMG amend -o "compat=1.1,size=128M" "$TEST_IMG" ||
echo "unexpected fail"
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
+_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)'
$QEMU_IMG snapshot -c bar "$TEST_IMG"
$QEMU_IMG resize --shrink "$TEST_IMG" 64M ||
echo "unexpected fail"
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
+_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)'
$QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" &&
echo "unexpected pass"
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
+_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)'
$QEMU_IMG snapshot -a bar "$TEST_IMG" ||
echo "unexpected fail"
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
+_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)'
$QEMU_IMG snapshot -d bar "$TEST_IMG"
$QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" ||
echo "unexpected fail"
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
+_qcow2_dump_header | grep '^\(version\|size\|nb_snap\)'
_check_test_img
_NO_VALGRIND \
$QEMU_IO -c "write -P 0x2a 0 128k" -c flush \
-c "sigraise $(kill -l KILL)" "$TEST_IMG" 2>&1 | _filter_qemu_io
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IMG amend -o "lazy_refcounts=off" "$TEST_IMG"
-$PYTHON qcow2.py "$TEST_IMG" dump-header
+_qcow2_dump_header
$QEMU_IO -c "read -P 0x2a 0 128k" "$TEST_IMG" | _filter_qemu_io
_check_test_img
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
data file: foo
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
data file raw: false
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
data file: TEST_DIR/t.IMGFMT.data
class TestQCow2(TestQemuImgInfo):
'''Testing a qcow2 version 2 image'''
- img_options = 'compat=0.10'
+ img_options = 'compat=0.10,compression_type=zlib'
json_compare = { 'compat': '0.10', 'refcount-bits': 16,
'compression-type': 'zlib' }
human_compare = [ 'compat: 0.10', 'compression type: zlib',
class TestQCow3NotLazy(TestQemuImgInfo):
'''Testing a qcow2 version 3 image with lazy refcounts disabled'''
- img_options = 'compat=1.1,lazy_refcounts=off'
+ img_options = 'compat=1.1,lazy_refcounts=off,compression_type=zstd'
json_compare = { 'compat': '1.1', 'lazy-refcounts': False,
'refcount-bits': 16, 'corrupt': False,
- 'compression-type': 'zlib', 'extended-l2': False }
- human_compare = [ 'compat: 1.1', 'compression type: zlib',
+ 'compression-type': 'zstd', 'extended-l2': False }
+ human_compare = [ 'compat: 1.1', 'compression type: zstd',
'lazy refcounts: false', 'refcount bits: 16',
'corrupt: false', 'extended l2: false' ]
class TestQCow3Lazy(TestQemuImgInfo):
'''Testing a qcow2 version 3 image with lazy refcounts enabled'''
- img_options = 'compat=1.1,lazy_refcounts=on'
+ img_options = 'compat=1.1,lazy_refcounts=on,compression_type=zlib'
json_compare = { 'compat': '1.1', 'lazy-refcounts': True,
'refcount-bits': 16, 'corrupt': False,
'compression-type': 'zlib', 'extended-l2': False }
class TestQCow3NotLazyQMP(TestQMP):
'''Testing a qcow2 version 3 image with lazy refcounts disabled, opening
with lazy refcounts enabled'''
- img_options = 'compat=1.1,lazy_refcounts=off'
+ img_options = 'compat=1.1,lazy_refcounts=off,compression_type=zlib'
qemu_options = 'lazy-refcounts=on'
compare = { 'compat': '1.1', 'lazy-refcounts': False,
'refcount-bits': 16, 'corrupt': False,
class TestQCow3LazyQMP(TestQMP):
'''Testing a qcow2 version 3 image with lazy refcounts enabled, opening
with lazy refcounts disabled'''
- img_options = 'compat=1.1,lazy_refcounts=on'
+ img_options = 'compat=1.1,lazy_refcounts=on,compression_type=zstd'
qemu_options = 'lazy-refcounts=off'
compare = { 'compat': '1.1', 'lazy-refcounts': True,
'refcount-bits': 16, 'corrupt': False,
- 'compression-type': 'zlib', 'extended-l2': False }
+ 'compression-type': 'zstd', 'extended-l2': False }
TestImageInfoSpecific = None
TestQemuImgInfo = None
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'],
- supported_protocols=['file'])
+ supported_protocols=['file'],
+ unsupported_imgopts=['refcount_bits'])
cluster_size: 4096
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: true
refcount bits: 16
corrupt: false
cluster_size: 8192
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: true
refcount bits: 16
corrupt: false
cluster_size: 4096
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: true
refcount bits: 16
corrupt: false
cluster_size: 8192
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: true
refcount bits: 16
corrupt: false
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: true
refcount bits: 16
corrupt: false
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: true
refcount bits: 16
corrupt: false
# This test will set refcount_bits on its own which would conflict with the
# manual setting; compat will be overridden as well;
# and external data files do not work well with our refcount testing
-_unsupported_imgopts refcount_bits 'compat=0.10' data_file
+# also, compression type is not supported with compat=0.10 used in test
+_unsupported_imgopts refcount_bits 'compat=0.10' data_file compression_type
print_refcount_bits()
{
$QEMU_IO -c "write 0 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
$QEMU_IO -c "write 8k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
$QEMU_IO -c "write 17k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
+$QEMU_IO -c "write -P 0 65k 1k" "$TEST_IMG" 2>&1 | _filter_qemu_io | _filter_testdir
for min_sparse in 4k 8k; do
echo
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 1024/1024 bytes at offset 17408
1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 1024/1024 bytes at offset 66560
+1 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
convert -S 4k
[{ "start": 0, "length": 4096, "depth": 0, "present": true, "zero": false, "data": true, "offset": OFFSET},
# The dirty bit must not be set
# (Filter the external data file bit)
-if $PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features \
+if _qcow2_dump_header | grep incompatible_features \
| grep -q '\<0\>'
then
echo 'ERROR: Dirty bit set'
# ================= qemu-img aes-256-xts-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha1.img qiotest-145-aes-256-xts-plain64-sha1
# ================= qemu-img twofish-256-xts-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=twofish-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-256-xts-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-twofish-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=twofish-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-256-xts-plain64-sha1.img qiotest-145-twofish-256-xts-plain64-sha1
# ================= qemu-img serpent-256-xts-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-256-xts-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-serpent-256-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-256-xts-plain64-sha1.img qiotest-145-serpent-256-xts-plain64-sha1
# ================= qemu-img cast5-128-cbc-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=cast5-128,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=cast5-128 cipher-mode=cbc ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-cast5-128-cbc-plain64-sha1.img qiotest-145-cast5-128-cbc-plain64-sha1
# ================= qemu-img aes-256-cbc-plain-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-cbc-plain-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain-sha1.img qiotest-145-aes-256-cbc-plain-sha1
# ================= qemu-img aes-256-cbc-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha1.img qiotest-145-aes-256-cbc-plain64-sha1
# ================= qemu-img aes-256-cbc-essiv-sha256-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=essiv ivgen-hash-alg=sha256 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-sha256-sha1.img qiotest-145-aes-256-cbc-essiv-sha256-sha1
# ================= qemu-img aes-256-xts-essiv-sha256-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=essiv,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=essiv ivgen-hash-alg=sha256 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-essiv-sha256-sha1.img qiotest-145-aes-256-xts-essiv-sha256-sha1
# ================= qemu-img aes-128-xts-plain64-sha256-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-128-xts-plain64-sha256-sha1.img qiotest-145-aes-128-xts-plain64-sha256-sha1
# ================= qemu-img aes-192-xts-plain64-sha256-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-192 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-192-xts-plain64-sha256-sha1.img qiotest-145-aes-192-xts-plain64-sha256-sha1
# ================= qemu-img twofish-128-xts-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=twofish-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-twofish-128-xts-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-twofish-128-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=twofish-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-twofish-128-xts-plain64-sha1.img qiotest-145-twofish-128-xts-plain64-sha1
# ================= qemu-img serpent-128-xts-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-128,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-128-xts-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-serpent-128-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-128 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-128-xts-plain64-sha1.img qiotest-145-serpent-128-xts-plain64-sha1
# ================= qemu-img serpent-192-xts-plain64-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=serpent-192,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha1 TEST_DIR/luks-serpent-192-xts-plain64-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-serpent-192-xts-plain64-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=serpent-192 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-serpent-192-xts-plain64-sha1.img qiotest-145-serpent-192-xts-plain64-sha1
# ================= qemu-img aes-256-xts-plain64-sha224 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha224 TEST_DIR/luks-aes-256-xts-plain64-sha224.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha224.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha224 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha224.img qiotest-145-aes-256-xts-plain64-sha224
# ================= qemu-img aes-256-xts-plain64-sha256 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha256 TEST_DIR/luks-aes-256-xts-plain64-sha256.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha256.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha256 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha256.img qiotest-145-aes-256-xts-plain64-sha256
# ================= qemu-img aes-256-xts-plain64-sha384 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha384 TEST_DIR/luks-aes-256-xts-plain64-sha384.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha384.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha384 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha384.img qiotest-145-aes-256-xts-plain64-sha384
# ================= qemu-img aes-256-xts-plain64-sha512 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=sha512 TEST_DIR/luks-aes-256-xts-plain64-sha512.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-plain64-sha512.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=sha512 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-sha512.img qiotest-145-aes-256-xts-plain64-sha512
# ================= qemu-img aes-256-xts-plain64-ripemd160 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain64,hash-alg=ripemd160 TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain64 hash-alg=ripemd160 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain64-ripemd160.img qiotest-145-aes-256-xts-plain64-ripemd160
# ================= qemu-img aes-256-xts-plain-sha1-pwallslots =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=c2xvdDE=,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=xts,ivgen-alg=plain,hash-alg=sha1 TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=xts ivgen-alg=plain hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-xts-plain-sha1-pwallslots.img qiotest-145-aes-256-xts-plain-sha1-pwallslots
# ================= qemu-img aes-256-cbc-essiv-auto-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=essiv,hash-alg=sha1 TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=essiv hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-essiv-auto-sha1.img qiotest-145-aes-256-cbc-essiv-auto-sha1
# ================= qemu-img aes-256-cbc-plain64-sha256-sha1 =================
# Create image
qemu-img create -f luks --object secret,id=sec0,data=MTIzNDU2,format=base64 -o key-secret=sec0,iter-time=10,cipher-alg=aes-256,cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha1,ivgen-hash-alg=sha256 TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img 4194304M
-Formatting 'TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img', fmt=luks size=4398046511104 key-secret=sec0 cipher-alg=aes-256 cipher-mode=cbc ivgen-alg=plain64 ivgen-hash-alg=sha256 hash-alg=sha1 iter-time=10
# Open dev
sudo cryptsetup -q -v luksOpen TEST_DIR/luks-aes-256-cbc-plain64-sha256-sha1.img qiotest-145-aes-256-cbc-plain64-sha256-sha1
if __name__ == '__main__':
iotests.main(supported_fmts=['raw', 'qcow2'],
- supported_protocols=['file'])
+ supported_protocols=['file'],
+ unsupported_imgopts=['compat'])
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'],
- supported_protocols=['file'])
+ supported_protocols=['file'],
+ unsupported_imgopts=['compat'])
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'],
- supported_protocols=['file'])
+ supported_protocols=['file'],
+ unsupported_imgopts=['compat'])
file format: IMGFMT
virtual size: 16 MiB (16777216 bytes)
Format specific information:
- compression type: zlib
+ compression type: COMPRESSION_TYPE
encrypt:
ivgen alg: plain64
hash alg: sha256
backing file: TEST_DIR/t.IMGFMT.base
backing file format: IMGFMT
Format specific information:
- compression type: zlib
+ compression type: COMPRESSION_TYPE
encrypt:
ivgen alg: plain64
hash alg: sha256
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
cluster_size: 2097152
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: true
refcount bits: 1
corrupt: false
backing file format: IMGFMT
Format specific information:
compat: 0.10
- compression type: zlib
+ compression type: COMPRESSION_TYPE
refcount bits: 16
=== Successful image creation (encrypted) ===
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
encrypt:
#
import iotests
-from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \
- file_path
+from iotests import qemu_img_create, qemu_io, qemu_img_log, qemu_nbd, \
+ file_path, log
iotests.script_initialize(supported_fmts=['qcow2'])
qemu_io('-f', iotests.imgfmt, '-c', 'write 0 512K', disk)
qemu_nbd('-k', nbd_sock, '-x', 'exp', '-f', iotests.imgfmt, disk)
-qemu_img_verbose('map', '-f', 'raw', '--output=json', nbd_uri)
+qemu_img_log('map', '-f', 'raw', '--output=json', nbd_uri)
+log('done.') # avoid new line at the end of output file
[{ "start": 0, "length": 524288, "depth": 0, "present": true, "zero": false, "data": true, "offset": 0},
{ "start": 524288, "length": 524288, "depth": 0, "present": true, "zero": true, "data": false, "offset": 524288}]
+
+done.
'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path),
filter_path=disk_path,
extra_args=['--object', 'secret,id=keysec0,data=foo'],
- imgopts=True)
+ use_image_opts=True)
#
# Successful image creation (with non-default options)
'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path),
filter_path=disk_path,
extra_args=['--object', 'secret,id=keysec0,data=foo'],
- imgopts=True)
+ use_image_opts=True)
#
# Invalid BlockdevRef
'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path),
filter_path=disk_path,
extra_args=['--object', 'secret,id=keysec0,data=foo'],
- imgopts=True)
+ use_image_opts=True)
#
# Invalid sizes
'driver=luks,file.driver=file,file.filename=%s,key-secret=keysec0' % (disk_path),
filter_path=disk_path,
extra_args=['--object', 'secret,id=keysec0,data=foo'],
- imgopts=True)
+ use_image_opts=True)
# The L2 entries of the two compressed clusters are located at
# 0x800000 and 0x800008, their original values are 0x4008000000a00000
# and 0x4008000000a00802 (5 sectors for compressed data each).
-_make_test_img 8M -o cluster_size=2M
+_make_test_img 8M -o cluster_size=2M,compression_type=zlib
$QEMU_IO -c "write -c -P 0x11 0 2M" -c "write -c -P 0x11 2M 2M" "$TEST_IMG" \
2>&1 | _filter_qemu_io | _filter_testdir
=== Other subformats ===
-Formatting 'TEST_DIR/PID-t.vmdk.1', fmt=vmdk size=0 compat6=off hwversion=undefined
-Formatting 'TEST_DIR/PID-t.vmdk.2', fmt=vmdk size=0 compat6=off hwversion=undefined
-Formatting 'TEST_DIR/PID-t.vmdk.3', fmt=vmdk size=0 compat6=off hwversion=undefined
== Missing extent ==
file_path, img_info_log, log, filter_qemu_io
iotests.script_initialize(supported_fmts=['qcow2'],
- supported_protocols=['file'])
+ supported_protocols=['file'],
+ unsupported_imgopts=['refcount_bits', 'compat'])
disk = file_path('disk')
chunk = 256 * 1024
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
bitmaps:
[0]:
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
bitmaps:
[0]:
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
bitmaps:
[0]:
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
bitmaps:
[0]:
import iotests
from iotests import log
-iotests.script_initialize(supported_fmts=['qcow2'])
+iotests.script_initialize(supported_fmts=['qcow2'],
+ unsupported_imgopts=['compat'])
size = 64 * 1024 * 1024 * 1024
gran_small = 32 * 1024
gran_large = 128 * 1024
import iotests
from iotests import qemu_img_create, file_path, log
-iotests.script_initialize(supported_fmts=['qcow2'])
+iotests.script_initialize(supported_fmts=['qcow2'],
+ unsupported_imgopts=['compat'])
disk, top = file_path('disk', 'top')
size = 1024 * 1024
=== Create backing chain and start VM ===
-Formatting 'TEST_DIR/PID-t.qcow2.mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-t.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
=== Start background read requests ===
=== Create images and start VM ===
-Formatting 'TEST_DIR/PID-src.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-dst.qcow2', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=134217728 lazy_refcounts=off refcount_bits=16
wrote 1048576/1048576 bytes at offset 0
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
from iotests import qemu_img_create, file_path, log, filter_qmp_event
iotests.script_initialize(
- supported_fmts=['qcow2']
+ supported_fmts=['qcow2'],
+ unsupported_imgopts=['compat']
)
base, top = file_path('base', 'top')
"name": "file",
"parent": 5,
"shared-perm": [
- "graph-mod",
"write-unchanged",
"consistent-read"
],
"name": "backing",
"parent": 5,
"shared-perm": [
- "graph-mod",
"resize",
"write-unchanged",
"write",
"name": "file",
"parent": 3,
"shared-perm": [
- "graph-mod",
"write-unchanged",
"consistent-read"
],
"name": "backing",
"parent": 3,
"shared-perm": [
- "graph-mod",
"resize",
"write-unchanged",
"write",
import iotests
iotests.script_initialize(supported_fmts=['qcow2'],
- supported_platforms=['linux'])
+ supported_platforms=['linux'],
+ unsupported_imgopts=['refcount_bits', 'compat'])
size_short = 1 * 1024 * 1024
size_long = 2 * 1024 * 1024
== Commit tests ==
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
backing file format: IMGFMT
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing HMP commit (top -> mid) ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
backing file format: IMGFMT
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing QMP active commit (top -> mid) ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
backing file format: IMGFMT
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing qemu-img commit (top -> base) ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
=== Testing QMP active commit (top -> base) ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-mid', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1048576 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=2097152 backing_file=TEST_DIR/PID-mid backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 2097152/2097152 bytes at offset 0
2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
backing file format: IMGFMT
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
== Resize tests ==
=== preallocation=off ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=6442450944 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=1073741824 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 5368709120
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ "start": 1073741824, "length": 7516192768, "depth": 0, "present": true, "zero": true, "data": false}]
=== preallocation=metadata ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=34359738368 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=32212254720 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 33285996544
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ "start": 34896609280, "length": 536870912, "depth": 0, "present": true, "zero": true, "data": false, "offset": 2685075456}]
=== preallocation=falloc ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=10485760 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=5242880 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 9437184
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ "start": 5242880, "length": 10485760, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}]
=== preallocation=full ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=16777216 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=8388608 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 11534336
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ "start": 8388608, "length": 4194304, "depth": 0, "present": true, "zero": false, "data": true, "offset": 327680}]
=== preallocation=off ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=393216 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=259072 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 259072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}]
=== preallocation=off ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=409600 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 344064
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{ "start": 262144, "length": 262144, "depth": 0, "present": true, "zero": true, "data": false}]
=== preallocation=off ===
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=524288 lazy_refcounts=off refcount_bits=16
-Formatting 'TEST_DIR/PID-top', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=262144 backing_file=TEST_DIR/PID-base backing_fmt=qcow2 lazy_refcounts=off refcount_bits=16
wrote 65536/65536 bytes at offset 446464
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-Formatting 'TEST_DIR/PID-base', fmt=qcow2 cluster_size=65536 extended_l2=off compression_type=zlib size=67108864 lazy_refcounts=off refcount_bits=16
=== Launch VM ===
Enabling migration QMP events on VM...
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'],
- supported_protocols=['file'])
+ supported_protocols=['file'],
+ unsupported_imgopts=['compat'])
echo "=== Testing compression type incompatible bit setting for zlib ==="
echo
_make_test_img -o compression_type=zlib 64M
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header --no-filter-compression | grep incompatible_features
echo
echo "=== Testing compression type incompatible bit setting for zstd ==="
echo
_make_test_img -o compression_type=zstd 64M
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header --no-filter-compression | grep incompatible_features
echo
echo "=== Testing zlib with incompatible bit set ==="
_make_test_img -o compression_type=zlib 64M
$PYTHON qcow2.py "$TEST_IMG" set-feature-bit incompatible 3
# to make sure the bit was actually set
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header --no-filter-compression | grep incompatible_features
if $QEMU_IMG info "$TEST_IMG" >/dev/null 2>&1 ; then
echo "Error: The image opened successfully. The image must not be opened."
_make_test_img -o compression_type=zstd 64M
$PYTHON qcow2.py "$TEST_IMG" set-header incompatible_features 0
# to make sure the bit was actually unset
-$PYTHON qcow2.py "$TEST_IMG" dump-header | grep incompatible_features
+_qcow2_dump_header --no-filter-compression | grep incompatible_features
if $QEMU_IMG info "$TEST_IMG" >/dev/null 2>&1 ; then
echo "Error: The image opened successfully. The image must not be opened."
_supported_fmt qcow2
_supported_proto file fuse
_supported_os Linux
-_unsupported_imgopts 'compat=0.10' refcount_bits data_file
+_unsupported_imgopts 'compat=0.10' refcount_bits data_file compression_type
echo
echo "### Test 'qemu-io -c discard' on a QCOW2 image without a backing file"
-Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
qemu-img: Failed to get shared "consistent read" lock
Is another process using the image [TEST_DIR/test.img]?
-.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10
-
+.
Job failed: Block node is read-only
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
{"return": {}}
{"execute": "job-dismiss", "arguments": {"id": "job0"}}
{"return": {}}
-.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10
-
+.
{"return": {}}
{"error": {"class": "GenericError", "desc": "Failed to get \"write\" lock"}}
-.Formatting 'TEST_DIR/test.img', fmt=luks size=1048576 key-secret=keysec0 iter-time=10
-
+.
{"return": {}}
{"return": {}}
.
qemu_img_measure,
qemu_io,
qemu_nbd_popen,
+ img_info_log,
)
iotests.script_initialize(supported_fmts=["qcow2"])
tar_file):
iotests.log("=== Target image info ===")
+ # Not img_info_log as it enforces imgfmt, but now we print info on raw
qemu_img_log("info", nbd_uri)
qemu_img(
nbd_uri)
iotests.log("=== Converted image info ===")
- qemu_img_log("info", nbd_uri)
+ img_info_log(nbd_uri)
iotests.log("=== Converted image check ===")
qemu_img_log("check", nbd_uri)
disk size: unavailable
=== Converted image info ===
-image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
-file format: qcow2
+image: TEST_IMG
+file format: IMGFMT
virtual size: 1 GiB (1073741824 bytes)
-disk size: unavailable
cluster_size: 65536
Format specific information:
compat: 1.1
- compression type: zlib
+ compression type: COMPRESSION_TYPE
lazy refcounts: false
refcount bits: 16
corrupt: false
import subprocess
from iotests import qemu_img_create, qemu_io, file_path, log, filter_qemu_io
-iotests.script_initialize(supported_fmts=['qcow2'])
+iotests.script_initialize(supported_fmts=['qcow2'],
+ unsupported_imgopts=['refcount_bits', 'compat'])
disk = file_path('disk')
chunk = 1024 * 1024
log('')
-qemu_img_create('-f', iotests.imgfmt, disk, '10M')
+def test(compression_type: str, json_output: bool) -> None:
+ qemu_img_create('-f', iotests.imgfmt,
+ '-o', f'compression_type={compression_type}',
+ disk, '10M')
+ add_bitmap(1, 0, 6, False)
+ add_bitmap(2, 6, 8, True)
-add_bitmap(1, 0, 6, False)
-add_bitmap(2, 6, 8, True)
-dump = ['./qcow2.py', disk, 'dump-header']
-subprocess.run(dump)
-# Dump the metadata in JSON format
-dump.append('-j')
-subprocess.run(dump)
+ cmd = ['./qcow2.py', disk, 'dump-header']
+ if json_output:
+ cmd.append('-j')
+
+ subprocess.run(cmd)
+
+
+test('zlib', False)
+test('zstd', True)
Bitmap table type size offset
0 all-zeroes 0 0
+Add bitmap 1
+wrote 1048576/1048576 bytes at offset 0
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+wrote 1048576/1048576 bytes at offset 1048576
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+wrote 1048576/1048576 bytes at offset 2097152
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+wrote 1048576/1048576 bytes at offset 3145728
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+wrote 1048576/1048576 bytes at offset 4194304
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+wrote 1048576/1048576 bytes at offset 5242880
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+
+Add bitmap 2
+wrote 1048576/1048576 bytes at offset 6291456
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+wrote 1048576/1048576 bytes at offset 7340032
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+
{
"magic": 1363560955,
"version": 3,
"refcount_table_clusters": 1,
"nb_snapshots": 0,
"snapshot_offset": 0,
- "incompatible_features": 0,
+ "incompatible_features": 8,
"compatible_features": 0,
"autoclear_features": 1,
"refcount_order": 4,
fuse_export_add 'export-mp' "'mountpoint': '$EXT_MP', 'writable': true"
# Check that writing to the read-only export fails
-$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
- | _filter_qemu_io | _filter_testdir | _filter_imgfmt
+output=$($QEMU_IO -f raw -c 'write -P 42 1M 64k' "$TEST_IMG" 2>&1 \
+ | _filter_qemu_io | _filter_testdir | _filter_imgfmt)
+
+# Expected reference output: Opening the file fails because it has no
+# write permission
+reference="Could not open 'TEST_DIR/t.IMGFMT': Permission denied"
+
+if echo "$output" | grep -q "$reference"; then
+ echo "Writing to read-only export failed: OK"
+elif echo "$output" | grep -q "write failed: Permission denied"; then
+ # With CAP_DAC_OVERRIDE (e.g. when running this test as root), the export
+ # can be opened regardless of its file permissions, but writing will then
+ # fail. This is not the result for which we want to test, so count this as
+ # a SKIP.
+ _casenotrun "Opening RO export as R/W succeeded, perhaps because of" \
+ "CAP_DAC_OVERRIDE"
+
+ # Still, write this to the reference output to make the test pass
+ echo "Writing to read-only export failed: OK"
+else
+ echo "Writing to read-only export failed: ERROR"
+ echo "$output"
+fi
# But here it should work
$QEMU_IO -f raw -c 'write -P 42 1M 64k' "$EXT_MP" | _filter_qemu_io
'mountpoint': 'TEST_DIR/t.IMGFMT.fuse', 'writable': true
} }
{"return": {}}
-qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
+Writing to read-only export failed: OK
wrote 65536/65536 bytes at offset 1048576
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 65536/65536 bytes at offset 1048576
p.add_argument('-n', '--dry-run', action='store_true',
help='show me, do not run tests')
- p.add_argument('-makecheck', action='store_true',
- help='pretty print output for make check')
p.add_argument('-j', dest='jobs', type=int, default=1,
help='run tests in multiple parallel jobs')
p.add_argument('--color', choices=['on', 'off', 'auto'],
default='auto', help="use terminal colors. The default "
"'auto' value means use colors if terminal stdout detected")
+ p.add_argument('-tap', action='store_true',
+ help='produce TAP output')
g_env = p.add_argument_group('test environment options')
mg = g_env.add_mutually_exclusive_group()
if args.dry_run:
print('\n'.join(tests))
else:
- with TestRunner(env, makecheck=args.makecheck,
+ with TestRunner(env, tap=args.tap,
color=args.color) as tr:
paths = [os.path.join(env.source_iotests, t) for t in tests]
ok = tr.run_tests(paths, args.jobs)
-e "/block_state_zero: \\(on\\|off\\)/d" \
-e "/log_size: [0-9]\\+/d" \
-e "s/iters: [0-9]\\+/iters: 1024/" \
+ -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \
-e "s/uuid: [-a-f0-9]\\+/uuid: 00000000-0000-0000-0000-000000000000/" | \
while IFS='' read -r line; do
if [[ $format_specific == 1 ]]; then
$SED -e 's/TLS x509 authz check for .* is denied/TLS x509 authz check for DISTINGUISHED-NAME is denied/'
}
+_filter_qcow2_compression_type_bit()
+{
+ $SED -e 's/\(incompatible_features\s\+\)\[3\(, \)\?/\1[/' \
+ -e 's/\(incompatible_features.*\), 3\]/\1]/' \
+ -e 's/\(incompatible_features.*\), 3\(,.*\)/\1\2/'
+}
+
# make sure this script returns success
true
-e "s#$TEST_DIR#TEST_DIR#g" \
-e "s#$SOCK_DIR/fuse-#TEST_DIR/#g" \
-e "s#$IMGFMT#IMGFMT#g" \
+ -e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \
-e "/^disk size:/ D" \
-e "/actual-size/ D" | \
while IFS='' read -r line; do
_notrun "$* not available"
}
+_qcow2_dump_header()
+{
+ if [[ "$1" == "--no-filter-compression" ]]; then
+ local filter_compression=0
+ shift
+ else
+ local filter_compression=1
+ fi
+
+ img="$1"
+ if [ -z "$img" ]; then
+ img="$TEST_IMG"
+ fi
+
+ if [[ $filter_compression == 0 ]]; then
+ $PYTHON qcow2.py "$img" dump-header
+ else
+ $PYTHON qcow2.py "$img" dump-header | _filter_qcow2_compression_type_bit
+ fi
+}
+
# make sure this script returns success
true
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
+import argparse
import atexit
import bz2
from collections import OrderedDict
def qemu_tool_pipe_and_status(tool: str, args: Sequence[str],
- connect_stderr: bool = True) -> Tuple[str, int]:
+ connect_stderr: bool = True,
+ drop_successful_output: bool = False) \
+ -> Tuple[str, int]:
"""
Run a tool and return both its output and its exit code
"""
cmd = ' '.join(args)
sys.stderr.write(f'{tool} received signal \
{-subp.returncode}: {cmd}\n')
+ if drop_successful_output and subp.returncode == 0:
+ output = ''
return (output, subp.returncode)
+def qemu_img_create_prepare_args(args: List[str]) -> List[str]:
+ if not args or args[0] != 'create':
+ return list(args)
+ args = args[1:]
+
+ p = argparse.ArgumentParser(allow_abbrev=False)
+ # -o option may be specified several times
+ p.add_argument('-o', action='append', default=[])
+ p.add_argument('-f')
+ parsed, remaining = p.parse_known_args(args)
+
+ opts_list = parsed.o
+
+ result = ['create']
+ if parsed.f is not None:
+ result += ['-f', parsed.f]
+
+ # IMGOPTS most probably contain options specific for the selected format,
+ # like extended_l2 or compression_type for qcow2. Test may want to create
+ # additional images in other formats that doesn't support these options.
+ # So, use IMGOPTS only for images created in imgfmt format.
+ imgopts = os.environ.get('IMGOPTS')
+ if imgopts and parsed.f == imgfmt:
+ opts_list.insert(0, imgopts)
+
+ # default luks support
+ if parsed.f == 'luks' and \
+ all('key-secret' not in opts for opts in opts_list):
+ result += ['--object', luks_default_secret_object]
+ opts_list.append(luks_default_key_secret_opt)
+
+ for opts in opts_list:
+ result += ['-o', opts]
+
+ result += remaining
+
+ return result
+
def qemu_img_pipe_and_status(*args: str) -> Tuple[str, int]:
"""
Run qemu-img and return both its output and its exit code
"""
- full_args = qemu_img_args + list(args)
- return qemu_tool_pipe_and_status('qemu-img', full_args)
+ is_create = bool(args and args[0] == 'create')
+ full_args = qemu_img_args + qemu_img_create_prepare_args(list(args))
+ return qemu_tool_pipe_and_status('qemu-img', full_args,
+ drop_successful_output=is_create)
def qemu_img(*args: str) -> int:
'''Run qemu-img and return the exit code'''
return qmsg
def qemu_img_create(*args):
- args = list(args)
-
- # default luks support
- if '-f' in args and args[args.index('-f') + 1] == 'luks':
- if '-o' in args:
- i = args.index('-o')
- if 'key-secret' not in args[i + 1]:
- args[i + 1].append(luks_default_key_secret_opt)
- args.insert(i + 2, '--object')
- args.insert(i + 3, luks_default_secret_object)
- else:
- args = ['-o', luks_default_key_secret_opt,
- '--object', luks_default_secret_object] + args
-
- args.insert(0, 'create')
-
- return qemu_img(*args)
+ return qemu_img('create', *args)
def qemu_img_measure(*args):
return json.loads(qemu_img_pipe("measure", "--output", "json", *args))
def qemu_img_check(*args):
return json.loads(qemu_img_pipe("check", "--output", "json", *args))
-def qemu_img_verbose(*args):
- '''Run qemu-img without suppressing its output and return the exit code'''
- exitcode = subprocess.call(qemu_img_args + list(args))
- if exitcode < 0:
- sys.stderr.write('qemu-img received signal %i: %s\n'
- % (-exitcode, ' '.join(qemu_img_args + list(args))))
- return exitcode
-
def qemu_img_pipe(*args: str) -> str:
'''Run qemu-img and return its output'''
return qemu_img_pipe_and_status(*args)[0]
log(result, filters=[filter_testfiles])
return result
-def img_info_log(filename, filter_path=None, imgopts=False, extra_args=()):
+def img_info_log(filename, filter_path=None, use_image_opts=False,
+ extra_args=()):
args = ['info']
- if imgopts:
+ if use_image_opts:
args.append('--image-opts')
else:
args += ['-f', imgfmt]
'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
line)
line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line)
+ line = re.sub('(compression type: )(zlib|zstd)', r'\1COMPRESSION_TYPE',
+ line)
lines.append(line)
return '\n'.join(lines)
notrun('Missing virtio-scsi-pci or virtio-scsi-ccw in QEMU binary')
+def _verify_imgopts(unsupported: Sequence[str] = ()) -> None:
+ imgopts = os.environ.get('IMGOPTS')
+ # One of usage examples for IMGOPTS is "data_file=$TEST_IMG.ext_data_file"
+ # but it supported only for bash tests. We don't have a concept of global
+ # TEST_IMG in iotests.py, not saying about somehow parsing $variables.
+ # So, for simplicity let's just not support any IMGOPTS with '$' inside.
+ unsup = list(unsupported) + ['$']
+ if imgopts and any(x in imgopts for x in unsup):
+ notrun(f'not suitable for this imgopts: {imgopts}')
+
+
def supports_quorum():
return 'quorum' in qemu_img_pipe('--help')
unsupported_fmts: Sequence[str] = (),
supported_protocols: Sequence[str] = (),
unsupported_protocols: Sequence[str] = (),
- required_fmts: Sequence[str] = ()) -> bool:
+ required_fmts: Sequence[str] = (),
+ unsupported_imgopts: Sequence[str] = ()) -> bool:
"""
Perform necessary setup for either script-style or unittest-style tests.
_verify_aio_mode(supported_aio_modes)
_verify_formats(required_fmts)
_verify_virtio_blk()
+ _verify_imgopts(unsupported_imgopts)
return debug
--- /dev/null
+if have_tools and targetos != 'windows'
+ qemu_iotests_binaries = [qemu_img, qemu_io, qemu_nbd, qsd]
+ qemu_iotests_env = {'PYTHON': python.full_path()}
+ qemu_iotests_formats = {
+ 'qcow2': 'quick',
+ 'raw': 'slow',
+ 'qed': 'thorough',
+ 'vmdk': 'thorough',
+ 'vpc': 'thorough'
+ }
+
+ foreach k, v : emulators
+ if k.startswith('qemu-system-')
+ qemu_iotests_binaries += v
+ endif
+ endforeach
+ foreach format, speed: qemu_iotests_formats
+ if speed == 'quick'
+ suites = 'block'
+ else
+ suites = ['block-' + speed, speed]
+ endif
+ test('qemu-iotests ' + format, sh, args: [files('../check-block.sh'), format],
+ depends: qemu_iotests_binaries, env: qemu_iotests_env,
+ protocol: 'tap',
+ suite: suites,
+ timeout: 0,
+ is_parallel: false)
+ endforeach
+endif
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
self.close()
- def print_env(self) -> None:
+ def print_env(self, prefix: str = '') -> None:
template = """\
-QEMU -- "{QEMU_PROG}" {QEMU_OPTIONS}
-QEMU_IMG -- "{QEMU_IMG_PROG}" {QEMU_IMG_OPTIONS}
-QEMU_IO -- "{QEMU_IO_PROG}" {QEMU_IO_OPTIONS}
-QEMU_NBD -- "{QEMU_NBD_PROG}" {QEMU_NBD_OPTIONS}
-IMGFMT -- {IMGFMT}{imgopts}
-IMGPROTO -- {IMGPROTO}
-PLATFORM -- {platform}
-TEST_DIR -- {TEST_DIR}
-SOCK_DIR -- {SOCK_DIR}
-GDB_OPTIONS -- {GDB_OPTIONS}
-VALGRIND_QEMU -- {VALGRIND_QEMU}
-PRINT_QEMU_OUTPUT -- {PRINT_QEMU}
-"""
+{prefix}QEMU -- "{QEMU_PROG}" {QEMU_OPTIONS}
+{prefix}QEMU_IMG -- "{QEMU_IMG_PROG}" {QEMU_IMG_OPTIONS}
+{prefix}QEMU_IO -- "{QEMU_IO_PROG}" {QEMU_IO_OPTIONS}
+{prefix}QEMU_NBD -- "{QEMU_NBD_PROG}" {QEMU_NBD_OPTIONS}
+{prefix}IMGFMT -- {IMGFMT}{imgopts}
+{prefix}IMGPROTO -- {IMGPROTO}
+{prefix}PLATFORM -- {platform}
+{prefix}TEST_DIR -- {TEST_DIR}
+{prefix}SOCK_DIR -- {SOCK_DIR}
+{prefix}GDB_OPTIONS -- {GDB_OPTIONS}
+{prefix}VALGRIND_QEMU -- {VALGRIND_QEMU}
+{prefix}PRINT_QEMU_OUTPUT -- {PRINT_QEMU}
+{prefix}"""
args = collections.defaultdict(str, self.get_env())
u = os.uname()
args['platform'] = f'{u.sysname}/{u.machine} {u.nodename} {u.release}'
-
+ args['prefix'] = prefix
print(template.format_map(args))
return results
- def __init__(self, env: TestEnv, makecheck: bool = False,
+ def __init__(self, env: TestEnv, tap: bool = False,
color: str = 'auto') -> None:
self.env = env
- self.makecheck = makecheck
+ self.tap = tap
self.last_elapsed = LastElapsedTime('.last-elapsed-cache', env)
assert color in ('auto', 'on', 'off')
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
self._stack.close()
- def test_print_one_line(self, test: str, starttime: str,
+ def test_print_one_line(self, test: str,
+ test_field_width: int,
+ starttime: str,
endtime: Optional[str] = None, status: str = '...',
lasttime: Optional[float] = None,
thistime: Optional[float] = None,
description: str = '',
- test_field_width: Optional[int] = None,
end: str = '\n') -> None:
""" Print short test info before/after test run """
test = os.path.basename(test)
if test_field_width is None:
test_field_width = 8
- if self.makecheck and status != '...':
- if status and status != 'pass':
- status = f' [{status}]'
- else:
- status = ''
-
- print(f' TEST iotest-{self.env.imgfmt}: {test}{status}')
+ if self.tap:
+ if status == 'pass':
+ print(f'ok {self.env.imgfmt} {test}')
+ elif status == 'fail':
+ print(f'not ok {self.env.imgfmt} {test}')
+ elif status == 'not run':
+ print(f'ok {self.env.imgfmt} {test} # SKIP')
return
if lasttime:
casenotrun=casenotrun)
def run_test(self, test: str,
- test_field_width: Optional[int] = None,
+ test_field_width: int,
mp: bool = False) -> TestResult:
"""
Run one test and print short status
last_el = self.last_elapsed.get(test)
start = datetime.datetime.now().strftime('%H:%M:%S')
- if not self.makecheck:
+ if not self.tap:
self.test_print_one_line(test=test,
+ test_field_width=test_field_width,
status = 'started' if mp else '...',
starttime=start,
lasttime=last_el,
- end = '\n' if mp else '\r',
- test_field_width=test_field_width)
+ end = '\n' if mp else '\r')
res = self.do_run_test(test, mp)
end = datetime.datetime.now().strftime('%H:%M:%S')
- self.test_print_one_line(test=test, status=res.status,
+ self.test_print_one_line(test=test,
+ test_field_width=test_field_width,
+ status=res.status,
starttime=start, endtime=end,
lasttime=last_el, thistime=res.elapsed,
- description=res.description,
- test_field_width=test_field_width)
+ description=res.description)
if res.casenotrun:
print(res.casenotrun)
notrun = []
casenotrun = []
- if not self.makecheck:
+ if self.tap:
+ self.env.print_env('# ')
+ else:
self.env.print_env()
test_field_width = max(len(os.path.basename(t)) for t in tests) + 2
if res.status == 'fail':
failed.append(name)
- if self.makecheck:
- self.env.print_env()
if res.diff:
print('\n'.join(res.diff))
elif res.status == 'not run':
if res.interrupted:
break
- if notrun:
- print('Not run:', ' '.join(notrun))
+ if not self.tap:
+ if notrun:
+ print('Not run:', ' '.join(notrun))
- if casenotrun:
- print('Some cases not run in:', ' '.join(casenotrun))
+ if casenotrun:
+ print('Some cases not run in:', ' '.join(casenotrun))
- if failed:
- print('Failures:', ' '.join(failed))
- print(f'Failed {len(failed)} of {n_run} iotests')
- return False
- else:
- print(f'Passed all {n_run} iotests')
- return True
+ if failed:
+ print('Failures:', ' '.join(failed))
+ print(f'Failed {len(failed)} of {n_run} iotests')
+ else:
+ print(f'Passed all {n_run} iotests')
+ return not failed
--- /dev/null
+#!/usr/bin/env python3
+# group: rw quick
+#
+# Test cases for the block-status cache.
+#
+# Copyright (C) 2022 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import signal
+import iotests
+from iotests import qemu_img_create, qemu_img_pipe, qemu_nbd
+
+
+image_size = 1 * 1024 * 1024
+test_img = os.path.join(iotests.test_dir, 'test.img')
+
+nbd_pidfile = os.path.join(iotests.test_dir, 'nbd.pid')
+nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock')
+
+
+class TestBscWithNbd(iotests.QMPTestCase):
+ def setUp(self) -> None:
+ """Just create an empty image with a read-only NBD server on it"""
+ assert qemu_img_create('-f', iotests.imgfmt, test_img,
+ str(image_size)) == 0
+
+ # Pass --allocation-depth to enable the qemu:allocation-depth context,
+ # which we are going to query to provoke a block-status inquiry with
+ # want_zero=false.
+ assert qemu_nbd(f'--socket={nbd_sock}',
+ f'--format={iotests.imgfmt}',
+ '--persistent',
+ '--allocation-depth',
+ '--read-only',
+ f'--pid-file={nbd_pidfile}',
+ test_img) \
+ == 0
+
+ def tearDown(self) -> None:
+ with open(nbd_pidfile, encoding='utf-8') as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGTERM)
+ os.remove(nbd_pidfile)
+ os.remove(test_img)
+
+ def test_with_zero_bug(self) -> None:
+ """
+ Verify that the block-status cache is not corrupted by a
+ want_zero=false call.
+ We can provoke a want_zero=false call with `qemu-img map` over NBD with
+ x-dirty-bitmap=qemu:allocation-depth, so we first run a normal `map`
+ (which results in want_zero=true), then using said
+ qemu:allocation-depth context, and finally another normal `map` to
+ verify that the cache has not been corrupted.
+ """
+
+ nbd_img_opts = f'driver=nbd,server.type=unix,server.path={nbd_sock}'
+ nbd_img_opts_alloc_depth = nbd_img_opts + \
+ ',x-dirty-bitmap=qemu:allocation-depth'
+
+ # Normal map, results in want_zero=true.
+ # This will probably detect an allocated data sector first (qemu likes
+ # to allocate the first sector to facilitate alignment probing), and
+ # then the rest to be zero. The BSC will thus contain (if anything)
+ # one range covering the first sector.
+ map_pre = qemu_img_pipe('map', '--output=json', '--image-opts',
+ nbd_img_opts)
+
+ # qemu:allocation-depth maps for want_zero=false.
+ # want_zero=false should (with the file driver, which the server is
+ # using) report everything as data. While this is sufficient for
+ # want_zero=false, this is nothing that should end up in the
+ # block-status cache.
+ # Due to a bug, this information did end up in the cache, though, and
+ # this would lead to wrong information being returned on subsequent
+ # want_zero=true calls.
+ #
+ # We need to run this map twice: On the first call, we probably still
+ # have the first sector in the cache, and so this will be served from
+ # the cache; and only the subsequent range will be queried from the
+ # block driver. This subsequent range will then be entered into the
+ # cache.
+ # If we did a want_zero=true call at this point, we would thus get
+ # correct information: The first sector is not covered by the cache, so
+ # we would get fresh block-status information from the driver, which
+ # would return a data range, and this would then go into the cache,
+ # evicting the wrong range from the want_zero=false call before.
+ #
+ # Therefore, we need a second want_zero=false map to reproduce:
+ # Since the first sector is not in the cache, the query for its status
+ # will go to the driver, which will return a result that reports the
+ # whole image to be a single data area. This result will then go into
+ # the cache, and so the cache will then report the whole image to
+ # contain data.
+ #
+ # Note that once the cache reports the whole image to contain data, any
+ # subsequent map operation will be served from the cache, and so we can
+ # never loop too many times here.
+ for _ in range(2):
+ # (Ignore the result, this is just to contaminate the cache)
+ qemu_img_pipe('map', '--output=json', '--image-opts',
+ nbd_img_opts_alloc_depth)
+
+ # Now let's see whether the cache reports everything as data, or
+ # whether we get correct information (i.e. the same as we got on our
+ # first attempt).
+ map_post = qemu_img_pipe('map', '--output=json', '--image-opts',
+ nbd_img_opts)
+
+ if map_pre != map_post:
+ print('ERROR: Map information differs before and after querying ' +
+ 'qemu:allocation-depth')
+ print('Before:')
+ print(map_pre)
+ print('After:')
+ print(map_post)
+
+ self.fail("Map information differs")
+
+
+if __name__ == '__main__':
+ # The block-status cache only works on the protocol layer, so to test it,
+ # we can only use the raw format
+ iotests.main(supported_fmts=['raw'],
+ supported_protocols=['file'])
--- /dev/null
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
if __name__ == '__main__':
- iotests.main(supported_fmts=['qcow2'])
+ iotests.main(supported_fmts=['qcow2'],
+ unsupported_imgopts=['compat'])
iotests.main(
supported_fmts=['qcow2'],
- supported_protocols=['file']
+ supported_protocols=['file'],
+ unsupported_imgopts=['compat']
)
--- /dev/null
+#!/usr/bin/env python3
+# group: migration
+#
+# Copyright (C) 2021 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import iotests
+from iotests import imgfmt, qemu_img_create, qemu_io
+
+
+test_img = os.path.join(iotests.test_dir, 'test.img')
+mig_sock = os.path.join(iotests.sock_dir, 'mig.sock')
+
+
+class TestMigrationPermissions(iotests.QMPTestCase):
+ def setUp(self):
+ qemu_img_create('-f', imgfmt, test_img, '1M')
+
+ # Set up two VMs (source and destination) accessing the same raw
+ # image file with a virtio-blk device; prepare the destination for
+ # migration with .add_incoming() and enable migration events
+ vms = [None, None]
+ for i in range(2):
+ vms[i] = iotests.VM(path_suffix=f'{i}')
+ vms[i].add_blockdev(f'file,node-name=prot,filename={test_img}')
+ vms[i].add_blockdev(f'{imgfmt},node-name=fmt,file=prot')
+ vms[i].add_device('virtio-blk,drive=fmt')
+
+ if i == 1:
+ vms[i].add_incoming(f'unix:{mig_sock}')
+
+ vms[i].launch()
+
+ result = vms[i].qmp('migrate-set-capabilities',
+ capabilities=[
+ {'capability': 'events', 'state': True}
+ ])
+ self.assert_qmp(result, 'return', {})
+
+ self.vm_s = vms[0]
+ self.vm_d = vms[1]
+
+ def tearDown(self):
+ self.vm_s.shutdown()
+ self.vm_d.shutdown()
+ try:
+ os.remove(mig_sock)
+ except FileNotFoundError:
+ pass
+ os.remove(test_img)
+
+ # Migrate an image in use by a virtio-blk device to another VM and
+ # verify that the WRITE permission is unshared both before and after
+ # migration
+ def test_post_migration_permissions(self):
+ # Try to access the image R/W, which should fail because virtio-blk
+ # has not been configured with share-rw=on
+ log = qemu_io('-f', imgfmt, '-c', 'quit', test_img)
+ if not log.strip():
+ print('ERROR (pre-migration): qemu-io should not be able to '
+ 'access this image, but it reported no error')
+ else:
+ # This is the expected output
+ assert 'Is another process using the image' in log
+
+ # Now migrate the VM
+ self.vm_s.qmp('migrate', uri=f'unix:{mig_sock}')
+ assert self.vm_s.wait_migration(None)
+ assert self.vm_d.wait_migration(None)
+
+ # Try the same qemu-io access again, verifying that the WRITE
+ # permission remains unshared
+ log = qemu_io('-f', imgfmt, '-c', 'quit', test_img)
+ if not log.strip():
+ print('ERROR (post-migration): qemu-io should not be able to '
+ 'access this image, but it reported no error')
+ else:
+ # This is the expected output
+ assert 'Is another process using the image' in log
+
+
+if __name__ == '__main__':
+ # Only works with raw images because we are testing the
+ # BlockBackend permissions; image format drivers may additionally
+ # unshare permissions and thus tamper with the result
+ iotests.main(supported_fmts=['raw'],
+ supported_protocols=['file'])
--- /dev/null
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
assert iotests.qemu_img_create('-f', iotests.imgfmt, target,
str(image_size)) == 0
+ # Ensure that mirror will copy something before READY so the
+ # target format layer will forward the pre-READY flush to its
+ # file child
+ assert iotests.qemu_io_silent('-c', 'write -P 1 0 64k', source) == 0
+
self.vm = iotests.VM()
self.vm.launch()
# Write something so will not leave the job immediately, but
# flush first (which will fail, thanks to blkdebug)
res = self.vm.qmp('human-monitor-command',
- command_line='qemu-io mirror-top "write 0 64k"')
+ command_line='qemu-io mirror-top "write -P 2 0 64k"')
self.assert_qmp(res, 'return', '')
# Drain status change events
import iotests
from iotests import log, qemu_img_create, qemu_img, qemu_img_pipe
-iotests.script_initialize(supported_fmts=['qcow2'])
+iotests.script_initialize(supported_fmts=['qcow2'],
+ unsupported_imgopts=['compat'])
top, base = iotests.file_path('top', 'base')
size = '1M'
--- /dev/null
+#!/usr/bin/env python3
+# group: rw quick
+#
+# Test what happens when a stream job completes in a blk_drain().
+#
+# Copyright (C) 2022 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+import os
+import iotests
+from iotests import imgfmt, qemu_img_create, qemu_io_silent, QMPTestCase
+
+
+image_size = 1 * 1024 * 1024
+data_size = 64 * 1024
+base = os.path.join(iotests.test_dir, 'base.img')
+top = os.path.join(iotests.test_dir, 'top.img')
+
+
+# We want to test completing a stream job in a blk_drain().
+#
+# The blk_drain() we are going to use is a virtio-scsi device resetting,
+# which we can trigger by resetting the system.
+#
+# In order to have the block job complete on drain, we (1) throttle its
+# base image so we can start the drain after it has begun, but before it
+# completes, and (2) make it encounter an I/O error on the ensuing write.
+# (If it completes regularly, the completion happens after the drain for
+# some reason.)
+
+class TestStreamErrorOnReset(QMPTestCase):
+ def setUp(self) -> None:
+ """
+ Create two images:
+ - base image {base} with {data_size} bytes allocated
+ - top image {top} without any data allocated
+
+ And the following VM configuration:
+ - base image throttled to {data_size}
+ - top image with a blkdebug configuration so the first write access
+ to it will result in an error
+ - top image is attached to a virtio-scsi device
+ """
+ assert qemu_img_create('-f', imgfmt, base, str(image_size)) == 0
+ assert qemu_io_silent('-c', f'write 0 {data_size}', base) == 0
+ assert qemu_img_create('-f', imgfmt, top, str(image_size)) == 0
+
+ self.vm = iotests.VM()
+ self.vm.add_args('-accel', 'tcg') # Make throttling work properly
+ self.vm.add_object(self.vm.qmp_to_opts({
+ 'qom-type': 'throttle-group',
+ 'id': 'thrgr',
+ 'x-bps-total': str(data_size)
+ }))
+ self.vm.add_blockdev(self.vm.qmp_to_opts({
+ 'driver': imgfmt,
+ 'node-name': 'base',
+ 'file': {
+ 'driver': 'throttle',
+ 'throttle-group': 'thrgr',
+ 'file': {
+ 'driver': 'file',
+ 'filename': base
+ }
+ }
+ }))
+ self.vm.add_blockdev(self.vm.qmp_to_opts({
+ 'driver': imgfmt,
+ 'node-name': 'top',
+ 'file': {
+ 'driver': 'blkdebug',
+ 'node-name': 'top-blkdebug',
+ 'inject-error': [{
+ 'event': 'pwritev',
+ 'immediately': 'true',
+ 'once': 'true'
+ }],
+ 'image': {
+ 'driver': 'file',
+ 'filename': top
+ }
+ },
+ 'backing': 'base'
+ }))
+ self.vm.add_device(self.vm.qmp_to_opts({
+ 'driver': 'virtio-scsi',
+ 'id': 'vscsi'
+ }))
+ self.vm.add_device(self.vm.qmp_to_opts({
+ 'driver': 'scsi-hd',
+ 'bus': 'vscsi.0',
+ 'drive': 'top'
+ }))
+ self.vm.launch()
+
+ def tearDown(self) -> None:
+ self.vm.shutdown()
+ os.remove(top)
+ os.remove(base)
+
+ def test_stream_error_on_reset(self) -> None:
+ # Launch a stream job, which will take at least a second to
+ # complete, because the base image is throttled (so we can
+ # get in between it having started and it having completed)
+ res = self.vm.qmp('block-stream', job_id='stream', device='top')
+ self.assert_qmp(res, 'return', {})
+
+ while True:
+ ev = self.vm.event_wait('JOB_STATUS_CHANGE')
+ if ev['data']['status'] == 'running':
+ # Once the stream job is running, reset the system, which
+ # forces the virtio-scsi device to be reset, thus draining
+ # the stream job, and making it complete. Completing
+ # inside of that drain should not result in a segfault.
+ res = self.vm.qmp('system_reset')
+ self.assert_qmp(res, 'return', {})
+ elif ev['data']['status'] == 'null':
+ # The test is done once the job is gone
+ break
+
+
+if __name__ == '__main__':
+ # Passes with any format with backing file support, but qed and
+ # qcow1 do not seem to exercise the used-to-be problematic code
+ # path, so there is no point in having them in this list
+ iotests.main(supported_fmts=['qcow2', 'vmdk'],
+ supported_protocols=['file'])
--- /dev/null
+.
+----------------------------------------------------------------------
+Ran 1 tests
+
+OK
qtest_quit(qtest);
}
+static void test_pci_unplug_json_request(void)
+{
+ QTestState *qtest = qtest_initf(
+ "-device '{\"driver\": \"virtio-mouse-pci\", \"id\": \"dev0\"}'");
+
+ /*
+ * Request device removal. As the guest is not running, the request won't
+ * be processed. However during system reset, the removal will be
+ * handled, removing the device.
+ */
+ device_del(qtest, "dev0");
+ system_reset(qtest);
+ wait_device_deleted_event(qtest, "dev0");
+
+ qtest_quit(qtest);
+}
+
static void test_ccw_unplug(void)
{
QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0");
*/
qtest_add_func("/device-plug/pci-unplug-request",
test_pci_unplug_request);
+ qtest_add_func("/device-plug/pci-unplug-json-request",
+ test_pci_unplug_json_request);
if (!strcmp(arch, "s390x")) {
qtest_add_func("/device-plug/ccw-unplug",
#qtests_i386 += ['dbus-vmstate-test']
dbus_vmstate1 = custom_target('dbus-vmstate description',
output: ['dbus-vmstate1.h', 'dbus-vmstate1.c'],
- input: meson.source_root() / 'backends/dbus-vmstate1.xml',
+ input: meson.project_source_root() / 'backends/dbus-vmstate1.xml',
command: [config_host['GDBUS_CODEGEN'],
'@INPUT@',
'--interface-prefix', 'org.qemu',
test: executable(test, src, dependencies: deps)
}
endif
- # FIXME: missing dependency on the emulator binary and qemu-img
test('qtest-@0@/@1@'.format(target_base, test),
qtest_executables[test],
- depends: [test_deps, qtest_emulator],
+ depends: [test_deps, qtest_emulator, emulator_modules],
env: qtest_env,
args: ['--tap', '-k'],
protocol: 'tap',
-include ../../../config-host.mak
-include ../config-$(TARGET).mak
ifeq ($(CONFIG_USER_ONLY),y)
--include $(SRC_PATH)/default-configs/targets/$(TARGET).mak
+-include $(SRC_PATH)/configs/targets/$(TARGET)/default.mak
endif
# for including , in command strings
echo "TARGET_NAME=$arch" >> $config_target_mak
echo "target=$target" >> $config_target_mak
case $target in
- *-linux-user | *-bsd-user)
+ *-linux-user)
echo "CONFIG_USER_ONLY=y" >> $config_target_mak
+ echo "CONFIG_LINUX_USER=y" >> $config_target_mak
+ echo "QEMU=$PWD/qemu-$arch" >> $config_target_mak
+ ;;
+ *-bsd-user)
+ echo "CONFIG_USER_ONLY=y" >> $config_target_mak
+ echo "CONFIG_BSD_USER=y" >> $config_target_mak
echo "QEMU=$PWD/qemu-$arch" >> $config_target_mak
;;
*-softmmu)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
- to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (INEXACT )
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
- to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (INEXACT )
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
- to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (INEXACT )
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
- to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (INEXACT )
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
- to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (INEXACT )
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
- to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (INEXACT )
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
- to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (INEXACT )
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.00000c00000000000000p-14:0x38800006)
- to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (INEXACT )
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint32: 1 (OK)
to uint64: 1 (OK)
from single: f32(0x1.00400000000000000000p+0:0x3f802000)
- to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (INEXACT )
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
to int32: 1 (INEXACT )
to int64: 1 (INEXACT )
to uint32: 1 (INEXACT )
to uint32: 2 (OK)
to uint64: 2 (OK)
from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
- to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (INEXACT )
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
to int32: 2 (INEXACT )
to int64: 2 (INEXACT )
to uint32: 2 (INEXACT )
to uint64: 2 (INEXACT )
from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
- to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (INEXACT )
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
to int32: 3 (INEXACT )
to int64: 3 (INEXACT )
to uint32: 3 (INEXACT )
to uint64: 3 (INEXACT )
from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
- to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (INEXACT )
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
to int32: 65503 (OK)
to int64: 65503 (OK)
to uint32: 65503 (OK)
to uint64: 65503 (OK)
from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
- to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
to int32: 65504 (OK)
to int64: 65504 (OK)
to uint32: 65504 (OK)
to uint64: 65504 (OK)
from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
- to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (INEXACT )
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
to int32: 65505 (OK)
to int64: 65505 (OK)
to uint32: 65505 (OK)
to uint64: 65505 (OK)
from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
- to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (INEXACT )
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
to int32: 131007 (OK)
to int64: 131007 (OK)
to uint32: 131007 (OK)
to uint64: 131007 (OK)
from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
- to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
to int32: 131008 (OK)
to int64: 131008 (OK)
to uint32: 131008 (OK)
to uint64: 131008 (OK)
from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
- to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (INEXACT )
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
to int32: 131009 (OK)
to int64: 131009 (OK)
to uint32: 131009 (OK)
to uint64: 131009 (OK)
from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
- to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (INEXACT )
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
to uint64: -1 (INVALID)
from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
- to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (INEXACT )
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
- to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (INEXACT )
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
- to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (INEXACT )
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
- to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (INEXACT )
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
- to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (INEXACT )
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
- to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (INEXACT )
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
- to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (INEXACT )
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
- to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (INEXACT )
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.00000c00000000000000p-14:0x38800006)
- to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (INEXACT )
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint32: 1 (OK)
to uint64: 1 (OK)
from single: f32(0x1.00400000000000000000p+0:0x3f802000)
- to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (INEXACT )
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
to int32: 1 (INEXACT )
to int64: 1 (INEXACT )
to uint32: 1 (INEXACT )
to uint32: 2 (OK)
to uint64: 2 (OK)
from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
- to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (INEXACT )
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
to int32: 2 (INEXACT )
to int64: 2 (INEXACT )
to uint32: 2 (INEXACT )
to uint64: 2 (INEXACT )
from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
- to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (INEXACT )
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
to int32: 3 (INEXACT )
to int64: 3 (INEXACT )
to uint32: 3 (INEXACT )
to uint64: 3 (INEXACT )
from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
- to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (INEXACT )
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
to int32: 65503 (OK)
to int64: 65503 (OK)
to uint32: 65503 (OK)
to uint64: 65503 (OK)
from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
- to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
to int32: 65504 (OK)
to int64: 65504 (OK)
to uint32: 65504 (OK)
to uint64: 65504 (OK)
from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
- to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (INEXACT )
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
to int32: 65505 (OK)
to int64: 65505 (OK)
to uint32: 65505 (OK)
to uint64: 65505 (OK)
from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
- to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (INEXACT )
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
to int32: 131007 (OK)
to int64: 131007 (OK)
to uint32: 131007 (OK)
to uint64: 131007 (OK)
from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
- to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
to int32: 131008 (OK)
to int64: 131008 (OK)
to uint32: 131008 (OK)
to uint64: 131008 (OK)
from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
- to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (INEXACT )
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
to int32: 131009 (OK)
to int64: 131009 (OK)
to uint32: 131009 (OK)
to uint64: 131009 (OK)
from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
- to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (INEXACT )
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
to uint64: -1 (INVALID)
from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
- to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (INEXACT )
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
- to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (INEXACT )
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
- to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (INEXACT )
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
- to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (INEXACT )
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
- to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (INEXACT )
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
- to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (INEXACT )
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
- to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (INEXACT )
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
- to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (INEXACT )
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.00000c00000000000000p-14:0x38800006)
- to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (INEXACT )
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint32: 1 (OK)
to uint64: 1 (OK)
from single: f32(0x1.00400000000000000000p+0:0x3f802000)
- to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (INEXACT )
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
to int32: 1 (INEXACT )
to int64: 1 (INEXACT )
to uint32: 1 (INEXACT )
to uint32: 2 (OK)
to uint64: 2 (OK)
from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
- to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (INEXACT )
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
to int32: 2 (INEXACT )
to int64: 2 (INEXACT )
to uint32: 2 (INEXACT )
to uint64: 2 (INEXACT )
from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
- to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (INEXACT )
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
to int32: 3 (INEXACT )
to int64: 3 (INEXACT )
to uint32: 3 (INEXACT )
to uint64: 3 (INEXACT )
from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
- to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (INEXACT )
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
to int32: 65503 (OK)
to int64: 65503 (OK)
to uint32: 65503 (OK)
to uint64: 65503 (OK)
from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
- to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
to int32: 65504 (OK)
to int64: 65504 (OK)
to uint32: 65504 (OK)
to uint64: 65504 (OK)
from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
- to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (INEXACT )
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
to int32: 65505 (OK)
to int64: 65505 (OK)
to uint32: 65505 (OK)
to uint64: 65505 (OK)
from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
- to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (INEXACT )
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
to int32: 131007 (OK)
to int64: 131007 (OK)
to uint32: 131007 (OK)
to uint64: 131007 (OK)
from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
- to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
to int32: 131008 (OK)
to int64: 131008 (OK)
to uint32: 131008 (OK)
to uint64: 131008 (OK)
from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
- to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (INEXACT )
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
to int32: 131009 (OK)
to int64: 131009 (OK)
to uint32: 131009 (OK)
to uint64: 131009 (OK)
from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
- to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (INEXACT )
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
to uint64: -1 (INVALID)
from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
- to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (INEXACT )
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
- to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (INEXACT )
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
- to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (INEXACT )
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
- to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (INEXACT )
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
to int32: -2147483648 (INVALID)
to int64: -9223372036854775808 (INVALID)
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
- to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (INEXACT )
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint64: 0 (INVALID)
from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
- to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (INEXACT )
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INVALID)
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
- to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (INEXACT )
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
- to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (INEXACT )
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint64: 0 (INEXACT )
from single: f32(0x1.00000c00000000000000p-14:0x38800006)
- to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (INEXACT )
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
to int32: 0 (INEXACT )
to int64: 0 (INEXACT )
to uint32: 0 (INEXACT )
to uint32: 1 (OK)
to uint64: 1 (OK)
from single: f32(0x1.00400000000000000000p+0:0x3f802000)
- to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (INEXACT )
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
to int32: 1 (INEXACT )
to int64: 1 (INEXACT )
to uint32: 1 (INEXACT )
to uint32: 2 (OK)
to uint64: 2 (OK)
from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
- to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (INEXACT )
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
to int32: 2 (INEXACT )
to int64: 2 (INEXACT )
to uint32: 2 (INEXACT )
to uint64: 2 (INEXACT )
from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
- to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (INEXACT )
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
to int32: 3 (INEXACT )
to int64: 3 (INEXACT )
to uint32: 3 (INEXACT )
to uint64: 3 (INEXACT )
from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
- to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (INEXACT )
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
to int32: 65503 (OK)
to int64: 65503 (OK)
to uint32: 65503 (OK)
to uint64: 65503 (OK)
from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
- to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
to int32: 65504 (OK)
to int64: 65504 (OK)
to uint32: 65504 (OK)
to uint64: 65504 (OK)
from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
- to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (INEXACT )
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
to int32: 65505 (OK)
to int64: 65505 (OK)
to uint32: 65505 (OK)
to uint64: 65505 (OK)
from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
- to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (INEXACT )
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
to int32: 131007 (OK)
to int64: 131007 (OK)
to uint32: 131007 (OK)
to uint64: 131007 (OK)
from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
- to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (INEXACT )
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
to int32: 131008 (OK)
to int64: 131008 (OK)
to uint32: 131008 (OK)
to uint64: 131008 (OK)
from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
- to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (INEXACT )
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
to int32: 131009 (OK)
to int64: 131009 (OK)
to uint32: 131009 (OK)
to uint64: 131009 (OK)
from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
- to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (INEXACT )
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
to uint64: -1 (INVALID)
from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
- to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (INEXACT )
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
to int32: 2147483647 (INVALID)
to int64: 9223372036854775807 (INVALID)
to uint32: -1 (INVALID)
op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
-res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (7/1)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2)
op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/0)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
-res: f32(0x1.80400000000000000000p+1:0x40402000) flags=INEXACT (15/1)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/2)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
-res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=INEXACT (16/0)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=INEXACT (16/1)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (7/0)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
-res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (7/1)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2)
op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/0)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
-res: f32(0x1.80400000000000000000p+1:0x40402000) flags=INEXACT (15/1)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/2)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
-res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=INEXACT (16/0)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=INEXACT (16/1)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
res: f32(0x1.2e23d400000000000000p+2:0x409711ea) flags=INEXACT (16/2)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
-res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (7/1)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2)
op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/0)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
-res: f32(0x1.80400000000000000000p+1:0x40402000) flags=INEXACT (15/1)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/2)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
-res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=INEXACT (16/0)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=INEXACT (16/1)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
-res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (7/1)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2)
op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/0)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
-res: f32(0x1.80400000000000000000p+1:0x40402000) flags=INEXACT (15/1)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.80200000000000000000p+1:0x40401000) flags=INEXACT (15/2)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
-res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=INEXACT (16/0)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
-res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=INEXACT (16/1)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2)
op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
# Set search path for all sources
VPATH += $(MULTIARCH_SRC)
MULTIARCH_SRCS = $(notdir $(wildcard $(MULTIARCH_SRC)/*.c))
-ifneq ($(CONFIG_LINUX),)
+ifneq ($(CONFIG_LINUX_USER),)
VPATH += $(MULTIARCH_SRC)/linux
MULTIARCH_SRCS += $(notdir $(wildcard $(MULTIARCH_SRC)/linux/*.c))
endif
output = input;
- out_fmt = fmt_f64(output);
flag_fmt = fmt_flags();
+ out_fmt = fmt_f64(output);
printf(" to double: %s (%s)\n", out_fmt, flag_fmt);
free(out_fmt);
free(flag_fmt);
{
char *r_fmt, *flag_fmt;
- r_fmt = fmt_f32(r);
flag_fmt = fmt_flags();
+ r_fmt = fmt_f32(r);
printf("res: %s flags=%s (%d/%d)\n", r_fmt, flag_fmt, j, k);
--- /dev/null
+### Rounding to nearest
+from single: f32(-nan:0xffa00000)
+ to double: f64(-nan:0x00fff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-nan:0xffc00000)
+ to double: f64(-nan:0x00fff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-inf:0xff800000)
+ to double: f64(-inf:0x00fff0000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.00000000000000000000p-126:0x80800000)
+ to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x0.00000000000000000000p+0:0000000000)
+ to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK)
+ to int32: 0 (OK)
+ to int64: 0 (OK)
+ to uint32: 0 (OK)
+ to uint64: 0 (OK)
+from single: f32(0x1.00000000000000000000p-126:0x00800000)
+ to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p-25:0x33000000)
+ to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000c00000000000000p-14:0x38800006)
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p+0:0x3f800000)
+ to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK)
+ to int32: 1 (OK)
+ to int64: 1 (OK)
+ to uint32: 1 (OK)
+ to uint64: 1 (OK)
+from single: f32(0x1.00400000000000000000p+0:0x3f802000)
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
+ to int32: 1 (INEXACT )
+ to int64: 1 (INEXACT )
+ to uint32: 1 (INEXACT )
+ to uint64: 1 (INEXACT )
+from single: f32(0x1.00000000000000000000p+1:0x40000000)
+ to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK)
+ to int32: 2 (OK)
+ to int64: 2 (OK)
+ to uint32: 2 (OK)
+ to uint64: 2 (OK)
+from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
+ to int32: 2 (INEXACT )
+ to int64: 2 (INEXACT )
+ to uint32: 2 (INEXACT )
+ to uint64: 2 (INEXACT )
+from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
+ to int32: 3 (INEXACT )
+ to int64: 3 (INEXACT )
+ to uint32: 3 (INEXACT )
+ to uint64: 3 (INEXACT )
+from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
+ to int32: 65503 (OK)
+ to int64: 65503 (OK)
+ to uint32: 65503 (OK)
+ to uint64: 65503 (OK)
+from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
+ to int32: 65504 (OK)
+ to int64: 65504 (OK)
+ to uint32: 65504 (OK)
+ to uint64: 65504 (OK)
+from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
+ to int32: 65505 (OK)
+ to int64: 65505 (OK)
+ to uint32: 65505 (OK)
+ to uint64: 65505 (OK)
+from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
+ to int32: 131007 (OK)
+ to int64: 131007 (OK)
+ to uint32: 131007 (OK)
+ to uint64: 131007 (OK)
+from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
+ to int32: 131008 (OK)
+ to int64: 131008 (OK)
+ to uint32: 131008 (OK)
+ to uint64: 131008 (OK)
+from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
+ to int32: 131009 (OK)
+ to int64: 131009 (OK)
+ to uint32: 131009 (OK)
+ to uint64: 131009 (OK)
+from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(inf:0x7f800000)
+ to double: f64(inf:0x007ff0000000000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(nan:0x7fc00000)
+ to double: f64(nan:0x007ff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(nan:0x7fa00000)
+ to double: f64(nan:0x007ff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+### Rounding upwards
+from single: f32(-nan:0xffa00000)
+ to double: f64(-nan:0x00fff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-nan:0xffc00000)
+ to double: f64(-nan:0x00fff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-inf:0xff800000)
+ to double: f64(-inf:0x00fff0000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.00000000000000000000p-126:0x80800000)
+ to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x0.00000000000000000000p+0:0000000000)
+ to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK)
+ to int32: 0 (OK)
+ to int64: 0 (OK)
+ to uint32: 0 (OK)
+ to uint64: 0 (OK)
+from single: f32(0x1.00000000000000000000p-126:0x00800000)
+ to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p-25:0x33000000)
+ to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000c00000000000000p-14:0x38800006)
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p+0:0x3f800000)
+ to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK)
+ to int32: 1 (OK)
+ to int64: 1 (OK)
+ to uint32: 1 (OK)
+ to uint64: 1 (OK)
+from single: f32(0x1.00400000000000000000p+0:0x3f802000)
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
+ to int32: 1 (INEXACT )
+ to int64: 1 (INEXACT )
+ to uint32: 1 (INEXACT )
+ to uint64: 1 (INEXACT )
+from single: f32(0x1.00000000000000000000p+1:0x40000000)
+ to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK)
+ to int32: 2 (OK)
+ to int64: 2 (OK)
+ to uint32: 2 (OK)
+ to uint64: 2 (OK)
+from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
+ to int32: 2 (INEXACT )
+ to int64: 2 (INEXACT )
+ to uint32: 2 (INEXACT )
+ to uint64: 2 (INEXACT )
+from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
+ to int32: 3 (INEXACT )
+ to int64: 3 (INEXACT )
+ to uint32: 3 (INEXACT )
+ to uint64: 3 (INEXACT )
+from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
+ to int32: 65503 (OK)
+ to int64: 65503 (OK)
+ to uint32: 65503 (OK)
+ to uint64: 65503 (OK)
+from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
+ to int32: 65504 (OK)
+ to int64: 65504 (OK)
+ to uint32: 65504 (OK)
+ to uint64: 65504 (OK)
+from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
+ to int32: 65505 (OK)
+ to int64: 65505 (OK)
+ to uint32: 65505 (OK)
+ to uint64: 65505 (OK)
+from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
+ to int32: 131007 (OK)
+ to int64: 131007 (OK)
+ to uint32: 131007 (OK)
+ to uint64: 131007 (OK)
+from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
+ to int32: 131008 (OK)
+ to int64: 131008 (OK)
+ to uint32: 131008 (OK)
+ to uint64: 131008 (OK)
+from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
+ to int32: 131009 (OK)
+ to int64: 131009 (OK)
+ to uint32: 131009 (OK)
+ to uint64: 131009 (OK)
+from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(inf:0x7f800000)
+ to double: f64(inf:0x007ff0000000000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(nan:0x7fc00000)
+ to double: f64(nan:0x007ff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(nan:0x7fa00000)
+ to double: f64(nan:0x007ff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+### Rounding downwards
+from single: f32(-nan:0xffa00000)
+ to double: f64(-nan:0x00fff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-nan:0xffc00000)
+ to double: f64(-nan:0x00fff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-inf:0xff800000)
+ to double: f64(-inf:0x00fff0000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.00000000000000000000p-126:0x80800000)
+ to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x0.00000000000000000000p+0:0000000000)
+ to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK)
+ to int32: 0 (OK)
+ to int64: 0 (OK)
+ to uint32: 0 (OK)
+ to uint64: 0 (OK)
+from single: f32(0x1.00000000000000000000p-126:0x00800000)
+ to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p-25:0x33000000)
+ to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000c00000000000000p-14:0x38800006)
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p+0:0x3f800000)
+ to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK)
+ to int32: 1 (OK)
+ to int64: 1 (OK)
+ to uint32: 1 (OK)
+ to uint64: 1 (OK)
+from single: f32(0x1.00400000000000000000p+0:0x3f802000)
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
+ to int32: 1 (INEXACT )
+ to int64: 1 (INEXACT )
+ to uint32: 1 (INEXACT )
+ to uint64: 1 (INEXACT )
+from single: f32(0x1.00000000000000000000p+1:0x40000000)
+ to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK)
+ to int32: 2 (OK)
+ to int64: 2 (OK)
+ to uint32: 2 (OK)
+ to uint64: 2 (OK)
+from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
+ to int32: 2 (INEXACT )
+ to int64: 2 (INEXACT )
+ to uint32: 2 (INEXACT )
+ to uint64: 2 (INEXACT )
+from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
+ to int32: 3 (INEXACT )
+ to int64: 3 (INEXACT )
+ to uint32: 3 (INEXACT )
+ to uint64: 3 (INEXACT )
+from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
+ to int32: 65503 (OK)
+ to int64: 65503 (OK)
+ to uint32: 65503 (OK)
+ to uint64: 65503 (OK)
+from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
+ to int32: 65504 (OK)
+ to int64: 65504 (OK)
+ to uint32: 65504 (OK)
+ to uint64: 65504 (OK)
+from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
+ to int32: 65505 (OK)
+ to int64: 65505 (OK)
+ to uint32: 65505 (OK)
+ to uint64: 65505 (OK)
+from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
+ to int32: 131007 (OK)
+ to int64: 131007 (OK)
+ to uint32: 131007 (OK)
+ to uint64: 131007 (OK)
+from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
+ to int32: 131008 (OK)
+ to int64: 131008 (OK)
+ to uint32: 131008 (OK)
+ to uint64: 131008 (OK)
+from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
+ to int32: 131009 (OK)
+ to int64: 131009 (OK)
+ to uint32: 131009 (OK)
+ to uint64: 131009 (OK)
+from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(inf:0x7f800000)
+ to double: f64(inf:0x007ff0000000000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(nan:0x7fc00000)
+ to double: f64(nan:0x007ff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(nan:0x7fa00000)
+ to double: f64(nan:0x007ff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+### Rounding to zero
+from single: f32(-nan:0xffa00000)
+ to double: f64(-nan:0x00fff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-nan:0xffc00000)
+ to double: f64(-nan:0x00fff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-inf:0xff800000)
+ to double: f64(-inf:0x00fff0000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+ to double: f64(-0x1.fffffe00000000000000p+127:0x00c7efffffe0000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+ to double: f64(-0x1.1874b200000000000000p+103:0x00c661874b20000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+ to double: f64(-0x1.c0bab600000000000000p+99:0x00c62c0bab60000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+ to double: f64(-0x1.31f75000000000000000p-40:0x00bd731f7500000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+ to double: f64(-0x1.50544400000000000000p-66:0x00bbd5054440000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(-0x1.00000000000000000000p-126:0x80800000)
+ to double: f64(-0x1.00000000000000000000p-126:0x00b810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x0.00000000000000000000p+0:0000000000)
+ to double: f64(0x0.00000000000000000000p+0:00000000000000000000) (OK)
+ to int32: 0 (OK)
+ to int64: 0 (OK)
+ to uint32: 0 (OK)
+ to uint64: 0 (OK)
+from single: f32(0x1.00000000000000000000p-126:0x00800000)
+ to double: f64(0x1.00000000000000000000p-126:0x003810000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p-25:0x33000000)
+ to double: f64(0x1.00000000000000000000p-25:0x003e60000000000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+ to double: f64(0x1.ffffe600000000000000p-25:0x003e6ffffe60000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+ to double: f64(0x1.ff801a00000000000000p-15:0x003f0ff801a0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000c00000000000000p-14:0x38800006)
+ to double: f64(0x1.00000c00000000000000p-14:0x003f100000c0000000) (OK)
+ to int32: 0 (INEXACT )
+ to int64: 0 (INEXACT )
+ to uint32: 0 (INEXACT )
+ to uint64: 0 (INEXACT )
+from single: f32(0x1.00000000000000000000p+0:0x3f800000)
+ to double: f64(0x1.00000000000000000000p+0:0x003ff0000000000000) (OK)
+ to int32: 1 (OK)
+ to int64: 1 (OK)
+ to uint32: 1 (OK)
+ to uint64: 1 (OK)
+from single: f32(0x1.00400000000000000000p+0:0x3f802000)
+ to double: f64(0x1.00400000000000000000p+0:0x003ff0040000000000) (OK)
+ to int32: 1 (INEXACT )
+ to int64: 1 (INEXACT )
+ to uint32: 1 (INEXACT )
+ to uint64: 1 (INEXACT )
+from single: f32(0x1.00000000000000000000p+1:0x40000000)
+ to double: f64(0x1.00000000000000000000p+1:0x004000000000000000) (OK)
+ to int32: 2 (OK)
+ to int64: 2 (OK)
+ to uint32: 2 (OK)
+ to uint64: 2 (OK)
+from single: f32(0x1.5bf0a800000000000000p+1:0x402df854)
+ to double: f64(0x1.5bf0a800000000000000p+1:0x004005bf0a80000000) (OK)
+ to int32: 2 (INEXACT )
+ to int64: 2 (INEXACT )
+ to uint32: 2 (INEXACT )
+ to uint64: 2 (INEXACT )
+from single: f32(0x1.921fb600000000000000p+1:0x40490fdb)
+ to double: f64(0x1.921fb600000000000000p+1:0x00400921fb60000000) (OK)
+ to int32: 3 (INEXACT )
+ to int64: 3 (INEXACT )
+ to uint32: 3 (INEXACT )
+ to uint64: 3 (INEXACT )
+from single: f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+ to double: f64(0x1.ffbe0000000000000000p+15:0x0040effbe000000000) (OK)
+ to int32: 65503 (OK)
+ to int64: 65503 (OK)
+ to uint32: 65503 (OK)
+ to uint64: 65503 (OK)
+from single: f32(0x1.ffc00000000000000000p+15:0x477fe000)
+ to double: f64(0x1.ffc00000000000000000p+15:0x0040effc0000000000) (OK)
+ to int32: 65504 (OK)
+ to int64: 65504 (OK)
+ to uint32: 65504 (OK)
+ to uint64: 65504 (OK)
+from single: f32(0x1.ffc20000000000000000p+15:0x477fe100)
+ to double: f64(0x1.ffc20000000000000000p+15:0x0040effc2000000000) (OK)
+ to int32: 65505 (OK)
+ to int64: 65505 (OK)
+ to uint32: 65505 (OK)
+ to uint64: 65505 (OK)
+from single: f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+ to double: f64(0x1.ffbf0000000000000000p+16:0x0040fffbf000000000) (OK)
+ to int32: 131007 (OK)
+ to int64: 131007 (OK)
+ to uint32: 131007 (OK)
+ to uint64: 131007 (OK)
+from single: f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+ to double: f64(0x1.ffc00000000000000000p+16:0x0040fffc0000000000) (OK)
+ to int32: 131008 (OK)
+ to int64: 131008 (OK)
+ to uint32: 131008 (OK)
+ to uint64: 131008 (OK)
+from single: f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+ to double: f64(0x1.ffc10000000000000000p+16:0x0040fffc1000000000) (OK)
+ to int32: 131009 (OK)
+ to int64: 131009 (OK)
+ to uint32: 131009 (OK)
+ to uint64: 131009 (OK)
+from single: f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+ to double: f64(0x1.c0bab600000000000000p+99:0x00462c0bab60000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+ to double: f64(0x1.fffffe00000000000000p+127:0x0047efffffe0000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(inf:0x7f800000)
+ to double: f64(inf:0x007ff0000000000000) (OK)
+ to int32: 2147483647 (INVALID)
+ to int64: 9223372036854775807 (INVALID)
+ to uint32: -1 (INVALID)
+ to uint64: -1 (INVALID)
+from single: f32(nan:0x7fc00000)
+ to double: f64(nan:0x007ff8000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
+from single: f32(nan:0x7fa00000)
+ to double: f64(nan:0x007ff4000000000000) (OK)
+ to int32: -2147483648 (INVALID)
+ to int64: -9223372036854775808 (INVALID)
+ to uint32: 0 (INVALID)
+ to uint64: 0 (INVALID)
--- /dev/null
+### Rounding to nearest
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffe00000) flags=INVALID (0/0)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/1)
+op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/2)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(-nan:0xffc00000) flags=OK (1/0)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=OK (1/1)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffc00000) flags=OK (1/2)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(inf:0x7f800000) flags=OK (2/0)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000)
+res: f32(-inf:0xff800000) flags=OK (2/1)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(inf:0x7f800000) flags=OK (2/2)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/0)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/1)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/2)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (4/0)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) flags=INEXACT (4/1)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (4/2)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(0x1.0c27fa00000000000000p+60:0x5d8613fd) flags=INEXACT (5/0)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (5/1)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(0x1.26c46200000000000000p+34:0x50936231) flags=INEXACT (5/2)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(0x1.91f94000000000000000p-106:0x0ac8fca0) flags=INEXACT (6/0)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(-0x1.31f75000000000000000p-40:0xab98fba8) flags=INEXACT (6/1)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (6/2)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (12/2)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.12804200000000000000p+3:0x41094021) flags=INEXACT (17/0)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.91ed3c00000000000000p+17:0x4848f69e) flags=INEXACT (18/1)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.08edf000000000000000p+18:0x488476f8) flags=INEXACT (19/0)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.08ee7a00000000000000p+18:0x4884773d) flags=INEXACT (19/2)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ff840800000000000000p+31:0x4f7fc204) flags=INEXACT (20/1)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820800000000000000p+31:0x4f7fc104) flags=INEXACT (20/2)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff860800000000000000p+31:0x4f7fc304) flags=INEXACT (21/0)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820800000000000000p+32:0x4fffc104) flags=INEXACT (21/1)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff830800000000000000p+32:0x4fffc184) flags=INEXACT (22/0)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff840800000000000000p+32:0x4fffc204) flags=INEXACT (22/2)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff820800000000000000p+33:0x507fc104) flags=INEXACT (23/1)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff810800000000000000p+33:0x507fc084) flags=INEXACT (23/2)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.c0838000000000000000p+116:0x79e041c0) flags=INEXACT (24/1)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/0)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/1)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/2)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(inf:0x7f800000) flags=OK (26/0)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(inf:0x7f800000) flags=OK (26/1)
+op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(inf:0x7f800000) flags=OK (26/2)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fc00000) flags=OK (27/0)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(nan:0x7fc00000) flags=OK (27/1)
+op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=OK (27/2)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/0)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=INVALID (28/1)
+op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/2)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(nan:0x7fc00000) flags=INVALID (29/0)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (29/1)
+op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (29/2)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (30/0)
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (30/1)
+op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (30/2)
+# LP184149
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0)
+op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001)
+res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0)
+### Rounding upwards
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffe00000) flags=INVALID (0/0)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/1)
+op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/2)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(-nan:0xffc00000) flags=OK (1/0)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=OK (1/1)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffc00000) flags=OK (1/2)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(inf:0x7f800000) flags=OK (2/0)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000)
+res: f32(-inf:0xff800000) flags=OK (2/1)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(inf:0x7f800000) flags=OK (2/2)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/0)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/1)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (3/2)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (4/0)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) flags=INEXACT (4/1)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (4/2)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(0x1.0c27fa00000000000000p+60:0x5d8613fd) flags=INEXACT (5/0)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (5/1)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(0x1.26c46200000000000000p+34:0x50936231) flags=INEXACT (5/2)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(0x1.91f94000000000000000p-106:0x0ac8fca0) flags=INEXACT (6/0)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(-0x1.31f74e00000000000000p-40:0xab98fba7) flags=INEXACT (6/1)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544200000000000000p-66:0x9ea82a21) flags=INEXACT (6/2)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (7/0)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (9/1)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.ffffe800000000000000p-25:0x337ffff4) flags=INEXACT (10/0)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.ffffe800000000000000p-50:0x26fffff4) flags=INEXACT (10/1)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000200000000000000p-25:0x33000001) flags=INEXACT (10/2)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (11/0)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00080000000000000000p-25:0x33000400) flags=INEXACT (11/1)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0001f400000000000000p-24:0x338000fa) flags=INEXACT (11/2)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00000e00000000000000p-14:0x38800007) flags=INEXACT (12/0)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0ffbf600000000000000p-24:0x3387fdfb) flags=INEXACT (12/1)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801c00000000000000p-15:0x387fc00e) flags=INEXACT (12/2)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00000200000000000000p+0:0x3f800001) flags=INEXACT (13/0)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ffc01a00000000000000p-14:0x38ffe00d) flags=INEXACT (13/1)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.ffc01a00000000000000p-14:0x38ffe00d) flags=INEXACT (13/2)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.00440200000000000000p+0:0x3f802201) flags=INEXACT (14/0)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00440200000000000000p+0:0x3f802201) flags=INEXACT (14/1)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00040200000000000000p+0:0x3f800201) flags=INEXACT (14/2)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.2e23d400000000000000p+2:0x409711ea) flags=INEXACT (16/2)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.12804200000000000000p+3:0x41094021) flags=INEXACT (17/0)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.51458200000000000000p+3:0x4128a2c1) flags=INEXACT (17/1)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.200c0600000000000000p+3:0x41100603) flags=INEXACT (17/2)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ffcf1600000000000000p+15:0x477fe78b) flags=INEXACT (18/0)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.91ed3c00000000000000p+17:0x4848f69e) flags=INEXACT (18/1)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.5bc56200000000000000p+17:0x482de2b1) flags=INEXACT (18/2)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.08edf000000000000000p+18:0x488476f8) flags=INEXACT (19/0)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.ff7e0a00000000000000p+31:0x4f7fbf05) flags=INEXACT (19/1)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.08ee7a00000000000000p+18:0x4884773d) flags=INEXACT (19/2)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800a00000000000000p+31:0x4f7fc005) flags=INEXACT (20/0)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ff840800000000000000p+31:0x4f7fc204) flags=INEXACT (20/1)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820800000000000000p+31:0x4f7fc104) flags=INEXACT (20/2)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff860800000000000000p+31:0x4f7fc304) flags=INEXACT (21/0)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820800000000000000p+32:0x4fffc104) flags=INEXACT (21/1)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800a00000000000000p+32:0x4fffc005) flags=INEXACT (21/2)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff830800000000000000p+32:0x4fffc184) flags=INEXACT (22/0)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff7f8a00000000000000p+33:0x507fbfc5) flags=INEXACT (22/1)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff840800000000000000p+32:0x4fffc204) flags=INEXACT (22/2)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.ff800a00000000000000p+33:0x507fc005) flags=INEXACT (23/0)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff820800000000000000p+33:0x507fc104) flags=INEXACT (23/1)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff810800000000000000p+33:0x507fc084) flags=INEXACT (23/2)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(0x1.c0bab800000000000000p+99:0x71605d5c) flags=INEXACT (24/0)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.c0838000000000000000p+116:0x79e041c0) flags=INEXACT (24/1)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.c082a000000000000000p+116:0x79e04150) flags=INEXACT (24/2)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/0)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/1)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(inf:0x7f800000) flags=OVERFLOW INEXACT (25/2)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(inf:0x7f800000) flags=OK (26/0)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(inf:0x7f800000) flags=OK (26/1)
+op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(inf:0x7f800000) flags=OK (26/2)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fc00000) flags=OK (27/0)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(nan:0x7fc00000) flags=OK (27/1)
+op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=OK (27/2)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/0)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=INVALID (28/1)
+op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/2)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(nan:0x7fc00000) flags=INVALID (29/0)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (29/1)
+op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (29/2)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (30/0)
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (30/1)
+op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (30/2)
+# LP184149
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0)
+op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001)
+res: f32(0x1.00000000000000000000p-148:0x00000002) flags=UNDERFLOW INEXACT (32/0)
+### Rounding downwards
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffe00000) flags=INVALID (0/0)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/1)
+op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/2)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(-nan:0xffc00000) flags=OK (1/0)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=OK (1/1)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffc00000) flags=OK (1/2)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(inf:0x7f800000) flags=OK (2/0)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000)
+res: f32(-inf:0xff800000) flags=OK (2/1)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(inf:0x7f800000) flags=OK (2/2)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/0)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/1)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/2)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (4/0)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(-0x1.1874b200000000000000p+103:0xf30c3a59) flags=INEXACT (4/1)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (4/2)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(0x1.0c27f800000000000000p+60:0x5d8613fc) flags=INEXACT (5/0)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) flags=INEXACT (5/1)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(0x1.26c46000000000000000p+34:0x50936230) flags=INEXACT (5/2)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(0x1.91f93e00000000000000p-106:0x0ac8fc9f) flags=INEXACT (6/0)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(-0x1.31f75000000000000000p-40:0xab98fba8) flags=INEXACT (6/1)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=INEXACT (6/2)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(-0x1.00000000000000000000p-149:0x80000001) flags=UNDERFLOW INEXACT (8/2)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (12/2)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.12804000000000000000p+3:0x41094020) flags=INEXACT (17/0)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.91ed3a00000000000000p+17:0x4848f69d) flags=INEXACT (18/1)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.08edee00000000000000p+18:0x488476f7) flags=INEXACT (19/0)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.08ee7800000000000000p+18:0x4884773c) flags=INEXACT (19/2)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ff840600000000000000p+31:0x4f7fc203) flags=INEXACT (20/1)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820600000000000000p+31:0x4f7fc103) flags=INEXACT (20/2)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff860600000000000000p+31:0x4f7fc303) flags=INEXACT (21/0)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820600000000000000p+32:0x4fffc103) flags=INEXACT (21/1)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff830600000000000000p+32:0x4fffc183) flags=INEXACT (22/0)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff840600000000000000p+32:0x4fffc203) flags=INEXACT (22/2)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff820600000000000000p+33:0x507fc103) flags=INEXACT (23/1)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff810600000000000000p+33:0x507fc083) flags=INEXACT (23/2)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.c0837e00000000000000p+116:0x79e041bf) flags=INEXACT (24/1)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/0)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/1)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/2)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(inf:0x7f800000) flags=OK (26/0)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(inf:0x7f800000) flags=OK (26/1)
+op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(inf:0x7f800000) flags=OK (26/2)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fc00000) flags=OK (27/0)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(nan:0x7fc00000) flags=OK (27/1)
+op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=OK (27/2)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/0)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=INVALID (28/1)
+op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/2)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(nan:0x7fc00000) flags=INVALID (29/0)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (29/1)
+op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (29/2)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (30/0)
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (30/1)
+op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (30/2)
+# LP184149
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0)
+op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001)
+res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0)
+### Rounding to zero
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffe00000) flags=INVALID (0/0)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/1)
+op : f32(-inf:0xff800000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=INVALID (0/2)
+op : f32(-nan:0xffc00000) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(-nan:0xffc00000) flags=OK (1/0)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-nan:0xffc00000)
+res: f32(-nan:0xffc00000) flags=OK (1/1)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-nan:0xffc00000) + f32(-inf:0xff800000)
+res: f32(-nan:0xffc00000) flags=OK (1/2)
+op : f32(-inf:0xff800000) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(inf:0x7f800000) flags=OK (2/0)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-inf:0xff800000)
+res: f32(-inf:0xff800000) flags=OK (2/1)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-inf:0xff800000) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(inf:0x7f800000) flags=OK (2/2)
+op : f32(-0x1.fffffe00000000000000p+127:0xff7fffff) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/0)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.fffffe00000000000000p+127:0xff7fffff)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/1)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.fffffe00000000000000p+127:0xff7fffff) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (3/2)
+op : f32(-0x1.1874b200000000000000p+103:0xf30c3a59) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (4/0)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.1874b200000000000000p+103:0xf30c3a59)
+res: f32(-0x1.1874b000000000000000p+103:0xf30c3a58) flags=INEXACT (4/1)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.1874b200000000000000p+103:0xf30c3a59) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (4/2)
+op : f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(0x1.0c27f800000000000000p+60:0x5d8613fc) flags=INEXACT (5/0)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.c0bab600000000000000p+99:0xf1605d5b)
+res: f32(-0x1.c0bab400000000000000p+99:0xf1605d5a) flags=INEXACT (5/1)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.c0bab600000000000000p+99:0xf1605d5b) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(0x1.26c46000000000000000p+34:0x50936230) flags=INEXACT (5/2)
+op : f32(-0x1.31f75000000000000000p-40:0xab98fba8) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(0x1.91f93e00000000000000p-106:0x0ac8fc9f) flags=INEXACT (6/0)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(-0x1.31f75000000000000000p-40:0xab98fba8)
+res: f32(-0x1.31f74e00000000000000p-40:0xab98fba7) flags=INEXACT (6/1)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(-0x1.31f75000000000000000p-40:0xab98fba8) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544200000000000000p-66:0x9ea82a21) flags=INEXACT (6/2)
+op : f32(-0x1.50544400000000000000p-66:0x9ea82a22) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (7/0)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(-0x1.50544400000000000000p-66:0x9ea82a22)
+res: f32(-0x1.50544400000000000000p-66:0x9ea82a22) flags=OK (7/1)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(-0x1.50544400000000000000p-66:0x9ea82a22) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (7/2)
+op : f32(-0x1.00000000000000000000p-126:0x80800000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (8/0)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(-0x1.00000000000000000000p-126:0x80800000)
+res: f32(-0x1.00000000000000000000p-126:0x80800000) flags=OK (8/1)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(-0x1.00000000000000000000p-126:0x80800000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(-0x0.00000000000000000000p+0:0x80000000) flags=UNDERFLOW INEXACT (8/2)
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000000000000000000p-25:0x33000000) flags=OK (9/0)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=UNDERFLOW INEXACT (9/1)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x0.00000000000000000000p+0:0000000000) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.00000000000000000000p-126:0x00800000) flags=OK (9/2)
+op : f32(0x1.00000000000000000000p-126:0x00800000) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.ffffe600000000000000p-25:0x337ffff3) flags=INEXACT (10/0)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.00000000000000000000p-126:0x00800000)
+res: f32(0x1.ffffe600000000000000p-50:0x26fffff3) flags=INEXACT (10/1)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.00000000000000000000p-126:0x00800000) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.00000000000000000000p-25:0x33000000) flags=INEXACT (10/2)
+op : f32(0x1.00000000000000000000p-25:0x33000000) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (11/0)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000000000000000000p-25:0x33000000)
+res: f32(0x1.0007fe00000000000000p-25:0x330003ff) flags=INEXACT (11/1)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000000000000000000p-25:0x33000000) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0001f200000000000000p-24:0x338000f9) flags=INEXACT (11/2)
+op : f32(0x1.ffffe600000000000000p-25:0x337ffff3) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00000c00000000000000p-14:0x38800006) flags=INEXACT (12/0)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.ffffe600000000000000p-25:0x337ffff3)
+res: f32(0x1.0ffbf400000000000000p-24:0x3387fdfa) flags=INEXACT (12/1)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.ffffe600000000000000p-25:0x337ffff3) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ff801a00000000000000p-15:0x387fc00d) flags=INEXACT (12/2)
+op : f32(0x1.ff801a00000000000000p-15:0x387fc00d) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00000000000000000000p+0:0x3f800000) flags=INEXACT (13/0)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.ff801a00000000000000p-15:0x387fc00d)
+res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/1)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.ff801a00000000000000p-15:0x387fc00d) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.ffc01800000000000000p-14:0x38ffe00c) flags=INEXACT (13/2)
+op : f32(0x1.00000c00000000000000p-14:0x38800006) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/0)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000c00000000000000p-14:0x38800006)
+res: f32(0x1.00440000000000000000p+0:0x3f802200) flags=INEXACT (14/1)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000c00000000000000p-14:0x38800006) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.00040000000000000000p+0:0x3f800200) flags=INEXACT (14/2)
+op : f32(0x1.00000000000000000000p+0:0x3f800000) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/0)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.00000000000000000000p+0:0x3f800000)
+res: f32(0x1.80400000000000000000p+1:0x40402000) flags=OK (15/1)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.00000000000000000000p+0:0x3f800000) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.80200000000000000000p+1:0x40401000) flags=OK (15/2)
+op : f32(0x1.00400000000000000000p+0:0x3f802000) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.2e185400000000000000p+2:0x40970c2a) flags=OK (16/0)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.00400000000000000000p+0:0x3f802000)
+res: f32(0x1.9c00a800000000000000p+2:0x40ce0054) flags=OK (16/1)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.00400000000000000000p+0:0x3f802000) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.2e23d200000000000000p+2:0x409711e9) flags=INEXACT (16/2)
+op : f32(0x1.00000000000000000000p+1:0x40000000) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.12804000000000000000p+3:0x41094020) flags=INEXACT (17/0)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.00000000000000000000p+1:0x40000000)
+res: f32(0x1.51458000000000000000p+3:0x4128a2c0) flags=INEXACT (17/1)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.00000000000000000000p+1:0x40000000) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.200c0400000000000000p+3:0x41100602) flags=INEXACT (17/2)
+op : f32(0x1.5bf0a800000000000000p+1:0x402df854) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ffcf1400000000000000p+15:0x477fe78a) flags=INEXACT (18/0)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.5bf0a800000000000000p+1:0x402df854)
+res: f32(0x1.91ed3a00000000000000p+17:0x4848f69d) flags=INEXACT (18/1)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.5bf0a800000000000000p+1:0x402df854) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.5bc56000000000000000p+17:0x482de2b0) flags=INEXACT (18/2)
+op : f32(0x1.921fb600000000000000p+1:0x40490fdb) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.08edee00000000000000p+18:0x488476f7) flags=INEXACT (19/0)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.921fb600000000000000p+1:0x40490fdb)
+res: f32(0x1.ff7e0800000000000000p+31:0x4f7fbf04) flags=INEXACT (19/1)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.921fb600000000000000p+1:0x40490fdb) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.08ee7800000000000000p+18:0x4884773c) flags=INEXACT (19/2)
+op : f32(0x1.ffbe0000000000000000p+15:0x477fdf00) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800800000000000000p+31:0x4f7fc004) flags=INEXACT (20/0)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbe0000000000000000p+15:0x477fdf00)
+res: f32(0x1.ff840600000000000000p+31:0x4f7fc203) flags=INEXACT (20/1)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbe0000000000000000p+15:0x477fdf00) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820600000000000000p+31:0x4f7fc103) flags=INEXACT (20/2)
+op : f32(0x1.ffc00000000000000000p+15:0x477fe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff860600000000000000p+31:0x4f7fc303) flags=INEXACT (21/0)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+15:0x477fe000)
+res: f32(0x1.ff820600000000000000p+32:0x4fffc103) flags=INEXACT (21/1)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+15:0x477fe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff800800000000000000p+32:0x4fffc004) flags=INEXACT (21/2)
+op : f32(0x1.ffc20000000000000000p+15:0x477fe100) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff830600000000000000p+32:0x4fffc183) flags=INEXACT (22/0)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc20000000000000000p+15:0x477fe100)
+res: f32(0x1.ff7f8800000000000000p+33:0x507fbfc4) flags=INEXACT (22/1)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc20000000000000000p+15:0x477fe100) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff840600000000000000p+32:0x4fffc203) flags=INEXACT (22/2)
+op : f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.ff800800000000000000p+33:0x507fc004) flags=INEXACT (23/0)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.ffbf0000000000000000p+16:0x47ffdf80)
+res: f32(0x1.ff820600000000000000p+33:0x507fc103) flags=INEXACT (23/1)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.ffbf0000000000000000p+16:0x47ffdf80) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.ff810600000000000000p+33:0x507fc083) flags=INEXACT (23/2)
+op : f32(0x1.ffc00000000000000000p+16:0x47ffe000) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(0x1.c0bab600000000000000p+99:0x71605d5b) flags=INEXACT (24/0)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.ffc00000000000000000p+16:0x47ffe000)
+res: f32(0x1.c0837e00000000000000p+116:0x79e041bf) flags=INEXACT (24/1)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.ffc00000000000000000p+16:0x47ffe000) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.c0829e00000000000000p+116:0x79e0414f) flags=INEXACT (24/2)
+op : f32(0x1.ffc10000000000000000p+16:0x47ffe080) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/0)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(0x1.ffc10000000000000000p+16:0x47ffe080)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/1)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(0x1.ffc10000000000000000p+16:0x47ffe080) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(0x1.fffffe00000000000000p+127:0x7f7fffff) flags=OVERFLOW INEXACT (25/2)
+op : f32(0x1.c0bab600000000000000p+99:0x71605d5b) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(inf:0x7f800000) flags=OK (26/0)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(0x1.c0bab600000000000000p+99:0x71605d5b)
+res: f32(inf:0x7f800000) flags=OK (26/1)
+op : f32(inf:0x7f800000) * f32(0x1.c0bab600000000000000p+99:0x71605d5b) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(inf:0x7f800000) flags=OK (26/2)
+op : f32(0x1.fffffe00000000000000p+127:0x7f7fffff) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fc00000) flags=OK (27/0)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(0x1.fffffe00000000000000p+127:0x7f7fffff)
+res: f32(nan:0x7fc00000) flags=OK (27/1)
+op : f32(nan:0x7fc00000) * f32(0x1.fffffe00000000000000p+127:0x7f7fffff) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=OK (27/2)
+op : f32(inf:0x7f800000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/0)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(inf:0x7f800000)
+res: f32(nan:0x7fc00000) flags=INVALID (28/1)
+op : f32(nan:0x7fa00000) * f32(inf:0x7f800000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (28/2)
+op : f32(nan:0x7fc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(nan:0x7fc00000) flags=INVALID (29/0)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(nan:0x7fc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (29/1)
+op : f32(-nan:0xffa00000) * f32(nan:0x7fc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (29/2)
+op : f32(nan:0x7fa00000) * f32(-nan:0xffa00000) + f32(-nan:0xffc00000)
+res: f32(nan:0x7fe00000) flags=INVALID (30/0)
+op : f32(-nan:0xffa00000) * f32(-nan:0xffc00000) + f32(nan:0x7fa00000)
+res: f32(-nan:0xffe00000) flags=INVALID (30/1)
+op : f32(-nan:0xffc00000) * f32(nan:0x7fa00000) + f32(-nan:0xffa00000)
+res: f32(-nan:0xffc00000) flags=INVALID (30/2)
+# LP184149
+op : f32(0x0.00000000000000000000p+0:0000000000) * f32(0x1.00000000000000000000p-1:0x3f000000) + f32(0x0.00000000000000000000p+0:0000000000)
+res: f32(0x0.00000000000000000000p+0:0000000000) flags=OK (31/0)
+op : f32(0x1.00000000000000000000p-149:0x00000001) * f32(0x1.00000000000000000000p-149:0x00000001) + f32(0x1.00000000000000000000p-149:0x00000001)
+res: f32(0x1.00000000000000000000p-149:0x00000001) flags=UNDERFLOW INEXACT (32/0)
TESTS+=pack
TESTS+=mvo
TESTS+=mvc
+TESTS+=shift
TESTS+=trap
TESTS+=signals-s390x
--- /dev/null
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#define DEFINE_SHIFT_SINGLE_COMMON(_name, _insn_str) \
+ static uint64_t _name(uint64_t op1, uint64_t op2, uint64_t *cc) \
+ { \
+ asm(" sll %[cc],28\n" \
+ " spm %[cc]\n" \
+ " " _insn_str "\n" \
+ " ipm %[cc]\n" \
+ " srl %[cc],28" \
+ : [op1] "+&r" (op1), \
+ [cc] "+&r" (*cc) \
+ : [op2] "r" (op2) \
+ : "cc"); \
+ return op1; \
+ }
+#define DEFINE_SHIFT_SINGLE_2(_insn, _offset) \
+ DEFINE_SHIFT_SINGLE_COMMON(_insn ## _ ## _offset, \
+ #_insn " %[op1]," #_offset "(%[op2])")
+#define DEFINE_SHIFT_SINGLE_3(_insn, _offset) \
+ DEFINE_SHIFT_SINGLE_COMMON(_insn ## _ ## _offset, \
+ #_insn " %[op1],%[op1]," #_offset "(%[op2])")
+#define DEFINE_SHIFT_DOUBLE(_insn, _offset) \
+ static uint64_t _insn ## _ ## _offset(uint64_t op1, uint64_t op2, \
+ uint64_t *cc) \
+ { \
+ uint32_t op1h = op1 >> 32; \
+ uint32_t op1l = op1 & 0xffffffff; \
+ register uint32_t r2 asm("2") = op1h; \
+ register uint32_t r3 asm("3") = op1l; \
+ \
+ asm(" sll %[cc],28\n" \
+ " spm %[cc]\n" \
+ " " #_insn " %[r2]," #_offset "(%[op2])\n" \
+ " ipm %[cc]\n" \
+ " srl %[cc],28" \
+ : [r2] "+&r" (r2), \
+ [r3] "+&r" (r3), \
+ [cc] "+&r" (*cc) \
+ : [op2] "r" (op2) \
+ : "cc"); \
+ op1h = r2; \
+ op1l = r3; \
+ return (((uint64_t)op1h) << 32) | op1l; \
+ }
+
+DEFINE_SHIFT_SINGLE_3(rll, 0x4cf3b);
+DEFINE_SHIFT_SINGLE_3(rllg, 0x697c9);
+DEFINE_SHIFT_SINGLE_2(sla, 0x4b0);
+DEFINE_SHIFT_SINGLE_2(sla, 0xd54);
+DEFINE_SHIFT_SINGLE_3(slak, 0x2832c);
+DEFINE_SHIFT_SINGLE_3(slag, 0x66cc4);
+DEFINE_SHIFT_SINGLE_3(slag, 0xd54);
+DEFINE_SHIFT_SINGLE_2(sll, 0xd04);
+DEFINE_SHIFT_SINGLE_3(sllk, 0x2699f);
+DEFINE_SHIFT_SINGLE_3(sllg, 0x59df9);
+DEFINE_SHIFT_SINGLE_2(sra, 0x67e);
+DEFINE_SHIFT_SINGLE_3(srak, 0x60943);
+DEFINE_SHIFT_SINGLE_3(srag, 0x6b048);
+DEFINE_SHIFT_SINGLE_2(srl, 0x035);
+DEFINE_SHIFT_SINGLE_3(srlk, 0x43dfc);
+DEFINE_SHIFT_SINGLE_3(srlg, 0x27227);
+DEFINE_SHIFT_DOUBLE(slda, 0x38b);
+DEFINE_SHIFT_DOUBLE(sldl, 0x031);
+DEFINE_SHIFT_DOUBLE(srda, 0x36f);
+DEFINE_SHIFT_DOUBLE(srdl, 0x99a);
+
+struct shift_test {
+ const char *name;
+ uint64_t (*insn)(uint64_t, uint64_t, uint64_t *);
+ uint64_t op1;
+ uint64_t op2;
+ uint64_t exp_result;
+ uint64_t exp_cc;
+};
+
+static const struct shift_test tests[] = {
+ {
+ .name = "rll",
+ .insn = rll_0x4cf3b,
+ .op1 = 0xecbd589a45c248f5ull,
+ .op2 = 0x62e5508ccb4c99fdull,
+ .exp_result = 0xecbd589af545c248ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "rllg",
+ .insn = rllg_0x697c9,
+ .op1 = 0xaa2d54c1b729f7f4ull,
+ .op2 = 0x5ffcf7465f5cd71full,
+ .exp_result = 0x29f7f4aa2d54c1b7ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "sla-1",
+ .insn = sla_0x4b0,
+ .op1 = 0x8bf21fb67cca0e96ull,
+ .op2 = 0x3ddf2f53347d3030ull,
+ .exp_result = 0x8bf21fb600000000ull,
+ .exp_cc = 3,
+ },
+ {
+ .name = "sla-2",
+ .insn = sla_0xd54,
+ .op1 = 0xe4faaed5def0e926ull,
+ .op2 = 0x18d586fab239cbeeull,
+ .exp_result = 0xe4faaed5fbc3a498ull,
+ .exp_cc = 3,
+ },
+ {
+ .name = "slak",
+ .insn = slak_0x2832c,
+ .op1 = 0x7300bf78707f09f9ull,
+ .op2 = 0x4d193b85bb5cb39bull,
+ .exp_result = 0x7300bf783f84fc80ull,
+ .exp_cc = 3,
+ },
+ {
+ .name = "slag-1",
+ .insn = slag_0x66cc4,
+ .op1 = 0xe805966de1a77762ull,
+ .op2 = 0x0e92953f6aa91c6bull,
+ .exp_result = 0xbbb1000000000000ull,
+ .exp_cc = 3,
+ },
+ {
+ .name = "slag-2",
+ .insn = slag_0xd54,
+ .op1 = 0xdef0e92600000000ull,
+ .op2 = 0x18d586fab239cbeeull,
+ .exp_result = 0xfbc3a49800000000ull,
+ .exp_cc = 3,
+ },
+ {
+ .name = "sll",
+ .insn = sll_0xd04,
+ .op1 = 0xb90281a3105939dfull,
+ .op2 = 0xb5e4df7e082e4c5eull,
+ .exp_result = 0xb90281a300000000ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "sllk",
+ .insn = sllk_0x2699f,
+ .op1 = 0x777c6cf116f99557ull,
+ .op2 = 0xe0556cf112e5a458ull,
+ .exp_result = 0x777c6cf100000000ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "sllg",
+ .insn = sllg_0x59df9,
+ .op1 = 0xcdf86cbfbc0f3557ull,
+ .op2 = 0x325a45acf99c6d3dull,
+ .exp_result = 0x55c0000000000000ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "sra",
+ .insn = sra_0x67e,
+ .op1 = 0xb878f048d5354183ull,
+ .op2 = 0x9e27d13195931f79ull,
+ .exp_result = 0xb878f048ffffffffull,
+ .exp_cc = 1,
+ },
+ {
+ .name = "srak",
+ .insn = srak_0x60943,
+ .op1 = 0xb6ceb5a429cedb35ull,
+ .op2 = 0x352354900ae34d7aull,
+ .exp_result = 0xb6ceb5a400000000ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "srag",
+ .insn = srag_0x6b048,
+ .op1 = 0xd54dd4468676c63bull,
+ .op2 = 0x84d026db7b4dca28ull,
+ .exp_result = 0xffffffffffffd54dull,
+ .exp_cc = 1,
+ },
+ {
+ .name = "srl",
+ .insn = srl_0x035,
+ .op1 = 0x09be503ef826815full,
+ .op2 = 0xbba8d1a0e542d5c1ull,
+ .exp_result = 0x9be503e00000000ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "srlk",
+ .insn = srlk_0x43dfc,
+ .op1 = 0x540d6c8de71aee2aull,
+ .op2 = 0x0000000000000000ull,
+ .exp_result = 0x540d6c8d00000000ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "srlg",
+ .insn = srlg_0x27227,
+ .op1 = 0x26f7123c1c447a34ull,
+ .op2 = 0x0000000000000000ull,
+ .exp_result = 0x00000000004dee24ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "slda",
+ .insn = slda_0x38b,
+ .op1 = 0x7988f722dd5bbe7cull,
+ .op2 = 0x9aed3f95b4d78cc2ull,
+ .exp_result = 0x1ee45bab77cf8000ull,
+ .exp_cc = 3,
+ },
+ {
+ .name = "sldl",
+ .insn = sldl_0x031,
+ .op1 = 0xaae2918dce2b049aull,
+ .op2 = 0x0000000000000000ull,
+ .exp_result = 0x0934000000000000ull,
+ .exp_cc = 0,
+ },
+ {
+ .name = "srda",
+ .insn = srda_0x36f,
+ .op1 = 0x0cd4ed9228a50978ull,
+ .op2 = 0x72b046f0848b8cc9ull,
+ .exp_result = 0x000000000000000cull,
+ .exp_cc = 2,
+ },
+ {
+ .name = "srdl",
+ .insn = srdl_0x99a,
+ .op1 = 0x1018611c41689a1dull,
+ .op2 = 0x2907e150c50ba319ull,
+ .exp_result = 0x0000000000000203ull,
+ .exp_cc = 0,
+ },
+};
+
+int main(void)
+{
+ int ret = 0;
+ size_t i;
+
+ for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+ uint64_t result;
+ uint64_t cc = 0;
+
+ result = tests[i].insn(tests[i].op1, tests[i].op2, &cc);
+ if (result != tests[i].exp_result) {
+ fprintf(stderr,
+ "bad %s result:\n"
+ "actual = 0x%" PRIx64 "\n"
+ "expected = 0x%" PRIx64 "\n",
+ tests[i].name, result, tests[i].exp_result);
+ ret = 1;
+ }
+ if (cc != tests[i].exp_cc) {
+ fprintf(stderr,
+ "bad %s cc:\n"
+ "actual = %" PRIu64 "\n"
+ "expected = %" PRIu64 "\n",
+ tests[i].name, cc, tests[i].exp_cc);
+ ret = 1;
+ }
+ }
+ return ret;
+}
include $(SRC_PATH)/tests/tcg/i386/Makefile.target
-ifneq ($(CONFIG_LINUX),)
+ifneq ($(CONFIG_LINUX_USER),)
X86_64_TESTS += vsyscall
TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64
else
'test-vmstate': [migration, io],
'test-yank': ['socket-helpers.c', qom, io, chardev]
}
- if 'CONFIG_INOTIFY1' in config_host
+ if config_host_data.get('CONFIG_INOTIFY1')
tests += {'test-util-filemonitor': []}
endif
static void set_event_notifier(AioContext *ctx, EventNotifier *notifier,
EventNotifierHandler *handler)
{
- aio_set_event_notifier(ctx, notifier, false, handler, NULL);
+ aio_set_event_notifier(ctx, notifier, false, handler, NULL, NULL);
}
static void dummy_notifier_read(EventNotifier *n)
for (i = 1; i < 3; i++) {
EventNotifierTestData data = { .n = 0, .active = 10, .auto_set = true };
event_notifier_init(&data.e, false);
- aio_set_event_notifier(ctx, &data.e, true, event_ready_cb, NULL);
+ aio_set_event_notifier(ctx, &data.e, true, event_ready_cb, NULL, NULL);
event_notifier_set(&data.e);
for (j = 0; j < i; j++) {
aio_disable_external(ctx);
for (size_t i = 0; i < n; i++) {
event_notifier_init(¬ifiers[i], false);
aio_set_event_notifier(ctx, ¬ifiers[i], false,
- dummy_fd_handler, NULL);
+ dummy_fd_handler, NULL, NULL);
}
}
static void remove_event_notifiers(EventNotifier *notifiers, size_t n)
{
for (size_t i = 0; i < n; i++) {
- aio_set_event_notifier(ctx, ¬ifiers[i], false, NULL, NULL);
+ aio_set_event_notifier(ctx, ¬ifiers[i], false, NULL, NULL, NULL);
event_notifier_cleanup(¬ifiers[i]);
}
}
name = "freebsd"
arch = "x86_64"
- link = "https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.2/FreeBSD-12.2-RELEASE-amd64-disc1.iso.xz"
- csum = "a4530246cafbf1dd42a9bd3ea441ca9a78a6a0cd070278cbdf63f3a6f803ecae"
+ link = "https://download.freebsd.org/ftp/releases/ISO-IMAGES/12.3/FreeBSD-12.3-RELEASE-amd64-disc1.iso.xz"
+ csum = "36dd0de50f1fe5f0a88e181e94657656de26fb64254412f74e80e128e8b938b4"
size = "20G"
pkgs = [
# build tools
"zstd",
]
- # TODO: Enable gnutls again once FreeBSD's libtasn1 got fixed
- # See: https://gitlab.com/gnutls/libtasn1/-/merge_requests/71
BUILD_SCRIPT = """
set -e;
rm -rf /home/qemu/qemu-test.*
mkdir src build; cd src;
tar -xf /dev/vtbd1;
cd ../build
- ../src/configure --python=python3.7 --disable-gnutls {configure_opts};
+ ../src/configure --python=python3.7 {configure_opts};
gmake --output-sync -j{jobs} {target} {verbose};
"""
#include <sys/wait.h>
#include <sys/xattr.h>
#include <syslog.h>
+#include <grp.h>
#include "qemu/cutils.h"
#include "passthrough_helpers.h"
#define OURSYS_setresuid SYS_setresuid
#endif
+static void drop_supplementary_groups(void)
+{
+ int ret;
+
+ ret = getgroups(0, NULL);
+ if (ret == -1) {
+ fuse_log(FUSE_LOG_ERR, "getgroups() failed with error=%d:%s\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+ if (!ret) {
+ return;
+ }
+
+ /* Drop all supplementary groups. We should not need it */
+ ret = setgroups(0, NULL);
+ if (ret == -1) {
+ fuse_log(FUSE_LOG_ERR, "setgroups() failed with error=%d:%s\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+}
+
/*
* Change to uid/gid of caller so that file is created with
* ownership of caller.
qemu_init_exec_dir(argv[0]);
+ drop_supplementary_groups();
+
pthread_mutex_init(&lo.mutex, NULL);
lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal);
lo.root.fd = -1;
specific_ss.add(files('control-target.c'))
trace_events_files = []
-foreach dir : [ '.' ] + trace_events_subdirs
- trace_events_file = meson.project_source_root() / dir / 'trace-events'
+foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events
+ if item in qapi_trace_events
+ trace_events_file = item
+ group_name = item.full_path().split('/')[-1].underscorify()
+ else
+ trace_events_file = meson.project_source_root() / item / 'trace-events'
+ group_name = item == '.' ? 'root' : item.underscorify()
+ endif
trace_events_files += [ trace_events_file ]
- group_name = dir == '.' ? 'root' : dir.underscorify()
group = '--group=' + group_name
fmt = '@0@-' + group_name + '.@1@'
.type = QEMU_CLIPBOARD_UPDATE_INFO,
.info = info,
};
- g_autoptr(QemuClipboardInfo) old = NULL;
-
assert(info->selection < QEMU_CLIPBOARD_SELECTION__COUNT);
notifier_list_notify(&clipboard_notifiers, ¬ify);
- old = cbinfo[info->selection];
+ qemu_clipboard_info_unref(cbinfo[info->selection]);
cbinfo[info->selection] = qemu_clipboard_info_ref(info);
}
*/
/*
- * When deltaY is zero, it means that this scrolling event was
- * either horizontal, or so fine that it only appears in
- * scrollingDeltaY. So we drop the event.
+ * We shouldn't have got a scroll event when deltaY and delta Y
+ * are zero, hence no harm in dropping the event
*/
- if ([event deltaY] != 0) {
+ if ([event deltaY] != 0 || [event deltaX] != 0) {
/* Determine if this is a scroll up or scroll down event */
- buttons = ([event deltaY] > 0) ?
+ if ([event deltaY] != 0) {
+ buttons = ([event deltaY] > 0) ?
INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN;
+ } else if ([event deltaX] != 0) {
+ buttons = ([event deltaX] > 0) ?
+ INPUT_BUTTON_WHEEL_LEFT : INPUT_BUTTON_WHEEL_RIGHT;
+ }
+
qemu_input_queue_btn(dcl.con, buttons, true);
qemu_input_event_sync();
qemu_input_queue_btn(dcl.con, buttons, false);
qemu_input_event_sync();
}
+
/*
- * Since deltaY also reports scroll wheel events we prevent mouse
+ * Since deltaX/deltaY also report scroll wheel events we prevent mouse
* movement code from executing.
*/
mouse_event = false;
static const TypeInfo dbus_vc_type_info = {
.name = TYPE_CHARDEV_VC,
.parent = TYPE_CHARDEV_DBUS,
+ .class_size = sizeof(DBusVCClass),
.class_init = dbus_vc_class_init,
};
if (info != qemu_clipboard_info(s)) {
gd->cbpending[s] = 0;
if (!self_update) {
- GtkTargetList *list;
+ g_autoptr(GtkTargetList) list = NULL;
GtkTargetEntry *targets;
gint n_targets;
targets = gtk_target_table_new_from_list(list, &n_targets);
gtk_clipboard_clear(gd->gtkcb[s]);
- gd->cbowner[s] = true;
- gtk_clipboard_set_with_data(gd->gtkcb[s],
- targets, n_targets,
- gd_clipboard_get_data,
- gd_clipboard_clear,
- gd);
-
- gtk_target_table_free(targets, n_targets);
- gtk_target_list_unref(list);
+ if (targets) {
+ gd->cbowner[s] = true;
+ gtk_clipboard_set_with_data(gd->gtkcb[s],
+ targets, n_targets,
+ gd_clipboard_get_data,
+ gd_clipboard_clear,
+ gd);
+
+ gtk_target_table_free(targets, n_targets);
+ }
}
return;
}
void *opaque)
{
VirtualConsole *vc = opaque;
- InputButton btn;
+ InputButton btn_vertical;
+ InputButton btn_horizontal;
+ bool has_vertical = false;
+ bool has_horizontal = false;
if (scroll->direction == GDK_SCROLL_UP) {
- btn = INPUT_BUTTON_WHEEL_UP;
+ btn_vertical = INPUT_BUTTON_WHEEL_UP;
+ has_vertical = true;
} else if (scroll->direction == GDK_SCROLL_DOWN) {
- btn = INPUT_BUTTON_WHEEL_DOWN;
+ btn_vertical = INPUT_BUTTON_WHEEL_DOWN;
+ has_vertical = true;
+ } else if (scroll->direction == GDK_SCROLL_LEFT) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_LEFT;
+ has_horizontal = true;
+ } else if (scroll->direction == GDK_SCROLL_RIGHT) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_RIGHT;
+ has_horizontal = true;
} else if (scroll->direction == GDK_SCROLL_SMOOTH) {
gdouble delta_x, delta_y;
if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll,
&delta_x, &delta_y)) {
return TRUE;
}
- if (delta_y == 0) {
- return TRUE;
- } else if (delta_y > 0) {
- btn = INPUT_BUTTON_WHEEL_DOWN;
+
+ if (delta_y > 0) {
+ btn_vertical = INPUT_BUTTON_WHEEL_DOWN;
+ has_vertical = true;
+ } else if (delta_y < 0) {
+ btn_vertical = INPUT_BUTTON_WHEEL_UP;
+ has_vertical = true;
+ } else if (delta_x > 0) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_RIGHT;
+ has_horizontal = true;
+ } else if (delta_x < 0) {
+ btn_horizontal = INPUT_BUTTON_WHEEL_LEFT;
+ has_horizontal = true;
} else {
- btn = INPUT_BUTTON_WHEEL_UP;
+ return TRUE;
}
} else {
return TRUE;
}
- qemu_input_queue_btn(vc->gfx.dcl.con, btn, true);
- qemu_input_event_sync();
- qemu_input_queue_btn(vc->gfx.dcl.con, btn, false);
- qemu_input_event_sync();
+ if (has_vertical) {
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, true);
+ qemu_input_event_sync();
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_vertical, false);
+ qemu_input_event_sync();
+ }
+
+ if (has_horizontal) {
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, true);
+ qemu_input_event_sync();
+ qemu_input_queue_btn(vc->gfx.dcl.con, btn_horizontal, false);
+ qemu_input_event_sync();
+ }
+
return TRUE;
}
*/
#include "qemu/osdep.h"
+#include "qemu/log.h"
#include "qapi/qapi-commands-ui.h"
#include "ui/console.h"
#include "keymaps.h"
1,
s->buttons);
}
+ if (btn->down && btn->button == INPUT_BUTTON_WHEEL_RIGHT) {
+ s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
+ s->axis[INPUT_AXIS_X],
+ s->axis[INPUT_AXIS_Y],
+ -2,
+ s->buttons);
+ }
+ if (btn->down && btn->button == INPUT_BUTTON_WHEEL_LEFT) {
+ s->qemu_put_mouse_event(s->qemu_put_mouse_event_opaque,
+ s->axis[INPUT_AXIS_X],
+ s->axis[INPUT_AXIS_Y],
+ 2,
+ s->buttons);
+ }
break;
case INPUT_EVENT_KIND_ABS:
move = evt->u.abs.data;
#include "sysemu/runstate-action.h"
#include "sysemu/sysemu.h"
#include "ui/win32-kbd-hook.h"
+#include "qemu/log.h"
static int sdl2_num_outputs;
static struct sdl2_console *sdl2_console;
btn = INPUT_BUTTON_WHEEL_UP;
} else if (wev->y < 0) {
btn = INPUT_BUTTON_WHEEL_DOWN;
+ } else if (wev->x < 0) {
+ btn = INPUT_BUTTON_WHEEL_RIGHT;
+ } else if (wev->x > 0) {
+ btn = INPUT_BUTTON_WHEEL_LEFT;
} else {
return;
}
/* spice display interface callbacks */
+#if SPICE_HAS_ATTACHED_WORKER
+static void interface_attached_worker(QXLInstance *sin)
+{
+ /* nothing to do */
+}
+#else
static void interface_attach_worker(QXLInstance *sin, QXLWorker *qxl_worker)
{
/* nothing to do */
}
+#endif
static void interface_set_compression_level(QXLInstance *sin, int level)
{
.base.major_version = SPICE_INTERFACE_QXL_MAJOR,
.base.minor_version = SPICE_INTERFACE_QXL_MINOR,
+#if SPICE_HAS_ATTACHED_WORKER
+ .attached_worker = interface_attached_worker,
+#else
.attache_worker = interface_attach_worker,
+#endif
.set_compression_level = interface_set_compression_level,
#if SPICE_NEEDS_SET_MM_TIME
.set_mm_time = interface_set_mm_time,
/* last client gone */
vnc_update_server_surface(vs->vd);
}
+ vnc_unlock_output(vs);
+
if (vs->cbpeer.notifier.notify) {
qemu_clipboard_peer_unregister(&vs->cbpeer);
}
- vnc_unlock_output(vs);
-
qemu_mutex_destroy(&vs->output_mutex);
if (vs->bh != NULL) {
qemu_bh_delete(vs->bh);
#include "trace.h"
#include "aio-posix.h"
+/*
+ * G_IO_IN and G_IO_OUT are not appropriate revents values for polling, since
+ * the handler may not need to access the file descriptor. For example, the
+ * handler doesn't need to read from an EventNotifier if it polled a memory
+ * location and a read syscall would be slow. Define our own unique revents
+ * value to indicate that polling determined this AioHandler is ready.
+ */
+#define REVENTS_POLL_READY 0
+
/* Stop userspace polling on a handler if it isn't active for some time */
#define POLL_IDLE_INTERVAL_NS (7 * NANOSECONDS_PER_SECOND)
IOHandler *io_read,
IOHandler *io_write,
AioPollFn *io_poll,
+ IOHandler *io_poll_ready,
void *opaque)
{
AioHandler *node;
bool deleted = false;
int poll_disable_change;
+ if (io_poll && !io_poll_ready) {
+ io_poll = NULL; /* polling only makes sense if there is a handler */
+ }
+
qemu_lockcnt_lock(&ctx->list_lock);
node = find_aio_handler(ctx, fd);
new_node->io_read = io_read;
new_node->io_write = io_write;
new_node->io_poll = io_poll;
+ new_node->io_poll_ready = io_poll_ready;
new_node->opaque = opaque;
new_node->is_external = is_external;
EventNotifier *notifier,
bool is_external,
EventNotifierHandler *io_read,
- AioPollFn *io_poll)
+ AioPollFn *io_poll,
+ EventNotifierHandler *io_poll_ready)
{
aio_set_fd_handler(ctx, event_notifier_get_fd(notifier), is_external,
- (IOHandler *)io_read, NULL, io_poll, notifier);
+ (IOHandler *)io_read, NULL, io_poll,
+ (IOHandler *)io_poll_ready, notifier);
}
void aio_set_event_notifier_poll(AioContext *ctx,
(IOHandler *)io_poll_end);
}
-static bool poll_set_started(AioContext *ctx, bool started)
+static bool poll_set_started(AioContext *ctx, AioHandlerList *ready_list,
+ bool started)
{
AioHandler *node;
bool progress = false;
}
/* Poll one last time in case ->io_poll_end() raced with the event */
- if (!started) {
- progress = node->io_poll(node->opaque) || progress;
+ if (!started && node->io_poll(node->opaque)) {
+ aio_add_ready_handler(ready_list, node, REVENTS_POLL_READY);
+ progress = true;
}
}
qemu_lockcnt_dec(&ctx->list_lock);
bool aio_prepare(AioContext *ctx)
{
+ AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list);
+
/* Poll mode cannot be used with glib's event loop, disable it. */
- poll_set_started(ctx, false);
+ poll_set_started(ctx, &ready_list, false);
+ /* TODO what to do with this list? */
return false;
}
}
QLIST_INSERT_HEAD(&ctx->poll_aio_handlers, node, node_poll);
}
+ if (!QLIST_IS_INSERTED(node, node_deleted) &&
+ revents == 0 &&
+ aio_node_check(ctx, node->is_external) &&
+ node->io_poll_ready) {
+ node->io_poll_ready(node->opaque);
+
+ /*
+ * Return early since revents was zero. aio_notify() does not count as
+ * progress.
+ */
+ return node->opaque != &ctx->notifier;
+ }
if (!QLIST_IS_INSERTED(node, node_deleted) &&
(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
}
static bool run_poll_handlers_once(AioContext *ctx,
+ AioHandlerList *ready_list,
int64_t now,
int64_t *timeout)
{
QLIST_FOREACH_SAFE(node, &ctx->poll_aio_handlers, node_poll, tmp) {
if (aio_node_check(ctx, node->is_external) &&
node->io_poll(node->opaque)) {
+ aio_add_ready_handler(ready_list, node, REVENTS_POLL_READY);
+
node->poll_idle_timeout = now + POLL_IDLE_INTERVAL_NS;
/*
return ctx->fdmon_ops->need_wait != aio_poll_disabled;
}
-static bool remove_idle_poll_handlers(AioContext *ctx, int64_t now)
+static bool remove_idle_poll_handlers(AioContext *ctx,
+ AioHandlerList *ready_list,
+ int64_t now)
{
AioHandler *node;
AioHandler *tmp;
* Nevermind about re-adding the handler in the rare case where
* this causes progress.
*/
- progress = node->io_poll(node->opaque) || progress;
+ if (node->io_poll(node->opaque)) {
+ aio_add_ready_handler(ready_list, node,
+ REVENTS_POLL_READY);
+ progress = true;
+ }
}
}
}
/* run_poll_handlers:
* @ctx: the AioContext
+ * @ready_list: the list to place ready handlers on
* @max_ns: maximum time to poll for, in nanoseconds
*
* Polls for a given time.
*
* Returns: true if progress was made, false otherwise
*/
-static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout)
+static bool run_poll_handlers(AioContext *ctx, AioHandlerList *ready_list,
+ int64_t max_ns, int64_t *timeout)
{
bool progress;
int64_t start_time, elapsed_time;
start_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
do {
- progress = run_poll_handlers_once(ctx, start_time, timeout);
+ progress = run_poll_handlers_once(ctx, ready_list,
+ start_time, timeout);
elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time;
max_ns = qemu_soonest_timeout(*timeout, max_ns);
assert(!(max_ns && progress));
} while (elapsed_time < max_ns && !ctx->fdmon_ops->need_wait(ctx));
- if (remove_idle_poll_handlers(ctx, start_time + elapsed_time)) {
+ if (remove_idle_poll_handlers(ctx, ready_list,
+ start_time + elapsed_time)) {
*timeout = 0;
progress = true;
}
/* try_poll_mode:
* @ctx: the AioContext
+ * @ready_list: list to add handlers that need to be run
* @timeout: timeout for blocking wait, computed by the caller and updated if
* polling succeeds.
*
*
* Returns: true if progress was made, false otherwise
*/
-static bool try_poll_mode(AioContext *ctx, int64_t *timeout)
+static bool try_poll_mode(AioContext *ctx, AioHandlerList *ready_list,
+ int64_t *timeout)
{
int64_t max_ns;
max_ns = qemu_soonest_timeout(*timeout, ctx->poll_ns);
if (max_ns && !ctx->fdmon_ops->need_wait(ctx)) {
- poll_set_started(ctx, true);
+ poll_set_started(ctx, ready_list, true);
- if (run_poll_handlers(ctx, max_ns, timeout)) {
+ if (run_poll_handlers(ctx, ready_list, max_ns, timeout)) {
return true;
}
}
- if (poll_set_started(ctx, false)) {
+ if (poll_set_started(ctx, ready_list, false)) {
*timeout = 0;
return true;
}
bool aio_poll(AioContext *ctx, bool blocking)
{
AioHandlerList ready_list = QLIST_HEAD_INITIALIZER(ready_list);
- int ret = 0;
bool progress;
bool use_notify_me;
int64_t timeout;
}
timeout = blocking ? aio_compute_timeout(ctx) : 0;
- progress = try_poll_mode(ctx, &timeout);
+ progress = try_poll_mode(ctx, &ready_list, &timeout);
assert(!(timeout && progress));
/*
* system call---a single round of run_poll_handlers_once suffices.
*/
if (timeout || ctx->fdmon_ops->need_wait(ctx)) {
- ret = ctx->fdmon_ops->wait(ctx, &ready_list, timeout);
+ ctx->fdmon_ops->wait(ctx, &ready_list, timeout);
}
if (use_notify_me) {
}
progress |= aio_bh_poll(ctx);
-
- if (ret > 0) {
- progress |= aio_dispatch_ready_handlers(ctx, &ready_list);
- }
+ progress |= aio_dispatch_ready_handlers(ctx, &ready_list);
aio_free_deleted_handlers(ctx);
IOHandler *io_read;
IOHandler *io_write;
AioPollFn *io_poll;
+ IOHandler *io_poll_ready;
IOHandler *io_poll_begin;
IOHandler *io_poll_end;
void *opaque;
IOHandler *io_read,
IOHandler *io_write,
AioPollFn *io_poll,
+ IOHandler *io_poll_ready,
void *opaque)
{
/* fd is a SOCKET in our case */
EventNotifier *e,
bool is_external,
EventNotifierHandler *io_notify,
- AioPollFn *io_poll)
+ AioPollFn *io_poll,
+ EventNotifierHandler *io_poll_ready)
{
AioHandler *node;
g_free(bh);
}
- aio_set_event_notifier(ctx, &ctx->notifier, false, NULL, NULL);
+ aio_set_event_notifier(ctx, &ctx->notifier, false, NULL, NULL, NULL);
event_notifier_cleanup(&ctx->notifier);
qemu_rec_mutex_destroy(&ctx->lock);
qemu_lockcnt_destroy(&ctx->list_lock);
return qatomic_read(&ctx->notified);
}
+static void aio_context_notifier_poll_ready(EventNotifier *e)
+{
+ /* Do nothing, we just wanted to kick the event loop */
+}
+
static void co_schedule_bh_cb(void *opaque)
{
AioContext *ctx = opaque;
aio_set_event_notifier(ctx, &ctx->notifier,
false,
aio_context_notifier_cb,
- aio_context_notifier_poll);
+ aio_context_notifier_poll,
+ aio_context_notifier_poll_ready);
#ifdef CONFIG_LINUX_AIO
ctx->linux_aio = NULL;
#endif
{
iohandler_init();
aio_set_fd_handler(iohandler_ctx, fd, false,
- fd_read, fd_write, NULL, opaque);
+ fd_read, fd_write, NULL, NULL, opaque);
}
void event_notifier_set_handler(EventNotifier *e,
{
iohandler_init();
aio_set_event_notifier(iohandler_ctx, e, false,
- handler, NULL);
+ handler, NULL, NULL);
}
util_ss.add(files('readline.c'))
util_ss.add(files('throttle.c'))
util_ss.add(files('timed-average.c'))
- util_ss.add(when: 'CONFIG_INOTIFY1', if_true: files('filemonitor-inotify.c'),
- if_false: files('filemonitor-stub.c'))
+ if config_host_data.get('CONFIG_INOTIFY1')
+ util_ss.add(files('filemonitor-inotify.c'))
+ else
+ util_ss.add(files('filemonitor-stub.c'))
+ endif
util_ss.add(when: 'CONFIG_LINUX', if_true: files('vfio-helpers.c'))
endif
static void fd_coroutine_enter(void *opaque)
{
FDYieldUntilData *data = opaque;
- aio_set_fd_handler(data->ctx, data->fd, false, NULL, NULL, NULL, NULL);
+ aio_set_fd_handler(data->ctx, data->fd, false,
+ NULL, NULL, NULL, NULL, NULL);
qemu_coroutine_enter(data->co);
}
data.co = qemu_coroutine_self();
data.fd = fd;
aio_set_fd_handler(
- data.ctx, fd, false, fd_coroutine_enter, NULL, NULL, &data);
+ data.ctx, fd, false, fd_coroutine_enter, NULL, NULL, NULL, &data);
qemu_coroutine_yield();
}
error_report("vu_panic: %s", buf);
}
+void vhost_user_server_ref(VuServer *server)
+{
+ assert(!server->wait_idle);
+ server->refcount++;
+}
+
+void vhost_user_server_unref(VuServer *server)
+{
+ server->refcount--;
+ if (server->wait_idle && !server->refcount) {
+ aio_co_wake(server->co_trip);
+ }
+}
+
static bool coroutine_fn
vu_message_read(VuDev *vu_dev, int conn_fd, VhostUserMsg *vmsg)
{
/* Keep running */
}
+ if (server->refcount) {
+ /* Wait for requests to complete before we can unmap the memory */
+ server->wait_idle = true;
+ qemu_coroutine_yield();
+ server->wait_idle = false;
+ }
+ assert(server->refcount == 0);
+
vu_deinit(vu_dev);
/* vu_deinit() should have called remove_watch() */
vu_fd_watch->cb = cb;
qemu_set_nonblock(fd);
aio_set_fd_handler(server->ioc->ctx, fd, true, kick_handler,
- NULL, NULL, vu_fd_watch);
+ NULL, NULL, NULL, vu_fd_watch);
vu_fd_watch->vu_dev = vu_dev;
vu_fd_watch->pvt = pvt;
}
if (!vu_fd_watch) {
return;
}
- aio_set_fd_handler(server->ioc->ctx, fd, true, NULL, NULL, NULL, NULL);
+ aio_set_fd_handler(server->ioc->ctx, fd, true,
+ NULL, NULL, NULL, NULL, NULL);
QTAILQ_REMOVE(&server->vu_fd_watches, vu_fd_watch, next);
g_free(vu_fd_watch);
QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) {
aio_set_fd_handler(server->ctx, vu_fd_watch->fd, true,
- NULL, NULL, NULL, vu_fd_watch);
+ NULL, NULL, NULL, NULL, vu_fd_watch);
}
qio_channel_shutdown(server->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) {
aio_set_fd_handler(ctx, vu_fd_watch->fd, true, kick_handler, NULL,
- NULL, vu_fd_watch);
+ NULL, NULL, vu_fd_watch);
}
aio_co_schedule(ctx, server->co_trip);
QTAILQ_FOREACH(vu_fd_watch, &server->vu_fd_watches, next) {
aio_set_fd_handler(server->ctx, vu_fd_watch->fd, true,
- NULL, NULL, NULL, vu_fd_watch);
+ NULL, NULL, NULL, NULL, vu_fd_watch);
}
qio_channel_detach_aio_context(server->ioc);